26. September 2010

10 years ago we spent weeks to develop a website chat. We implemented a chat server in C++, a PHP library, which talked to the chat server and JavaScript streaming in an iframe. Today it is much simpler.

Today we can use XMPP and BOSH and let the web page talk to my GTalk client, which runs all the time anyway.

For XMPP to work we need XMPP users. I prefer to run ejabberd with MySQL storage, because MySQL is the easiest way for me to add users and to manage the user list programatically. But the mnesia database also works.

Here is the config to use MySQL with ejabberd (to be added to ejabberd.cfg):

Download Strophe and jQuery (or use the CDN version http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js). Add references to the HTML-head:

<script type="text/javascript" src="jquery-1.4.2.min.js"></script>

<script type="text/javascript" src='strophe.min.js'></script>

4. Now comes the real fun: coding

We basically create a BOSH connection from Javascript to ejabberd through apache/mod_proxy:

var conn = new Strophe.Connection('/xmpp-httpbind');

Create an XMPP user in MySQL (I am using phpmyadmin) and connect with this user:

conn.connect('test@wolfspelz.de', 'secret', OnConnectionStatus);

The OnConnectionStatus function may look like:

function OnConnectionStatus(nStatus)

{

if (nStatus == Strophe.Status.CONNECTING) {

} else if (nStatus == Strophe.Status.CONNFAIL) {

} else if (nStatus == Strophe.Status.DISCONNECTING) {

} else if (nStatus == Strophe.Status.DISCONNECTED) {

} else if (nStatus == Strophe.Status.CONNECTED) {

OnConnected();

}

}

When the connection is established, register message handlers and send our own presence:

function OnConnected()

{

conn.addHandler(OnPresenceStanza, null, "presence");

conn.addHandler(OnMessageStanza, null, "message");

conn.send($pres());

}

BTW: handlers should always return "true". Otherwise they are removed from the handler list. A message handler may look like:

function OnMessageStanza(stanza)

{

var sFrom = $(stanza).attr('from');

var sType = $(stanza).attr('type');

var sBareJid = Strophe.getBareJidFromJid(sFrom);

var sBody = $(stanza).find('body').text();

// do something, e.g. show sBody with jQuery

return true;

}

A presence handler may be:

function OnPresenceStanza(stanza)

{

var sFrom = $(stanza).attr('from');

var sBareJid = Strophe.getBareJidFromJid(sFrom);

var sType = $(stanza).attr('type');

var sShow = $(stanza).find('show').text();

// do something, e.g. show status icon with jQuery

return true;

}

The connection should be closed when the page unloads. Unfortunately strophe.js (at least up to version 1.0.2) disconnects asynchronously, which does not work when the page is destroyed. After some time the XMPP server will notice, that the page disappeared and will close the connection. But if you do not want to wait, then we have to force strophe to close immediately.

There is a patch, which allows for synchronous connection closing. The patch must be applied to the strophe.js file:

Of course, all these functions and callbacks should be prototype based and bind the instance to the closure. We should also use a model-view architecture and handle the protocol stuff in the model, while notifying the view of really important events.

Here are the files:

contact.html - the "driver" which loads everything and produces the GUI