MatriX developer tutorial

About

MatriX

MatriX is a library for the eXtensible Messaging and Presence Protocol (XMPP) for the Microsoft .NET platform. MatriX can be used to build high quality and high performance XMPP Software products. Like XMPP MatriX is designed for easy extendibility.

MatriX is the successor of our successful agsXMPP library. It was rewritten from scratch to include all new features of the latest .NET platforms. Requirements for MatriX are .NET 3.5 and newer.

Install from NuGet

NuGet packages are available for all platforms we offer MatriX for. You can find all packages also here.

MatriX for Windows

PM> Install-Package MatriX

MatriX for WinRT

PM> Install-Package MatriX.WinRT

MatriX for WinRT Universal Apps

PM> Install-Package MatriX.WinRT.Uni

MatriX for Windowns Phone Silverlight

PM> Install-Package MatriX.WindowsPhone

MatriX for Xamarin iOS

PM> Install-Package MatriX.Xamarin.iOS

MatriX for Xamarin Android

PM> Install-Package MatriX.Xamarin.Android

MatriX for Mono

PM> Install-Package MatriX.Mono

Download

If you have not downloaded the MatriX XMPP SDKs yet you can find more information and the download here

Examples

You can find many code examples online here:
https://gitlab.com/matrix-xmpp/samples

The API of all MatriX editions is exactly the same. There are only a few platform specific features which are only available on specific MatriX editions.
When there are no examples available for the platform you target, then just look at the examples for the other platforms. The most examples are available for the Full .NET framework edition.

This document

  • This document is tutorial for XMPP development.
  • You should already be familiar with the XMPP basic before you start working with MatriX and this document.
  • If you are not familiar with the XMPP basics then we suggest to read a book about XMPP first

Logging

When you have a specific problem then in many cases we request a Xml log from you. This Xml log is all incoming and outgoing Xml of the XMPP session. You get this information from the OnReceiveXml and OnSendXml handlers in all XmppStream classes.

xmppClient.OnReceiveXml += new EventHandler<TextEventArgs>(XmppClientOnReceiveXml);
xmppClient.OnSendXml += new EventHandler<TextEventArgs>(XmppClientOnSendXml);

void XmppClientOnSendXml(object sender, TextEventArgs e)
{
	AddDebug("SEND: " + e.Text);
}

void XmppClientOnReceiveXml(object sender, TextEventArgs e)
{
	AddDebug("RECV: " + e.Text);
}

void AddDebug(string debug)
{
	// write the debug data to a file, textbox etc...
}

When you are using BOSH as transport you can get the complete BOSH HTTP payloads like described here:

xmppClient.OnReceiveBody += new EventHandler<BodyEventArgs>(XmppClientOnReceiveBody);
xmppClient.OnSendBody += new EventHandler<BodyEventArgs>(XmppClientOnSendBody);

void XmppClientOnReceiveBody(object sender, BodyEventArgs e)
{
	AddDebug(e.Body.ToString());
}

void XmppClientOnSendBody(object sender, BodyEventArgs e)
{
	AddDebug(e.Body.ToString());
}

void AddDebug(string debug)
{
	// write the debug data to a file, textbox etc...
}

Client connection

Client connections are handled in the XmppClient class in MatriX.

XMPP is a domain based protocol like email. So never use IP adresses for your XMPP domain (server name). Or do you write emails to addresses like user@166.166.166.166?

Open

Connecting to a XMPP server is very simple. All you have to do is set the Username, Password and XMPP server. After that call the Open method to establish the connection.

The Open method is asnychronous. This means when the Open call returns you are not connected, MatriX just started the process to connect to the XMPP server. You have to use the MatriX events line OnLogin, OnRosterEnd etc… to detect a sucessful login. Use the error events to find out if connect failed.

If you want to execute an action right after the Login we suggest to start the action from the OnRosterEnd, OnPresence, OnLogin or OnBind event.

// instantiate a new XmppClient first
var xmppClient = new XmppClient();

private void Connect()
{
	xmppClient.SetUsername("username");
	xmppClient.SetXmppDomain("server.com");
	xmppClient.Password = "secret";

	xmppClient.Status = "I'm chatty";
	xmppClient.Show = Matrix.Xmpp.Show.chat;

	xmppClient.Open();
}

Close

To close a XMPP session properly only Close() must be called. The server sets your presence automatically to unavailable (offline) and notifies all your subscribed contacts with of the unavailable presence.

If you still want to send the unavailable presence on your own, or want to include a status message then call SendUnavailablePresence() followed by Close().

private void CloseSession()
{
	xmppClient.SendUnavailablePresence("Gone home from the office");
	xmppClient.Close();
}

Transport

XMPP sessions are persistent TCP socket connections by default on port 5222. The socket gets connected on the start of the sessions and disconnected when the session (XMPP stream) ends.

SRV-Records

XMPP is using SRV records to find the hostname and port of the XMPP service automatically. MatriX for .NET automatically looks up the SRV records and is using the data to establish the connection. SRV records are required by the XMPP RFCs for XMPP services. If you don’t want to use SRV records you can specify the hostname and port manual and disable the SRV lookups. This is often necessary during development the develoment when you are not testing against live servers.

private void Connect()
{
	xmppClient.SetUsername("username");
	xmppClient.SetXmppDomain("gmail.com");
	xmppClient.Password = "secret";

	// disable SRV lookups and specify hostname manual
	xmppClient.ResolveSrvRecords = false;
	xmppClient.Hostname = "talk.google.com";

	xmppClient.Status = "I'm chatty";
	xmppClient.Show = Matrix.Xmpp.Show.chat;

	xmppClient.Open();
}

MartriX Mobile and MatriX for Silverlight don’t support SRV lookups because this API’s are missing in the underlying .NET Framework editions.

Encryption

XMPP is using TLS to encrypt the XML stream. With the StartTls property of the XMPP client you can control the encryption settings. By default its set to true and automatically enabled when supported by teh target server.

When you open a new connection and StartTls is true MatriX will automatically initialize TLS on this session and encrypt ithe socket stream using TLS when supported by the target server. The OnTls event will fire during TLS negotiation. This event can be used as an indicator whether the session is secured via TLS or not.

SSL/TLS Certificate

TLS is based on certificates. When the stream gets updated to TLS then the server presents a certificate to the client. The .NET Framework and MatriX automatically validates the certificate. When there are any validation errors MatriX will not proceed during TLS negotiation and terminate the stream.

However in some deployments, testing or staging environments you may have an “invalid”, self signed or untrusted TLS certificate and still want to secure the connection using TLS. In this case you can subscribe to the OnValidateCertificate handler and validate the TLS certifcate and tell MatriX whether you trust this certificate or not.

private void xmppClient_OnValidateCertificate(object sender, CertificateEventArgs e)
{
	// validate the certficate here accoding to your rules
	// or present it to your enduser in case of validation problems
	// in this demo code we just trust any certificate
	e.AcceptCertificate = true;
}

The OnValidateCertificate event handler is not available on all MatriX platforms. On Windows phone and WinRT there is no way to validate certificates manual. Its all handled by the device and OS there for security reasons.

BOSH

Bidirectional-streams Over Synchronous HTTP (BOSH) is an extensions to transport XMPP streams over the HTTP protocol. BOSH was developed for constrained clients like web browsers which can’t open sockets any only communicate over the XMPP protocol.

Even Silverlight is able to create sockets it makes sense in many cases to choose the BOSH Transport for Silverlight applications.

  • Easier server setup
  • Non standard ports are often blocked in firewalls while HTTP requests are not blocked
  • You can secure the connection when your BOSH service supports HTTPS. Silverlight has no SslStream, therefore sockets cannot be secured using the TLS protocol.

Many enterprise companies use HTTP proxies as the only connection to the Internet. In this scenario the only way to establish a XMPP connection will be BOSH as well.

The following example show how you choose BOSH as the transport layer and setup a XMPP client stream.

private void Connect()
{
	xmppClient.SetUsername("username");
	xmppClient.SetXmppDomain("ag-software.de");
	xmppClient.Password = "secret";

	xmppClient.Transport = Matrix.Net.Transport.BOSH;
	xmppClient.Uri = new System.Uri("http://ag-software.de:5280/http-bind/");

	xmppClient.Status = "I'm chatty";
	xmppClient.Show = Matrix.Xmpp.Show.chat;

	xmppClient.Open();
}

The BOSH layer in MatriX is based on .NET HTTP WebRequests. Be aware that there is a limitation of 2 concurrent Web requests by default. If you want to open more than one XMPP connection using BOSH, or your application is also using WebRequests you should increase this limit in the ServicePointManager.
See also: http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.defaultconnectionlimit.aspx

Security

Because BOSH is using HTTP as transport layer BOSH connections can be encrypted using HTTPS. All you have to do is use a https uri instead of a http uri when supported by your XMPP server.

When using HTTPS then the .NET Framework validates the SSL certificate automatically. Many servers use self signed SSL certificates in the default configuration. In this case the connect will fail when the certificate or the CA is not installed and trusted in your Windows certificate store. Because the .NET Framework does not allow us to send WebRequests to unsecure servers and cancels them.

For testing with self signed certificates you can also accept all certificates with the following code.

Never use this code in production, because it disables all security for certificate validation.

ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertficate;

private static bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
{
    
	return true;    
}

Silverlight

Also the Silverlight version on MatriX is based on WebRequests. Therefore you should be aware of the Silverlight security access restrictions. You can read more about this in the Silverlight section.

Silverlight has 2 HTTP stacks.

  • Browser HTTP stack
  • Client HTTP stack

MatriX is working well with both of these stacks. They both have their advantages and disadvantages. You can find more information about these 2 stacks here in the MSDN.

If you want to select a HTTP stack in your client you can do this with the following code:

Select the Client HTTP stack

bool httpResult = WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
bool httpsResult = WebRequest.RegisterPrefix("https://", WebRequestCreator.ClientHttp);

Select the Browser HTTP stack

bool httpResult = WebRequest.RegisterPrefix("http://", WebRequestCreator.BrowserHttp);
bool httpsResult = WebRequest.RegisterPrefix("https://", WebRequestCreator.BrowserHttp);

Authentication

A XMPP session can be authenticated with the following mechanisms in MatriX.

  • user and password
  • Single sign on using kerberos
  • Certificates

Single Sign On

Using Kerberos you can authenticate with your Windows logon credentials in MatriX. If you have a working XMPP server which supports Kerberos authentication you don’t have to provide username and password. Just set UseSso to true and login. You can get your full Jid which includes the username in the OnBind event when required.

private void Connect()
{
	xmppClient.OnBind += new System.EventHandler<JidEventArgs>(xmppClient_OnBind);

	xmppClient.Status = "I'm chatty";
	xmppClient.Show = Matrix.Xmpp.Show.chat;

	xmppClient.UseSso = true;

	xmppClient.Open();
}

private void xmppClient_OnBind(object sender, JidEventArgs e)
{
	// get our own Jid in the bind event.
	Debug.WriteLine(e.Jid);
}

Certificates

When you sign on using certificates you have to provide a X509Certificate2 object to the XmppClient object. When you provide a certificate no username ad password is required. When you set a username then this username gets used at the authz id during the SASL EXTERNAL authentication.

When no username is provided and the authentication was successful you can get your full Jid which includes the username from the OnBind event.

The following example uses a certificate file to login. No username is provided.

private void Connect()
{
	xmppClient.OnBind += new System.EventHandler<JidEventArgs>(xmppClient_OnBind);

	xmppClient.Status = "I'm chatty";
	xmppClient.Show = Matrix.Xmpp.Show.chat;

	xmppClient.ClientCertificate = new X509Certificate2(@"C:\certs\xmpp\alex.ag-software.de.p12", "secret");

	xmppClient.Open();
}

private void xmppClient_OnBind(object sender, JidEventArgs e)
{
	// get our own Jid in the bind event.
	Debug.WriteLine(e.Jid);
}

Anonymous login

MatriX handles anonymous logins automatically when supported by the server. You can fetch your server assigned Jid in the OnBind event.

xmppClient.XmppDomain = "myserver.com";		
xmppClient.AnonymousLogin = true;
xmppClient.Open();

private void xmppClient_OnBind(object sender, JidEventArgs e)
{
    var myJid = e.Jid;
}

Select Sasl mechanism

MatriX selects the most secure and appropriate SASL mechanism by default. But sometimes you want to specify the SASL mechanism manual and disable the automatic selection in the library.

You have to subscribe to the OnBeforeSasl event handler set Auto to false and specify the SaslMechanism in the SaslEventArgs.

In the following example we select the PLAIN Sasl mechanism for authentication.

private void xmppClient_OnBeforeSasl(object sender, SaslEventArgs e)
{
	// disable automatic selection
	e.Auto = false;
    e.SaslMechanism = Matrix.Xmpp.Sasl.SaslMechanism.PLAIN;
}

Custom authentication

You can extend MatriX to use a custom SASL mechanism if you want. This is useful when you want to extend the client and server side with your own custom SASL mechanism, or want to use a SASL mechanism which is not already supported in MatriX. All you have to do is to create a custom Sasl processor class which inherits from Matrix.Xmpp.Sasl.Processor.SaslProcessor.
In the OnBeforeSasl event you have to turn the automatic SASL mechanism selection in MatriX off and pass a instance of your custom SASL Processor.

public class CustomSaslProcessor : SaslProcessor
{
    public override void Init(XmppClient con)
    {
        con.Send(new Auth("MY_CUSTOM_SASL_MECHANISM", GetPayload()));
    }

    public override void Parse(Challenge ch)
    {
        // not needed here
    }

    internal string GetMessage()
    {
		return "my_custom_token_or_auth_message";
    }
}		
private void xmppClient_OnBeforeSasl(object sender, Matrix.Xmpp.Sasl.SaslEventArgs e)
{
    e.Auto = false;
    e.CustomSaslProcessor = new CustomSaslProcessor();
}	

Facebook authentication

Facebook currently provides two Sasl mechanisms for authentication.

  • DIGEST-MD5
  • X-FACEBOOK-PLATFORM

By default MatriX will choose DIGEST-MD5 because this is a well known and secure Sasl mechaism.

X-FACEBOOK-PLATFORM can be used to provide better user experience and higher security for the Facebook platform. When your application is already authenticated with the Facebook API you can authenticate to the XMPP server using this existing session. This requires the following data:

  • Api-Key
  • Api-Secret
  • Access-Token

The following example will demonstrate how to use the X-FACEBOOK-PLATFORM authentication.

private const string API_KEY 	= "12345678912345678912345678912345";
private const string API_SECRET = "98765432198765432198765432198765";

private void xmppClient_OnBeforeSasl(object sender, Matrix.Xmpp.Sasl.SaslEventArgs e)
{
    e.Auto = false;
    e.SaslMechanism = Matrix.Xmpp.Sasl.SaslMechanism.X_FACEBOOK_PLATFORM;
    
    e.SaslProperties = new Matrix.Xmpp.Sasl.Processor.Facebook.FacebookProperties
                           {
                               ApiKey = API_KEY,
                               ApiSecret = API_SECRET,
                               AccessToken = "your_access_token_retrieved_from_your_api_session"
                           };
}

Its important that you request the xmpp_login permission in your Facebook App session, otherwise the authentication will fail.

This API has been deprecated with the release of of the Facebook Platform API v2.0.
X_FACEBOOK_PLATFORM will work only with API version 1.0. Instead of X_FACEBOOK_PLATFORM you can also use normal login with username and password.

See also:
 * https://developers.facebook.com/docs/chat
 * https://developers.facebook.com/docs/apps/changelog

Facebook will shutdown the XMPP server with the termination of API 1.0 on April 30th, 2015.

Roster

In XMPP the contact list aka buddy list™ is called roster. When AutoRoster is true (default) MatriX requests the roster automatically on each login. We call this the full roster. If you don’t want to receive the roster on each login set AutoRoster to false. The most existing clients request the roster on each login. Here is a small list where setting AutoRoster to false makes sense.

  • save bandwidth (mobile devices)
  • faster login time
  • you cache the roster in a database and synchronize it manual

Events

The following events get raised while the contact list is received

OnRosterStart  occurs before the first OnRosterItem gets fired. This event is often used to stop GUI updates (BeginUpdate) of the roster control while receiving the complete contact list for faster GUI updates.
OnRosterItem gets raised for each contact in the contact list when the full roster gets received. This event gets also fired when a single contact is added, removed or updated.
OnRosterEnd occurs when all contacts are received. This is also the place where you call EndUpdate when you used BeginUpdate before. When AutoRoster is true this event also indicates that the XMPP session is ready to execute actions in your business logic.

You have to consider all contacts as offline until you get a presence from them with another show type.

OnRosterStart and OnRosterEvent get only fired when the full roster is received, not for single contact updates.

Management

You can manage your contacts (roster) with the RosterManager class.

Add contacts

Add the user with the Jid joe@server.com under the name Joe Jones to the roster.

var rm = new RosterManager(xmppClient);
Jid jid = "joe@server.com";
rm.Add(jid, "Joe Jones");

Add the user with the Jid joe@server.com under the name Joe Jones to the roster and put it in the group Work.

var rm = new RosterManager(xmppClient);
Jid jid = "joe@server.com";
rm.Add(jid, "Joe Jones", "Work");

Add the user with the Jid joe@server.com under the name Joe Jones to the roster and put it in the 2 groups Work and Friends.

var rm = new RosterManager(xmppClient);
Jid jid = "joe@server.com";
rm.Add(jid, "Joe Jones", string[] {"Work", "Friends"});

Add and subscribe

When we add a contact then in the most cases we also want to exchange presence with the contact. To exchange presence we must subscribe to the contacts presence. The following example describes how to add a contact and subscribe to the contacts presence at the same time.

var rm = new RosterManager(xmppClient);
var pm = new PresenceManager(xmppClient);

Jid jid = "joe@server.com";
rm.Add(jid, "Jones", "Friends");
pm.Subscribe(jid);

Update contacts

We have the user joe@server.com with no name on no groups in the contact list. Now we want to add the name “Joe” to the contact and put him in the group “Friends”.

var rm = new RosterManager(xmppClient);
Jid jid = "joe@server.com";
rm.Update(jid, "Joe", "Friends");

When we update a contact we always have the pass the complete contact information, not only a diff of the properties which we want to add, change or upate.

Remove contacts

Remove the contact with the Jid joe@server.com.

var rm = new RosterManager(xmppClient);
Jid jid = "joe@server.com";
rm.Remove(jid);

Subscription

To protect privacy in XMPP presence information gets only exchanged with entities that the user has approved. Approved subscriptions last across sessions and are stored on the XMPP server. You can revoke subscriptions at any time. Depending on the server implementation and security policies of the server subscription can also effect messaging. A server may drop or revoke messages from unsubscribed entities to prevent Spam and protect your privacy.

A roster item can have the following subscriptions.

  • none: You are not interested in the contacts presence, and neither is the contact interested in yours. No presence information is exchanged at all.
  • from: The item is interested in your presence information, but you don’t care about the contacts presence information. must be somebody important! ;)
  • to: You are interested in the contacts presence information, but the contact is not interested in yours.
  • both: You and the contact are interested in each other’s presence information.

The PresenceManager can be used to handle subscription and presence.

Subscribe

To send a subscription request to the user joe@server.com we use the following code:

var pm = new PresenceManager(xmppClient);
Jid jid = "joe@server.com";
pm.Subscribe(jid);

Optional we can add a reason for the subscription request and provide our nickname in this request. The receiving client can display the reason with the request in the GUI, and use the provided nickname when the sender gets added to the roster.

var pm = new PresenceManager(xmppClient);
Jid jid = "alex@server.com";
string reason = "It's Alex from AG-Software";
pm.Subscribe(jid, reason, "Alex");

Process requests

Incoming subscription requests can be handled with the PresenceManger as well. We can either use the OnPresence event of XmppClient or the OnSubscribe event of the PresenceManager. We use the latter in the following example, because its easier.

Accept a subscription request:

private void presenceManager_OnSubscribe(object sender, PresenceEventArgs e)
{
	presenceManager.ApproveSubscriptionRequest(e.Presence.From);
}

Deny a subscription request:

private void presenceManager_OnSubscribe(object sender, PresenceEventArgs e)
{
	presenceManager.DenySubscriptionRequest(e.Presence.From);
}

Presence

XMPP includes the ability for an entity to advertise its network availability or “presence” to other entities. By default MatriX automatically sends the own presence to the server after a successful login.

Change presence

The own presence of a client connection can be changed at any time with the SendPresence method.

Changing the show type to “do not disturb”.

xmppClient.SendPresence(Matrix.Xmpp.Show.dnd);

Changing the show type to “do not disturb” and set a status message.

xmppClient.SendPresence(Matrix.Xmpp.Show.dnd, "In a developer meeting");

Changing the show type to “do not disturb”, set a status message and a priority of 5.

xmppClient.SendPresence(Matrix.Xmpp.Show.dnd, "In a developer meeting", 5);

Building the Presence stanza on your own and send it to the server.

var pres = new Presence
                {
                    Show = Matrix.Xmpp.Show.away,
                    Status = "out of the office",
                    Priority = 25
                };
// you can add additional data to the outgoing presence if you want
xmppClient.Send(pres);

User presences

After the XMPP server has received the own presence of a user is sends out the presences of all subscribed contacts for this user automatically. For each incoming presence the OnPresence handler will fire in XmppClient.

Consider all contacts as offline by default. For offline contacts the server normally sends no presense stanza.

private void xmppClient_OnPresence(object sender, PresenceEventArgs e)
{
	Debug.WriteLine(string.Format("OnPresence from {0}", e.Presence.From));
	Debug.WriteLine(string.Format("Status {0}", e.Presence.Status));
	Debug.WriteLine(string.Format("Show type {0}", e.Presence.Show));
	Debug.WriteLine(string.Format("Priority {0}", e.Presence.Priority));
}

Messaging

After successful login a message can be sent to any entity at any time. The follwing example sends a simple text message of type chat.

var msg = new Matrix.Xmpp.Client.Message
                {
                    Type = MessageType.chat,
                    To = "user1@server.com",
                    Body = "Hello World!"
                };
xmppClient.Send(msg);

Formatted messages

Formatted messages (rich text) in XMPP is using the XEP-0071 XHTML-IM extension.
When formatted messages get sent you should always also include a plain text version of the content for clients which don’t understand the XHTML-IM extension.

var msg = new Matrix.Xmpp.Client.Message
            {
                Type = MessageType.chat,
                To = "user1@server.com",
                Body = "Hello World formatted!",
                XHtml = new Matrix.Xmpp.XHtmlIM.Html
                {
                    Body = new Matrix.Xmpp.XHtmlIM.Body
                        {
                            InnerXHtml = "<p><i>Hello</i> <strong>World</strong> formatted!</p>"
                        }
                }
            };
xmppClient.Send(msg);

Chat states

XMPP supports chat state notifications of conversations partners. Those notifications are events like “the user starts typing”, or “the user closed this chat windows and is not partcipating any,ore at this conversation”. For chat state notificatons XEP-0085: Chat State Notifications gets used.

Here is an example how to send the “user is typing” notification.

var msg = new Matrix.Xmpp.Client.Message
            {
                Type = MessageType.chat,
                To = "user1@server.com/MatriX",
                Chatstate = Matrix.Xmpp.Chatstates.Chatstate.composing
            };	
xmppClient.Send(msg);

Offline messages

Offline messages are a core feature of XMPP. When a message gets sent to a user which is offline at the momen then the server must store it. As soon as the receipient comes online again the server delivers the stored offline messages for this user.

When a server stores a message offline then it adds a timestamp to the message. This timestamp in included when the offline message gets delivered.

In the following example we check if a message has a timestamp and store it in the ts variable.

private void xmppClient_OnMessage(object sender, MessageEventArgs e)
{    
    if (e.Message.XDelay != null)
    {
        // message has a timestamp
        var ts = e.Message.XDelay.Stamp;
    }
}

Incoming messages

Incoming message get received by the OnMessage event handler of the XmppClient instance.

private void xmppClient_OnMessage(object sender, MessageEventArgs e)
{
	Debug.WriteLine(string.Format("OnMessage from {0}", e.Message.From));
	Debug.WriteLine(string.Format("Body {0}", e.Message.Body));
	Debug.WriteLine(string.Format("Type {0}", e.Message.Type));
}

Filters

Filters can be used to get notifications (events) for stanzas you are interested at. Using filters you code is much easier to read, because you get rid of lots of nested if-then-else statements.

Currently there are 3 filter classes:

  • IqFilter
  • MessageFilter
  • PresenceFilter
  • XPathFilter

IqFilter

In XMPP we have a request response mechanism with the Iq stanzas. Its similar to HTTP GET and HTTP PUT.

<!-- Example 1 -->
<!-- Client request -->
<iq from='juliet@example.com/balcony' id='rg1' type='get'>
	<query xmlns='jabber:iq:roster'/>
</iq>

<!-- Server response -->
<iq id='rg1' to='juliet@example.com/chamber' type='result'>
	<query xmlns='jabber:iq:roster' ver='ver7'>
		<item jid='nurse@example.com'/>
		<item jid='romeo@example.net'/>
	</query>
</iq>

The example above is a roster query. The client requests the contact list from the server. The type of the request is get because the client wants to retrieve information. The server responds with a type of result and sets the id to the same id as in the request. In this case ‘rg1’. You can use your own logic with many if-then-else clauses to assign the response to the associated request or the IqFilter class of MatriX.

 When you send a request with the IqFilter you can define a callback which handles the response. The IqFilter handles all processing and raises your callback automatically when the result is received. The logic of the IqFilter is based on the unique id IQ’s.

 

Asynchronous Iq Filter

This example describes the asynchronous usage of the IqFilter. A RosterIq query gets created with the RosterIq class. The RosterIq class automatically assigns a unique id to the request. You pass the query stanza and your defined callback to the SendIq member of the IqFilter. Once Matrix receives the packet the callback gets raised and removed from the IqFilter collection.

// Example 2

private void RequestRoster()
{
	var riq = new RosterIq(IqType.get);
	xmppClient.IqFilter.SendIq(riq, RosterResponse);
}

private void RosterResponse(object sender, IqEventArgs e)
{
	var iq = e.Iq;
    
	if (iq.Type == IqType.result)
	{
		// process result here
	}
    else if (iq.Type == IqType.error)
    {
		// process errors here
    }
}

Synchronous Iq Filter

We normally suggest to use asynchronous patterns. But often developers prefer synchronous programming and in some environments synchronous programming is required. You can also do synchronous requests in the IqFilter with the SendIqSynchronous member, when required with a given timeout.
The default timeout is 5000ms. When no response was received from the server within the given timeout then the return value is null, otherwise the resulting Iq stanza.

the following example requests a user VCard using an synchronous IqFilter with a timeout of 2 seconds.

Because the synchronous calls are using AutoResetEvents they cannot be executed from the MatriX thread. Doing this would block until you reach the timeout and return always null.

// Example 3
private void RequestVcardSynchronous()
{
	var viq = new VcardIq {To = "user@server.com", Type = IqType.get};
	Iq result = xmppClient.IqFilter.SendIqSynchronous(viq, 2000);
	if (result != null)
	{
		// process result here
	}
}

XPathFilter

The XPath filter can be used to filter stanzas using XPath expressions. The XPathFilter is based on the XPathSelectElement extension of System.Xml.XPath Namespace.

Of course you can do filtering based on powerful LinQ statements and the other filter classes in MatriX. But in many cases it’s required to build expressions dynamically on the fly. Also many programmers are familiar with XPath and prefer XPath over LinQ statements. The XPathFilter makes it also much easier to filter big complex stanzas with many nested elements.

The following example describes the usage of the XPathFilter. We setup a filter that matches all presence stanzas from the full Jid ‘user@jabber.org/MatriX’. Because MatriX and XPath in .Net are namespace aware we have to define prefixes in the XmlNamespaceManager. Otherwise we would get no results.

e.Stanza is the complete stanza which matches the expression
e.Result is the result of the XPath expression. This is useful when you are interested only in fragments of the complete stanza.

void XPathFilter()
{  
	xmppClient.XPathFilter.XmlNamespaceManager.AddNamespace("JC", "jabber:client");            
	xmppClient.XPathFilter.Add("/JC:presence[@from='user@jabber.org/MatriX']", XPathCallback);       
}

void XPathCallback(object sender, XPathEventArgs e)
{
	Debug.WriteLine("Stanza: " + e.Stanza);
	Debug.WriteLine("Result: " + e.Result);
}

Here are some other XPath example expressions to filter stanzas:

  • filter all messages with the exact body of ‘Hello MatriX
    /JC:message[JC:body=’Hello MatriX’]
  • filter all presences where the Jid starts with ‘gnauck@’.
    /JC:presence[starts-with(@from ,’gnauck@’)]
  • filter all messages of type=error which have a from and and a to Jid
    /JC:message[@from and @to and @type=’error’]

Registration

Register new account

ItIt is possible to register new accounts over XMPP and with MatriX. But for most services its not recommended to enable automatic account creation over XMPP because it invites spammers which can use the accounts as “throw away” addresses. Its also easier in the most cases to write new accounts directly to the SQL database of the server using scripting language for HTML like ASP, PHP and others.

Below is a code snippet which shows how to register a new account. In the OnRegisterInformation event required the Register object must be populated with the data. Most severs offer simple fields like username, password and email, or extended registration over XData. In our example we use the simple fields for username and password and remove XData from.

var XmppClient = new XmppClient();

xmppClient.OnRegister += new EventHandler<Matrix.EventArgs>(xmppClient_OnRegister);
xmppClient.OnRegisterInformation += new EventHandler<Matrix.Xmpp.Client.RegisterEventArgs>(xmppClient_OnRegisterInformation);
xmppClient.OnRegisterError += new EventHandler<Matrix.Xmpp.Client.IqEventArgs>(xmppClient_OnRegisterError);

xmppClient.SetUsername(txtUsername.Text);
xmppClient.SetXmppDomain(txtServer.Text);
xmppClient.Password = txtPassword.Text;
xmppClient.RegisterNewAccount = true;

xmppClient.Open();

private void xmppClient_OnRegisterInformation(object sender, RegisterEventArgs e)
{
	e.Register.RemoveAll<Data>();

	e.Register.Username = xmppClient.Username;
	e.Register.Password = xmppClient.Password;
}

private void xmppClient_OnRegister(object sender, EventArgs e)
{
	// registration was successful
}

private void xmppClient_OnRegisterError(object sender, IqEventArgs e)
{
	// registration failed.
	xmppClient.Close();
}

Extending MatriX

XMPP was designed for extensibility from ground up. It’s very easy to define your own subsets of XMPP protocol and send them over the wire. Its very easy to add you own custom extensions to MatriX.

In the following example we will create a simple extension for a weather service. We will request weather information like temperature and humidity from a weather service over XMPP. Because this is a request <==> response mechanism we will use the XMPP IQ stanza for this.

Protocol design

in the first step we design the Xml representation of the protocol we are going to use.

<!-- weather request -->
<iq from='user1@server.com/MatriX' to='user2@server.com/MatriX' type='get' id='weather1'>
	<weather xmlns='ag-software:weather'>
		<zip>74080</zip>
	</weather>
</iq>


<!-- weather response -->
<iq from='user2@server.com/MatriX' to='user1@server.com/MatriX' type='result' id='weather1'>
	<weather xmlns='ag-software:weather'>
		<humidity>60</humidity >
		<temperature>24</temperature> 
	</weather>
</iq>

In XMPP protocols are defined by their namespaces. I have chosen the namespace agsoftware:weather for this extensions. And I have chosen the tag name <weather/> for the root element. This makes sense because Xml is verbose and human readable. As first level children of the weather root element I defined

  • <humidity/>, the humidity as Integer returned in the response.
  • <temperature/>, the temperature as Integer returned in the response
  • <zip/>, zip code of the city we request the weather information from. Here in Germany we have numeric zip codes with 5 digits. Because this example should work worldwide and the other 2 tags are already Integer values we choose a String here.

Class design

After the design of the protocol we can start to define our classes now. For this basic example we will create 2 classes Weather.cs and WeatherIq.cs.

using Matrix.Xml;

namespace WeatherExample
{
	public class Weather : XmppXElement
	{
		public Weather() : base("ag-software:weather", "weather")
		{
		}
	
		public int Humidity
		{
			get { return GetTagInt("humidity"); }
			set { SetTag("humidity", value); }
		}

		public int Temperature
		{
			get { return GetTagInt("temperature");}
			set { SetTag("temperature", value);}
		}
	
		public string Zip
		{
			get { return GetTag("zip"); }
			set { SetTag("zip", value); }
		}
	}
}

A parameterless constructor is required for all custom protocol classes derived from XmppXElement. This constructor is called by MatriX when the XML gets deserialized.

The weather.cs class which represents our custom Xml object must derive from XmppXElement. XmppXElement is inherited from System.Xml.Linq.XElement and is the base of all XMPP protocol classes in MatriX.

XmppXElement has many helper functions to serialize and deserialize Xml. We use GetTag, and GetTagInt here which return the value of the given Xml Tag as String or Integer, and we use SetTag to add a tag and set its value as String or Integer. There are many other helper functions which makes it very simple to build complex Xml structures. And of course you can use all members of the powerful System.Xml.LinQ namespace.

After we have written this class we can run a simple test to see if we get the desired results.

private void TestWeather()
{
	var weather = new Weather {Zip = "74080", Humidity = 60, Temperature = 22};
	Debug.WriteLine(weather);
}

The Debug Console shows the following result. This is what we expected.

<weather xmlns="ag-software:weather">
	<zip>74080</zip>
	<humidity>60</humidity>
	<temerature>22</temerature>
</weather>

Now we create the WeatherIq class. This class is only a helper to create WeatherIq’s in a easier fashion. Instead of WeatherIq.cs we could also use a normal Iq object and add the weather childnode with the Add member.

using Matrix.Xmpp.Client;

namespace WeatherExample
{
	public class WeatherIq : Iq
	{
		public WeatherIq()
		{
			GenerateId();
		}
		
		public Weather Weather
		{
			get { return Element<Weather>(); }
			set { Replace(value); }
		}
	}
}

We write again a simple test to build a weather Iq request with the new WeatherIq class.

private void TestWeatherIq()
{
	var wiq = new WeatherIq
	{
		Type = IqType.get,
		To = "user2@server.com/MatriX",
		Weather = new Weather {Zip = "74080"}
	}
	Debug.WriteLine(wiq);
}

And again we get the result we expected. Don’t care about the additional jabber:client namespace declaration. Its there because this is a Xml fragment of the complete Xml stream. When we inject the packet into the Xmpp Xml stream its automatically removed because the jabber:client namespace is already declared in the stream header. MatriX is namespace aware which is the reason why we see this namespace declarations on partial Xml fragments.

<iq id="MX_1" type="get" to="user2@server.com/MatriX" xmlns="jabber:client">
	<weather xmlns="ag-software:weather">
		<zip>74080</zip>
	</weather>
</iq>

Now we can put the pieces together.

  • first we have to register our custom elements in the factory. The function RegisterCustomElements is doing this and should be called once at the very beginning of your app.
  • RequesWeatherInfo makes a weather request to the specified Jid for a given zip code. The jid must be a full jid. The response of the request is received in the WeatherInfoResponse callback.
  • To handle incoming weather requests and respond to them we subscribe to the OnIq handler of the XmppClient class, check if the incoming Iq is a weather request, read the zip code, lookup the weather data build the response and send it back to the requesting XMPP entity.
private static void RegisterCustomElements()
{ 
	Factory.RegisterElement<Weather>("ag-software:weather", "weather");
}

private void RequestWeatherInfo(Jid from, string zip)
{
	var wiq = new WeatherIq
	{
		Type = IqType.get,
		To = from,
		Weather = new Weather { Zip = zip }
	};
	// we pass the zip code as state object to the IqFilter
	xmppClient.IqFilter.SendIq(wiq, WeatherInfoResponse, zip);
}

private void WeatherInfoResponse(object sender, IqEventArgs e)
{
	var iq = e.Iq;

	if (iq.Type == IqType.result)
	{
		var weather = iq.Element<Weather>();
		if (weather != null)
		{
			// read the zip code from the stat again because its not contained in the xml object
			var zip = e.State as string;

			Debug.WriteLine("Humidity: " + weather.Humidity);
			Debug.WriteLine("Temperature: " + weather.Temperature); 
		}
	}
}

private void xmppClient_OnIq(object sender, IqEventArgs e)
{ 
	if (e.Iq.Type == IqType.get && e.Iq.Query is Weather)
	{
		var weather = e.Iq.Query as Weather;
		string zip = weather.Zip;
		// here you should lookup the weather information for the given zip code in a database or webservice
		// we just return some random numbers

		var temp = new Random().Next(-10, 40);
		var humidity = new Random().Next(10, 90);

		var wiq = new WeatherIq
		{
			Id = e.Iq.Id, // Id of the result must match the Id of the request
			To = e.Iq.From,
			Type = IqType.result,
			Weather = new Weather {Temperature = temp, Humidity = humidity}
		};
		// send the response
		xmppClient.Send(wiq);
	}
}

You should be able copy & paste the code snippets above to a new project an test the code with 2 different XMPP users. You can also login with the same user twice using different resources and send the weather packets from one resource to the other.

Don’t forget to register your own protocol classes in the MatriX Factory (Matrix.Xml.Factory).
When your classes are not registered in the Factory MatriX is unable to deserialize the Xml to your custom class.

Other Serializers

In many cases you already have your business logic which you don’t want to rewrite for MatriX. Of course you can use also the standard .NET serializers or most other serializers and deserializers available for .NET.

In the following exmaple we use System.Xml.Serialization. All we have to do is adding the attributes for the serialization if we don’t have them already.

using System.Xml.Serialization;

[XmlRoot(ElementName = "Person", Namespace = "ag-software:person")]
public class Person
{
    [XmlElement(ElementName = "Id")]
    public int Id;

    [XmlElement(ElementName = "FirstName")]
    public string FirstName;
    
    [XmlElement(ElementName = "LastName")]
    public string LastName;
}

The following helper funtion serializes our Person business object to a System.Xml.Linq XElement.

public XElement SerializeToXElement<T>(T source)
{
	var doc = new XDocument();
	var s = new XmlSerializer(typeof(T));
	var writer = doc.CreateWriter();
	s.Serialize(writer, source);
	writer.Close();

	return doc.Root;
}

The following code serializes a Person object and embeds it to XMPP message which can be sent over an existing XMPP stream.

var person = new Person {FirstName = "Alexander", LastName = "Gnauck", Id = 1};
var el = SerializeToXElement(person);

var msg = new Matrix.Xmpp.Client.Message{ Type = Matrix.Xmpp.MessageType.chat};
msg.Add(el);

Deserializing the Person object embedded to the message on the receiver side is very simple as well.

This is a helper function for deserializing a XElement to an object of a given type.

private T Deserialize<T>(XElement el)
{
	using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(el.ToString())))
	{
		var xmlSerializer = new XmlSerializer(typeof(T));
		return (T) xmlSerializer.Deserialize(memoryStream);
	}
}

Now we can deserialize the payload of a XMPP message to a Person object with the following code in a OnMessage event handler.

private void xmppClient_OnMessage(object sender, MessageEventArgs e)
{
	var person = Deserialize<Person>(e.Message.FirstElement);
}

This is only a very basic example. If you want to use this in your production code you should embed the serialized object in a another XML tag as container which transmits the type of your serialized object. Or use a serializer is aware of the types.

Silverlight

There are different ways to setup a XMPP connection with MatriX in Silverlight

  1. direct socket connection on a port within the Silverlight port range
  2. direct connection over a HTTP proxy with the CONNECT command (HTTP tunneling)
  3. BOSH connection

Silverlight has different network security access restrictions for sockets and web requests. If you choose option 1 or 2 then the socket policies apply, if you choose 3 then the web request policy apply because MatriX is using .NET WebRequest classes for the connections. Read more details about the Silverlight network security access restrictions here.

Since Silverlight 4 a connection request using sockets can choose to retrieve the policy file via the HTTP protocol on TCP port 80 instead of the custom TCP protocol on port 943. Web servers are already running in mosts environments. This makes your setup much easier because you don’t need the special policy server on port 943. To enable policy download over HTTP use the following code in the latest MatriX for Silverlight version.

XmppClient.DownloadClientAccessPolicyViaHttp = true;

Silverlight allows socket connections only on ports 4502-4534. For option 1 and 2 this means you have to configure your XMPP or proxy server to listen on a port within this port range, or use other technologies like port forwarding to redirect connections to the XMPP default port 5222. Before a socket can be connected the Silverlight runtime requests the policy on the target site at port 943 and checks the permissions. You have to run a policy server which must be able to serve requests on the same domain as your XMPP or proxy server is hosted.

When using BOSH and crossing domains the Silverlight runtime requests a policy xml file from the root of the BOSH Uri. This means you can connect to other XMPP servers (not under your control) only if they host a policy file on the root of the BOSH Uri which allows you to connect. Or you run your own BOSH server which is able to connect to any XMPP server in the federated network. Punjab is a good choice for the latter.

Example:
when the BOSH Uri is http://example.com:5280/http-bind/ the Silverlight runtime request either a Flash policy file at http://example.com:5280/crossdomain.xml or a Silverlight policy file at http://example.com:5280/clientaccesspolicy.xml when crossing domains.

To avoid cross domain requests on your own server you can either

  • choose a XMPP server which allows you to host your application on the BOSH Uri
  • proxy from your web server to the BOSH server e.g. with mod_proxy in Apache.

Unity 3D

If you develop under Unity with MatriX please read this post first:
http://www.ag-software.net/2014/09/05/unity-development-with-matrix/ 

Glossary

BOSH

A technique for simulatingthe bidrectional asynchronous TCP connection with two long-lived HTTP requests. BOSH is used in XMPP to use HTTP as trasport layer instead of TCP.

C2S

Client to server connection. XMPP client initiate C2S connections to a XMPP server.

Jid

The Jabber Identifier (Jid) is the address of an entity in the XMPP network. Here are some examples of Jids:

  • user@domain.com
  • user@domain.com/Phone
  • domain.com

The Jid consists of 3 parts

  • node
  • domain
  • resource
alice @ example.com / Home
node   domain   resource

Bare Jid

Jid without resource identifier, eg. user@domain.com.

Full Jid

Jid including the resource, eg. user@domain.com/Home.

Roster

The contact list (buddy list ™) is called roster in XMPP.

Presence

A stanza which contains the presence information (online/offline status) of a XMPP idendity. The XMPP server automatically distributes this information to all subscribed contacts.

Directed Presence

A directed presence is a presence which is sent manual from idendity to another idendity, and not automatically distributed by the server.  In MUC for example we need directed presence when we join or leave a room.

Priority

The priority is a number that specifies the priority level of the resource. The value must be an integer between -128 and +127. The priority is important when messages gets sent to a bare Jid. The highest priority will receive the message in this case. A resource with a negative priority will never receive messages sent to a bare Jid.

S2S

Server to server connection. XMPP server communicate to each other over S2S connections.

XEP

XEP stands for XMPP Extension Protocols. The XMPP core protocols were designed to be extenible. The XMPP Standards Foundation (XSF) maintains all the XMPP Extension Protocols.

XSF

XSF stands for XMPP Standards Foundation, the standards organization that defines the XEPs and promotes the XMPP protocol.