Exchange ActiveSync Building Blocks – DocumentLibrary

Index for the series:
Exchange ActiveSync Building Blocks – Intro

Background reading (not a pre-requisite):
[MS-ASDOC] ActiveSync Document Class
[MS-ASCMD] Search Command
[MS-ASCMD] ItemOperations Command

So, it’s been a couple of months since I’ve been digging through the Exchange ActiveSync protocol and creating “building blocks”, but the series is not abandoned yet. Sure, it went on a hiatus after covering a lot of ground in a comparably small time frame, but I will return to the topic whenever there’s something I find worthwhile. (Replicating every bit of info already in MSDN is obviously not worthwhile.)

Today I thought I’d take a closer look at a feature many probably don’t consider as an ActiveSync feature; file browsing 🙂

Ehh, isn’t ActiveSync a protocol for synchronizing and creating mail items, contacts, and calendar entries? Where does the “file browser” part fit in?

Yeah, PIM is the core functionality, but to support the main feature the protocol has some non-core features as well. Starting with Exchange 2007 it became possible to open internal links in mails. Often people will attach a link to a document instead of attaching the file itself, (a good thing by the way if it’s large files we’re linking to), so instead of an attachment called “HelloWorld.txt” you get a link in the body of the mail; http://contoso.com/HelloWorld.txt. Which works nicely with your web browser, but often people include internal links like \\server1\share1\HelloWorld.txt. In the “old” days of ActiveSync the internal links were an annoyance because you couldn’t open them from your mobile device, and had to pop out your laptop and bring up VPN to actually view the attached link.

It’s fairly obvious why you as a user would like to open all links directly in your ActiveSync client, and with the “DocumentLibrary” feature that is no longer a problem. In addition it gives us the opportunity to think out of the box. So, I thought that I’d take a stab at putting together a small code sample that will show you how to actually pull down those files without bothering with mail items.

As per usual I have cooked up a very basic Visual Studio solution, with a basic UI, and some non-optimized C# code.

image

As shown in the UI you’ll notice that in addition to the usual fields there’s “\\Server\Share” and “File Name” as well as one button for browsing and one for getting a file. It might not be obvious here, but they’re mutually exclusive. You can either:
– Enter a share name and click “Browse” to list out the files in a given share.
– Enter a complete path (share name + file name) and click “Get File” to retrieve a file.

Let me first draw up the scenario.
I have a file share on my server; let’s call this “\\server1\share1”. In this share I have 1 folder called “HelloFolder”, and 1 file called “HelloWorld.txt”.
Contents of said file:
image

If I try to browse the share I get a print-out of the folders and files in this share. (Yes, I know it looks really good just dumping the WBXML in a semi-readable format in the output window.)

image

The “1” next to the “G” character indicates that this is a folder, and correspondingly you can see that for the “HelloWorld.txt” file this value is “0”.
The “10” next to the “K” character for “HelloWorld.txt” indicates the size of the file; in this case 10 bytes.
Notice that for the files we also get the mime type of the file (it will be more evident in the next screenshot why this is neat). I have edited the output to fit into one screenshot so it’ll look slightly different if you run it.

Let’s try and fetch a file as well and see how that looks:

image

You actually have to type in the full path here; you cannot browse a share, select a file, and then fetch. You’ll also notice that while it is a text file the returned content doesn’t look much like readable text. If you run “SGVsbG8gRUFTIQ==” through a base64-decoder you’ll get the human-readable form of it (“Hello EAS!”). The reason for this is that all files are transferred as base64 content. This is because basically all files can be converted to “long strings” and transferred as text through the HTTP protocol. With the mime type you can figure out the type of content, (in addition to looking at the file extension of course), and in this example you could have just converted the content to plain text on the fly without actually storing the file to disk because the mime type “text/plain” is a good indicator that we are dealing with a text file.  For other files you could trigger the relevant app for the specific file. Notice that I only get the mime type when browsing the share, when I decide to retrieve the file I don’t have this info available. The assumption is that you will do the browse operation first to learn what you need to know to perform the fetch operation afterwards.

There are however a couple of other things we can read out here too:
The size of “10” is now next to the “J” character, and next to the “K” character it says 0-9 meaning that these are the bytes we have fetched. In this example it fits with the size of the file, but if the file was larger, (in this case a longer string), we’d still only be fetching the first 10 characters. The intention of this is that you can do things like previewing files before pulling down the entire content, or respect the “Maximum Attachment Size” policy. (Not all file types would support previewing of course.)

So, let’s talk code – how does it look underneath?
btnBrowseShare_Click

 
UTF8Encoding utf8Encoding = new UTF8Encoding(); 
byte[] bShare = utf8Encoding.GetBytes(txtServerShare.Text);
 
int iSearchXmlLength = bShare.Length + bShare.Length + 56; 
byte[] bBrowseShare = new byte[iSearchXmlLength]; 

byte[] bpart1 = {0x03, 0x01, 0x6a, 0x00, 0x00, 0x0f, 0x45, 0x47, 0x48, 0x03, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x00, 0x01, 0x49, 0x51, 0x00, 0x13, 0x45, 0x03}; 
byte[] bpart3 = {0x00, 0x01, 0x00, 0x0f, 0x52, 0x03}; 
byte[] bpart5 = {0x00, 0x01, 0x01, 0x01, 0x4a, 0x4b, 0x03, 0x30, 0x2d, 0x39, 0x39, 0x39, 0x00, 0x01, 0x01, 0x01, 0x01}; 

int iCount = 0; 

bpart1.CopyTo(bBrowseShare, iCount); 
iCount += bpart1.Length; 
bShare.CopyTo(bBrowseShare, iCount); 
iCount += bShare.Length; 
bpart3.CopyTo(bBrowseShare, iCount); 
iCount += bpart3.Length; 
bShare.CopyTo(bBrowseShare, iCount); 
iCount += bShare.Length; 
bpart5.CopyTo(bBrowseShare, iCount); 

btnGetFile_Click

 
UTF8Encoding utf8Encoding = new UTF8Encoding(); 
byte[] bFile = utf8Encoding.GetBytes(txtFileName.Text); 

int iSearchXmlLength = bFile.Length + 48; 
byte[] bFetchFile = new byte[iSearchXmlLength]; 

byte[] bPart1 = {0x03, 0x01, 0x6a, 0x00, 0x00, 0x14, 0x45, 0x46, 0x47, 0x03, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x00, 0x01, 0x00, 0x13, 0x45, 0x03}; 
byte[] bPart3 = {0x00, 0x01, 0x00, 0x14, 0x48, 0x49, 0x03}; 
byte[] bPart4 = {0x30, 0x30, 0x2d, 0x31, 0x30}; 
byte[] bPart5 = {0x00, 0x01, 0x01, 0x01, 0x01}; 

int iCount = 0; 

bPart1.CopyTo(bFetchFile, iCount); 
iCount += bPart1.Length; 
bFile.CopyTo(bFetchFile, iCount); 
iCount += bFile.Length;
bPart3.CopyTo(bFetchFile, iCount); 
iCount += bPart3.Length; 
bPart4.CopyTo(bFetchFile, iCount); 
iCount += bPart4.Length;
bPart5.CopyTo(bFetchFile, iCount); 

This is still the same approach of hardwiring the arrays I have used in previous building blocks, and not actual encoding of XML to WBXML. It’s an approach that doesn’t scale at all, but it works for small samples. I’ll admit though; I wasn’t actually looking up everything manually in the code pages and typing in the hex values when I wrote the code – I did a combo of wiresharking and using a parser for encoding the XML. I’ll probably get to the details of that in another article.

I have plenty of opinions on how this could be turned into a more functional app, but this is a working proof-of-concept, one can always polish things later 🙂

There are a couple of things to be aware of though if you think you’re onto a Dropbox-killer:
– While the protocol is called ActiveSync this is a one-way sync; you can fetch files from a share, but not modify or upload files to said share.
– The default size limit for ActiveSync is 10MB.
– The default size limit for IIS is 30MB.
– There doesn’t seem to be a policy or setting for preventing this. For instance there is a policy for preventing access to shares through OWA, but this policy does not apply to EAS.

Even if you modify web.config files to allow larger files there’s probably still some scaling issues I’m not aware of. But hey, it is fun to pull off tricks like this isn’t it? Smile Might not please your Exchange admin accordingly though…

Downloads:
C# Source – DocumentLibrary.cs
C# Source – EAS_BB_Part-07_01.zip

2 thoughts on “Exchange ActiveSync Building Blocks – DocumentLibrary”

Leave a Reply

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

*