Click here to Skip to main content
Click here to Skip to main content

A simple XMPP-client based on Workflow Foundation

By , 27 Dec 2009
 

Introduction

The article describes a simple XMPP client application based on the Windows Workflow Foundation, which allows to send and receive messages and also to add and remove contacts from your contact list.

Background

Extensible Messaging and Presence Protocol

The Extensible Messaging and Presence Protocol (XMPP) is an open technology for real time communication, using Extensible Markup Language (XML) as the base format for exchanging information. In essence, XMPP provides a way to send small pieces of XML from one entity to another in close to real time.

The XMPP protocol is taken as the RFC standard, and is described by the following documents:

  • RFC 3920 Extensible Messaging and Presence Protocol: Core
  • RFC 3921 Extensible Messaging and Presence Protocol: Instant Messaging and Presence

Windows Workflow Foundation

Windows Workflow Foundation (WF) is a Microsoft technology for defining, executing, and managing workflows. This technology was first released in November 2006 as a part of .NET Framework 3.0.

WF is a special runtime environment that manages the execution of programs that are composed of resumable program statements. In WF, a resumable program statement is called an Activity. A resumable program is a composition of activities. In WF, such a program is often called a Workflow.

Using the Code

The project consists of two libraries and an XOML-file: JabberClient.dll (root namespace is Jabber.Client), WorkflowActivities.dll (root namespace is Workflow), and Sequence.xml.

Namespace Jabber.Client

The Jabber.Client namespace includes the types for the XMPP client realization with minimal functionality, which are presented in the following list:

  • Jabber.Client.Core.TcpBinder wraps the standard System.Net.Sockets.Socket class.
  • Jabber.Client.Core.ClientState enumerates the possible client states.
  • Jabber.Client.Xml.PacketBuilder contains the methods for building XML-strings for XMPP-server requests (authentication, contact list request, message sending, etc.).
  • Jabber.Client.Xml.PacketParser contains the methods for XMPP-server response parsing.
  • Jabber.Client.Cryptography.DigestMD5Auth implies digest authentication according to RFC2831.
  • Jabber.Client.JabberClient encapsulates all of XMPP-client logic.

Namespace Worflow

The Worflow namespace includes the set of activities and helper classes which help to specify the program algorithm. You can see the list of main activities below.

  • Workflow.Activities.Receiver retrieves the message from a Queue, converts it with the help of the ReceiveMethod method of the ReceiveType class of the AssemblyFile assembly, and calls the OnReceived method of the IWorkflowDataService interface.
  • Workflow.Activities.Sender retrieves the message from a Queue, converts it with the help of the SendMethod method of the SendType class of the AssemblyFile assembly, and calls the Send method of the IWorkflowDataService interface.
  • Workflow.Activities.StateSendAndReceive sends the message and receives the response. For it to work, the activity needs these properties: ReceiveMethod, ReceiveType, AssemblyFile, SendMethod, SendType, and Context. The Context property is used as in-out params.
  • Workflow.Activities.Sequence is a type of CompositeActivity, which sequentially executes its child activities.
  • Workflow.Activities.Switch emulates a switch instruction.

Thus, the simple XMPP client's XOML-file is represented below:

<Sequence x:Name="root"
   xmlns="http://Workflow/Activities"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:wf="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
  <Switch  x:Name="sw1">
    <StateSequence x:Name="Auth" Queue="AuthInputQueue">
      <StateSendAndReceive Name="step1_0" AssemblyFile="JabberClient.dll" 
        SendType="Jabber.Client.Xml.PacketBuilder"  SendMethod="AuthStreamHeader" 
        ReceiveType="Jabber.Client.Xml.PacketParser" ReceiveMethod="AuthStreamHeader1" 
        Context="{wf:ActivityBind Auth, Path=Context}" />
      <StateSendAndReceive Name="step1_1" AssemblyFile="JabberClient.dll" 
            ReceiveType="Jabber.Client.Xml.PacketParser" 
            ReceiveMethod="AuthStreamHeader2" 
            Context="{wf:ActivityBind step1_0, Path=Context}" />
            ........
    </StateSequence>
    <Receiver x:Name="Receive" Queue="ReceiveQueue" 
            AssemblyFile="JabberClient.dll" 
            ReceiveType="Jabber.Client.Xml.PacketParser" 
            ReceiveMethod="ResponseMessage" />
    <Sender x:Name="Send" Queue="SendQueue" 
            AssemblyFile="JabberClient.dll" 
            SendType="Jabber.Client.Xml.PacketBuilder" 
            SendMethod="ChatMessage"/>
    <Sender x:Name="Status" Queue="StatusQueue" 
            AssemblyFile="JabberClient.dll" 
            SendType="Jabber.Client.Xml.PacketBuilder" 
            SendMethod="PresenceShow"/>
    <Sender x:Name="Roster" Queue="RosterQueue" 
            AssemblyFile="JabberClient.dll" 
            SendType="Jabber.Client.Xml.PacketBuilder" 
            SendMethod="Roster"/>
    <Sender x:Name="Subscribe" Queue="RosterQueue" 
            AssemblyFile="JabberClient.dll" 
            SendType="Jabber.Client.Xml.PacketBuilder" 
            SendMethod="Subscribe"/>
    <Sender x:Name="Unsubscribe" Queue="RosterQueue" 
            AssemblyFile="JabberClient.dll"
            SendType="Jabber.Client.Xml.PacketBuilder" 
            SendMethod="Unsubscribe"/>
  </Switch>
</Sequence>

It follows from the XOML-file that the StateSequence activity performs the client authentication on the XMPP-server according to RFC2831. Dynamic binding of the Context property is used in child Activities.

An example of using a simple XMPP-client is shown below:

....
using Jabber.Client;
using Jabber.Client.Core;
....

using (JabberClient jabber =
            new JabberClient("jabber.org", "vba32cc", "1234qwer"))
{
    //subscribe to events
    jabber.Authentificated += new EventHandler(jabber_Authentificated);
    jabber.RosterReceived += new RosterHandler(jabber_RosterReceived);
    jabber.MessageReceived += new MessageHandler(jabber_MessageReceived);

    Console.WriteLine("Authentification...");
    jabber.Auth();
    string s = Console.ReadLine();
    Console.WriteLine("Client state={0}", jabber.State);
    while (!string.IsNullOrEmpty(s))
    {
        if (jabber.State == ClientState.Authentificated)
        {
            Contact contact = new Contact("", "veleslaff@jabber.ru");
            jabber.AddToContacts(contact); //Add to contacts
            jabber.SetStatusForContact(contact, UserStatus.Online);//Set status
            jabber.Send(contact, s); //Send  message
            jabber.Contacts(); //Get roster list
        }
        s = Console.ReadLine();
    }
}
....

Points of Interest

The error handling in the workflow library and the host program's notification need improving. Classes in the Jabber.Client.Xml namespace need refactoring.

History

  • 27th December, 2009: First version of a simple XMPP-client with basic functionality.
  • 28th December, 2009: Updated the sample project.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ilya Builuk
Software Developer (Senior) EPAM Systems
Belarus Belarus
Member
Interested in design/development of framework functionality using the best patterns and practices.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHow do you build the project?membernveri24 Feb '10 - 10:39 
Stupid question, but can you specify how to build this project from the source code? I created a new command-line solution and added in the two projects. What is the correct way?
AnswerRe: How do you build the project?memberIlya Builuk24 Feb '10 - 19:43 
1. Create solution
2. Add JabberClient project. The 'WorkflowActivities' reference looks damaged.
3. Add 'WorkflowActivities' project in solution. In my case, reference 'WorkflowActivities' in JabberClient project become normal.
4. Add new console project.
5. Add 'JabberClient' reference in this project.
6. Add namespace: using Jabber.Client. If the namespace is not available, make sure that the version of frameworks in all projects coincide.
7. Finally, you must add Sequence.xml file in output directory of console project
8. Use classes from Jabber.Client namespace
GeneralRe: How do you build the project?membernveri26 Feb '10 - 6:02 
Thank you! Indeed, after a lot of randomly doing this and that, I did do the above. The problem is on a 64-bit machine, the Workflow project somehow became an "x86" output, making the whole solution have "Mixed Architecture". Changing that to "Any CPU" fixed the problem. Much thanks for this tutorial!
Questiondoes it work with Openfire Sever?membersamfayad8 Feb '10 - 10:32 
im trying to make this work with Openfire server but it's not.(neither locally or remotely)
when connecting:
1)It throws an exception (No connection could be made because the target machine actively refused it fe80::c8c3:7c6d:e976:7d62%11:5222)
 
2)I get this message exception(Exception has been thrown by the target of an invocation.)
location: Context = assembly.
GetMethod(ReceiveType, ReceiveMethod).
Invoke(null, new object[] { Context, msg.Data });
3)Then i get authentication failure on the console
 
thanks for any help you may provide! Smile | :)
AnswerRe: does it work with Openfire Sever?memberVeleslav9 Feb '10 - 7:13 
I'll try to reproduce this error.
My XMPP-client supports only digest MD5 authentication, maybe this caused the error?
AnswerRe: does it work with Openfire Sever?memberVeleslav9 Feb '10 - 21:08 
I installed Openfire server and got authentication error. I explored my code and found one error in auth algorythm: I incorrectly extracted the nonce-value of the server response (in AuthNonce method of PacketParser).
 
Fix:
 
Regex r = new Regex("nonce=\"(?<nonce>[a-zA-z0-9/]+)\"");
Match m = r.Match(request);
string nonce = m.Groups["nonce"].Value;
 
But still it did not help. I suspect that error can be in my implementation of DigestMD5 algorythm(DigestMD5Auth class)..
 
I checked my code on ejabber server and on remote jabber.org/jabber.ru servers.
GeneralRe: does it work with Openfire Sever?memberFreddy Rogers24 May '10 - 18:34 
I get this error too. Any resolution?
GeneralRe: does it work with Openfire Sever?memberFreddy Rogers24 May '10 - 19:08 
Fixed it!
 
Added try/catch in TcpBinder.cs since the code is trying the multiple options for connecting to the supplied server. The failure occured on IPv6, but it worked on a subsequent trip around the loop. Here's the new code:
 
foreach (IPAddress address in hostEntry.AddressList)
{
try
{
IPEndPoint ipe.......
 
.... continue;
 
} catch { }
}
Questionwhere is the sample code for the XMPP-client?memberUnruled Boy27 Dec '09 - 15:02 
is there a sample code for the XMPP-client only, without the Workflow?
 
Regards,
unruledboy_at_gmail_dot_com
http://www.xnlab.com

AnswerRe: where is the sample code for the XMPP-client?memberVeleslav27 Dec '09 - 21:14 
Download source code - 26.4 KB -
Source code of workflow and XMPP-client libraries.
Download demo - 23.3 KB - Source code with executable files of demo application that show how to use Workflow and XMPP-client libraries
GeneralRe: where is the sample code for the XMPP-client?memberUnruled Boy28 Dec '09 - 1:10 
hi, thanks, I noticed that.
 
but I mean, I want a pure xmpp client WITHOUT the workflow.
 
Regards,
unruledboy_at_gmail_dot_com
http://www.xnlab.com

GeneralRe: where is the sample code for the XMPP-client?memberVeleslav28 Dec '09 - 2:05 
Pure xmpp client may be written if you correctly use methods of Jabber.Client.Xml.PacketParser and Jabber.Client.Xml.PacketBuilder classes.
Only these classes communicate with XMPP-Server and are independent from WF. The task of WF is to call methods of these classes in specified order. Xoml-file 'Sequence.xml' defines this order.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 27 Dec 2009
Article Copyright 2009 by Ilya Builuk
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid