Exchange ActiveSync Building Blocks–Encode & Decode

Index for the series:
Exchange ActiveSync Building Blocks – Intro

Background reading (not a pre-requisite):
[MS-ASCMD] ActiveSync Commands
[MS-ASCMD] Common Status Codes
[MS-ASWBXML] Algorithm Example

If you have followed the "building blocks" series you will have noticed that I have done the AS-WBXML by hand. This approach clearly doesn’t scale, and will not work outside these constrained snippets intended for learning. And it makes for sample apps that are only able to serve up a very specific purpose. (Now, the sample code is meant for copying and pasting so that’s pretty much by design anyways.) Not to mention it has made me look lazy for not doing things properly Smile

Clearly we would be a significant step further if we didn’t have to create the web requests by looking up values in code pages and type in each individual byte. So, how about we take it to that very next level of Exchange ActiveSync "hacking"?

Now, in addition to not wanting to focus on that part of the Exchange ActiveSync protocol so far, I have been procrastinating as well when it comes to writing a method for converting "plain" XML to WBXML. I have now gotten around to implementing a test app that does this very thing. I’ll point out though that I cannot take full credit for it as the actual conversion routines were written by one Joaquin Jares (contact info in the zip file). If he publishes the same code online later on he didn’t get it from me – it’s the other way around 🙂 (The source is posted here with his permission.)

Other than the creation of the byte arrays we can still build on top of what we have created thus far in this series. You can choose if you include the WBXML namespace directly in your code, or adapt it in some way. To make this sample "simpler" I have compiled Joaquin’s code into a dll-file which I’m including in my code.

The UI is as pretty as ever 🙂

image

The way it works is as follows:
– Fill in username/password/domain & server address. (Nothing new here.)
– For the "command" parameter you need to look up which command belongs to the XML you’re going to use. (As defined in the MS-ASCMD protocol docs.) For instance; last time we used the Document class to fetch a file, and this is done with the "ItemOperations" command. If you were to browse a share, (which we did to see which files could be fetched), that would be done using the "Search" command.
Don’t worry – it is documented and you don’t have to guess 🙂
– Copy & paste plain-text XML, possibly from the MSDN library, into the "Input" textbox. (Or manually type it in of course.)
– Click "Execute AS-WBXML". The input XML will be converted to WBXML behind the scenes, sent off to Exchange and the response will be parsed into plain-text XML upon return and written to the output window.

Which means that the output is still very much in a techie kind of format, but should be understandable. (This is after all not an Exchange ActiveSync client for an end-user.)

I don’t do any filtering of the XML you type in, so what you see is what Exchange will receive. This makes it great for really nerdy Exchange troubleshooting, but also means you might get error/status codes in return if your source XML contained any errors.
It also means this utility is stateless and does not keep track of sync keys or provisioning keys. You could track these in notepad manually if you like to give yourself the illusion of being stateful I suppose 🙂

Anyways, if you would like to fetch a document this would be the XML to use:

<?xml version="1.0" encoding="utf-8"?>
<ItemOperations xmlns:documentlibrary="DocumentLibrary:" xmlns="ItemOperations:">
  <Fetch>
      <Store>DocumentLibrary</Store>
      <documentlibrary:LinkId>\\server\share\file.extension</documentlibrary:LinkId>
  </Fetch>
</ItemOperations>

The code doing the main work:

 
private void btnExecute_Click(object sender, EventArgs e)
{           
    string strUsername = txtUsername.Text;
    string strPassword = txtPassword.Text;
    string strDomain = txtDomain.Text;
    string strServerAddress = txtServerAddress.Text;
    string strCmd = txtCmd.Text;

    string strEncCredentials = null;

    strEncCredentials = getEncCredentials(strUsername, strPassword, strDomain);

    string strInputXml = txtInput.Text;            
    StringReader sourceXml = new StringReader(strInputXml);

    XDocument sourceWbxml = XDocument.Load(sourceXml);

    WBXMLWriter wbxmlWriter = new WBXMLWriter(new WBXML.ASCodePageProvider());
    WBXMLConverter wbxmlConverter = new WBXMLConverter(new ASCodePageProvider(), wbxmlWriter, null);

    IList<byte> bDestWbxml = wbxmlConverter.Parse(sourceWbxml);

    string strDeviceId = "IMEI1234";
    string strDeviceType = "FakeDevice";
    string strUserAgent = "MobilityDojo.net";
    string strASVersion = "14.1";

    string uri = strServerAddress + "/Microsoft-Server-ActiveSync" + "?Cmd=" + strCmd + "&User=" + strUsername + "&DeviceId=" + strDeviceId + "&DeviceType=" + strDeviceType;
    HttpWebRequest webRequestWbxml = (HttpWebRequest)WebRequest.Create(uri);
    webRequestWbxml.ContentType = "application/vnd.ms-sync.wbxml";

    webRequestWbxml.Method = "POST";
    webRequestWbxml.Headers.Add("Authorization", "Basic " + strEncCredentials);
    webRequestWbxml.Headers.Add("MS-ASProtocolVersion", strASVersion);
    webRequestWbxml.UserAgent = strUserAgent;

    webRequestWbxml.ContentLength = bDestWbxml.Count;
    try
    {
        byte[] bBuffer = new byte[bDestWbxml.Count];
        bDestWbxml.CopyTo(bBuffer,0);

        Stream sw = webRequestWbxml.GetRequestStream();
        sw.Write(bBuffer, 0, bBuffer.Length);
        sw.Close();

        txtOutput.Text += "Response: " + "\r\n\r\n";
        WebResponse webResponseWbxml = webRequestWbxml.GetResponse();
        if (webResponseWbxml == null)
        {
        }

        System.Text.UTF8Encoding utf8Encoding = new System.Text.UTF8Encoding();
        StreamReader srResponse = new StreamReader(webResponseWbxml.GetResponseStream());
                
        string responseBody = srResponse.ReadToEnd().Trim();
        Byte[] byteResp = utf8Encoding.GetBytes(responseBody);                               

        XElement destXml = wbxmlConverter.Parse(byteResp);

        txtOutput.Text += destXml.ToString();                
    }

    catch (WebException)
    {
    }
}

Notice the references to WBXMLWriter and WBXMLConverter which is what you’d be using in your own code if you were to use the WBXML.dll.

I don’t know about you, but I find this really useful when scanning the EAS documents over at MSDN, and it lets me perform related low-level testing.

This utility will of course be useful outside of this building block, so I’ll be rolling this into my EAS MD utility, and release a new version of it in the near future.

Downloads:
C# Source – EncodeDecode.cs
C# Source – EAS_BB_Part-08_01.zip
C# Source – WBXML.zip

4 thoughts on “Exchange ActiveSync Building Blocks–Encode & Decode”

  1. Andreas,

    I’m running into an issue when encoding any XML that contains modified opening tags (for example ). If I encode this XML:

    633916348086136194

    From: testuser1
    To: testuser2
    Cc:
    Bcc:
    Subject:
    MIME-Version: 1.0
    Content-Type: text/plain; charset=”iso-8859-1″
    Content-Transfer-Encoding: 7bit
    X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.3350
    This is the e-mail body content.

    This is what I get when I decode the result:

    633916348086136194

    It seems that it doesn’t like the “” tag. The same thing happens any time there is a modified opening tag in the source XML. Have you run into this issue?

    Thanks,
    Mike

    (Oh, and your code samples and examples are AWESOME! Thank you so much!!)

  2. WordPress isn’t too happy about html and xml tags, so it does some cleaning automatically when comments are submitted. So the mail was more comprehensible 🙂

    I have run across it, and it seems to be a bug in my WBXML encoder that causes it to not recognize self-closing tags and insisting upon “proper” tags. But the EAS protocol expects some of these tags to be self-enclosing so they may or may not work when closing them in a non-self fashion. The WBXML comes out wrong on my end anyways.

    I don’t have an ETA for a fix. (It might be easy to fix; I haven’t investigated this closely.)

Leave a Reply

Your email address will not be published. Required fields are marked *

*