Apr 20, 2012

Quick tutorial to get started with the MatriX XMPP SDK

A couple of weeks ago I started playing around with Microsoft's Visual Studio and C#. I hastily decided I liked it so I started looking for stuff I could write with it. I've always wanted a better Jabber client so I decided I should try writing one. Looking around I found a few libraries...
Not as many as I would have expected. I presume there's a good reason for that... Among all the libraries, I found MatriX to be the only .NET XMPP SDK that supports writing all three major components of XMPP: clients, servers and server components. So far, I have only tried writing clients which are the simplest and MatriX provides the most support for. Even for this simple task, some people still have problems getting started with XMPP and MatriX, even problems connecting to their servers. I haven't had such problems so I'll get to the point and provide everyone with my simple way of  writing a client that connects to my own server running on the localhost.
using Matrix;

namespace MyMessenger
{
    public partial class MainWindow : Window
    {
        private Matrix.Xmpp.Client.XmppClient xmppClient;
        private Matrix.Xmpp.Client.RosterManager rosterManager;
        private Matrix.Xmpp.Client.PresenceManager presenceManager;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Log(string message)
        {
            LogBox.Text += "\n" + message + "\n";
            // Or instead just print the message on the Console if you want
        }

        private void InstantiateMatriXComponents()
        {
            // We're just using the XmppClient component in this example
            xmppClient = new Matrix.Xmpp.Client.XmppClient();
            Log("MatriX COMPONENTS HAVE BEEN INSTANTIATED");
        }

        private void RegisterDefaultEventHandlers()
        {
            xmppClient.OnRosterStart += new EventHandler(xmppClient_OnRosterStart);
            xmppClient.OnRosterEnd += new EventHandler(xmppClient_OnRosterEnd);
            xmppClient.OnRosterItem += new EventHandler(xmppClient_OnRosterItem);

            xmppClient.OnMessage += new EventHandler(xmppClient_OnMessage);
            xmppClient.OnPresence += new EventHandler(xmppClient_OnPresence);
            xmppClient.OnIq += new EventHandler(xmppClient_OnIq);

            xmppClient.OnError += new EventHandler(xmppClient_OnError);
            xmppClient.OnAuthError += new EventHandler(xmppClient_OnAuthError);
            xmppClient.OnLogin += new EventHandler(xmppClient_OnLogin);

            xmppClient.OnBind += new EventHandler(xmppClient_OnBind);
            xmppClient.OnClose += new EventHandler(xmppClient_OnClose);

            xmppClient.OnReceiveXml += new EventHandler(xmppClient_OnReceiveXml);
            xmppClient.OnSendXml += new EventHandler(xmppClient_OnSendXml);

            Log("DEFAULT EVENT HANDLERS HAVE BEEN REGISTERED");
        }

        private void OnConnectCommand(object sender, RoutedEventArgs e)
        {
            xmppClient.SetUsername("user");
            xmppClient.SetXmppDomain("domain.com"); // don't use an IP adress as the domain!
            xmppClient.Password = "secret password";
            
            if (your_server_is_not_at_the_same_host_as_the_domain)
            {
                xmppClient.ResolveSrvRecords = false;
                xmppClient.Hostname = "custom host";
            }
            else
            {
                xmppClient.ResolveSrvRecords = true;
                xmppClient.Hostname = "";
            }

            xmppClient.Status = "your status message";
            xmppClient.Resource = "Resource" + new Random().Next().ToString();
            xmppClient.Show = Matrix.Xmpp.Show.chat; // set initial presence type to available for chat

            xmppClient.Open(); // asynchrounous call, this returns immediately
            e.Handled = true;

            Log("MANUAL CONNECT COMMAND HAS BEEN HANDLED");
        }

        private void OnDisconnectCommand(object sender, RoutedEventArgs e)
        {
            xmppClient.Close();
            e.Handled = true;
            Log("MANUAL DISCONNECT COMMAND HAS BEEN HANDLED");
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Do this once the main window has been loaded:
            InstantiateMatriXComponents();
            RegisterDefaultEventHandlers();
            // you can call this now if you want to connect once the window has been loaded
            // otherwise call this from a buton click or something:
            //OnConnectCommand();
            e.Handled = true;
            Log("WINDOW LOADED HAS BEEN HANDLED");
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            // Do this if we want to close the main window
            xmppClient.Close();
            Log("WINDOW CLOSING HAS BEEN HANDLED");
        }

        // Here come the event handlers for our XmppClient
        void xmppClient_OnIq(object sender, Matrix.Xmpp.Client.IqEventArgs e)
        {
            Log("NEW IQ");
            e.Handled = true;
        }

        void xmppClient_OnPresence(object sender, Matrix.Xmpp.Client.PresenceEventArgs e)
        {
            Log("NEW PRESENCE");
        }

        void xmppClient_OnError(object sender, Matrix.ExceptionEventArgs e)
        {
            // Show a message box when exceptional errors are encountered
            Log("*******************   ERROR   *******************\n" + e.Exception.Message);
            MessageBox.Show(this, e.Exception.Message, "Eroare");
        }

        void xmppClient_OnSendXml(object sender, Matrix.TextEventArgs e)
        {
            Log("Client says:\n" + e.Text);
        }
        void xmppClient_OnReceiveXml(object sender, Matrix.TextEventArgs e)
        {
            Log("Server says:\n" + e.Text);
        }

        void xmppClient_OnClose(object sender, Matrix.EventArgs e)
        {
            Log("XML STREAM CLOSED");
        }

        void xmppClient_OnBind(object sender, Matrix.JidEventArgs e)
        {
            // this practically means we can start sending messages
            // and do other stuff because we now have a resource assigned by the server
            // we connected to.
            Log("BOUND RESOURCE: " + e.Jid.Resource);
        }

        void xmppClient_OnLogin(object sender, Matrix.EventArgs e)
        {
            Log("LOGGED IN");
            // means a successful authentication with the server
        }

        void xmppClient_OnAuthError(object sender, Matrix.Xmpp.Sasl.SaslEventArgs e)
        {
            Log("AUTH ERROR: " + e.Error.Text);
        }

        void xmppClient_OnMessage(object sender, Matrix.Xmpp.Client.MessageEventArgs e)
        {
            Log("NEW MESSAGE FROM: " + e.Message.From.Bare + "\t(" + e.Message.Id + "):\t" + e.Message.Body + "\n");
        }

        void xmppClient_OnRosterStart(object sender, Matrix.EventArgs e)
        {
            // server is sending roster
            Log("ROSTER START");
        }

        void xmppClient_OnRosterEnd(object sender, Matrix.EventArgs e)
        {
            // server finished sending complete roster
            Log("ROSTER END");
        }

        void xmppClient_OnRosterItem(object sender, Matrix.Xmpp.Roster.RosterEventArgs e)
        {
            // there's a manual modification of the roster when this event is called
            Log("NEW ROSTER ITEM FOR ROSTER VERSION " + e.Version);
            List groups = e.RosterItem.GetGroups();
            string jid = e.RosterItem.Jid;
            string nickname = e.RosterItem.Name;
        }
    }
}