XMPP and WCF .Net – Connecting a XMPP client to Openfire

With Openfire installed (http://www.johandekoning.nl/index.php/2009/11/30/xmpp-and-wcf-net-installation-of-openfire/) it is time to create some users and connect with a XMPP client to the server.

User management

Because of the simple installation, users created on the Openfire server are stored inside the embedded database. To add new users, make sure that the server runs and open the administration console (default address http://localhost:9090).

Note: I already mentioned this in my previous blog. Within my Vista environment I had to make sure that I start the Openfire server as administrator

Within the Administration Console, you will find the Users/Groups section at the top of the page. When you open this section you will get an overview of the users created on the Openfire server

User Summary

User Summary

Click on the Create New User option on the left, to create a new user. The Create User page needs some information for the new user. Provide this information and click create user to add the user. Or choose Create & Create another if you want to create another user after this user account is added.

Create User

Create User

I create two users which are called user1 and user2. I gave the the same password as their username. The overview shows the following user list.

User Summary (users created)

User Summary (users created)

One of the core extensions of the XMPP protocol is the ability to have a user list which is stored on the server (which is called a Roster). Within the Administration Console you can manage those lists, to make it possible that user1 has user2 as a contact and visa versa. This is off course also possible from within a XMPP client where you can add new users.

To manage a contacts list (roster) for a user, click on the username (of the user you want to manage) from the overview. You will get the User Properties screen, with some basic information.

User properties

User properties

At the left you will find a Roster option. When you click that option, you get an overview of the roster items. These items can be other users, or groups of users. Click the Add New Item (at the right of the screen) to add user 2 to the roster items of user 1.

Add roster item

Add roster item

When added, the user roster overview will show user 2. Perform the same action for user 2 but this time add user 1 to the roster list.

User Roster

User Roster

When both users are on both list, the user configuration part is finished. I know it is very basic, but this is jus for development. When can now use a XMPP client to make a connection with our server.

Using Psi (XMPP Client)

There are a lot of XMPP clients available on the Internet. I use Psi because it has a nice XML logging feature, to make the XMPP stream visible. I can use this sample stream for development of a XMPP client in .Net without the use of WCF. You can download the Psi client at http://psi-im.org/download/

When you start the client for the first time, it will show you a message that you need to set up an account. Click on the Use existing account option, because our test accounts are already available on the server.

Account setup

Account setup

On the first tab (Account) fill in the Jabber ID (JID) and password. The jabber ids for our users are user1@localfire and user2@localfire . The password are user1 and user2.

Account Tab

Account Tab

At the connection tab, we need to specify our server location. This is localhost when it runs on the same machine where you installed this client. For testing purposes we will not use an encrypt connection (Never).

Connection tab

Connection tab

Click save to store the account information. The main screen of Psi will be visible whereby the user status is set to Offline.

Psi main screen

Psi main screen

Before we connect to our Openfire server, I want to enable the XML console to get an idea of the stream which is send between client and server. Right click on the first item of the list (openfire) and select Open XML Console… Within the XML Console, mark the enable checbox at the bottem left. To get the client connected, select from the dropdownlist at the bottom of the main screen, the Online status. The client will connect to the server and the stream will be made visible in the XML Console dialog.

XML Console

XML Console

The XMPP stream

I connected another client to the server as user 2. Between both users i send a message with the text “Hello” (User 1 -> User 2) and “Goodbye” (User 2 -> User 1). The presence of user 1 will be set to Offline and the application will be closed. Below you find the captured XMPP stream.

What’s next?

This blog post explained how to set up the connecting between a XMPP client and server. The captured stream will be used for further analysis. The next step will be the development of a simple XMPP client which will echo back received messages in reverse order. This client will not be written with Windows Communication Foundation to get some feeling how a .Net client with the TcpClient class will work.

If you have questions after reading this post, or have other feedback? Please send in a comment.

<!--client-->
<?xml version="1.0"?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams" version="1.0" xmlns="jabber:client" to="localfire" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" >

<!--server-->
<?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="localfire" id="c035b19c" xml:lang="en" version="1.0">

<stream:features>
<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
<mechanism>ANONYMOUS</mechanism>
<mechanism>CRAM-MD5</mechanism>
</mechanisms>
<compression xmlns="http://jabber.org/features/compress">
<method>zlib</method>
</compression>
<auth xmlns="http://jabber.org/features/iq-auth"/>
<register xmlns="http://jabber.org/features/iq-register"/>
</stream:features>

<!--client-->
<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="DIGEST-MD5" />

<!--server-->
<challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cmVhbG09ImxvY2FsZmlyZSIsbm9uY2U9IitaQVRkeUZNeWJjV1ZveHJHTXNIV2dxNVR1QWhNUkFoQ08wVWlp
VzciLHFvcD0iYXV0aCIsY2hhcnNldD11dGYtOCxhbGdvcml0aG09bWQ1LXNlc3M=</challenge>

<!--client-->
<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">dXNlcm5hbWU9InVzZXIxIixyZWFsbT0ibG9jYWxmaXJlIixub25jZT0iK1pBVGR5Rk15YmNXVm94ckdNc0hXZ3E1
VHVBaE1SQWhDTzBVaWlXNyIsY25vbmNlPSJhM0dMY21ra3VFUjEwaVBTMlJzQnpRZWlBY2pzSnIzNVNyTzRkTFNzM
2FnPSIsbmM9MDAwMDAwMDEsZGlnZXN0LXVyaT0ieG1wcC9sb2NhbGZpcmUiLHFvcD1hdXRoLHJlc3BvbnNlPWM1N
TlhMjBhMmFkZGVjYmJjOTFkZTZkNDYwODYyODU5LGNoYXJzZXQ9dXRmLTg=</response>

<!--server-->
<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cnNwYXV0aD04NjMxMTIzNDZlM2MxOTUxMGZlNzFmNTA5N2E0MWViMg==</success>

<!--client-->
<?xml version="1.0"?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams" version="1.0" xmlns="jabber:client" to="localfire" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" >

<!--server-->
<?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="localfire" id="c035b19c" xml:lang="en" version="1.0">
<stream:features>
<compression xmlns="http://jabber.org/features/compress">
<method>zlib</method>
</compression>
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
</stream:features>

<!--client-->
<iq type="set" id="bind_1" >
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<resource>Home</resource>
</bind>
</iq>

<!--server-->
<iq xmlns="jabber:client" type="result" id="bind_1" to="localfire/c035b19c" >
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<jid>user1@localfire/Home</jid>
</bind>
</iq>

<!--client-->
<iq type="set" id="aab7a" >
<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
</iq>

<!--server-->
<iq type="result" id="aab7a" to="user1@localfire/Home" >
<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
</iq>

<!--client-->
<iq type="get" id="aab8a" >
<query xmlns="jabber:iq:roster"/>
</iq>

<!--server-->
<iq type="result" id="aab8a" to="user1@localfire/Home" >
<query xmlns="jabber:iq:roster">
<item subscription="both" jid="user2@localfire" />
</query>
</iq>

<!--client-->
<presence>
<priority>5</priority>
<c xmlns="http://jabber.org/protocol/caps" node="http://psi-im.org/caps" ver="0.13-dev-rev2" ext="ca cs ep-notify html" />
</presence>

<iq type="get" to="user1@localfire/Home" id="aabaa" >
<query xmlns="http://jabber.org/protocol/disco#info" node="http://psi-im.org/caps#ca" />
</iq>

<iq type="get" id="aabba" >
<query xmlns="jabber:iq:private">
<storage xmlns="storage:bookmarks"/>
</query>
</iq>

<iq type="get" to="user1@localfire" id="aabca" >
<vCard xmlns="vcard-temp" version="2.0" prodid="-//HandGen//NONSGML vGen v1.0//EN" />
</iq>

<iq type="get" to="localfire" id="aabda" >
<query xmlns="http://jabber.org/protocol/disco#info"/>
</iq>

<!--server-->
<presence from="user2@localfire/Work" to="user1@localfire/Home" >
<c xmlns="http://jabber.org/protocol/caps" node="http://exodus.jabberstudio.org/caps" ext="xhtml-im" ver="0.10.0.0" />
<priority>1</priority>
</presence>

<!--client-->
<iq type="get" to="user2@localfire/Work" id="aabea" >
<query xmlns="http://jabber.org/protocol/disco#info" node="http://exodus.jabberstudio.org/caps#0.10.0.0" />
</iq>

<iq type="get" to="user2@localfire/Work" id="aabfa" >
<query xmlns="http://jabber.org/protocol/disco#info" node="http://exodus.jabberstudio.org/caps#xhtml-im" />
</iq>

<!--server-->
<iq from="user1@localfire/Home" type="get" to="user1@localfire/Home" id="aabaa" >
<query xmlns="http://jabber.org/protocol/disco#info" node="http://psi-im.org/caps#ca" />
</iq>

<!--client-->
<iq type="result" to="user1@localfire/Home" id="aabaa" >
<query xmlns="http://jabber.org/protocol/disco#info" node="http://psi-im.org/caps#ca" >
<identity category="client" type="pc" name="Psi" />
<feature var="urn:xmpp:jingle:1" />
<feature var="urn:xmpp:jingle:transports:ice-udp:1" />
<feature var="urn:xmpp:jingle:apps:rtp:1" />
<feature var="urn:xmpp:jingle:apps:rtp:audio" />
</query>
</iq>

<!--server-->
<iq type="result" id="aabba" to="user1@localfire/Home" >
<query xmlns="jabber:iq:private">

<storage xmlns="storage:bookmarks"/>
</query>
</iq>

<iq from="user1@localfire" type="result" id="aabca" to="user1@localfire/Home" >
<vCard xmlns="vcard-temp" version="2.0" prodid="-//HandGen//NONSGML vGen v1.0//EN" >
<FN>User 1</FN>
</vCard>
</iq>

<iq from="localfire" type="result" id="aabda" to="user1@localfire/Home" >
<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="server" type="im" name="Openfire Server" />
<identity category="pubsub" type="pep" />
<feature var="http://jabber.org/protocol/pubsub#manage-subscriptions" />
<feature var="http://jabber.org/protocol/pubsub#modify-affiliations" />
<feature var="http://jabber.org/protocol/pubsub#retrieve-default" />
<feature var="http://jabber.org/protocol/pubsub#collections" />
<feature var="jabber:iq:private" />
<feature var="http://jabber.org/protocol/disco#items" />
<feature var="vcard-temp" />
<feature var="http://jabber.org/protocol/pubsub#publish" />
<feature var="http://jabber.org/protocol/pubsub#subscribe" />
<feature var="http://jabber.org/protocol/pubsub#retract-items" />
<feature var="http://jabber.org/protocol/offline" />
<feature var="http://jabber.org/protocol/pubsub#meta-data" />
<feature var="jabber:iq:register" />
<feature var="http://jabber.org/protocol/pubsub#retrieve-subscriptions" />
<feature var="http://jabber.org/protocol/pubsub#default_access_model_open" />
<feature var="jabber:iq:roster" />
<feature var="http://jabber.org/protocol/pubsub#config-node" />
<feature var="http://jabber.org/protocol/address" />
<feature var="http://jabber.org/protocol/pubsub#publisher-affiliation" />
<feature var="http://jabber.org/protocol/pubsub#item-ids" />
<feature var="http://jabber.org/protocol/pubsub#instant-nodes" />
<feature var="http://jabber.org/protocol/commands" />
<feature var="http://jabber.org/protocol/pubsub#multi-subscribe" />
<feature var="http://jabber.org/protocol/pubsub#outcast-affiliation" />
<feature var="http://jabber.org/protocol/pubsub#get-pending" />
<feature var="google:jingleinfo" />
<feature var="jabber:iq:privacy" />
<feature var="http://jabber.org/protocol/pubsub#subscription-options" />
<feature var="jabber:iq:last" />
<feature var="http://jabber.org/protocol/pubsub#create-and-configure" />
<feature var="urn:xmpp:ping" />
<feature var="http://jabber.org/protocol/pubsub#retrieve-items" />
<feature var="jabber:iq:time" />
<feature var="http://jabber.org/protocol/pubsub#create-nodes" />
<feature var="http://jabber.org/protocol/pubsub#persistent-items" />
<feature var="jabber:iq:version" />
<feature var="http://jabber.org/protocol/pubsub#presence-notifications" />
<feature var="http://jabber.org/protocol/pubsub" />
<feature var="http://jabber.org/protocol/pubsub#retrieve-affiliations" />
<feature var="http://jabber.org/protocol/pubsub#delete-nodes" />
<feature var="http://jabber.org/protocol/pubsub#purge-nodes" />
<feature var="http://jabber.org/protocol/disco#info" />
<feature var="http://jabber.org/protocol/rsm" />
</query>
</iq>

<iq from="user2@localfire/Work" type="result" id="aabea" to="user1@localfire/Home" >
<query xmlns="http://jabber.org/protocol/disco#info" node="http://exodus.jabberstudio.org/caps#0.10.0.0" >
<feature var="jabber:iq:agents" />
<feature var="jabber:iq:oob" />
<feature var="jabber:iq:browse" />
<feature var="jabber:iq:time" />
<feature var="jabber:iq:version" />
<feature var="jabber:iq:last" />
<feature var="http://jabber.org/protocol/disco#items" />
<feature var="http://jabber.org/protocol/disco#info" />
<feature var="storage:bookmarks" />
<feature var="jabber:x:data" />
<feature var="jabber:x:conference" />
<feature var="jabber:x:event" />
<feature var="http://jabber.org/protocol/muc" />
<feature var="http://jabber.org/protocol/muc#user" />
<feature var="http://jabber.org/protocol/muc#owner" />
<feature var="http://jabber.org/protocol/si" />
<feature var="http://jabber.org/protocol/si/profile/file-transfer" />
<feature var="http://jabber.org/protocol/bytestreams" />
</query>
</iq>

<iq from="user2@localfire/Work" type="result" id="aabfa" to="user1@localfire/Home" >
<query xmlns="http://jabber.org/protocol/disco#info" node="http://exodus.jabberstudio.org/caps#xhtml-im" >
<feature var="http://jabber.org/protocol/xhtml-im" />
</query>
</iq>

<iq from="user1@localfire/Home" type="result" to="user1@localfire/Home" id="aabaa" >
<query xmlns="http://jabber.org/protocol/disco#info" node="http://psi-im.org/caps#ca" >
<identity category="client" type="pc" name="Psi" />
<feature var="urn:xmpp:jingle:1" />
<feature var="urn:xmpp:jingle:transports:ice-udp:1" />
<feature var="urn:xmpp:jingle:apps:rtp:1" />
<feature var="urn:xmpp:jingle:apps:rtp:audio" />
</query>
</iq>

<!--client-->
<message type="chat" to="user2@localfire" id="aac0a" >
<body>Hello</body>
<active xmlns="http://jabber.org/protocol/chatstates"/>
</message>

<!--server-->
<message from="user2@localfire/Work" type="chat" id="jcl_13" to="user1@localfire/Home" >
<body>Goodbye</body>
<x xmlns="jabber:x:event">
<composing/>
</x>
<html xmlns="http://jabber.org/protocol/xhtml-im">
<body xmlns="http://www.w3.org/1999/xhtml">
<p style="font-size:small;font-family:Arial;color:#000000" >Goodbye</p>
</body>
</html>
</message>

<!--client-->
<presence type="unavailable" >
<status>Logged out</status>
</presence>

</stream:stream>

<!--server-->
</stream:stream>

Read more from the Uncategorized category. If you would like to leave a comment, click here: Comment. or stay up to date with this post via RSS, or you can Trackback from your site.

XMPP and WCF .Net – Installation of OpenFire

In my previous blogpost, I talked about my goal to make an XMPP implementation within .Net based on Windows Communication Foundation. Before I can start, I had to find out how a normal XMPP stream would be send/retrieved between client and server.

In this blogitem, I will explain the steps I took to install the OpenFire server. http://www.igniterealtime.org/projects/openfire/index.jsp Within my other blog I used a seperate environment which is mostly based on Ubuntu. This time I install the OpenFire server within Windows Vista. The reason for this is that it is also the environment of my .Net development tools.

Installation wizard

First download the installation file of OpenFire from the website. I use Openfire 3.6.4 http://www.igniterealtime.org/downloads/index.jsp

The OpenFire website

The OpenFire website

Download page

Download page

The installation wizard is quite straight forward and more like next-next-finish.

Language selection

Language selection

Welcome step of the wizard

Welcome step of the wizard

License Agreement

License Agreement

Installation location

Installation location

Start menu shortcut

Start menu shortcut

Progress of installation

Progress of installation

Completing wizard step

Completing wizard step

On the last step, mark the Run Openfire option (which is marked by default) to launch the Openfire server. This makes it possible to continue the installation from the Admin Console (which runs inside the browser).

Openfire server status

Openfire server status

Admin console installation/configuration

To finalize the installation, some setup steps will be shown within the Admin Console. Select the prefered language in the first step

Language selection

Language selection

You can keep the server settings values the default. Because I will only use the Openfire server locally (or within my own netwerk) it is not important that the right domain is set. The clients will connect to localhost or the ipaddress of the computer.

Server settings

Server settings

Because I want to keep the installation as simple as possible, I choose for an Embedded database. The performance penalty with the embedded database is for test purposes not an issue.

Database settings

Database settings

I will create the test users from within the Admin console. This means that I don’t need Active Directory/LDAP or Clearspace integration. I choose for the Default option to store users inside the server database.

Profile settings

Profile settings

To access the admin console after these setup steps, it is important to setup an Administration account. The username of this account will be “admin”

Administrator account

Administrator account

This completes the openfire setup process. You can now login to the real admin console

Note: I had some trouble to log in to the Admin Console on my Vista enviroment. The admin user was not valid. I found out that I had to restart the Openfire server by stopping the server, quit the status dialog and start the Openfire server again for the start menu. It is important to start the Openfire server as administrator (right click on the shortcut and select Run as administrator) otherwise you are still not able to login.

Admin console login

Admin console login

Server information page

Server information page

What’s next?

As a mentioned the installation of Openfire, with the (default) settings I chose, is easy and straight forward. The next step will be to configure some users and to connect a XMPP client to the server. In the meantime I really like to have you feedback on this blogpost (did you like the images to explain the installation) or on my other blogposts.

Read more from the Uncategorized category. If you would like to leave a comment, click here: 1 Comment. or stay up to date with this post via RSS, or you can Trackback from your site.

XMPP and WCF .Net – Introduction

Hummm… XMPP and WCF? Is this not a blog page about Android or other Google related stuff. That’s partly true but as a Microsoft .Net developer I am more interested in combining the best of both world. I like Google and Microsoft products. I really like the open source vision of Google. But when it comes to development tools I prefer using Microsoft.

So that’s me, living between both worlds. Using the best of both worlds to develop good implementations. And that is where this new blog post are about.

Some time ago Google launched Google Wave presentation (http://wave.google.com/help/wave/about.html) When I watched the presentation, I was (and still am) impressed.

At my previous job, we developed a real time location aware application. A huge amount of data was collected by the location systems, was processed by the server and was made visible on the clients. The made visible part is a tricky one, because we wanted to make use of protocols which where cross platform like SOAP. The result was a polling mechanism which sends requests on interval basis to the server to get new data.

We knew that this was not the best implementation. At that time (almost 3 years ago) we were also thicking about as a possible solution. XMPP stands for Extensible Messaging and Presence Protocol (http://xmpp.org/). A protocol which was developed already in 1999 and is based on XML. I think that the popularity of this protocol was getting a boost when Google launched their Google Talk client in augustus 2005.

I found a presentation on the Web about XMPP (http://www.slideshare.net/kellan/beyond-rest) which has a nice slide (shown in the image below). This reminds me of an episode of the Smurfs (http://en.wikipedia.org/wiki/The_Smurfs) where the Smurfs are on a long trip.

Brainy Smurf: Is it much further, Papa Smurf?
Papa Smurf: Not much further my little smurfs.
Jokey Smurf: (different location) Is it much further, Papa Smurf?
Papa Smurf: Not much further my little smurfs.
Grouchy Smurf: (third location) Is it much further, Papa Smurf?
Papa Smurf: (exasperated) Yes…it is!

Are we there yet?

Are we there yet?

So when all Smurfs are requesting data from Papa Smurf, Papa Smurf becomes overloaded. A better solution would be a Publish Subscribe implementation  (http://en.wikipedia.org/wiki/Publish/subscribe). XMPP has such extension: http://xmpp.org/extensions/xep-0060.html. This way the clients (little smurfs) don’t become bored (they can do other things) and Papa Smurfs don’t get distracted or even overloaded.

Tell me when I get there

Tell me when I get there

The protocol of Google Wave is based on this model. The protocol makes use of the XMPP protocol together with some extensions. Installation document of the Google Wave Federation Prototype server can be found at http://code.google.com/p/wave-protocol/wiki/Installation Of course Google is using Java or Phyton for the example clients, and that is exactly the part where my love for Microsoft fits in. I am really interested in building a Google Wave protocol client within .Net

Because XMPP is involved, the first step would be to develop a XMPP client in .Net. There are some XMPP libraries available for .Net but those don’t have a flexible extension model. And when looking at Windows Communication Foundation, there are already some parts available for setting up the connection and send/retrieve messages. Developing an XMPP library based on the WCF technology is the first goal of these new blog posts.

What’s next?

Before looking at a WCF implemtation, it is good to know how XMPP works. Therefore I will develop a sample client which makes use of the .Net TcpClient to initiate and send/retrieve messages (no WCF involved). To test this client I will install a XMPP server called OpenFire (http://www.igniterealtime.org/projects/openfire/index.jsp). Check this blog often for new updates… and remember Papa Smurf, we are not there yet!

BTW I love to retrieve comments on this (or my other blogposts). Your feedback is really important!

Read more from the Uncategorized category. If you would like to leave a comment, click here: 1 Comment. or stay up to date with this post via RSS, or you can Trackback from your site.