Implementing an Exchange ActiveSync client: the transport mechanism
Applies to: Exchange Server 2010 Enterprise
In this article
Prerequisites
Planning the implementation
Implementing the ActiveSync WBXML protocol
Implementing the ActiveSync HTTP protocol
Sample application
Digging deeper
Seeing it in action
What's next?
Sample source
Related resources
By Jason Johnston
Microsoft® Exchange ActiveSync® is a collection of protocols that enables mobile devices to synchronize and exchange messaging objects such as email, contacts, or calendar items with a server. The Microsoft Exchange Server protocol documentation includes a number of Exchange ActiveSync protocol specifications that provide the information you need to implement a fully-functional Exchange ActiveSync client or server. This article is the first in a series of articles that will provide detailed guidance for implementing an Exchange ActiveSync client by using the Exchange ActiveSync protocols. In this article, we will examine the process of establishing the basic transport mechanism when implementing an Exchange ActiveSync client. Our discussion will leverage information in the following Exchange Server protocol specifications:
Note
The complete set of Exchange ActiveSync protocol specifications is available in the MSDN Library. If you’re new to using the Exchange Server Open Specifications, I’d highly recommend that you review Exploring the Microsoft Exchange Server Open Specifications for information about how the specifications are named and structured, and for tips that’ll help you derive maximum benefit from using the specifications.
Prerequisites
While the Exchange Server protocols can be implemented with any development tools and on any platform, this sample relies on the following items.
Item |
Tool/Platform |
---|---|
Development environment |
Microsoft® Visual Studio® 2010 Professional (using C#) |
Platform |
Microsoft® .NET Framework version 4.0 |
Exchange ActiveSync server |
Microsoft® Exchange Server 2010 Service Pack 1 (SP1) |
Since we are using Exchange Server 2010 SP1 as the ActiveSync server, we will focus on Exchange ActiveSync version 14.1. This sample will not include any special handling for previous Exchange ActiveSync versions.
Planning the implementation
Based on a read-through of the [MS-ASHTTP]: ActiveSync HTTP Protocol Specification and the [MS-ASWBXML]: ActiveSync WAP Binary XML (WBXML) Protocol Specification, and a quick review of one of the higher-layer ActiveSync protocols (such as the [MS-ASCMD]: ActiveSync Command Reference Protocol Specification), we can conceptualize the following high-level order of events for an Exchange ActiveSync implementation.
The high-level order of events for an implementation
The client generates an XML payload based on the Exchange ActiveSync command and passes the payload to an implementation of the ActiveSync WBXML protocol for encoding.
The ActiveSync WBXML protocol implementation initializes a WBXML implementation with the necessary information to encode Exchange ActiveSync XML payloads. The XML payload is passed to the WBXML implementation, and a WBXML payload is generated.
The client passes the WBXML payload to an implementation of the ActiveSync HTTP protocol.
The ActiveSync HTTP protocol implementation adds the WBXML payload to an HTTP request, generates a URI, and sets appropriate headers. The HTTP request is sent to the server.
The server response comes back to the ActiveSync HTTP protocol implementation. The WBXML payload is extracted from the response and passed back to the client.
The client passes the WBXML payload to the ActiveSync WBXML implementation for decoding.
The ActiveSync WBXML implementation initializes a WBXML implementation with the necessary information to decode ActiveSync WBXML payloads. The WBXML payload is passed to the WBXML implementation, and an XML payload is generated.
Now that we understand the basic process flow of an Exchange ActiveSync implementation, let’s explore what’s required to establish the basic transport mechanism for Exchange ActiveSync. To establish the transport mechanism (by using the ActiveSync HTTP protocol and the ActiveSync WBXML protocol), we will need to implement both HTTP and WBXML. Fortunately, the .NET Framework provides an HTTP implementation in the System.Net namespace that we can use. However, we are not so fortunate for WBXML—we’ll need to build the WBXML implementation ourselves.
Implementing the ActiveSync WBXML protocol
This article does not provide detailed information about how to implement WBXML, because the information you’ll need to do that is already documented by the WAP Binary XML Content Format. However, it's important to note that not all WBXML features must be implemented when implementing the ActiveSync WBXML protocol. According to [MS-ASWBXML] section 2.2.1.1, we'll just need the following features of WBXML:
WBXML tokens
WBXML code pages
Inline strings
Opaque data
Once WBXML is implemented, implementing the ActiveSync WBXML protocol is fairly straightforward. The protocol consists of a set of WBXML code pages, one for each XML namespace that is defined by the Exchange ActiveSync protocols.
This mapping of namespaces to code pages actually imposes an additional requirement on the implementation. Code pages aren't required by the WBXML specification to map to namespaces, but the ActiveSync WBXML protocol requires a namespace-to-code page mapping. It is important to ensure that the parser can determine what namespace an XML tag belongs to so that it can use the appropriate code page.
The ASWBXML, ASWBXMLByteQueue, and ASWBXMLCodePage classes (see the "Sample source" section later in this article) comprise this sample's implementation of the ActiveSync WBXML protocol.
Implementing the ActiveSync HTTP protocol
The ActiveSync HTTP protocol requires that we implement the following functionality:
The ability to send an HTTP OPTIONS method and parse the headers in the response.
The ability to send an HTTP POST method and parse the response.
The ability to generate a URI based on the Exchange ActiveSync command being executed. This URI may be in a plain-text format, or may be a base64-encoded representation of a specific binary format that encapsulates parameters for the Exchange ActiveSync command.
The ASOptionsRequest, ASOptionsResponse, ASCommandRequest, ASCommandResponse, and EncodedRequest classes (see the "Sample source" section later in this article) comprise the sample's implementation of the ActiveSync HTTP protocol.
Sample application
The sample application code (see the "Sample source" section later in this article) consists of several classes that can be used together to implement the ActiveSync WBXML protocol and the ActiveSync HTTP protocol. Let's take a look at the classes implemented in the sample.
Class name |
Description |
---|---|
ASWBXML |
This class contains all of the XML-to-WBXML and WBXML-to-XML functionality. It also contains the WBXML code pages defined in the ActiveSync WBXML protocol. |
ASWBXMLByteQueue |
This class contains functionality to manage the bytes that comprise WBXML data. It is used by the ASWBXML class during WBXML-to-XML decoding. |
ASWBXMLCodePage |
This class contains the functionality to do XML tag-to-WBXML token lookups and WBXML token-to-XML tag lookups. It is used by the ASWBXML class during encoding and decoding. |
ASOptionsRequest |
This class contains the functionality to query the server using an HTTP OPTIONS method and generate an instance of the ASOptionResponse class. |
ASOptionsResponse |
This class contains the functionality to parse an HTTP OPTIONS method response and extract the relevant information, such as supported Exchange ActiveSync versions. |
ASCommandRequest |
This class contains the functionality to generate and send a properly formatted HTTP POST method that contains an Exchange ActiveSync command, and generate an instance of the ASCommandResponse class. |
ASCommandResponse |
This class contains the functionality to parse an HTTP POST method response that contains the response to an Exchange ActiveSync command. |
EncodedRequest |
This class contains the functionality to generate a relative URI for an HTTP POST method that contains an Exchange ActiveSync command. It is used by the ASCommandRequest class. |
The following diagram depicts how the classes relate to each other.
The classes used by the sample application
Digging deeper
Now, let's take a closer look at the implementation of each piece. For more information, the full source code for each class is listed in the "Sample source" section later in this article.
ActiveSync WBXML
Let's start with the ActiveSync WBXML protocol. Our approach is a single class (ASWBXML) that acts as an encoder and decoder. To accommodate this approach, our class has 4 public functions: LoadXml, GetXml, LoadBytes, and GetBytes. The LoadXml and GetXml functions load or return a string containing an XML document. The LoadBytes and GetBytes functions load or return a byte array containing WBXML bytes.
Internally, the class uses the XmlDocument class from the System.Xml namespace to manage all XML operations. It also needs to be able to handle binary WBXML data and to perform lookups against the WBXML code pages specified in the ActiveSync WBXML protocol.
Parsing binary WBXML data requires that the data be read byte-by-byte, with no need to jump forward or backward in the data (essentially a first-in, first-out scenario). This makes the Queue class in the System.Collections.Generic namespace a good candidate for handling our binary data in the WBXML-to-XML scenario. For most cases, the Dequeue function will suit our needs. However, WBXML uses a slightly more complicated encoding for integers (see "Multi-byte Integers" in WAP Binary XML Content Format). So our approach is to create the ASWBXMLByteQueue class, which inherits from the Queue class and adds two functions: DequeueMultibyteInt and DequeueString.
In order to perform WBXML code page lookups, we create the ASWBXMLCodePage class. This class represents a single code page and provides lookup functionality by using the Dictionary class from the System.Collections.Generic namespace. Because we need to be able to do lookups both ways (token-to-tag and tag-to-token), the ASWBXMLCodePage class actually has two instances of the Dictionary class. It also has two string properties, Namespace and Xmlns. Their purpose is to identify the XML namespace that corresponds to the code page. The ASWBXML class contains an array of ASWBXMLCodePage classes that correspond to the code pages in the ActiveSync WBXML protocol.
ActiveSync HTTP
Now that we are able to encode our XML payloads, let's turn our attention to actually sending them. We'll start by implementing two classes to handle the OPTIONS method: ASOptionsRequest and ASOptionsResponse. The ASOptionsRequest has one function: GetOptions. This function uses the HttpWebRequest class in the System.Net namespace to send an OPTIONS method request to the server. It passes the HttpWebResponse class returned to the ASOptionsResponse class, which parses the headers for the MS-ASProtocolCommands and MS-ASProtocolVersions headers.
Next, we implement two classes for the POST method: ASCommandRequest and ASCommandResponse. The ASCommandRequest class exposes a number of properties that must be set to control which Exchange ActiveSync command is used and to set parameters. It also exposes properties allowing the caller to set an XML payload or a pre-encoded WBXML payload. Internally, it uses the ASWBXML class to encode XML payloads. It exposes one public function, GetResponse, which returns an ASCommandResponse class.
The ASCommandResponse class parses an HttpWebResponse class and uses the ASWBXML class to decode the WBXML payload.
Finally, the ASCommandRequest class needs to be able to create base64-encoded URIs. Using base64-encoded URIs saves a bit of data on the wire, since the URI itself tends to be a bit shorter, and a number of headers don't need to be transmitted since their values are included in the URI itself. To do this, we'll implement the EncodedRequest class. This class exposes the fields of the Base64 Encoded Query Value structure defined in the ActiveSync HTTP protocol as properties. It also exposes the function GetBase64EncodedString which returns the encoded URI based on the values of the properties.
Seeing it in action
The following example shows how to use the ASOptionsRequest and ASCommandRequest classes to send requests to the server. In this example, the client sends a Provision command to the server and outputs the response.
// Create credentials for the user
NetworkCredential cred = new NetworkCredential("contoso\\deviceuser", "password");
//Initialize the OPTIONS request
ASOptionsRequest optionsRequest = new ASOptionsRequest();
optionsRequest.Server = "mail.contoso.com";
optionsRequest.UseSSL = true;
optionsRequest.Credentials = cred;
// Send the request
ASOptionsResponse optionsResponse = optionsRequest.GetOptions();
Console.WriteLine("Supported Versions: {0}", optionsResponse.SupportedVersions);
Console.WriteLine("Highest Supported Version: {0}", optionsResponse.HighestSupportedVersion);
Console.WriteLine("Supported Commands: {0}", optionsResponse.SupportedCommands);
// Initialize the command request
ASCommandRequest commandRequest = new ASCommandRequest();
commandRequest.Command = "Provision";
commandRequest.Credentials = cred;
commandRequest.DeviceID = "TestDeviceID";
commandRequest.DeviceType = "TestDeviceType";
commandRequest.ProtocolVersion = "14.1";
commandRequest.Server = "mail.contoso.com";
commandRequest.UseEncodedRequestLine = true;
commandRequest.User = "deviceuser";
commandRequest.UseSSL = true;
// Create the XML payload
StringBuilder xmlBuilder = new StringBuilder();
xmlBuilder.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
xmlBuilder.Append("<Provision xmlns=\"Provision:\" xmlns:settings=\"Settings:\">");
xmlBuilder.Append(" <settings:DeviceInformation>");
xmlBuilder.Append(" <settings:Set>");
xmlBuilder.Append(" <settings:Model>Test 1.0</settings:Model>");
xmlBuilder.Append(" <settings:IMEI>012345678901234</settings:IMEI>");
xmlBuilder.Append(" <settings:FriendlyName>My Test App</settings:FriendlyName>");
xmlBuilder.Append(" <settings:OS>Test OS 1.0</settings:OS>");
xmlBuilder.Append(" <settings:OSLanguage>English</settings:OSLanguage>");
xmlBuilder.Append(" <settings:PhoneNumber>555-123-4567</settings:PhoneNumber>");
xmlBuilder.Append(" <settings:MobileOperator>My Phone Company</settings:MobileOperator>");
xmlBuilder.Append(" <settings:UserAgent>TestAgent</settings:UserAgent>");
xmlBuilder.Append(" </settings:Set>");
xmlBuilder.Append(" </settings:DeviceInformation>");
xmlBuilder.Append(" <Policies>");
xmlBuilder.Append(" <Policy>");
xmlBuilder.Append(" <PolicyType>MS-EAS-Provisioning-WBXML</PolicyType> ");
xmlBuilder.Append(" </Policy>");
xmlBuilder.Append(" </Policies>");
xmlBuilder.Append("</Provision>");
commandRequest.XmlString = xmlBuilder.ToString();
// Send the request
ASCommandResponse commandResponse = commandRequest.GetResponse();
Console.WriteLine("XML Response: {0}", commandResponse.XmlString);
The following output is generated by the previous example.
Supported Versions: 2.0,2.1,2.5,12.0,12.1,14.0,14.1
Highest Supported Version: 14.1
Supported Commands: Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,Provision,ResolveRecipients,ValidateCert
XML Response: <?xml version="1.0" encoding="utf-8"?>
<provision:Provision xmlns:provision="Provision:">
<settings:DeviceInformation xmlns:settings="Settings:">
<settings:Status>1</settings:Status>
</settings:DeviceInformation>
<provision:Status>1</provision:Status>
<provision:Policies>
<provision:Policy>
<provision:PolicyType>MS-EAS-Provisioning-WBXML</provision:PolicyType>
<provision:Status>1</provision:Status>
<provision:PolicyKey>221663478</provision:PolicyKey>
<provision:Data>
<provision:EASProvisionDoc>
<provision:DevicePasswordEnabled>0</provision:DevicePasswordEnabled>
<provision:AlphanumericDevicePasswordRequired>0</provision:AlphanumericDevicePasswordRequired>
<provision:PasswordRecoveryEnabled>0</provision:PasswordRecoveryEnabled>
<provision:RequireStorageCardEncryption>0</provision:RequireStorageCardEncryption>
<provision:AttachmentsEnabled>1</provision:AttachmentsEnabled>
<provision:MinDevicePasswordLength>4</provision:MinDevicePasswordLength>
<provision:MaxInactivityTimeDeviceLock>900</provision:MaxInactivityTimeDeviceLock>
<provision:MaxDevicePasswordFailedAttempts>8</provision:MaxDevicePasswordFailedAttempts>
<provision:MaxAttachmentSize />
<provision:AllowSimpleDevicePassword>1</provision:AllowSimpleDevicePassword>
<provision:DevicePasswordExpiration />
<provision:DevicePasswordHistory>0</provision:DevicePasswordHistory>
<provision:AllowStorageCard>1</provision:AllowStorageCard>
<provision:AllowCamera>1</provision:AllowCamera>
<provision:RequireDeviceEncryption>0</provision:RequireDeviceEncryption>
<provision:AllowUnsignedApplications>1</provision:AllowUnsignedApplications>
<provision:AllowUnsignedInstallationPackages>1</provision:AllowUnsignedInstallationPackages>
<provision:MinDevicePasswordComplexCharacters>3</provision:MinDevicePasswordComplexCharacters>
<provision:AllowWiFi>1</provision:AllowWiFi>
<provision:AllowTextMessaging>1</provision:AllowTextMessaging>
<provision:AllowPOPIMAPEmail>1</provision:AllowPOPIMAPEmail>
<provision:AllowBluetooth>2</provision:AllowBluetooth>
<provision:AllowIrDA>1</provision:AllowIrDA>
<provision:RequireManualSyncWhenRoaming>0</provision:RequireManualSyncWhenRoaming>
<provision:AllowDesktopSync>1</provision:AllowDesktopSync>
<provision:MaxCalendarAgeFilter>0</provision:MaxCalendarAgeFilter>
<provision:AllowHTMLEmail>1</provision:AllowHTMLEmail>
<provision:MaxEmailAgeFilter>0</provision:MaxEmailAgeFilter>
<provision:MaxEmailBodyTruncationSize>-1</provision:MaxEmailBodyTruncationSize>
<provision:MaxEmailHTMLBodyTruncationSize>-1</provision:MaxEmailHTMLBodyTruncationSize>
<provision:RequireSignedSMIMEMessages>0</provision:RequireSignedSMIMEMessages>
<provision:RequireEncryptedSMIMEMessages>0</provision:RequireEncryptedSMIMEMessages>
<provision:RequireSignedSMIMEAlgorithm>0</provision:RequireSignedSMIMEAlgorithm>
<provision:RequireEncryptionSMIMEAlgorithm>0</provision:RequireEncryptionSMIMEAlgorithm>
<provision:AllowSMIMEEncryptionAlgorithmNegotiation>2</provision:AllowSMIMEEncryptionAlgorithmNegotiation>
<provision:AllowSMIMESoftCerts>1</provision:AllowSMIMESoftCerts>
<provision:AllowBrowser>1</provision:AllowBrowser>
<provision:AllowConsumerEmail />
</provision:EASProvisionDoc>
</provision:Data>
</provision:Policy>
</provision:Policies>
</provision:Provision>
What's next?
Now that we've implemented the ActiveSync WBXML protocol and the ActiveSync HTTP protocol, we have the transport in place to begin implementing additional Exchange ActiveSync protocols. Since servers often require provisioning before they allow other Exchange ActiveSync operations, a logical next step is to implement the ActiveSync Provisioning protocol. The next article in this series will walk through an ActiveSync Provisioning protocol implementation, so stay tuned!
Sample source
For convenience, the following is the complete C# source code for the classes discussed in this article.
ASWBXML.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
namespace TestApp
{
enum GlobalTokens
{
SWITCH_PAGE = 0x00,
END = 0x01,
ENTITY = 0x02,
STR_I = 0x03,
LITERAL = 0x04,
EXT_I_0 = 0x40,
EXT_I_1 = 0x41,
EXT_I_2 = 0x42,
PI = 0x43,
LITERAL_C = 0x44,
EXT_T_0 = 0x80,
EXT_T_1 = 0x81,
EXT_T_2 = 0x82,
STR_T = 0x83,
LITERAL_A = 0x84,
EXT_0 = 0xC0,
EXT_1 = 0xC1,
EXT_2 = 0xC2,
OPAQUE = 0xC3,
LITERAL_AC = 0xC4
}
class ASWBXML
{
const byte versionByte = 0x03;
const byte publicIdentifierByte = 0x01;
const byte characterSetByte = 0x6A; // UTF-8
const byte stringTableLengthByte = 0x00;
private XmlDocument xmlDoc = new XmlDocument();
private ASWBXMLCodePage[] codePages;
private int currentCodePage = 0;
private int defaultCodePage = -1;
public ASWBXML()
{
// Load up code pages
// Currently there are 25 code pages as per MS-ASWBXML
codePages = new ASWBXMLCodePage[25];
#region Code Page Initialization
// Code Page 0: AirSync
#region AirSync Code Page
codePages[0] = new ASWBXMLCodePage();
codePages[0].Namespace = "AirSync";
codePages[0].Xmlns = "airsync";
codePages[0].AddToken(0x05, "Sync");
codePages[0].AddToken(0x06, "Responses");
codePages[0].AddToken(0x07, "Add");
codePages[0].AddToken(0x08, "Change");
codePages[0].AddToken(0x09, "Delete");
codePages[0].AddToken(0x0A, "Fetch");
codePages[0].AddToken(0x0B, "SyncKey");
codePages[0].AddToken(0x0C, "ClientId");
codePages[0].AddToken(0x0D, "ServerId");
codePages[0].AddToken(0x0E, "Status");
codePages[0].AddToken(0x0F, "Collection");
codePages[0].AddToken(0x10, "Class");
codePages[0].AddToken(0x12, "CollectionId");
codePages[0].AddToken(0x13, "GetChanges");
codePages[0].AddToken(0x14, "MoreAvailable");
codePages[0].AddToken(0x15, "WindowSize");
codePages[0].AddToken(0x16, "Commands");
codePages[0].AddToken(0x17, "Options");
codePages[0].AddToken(0x18, "FilterType");
codePages[0].AddToken(0x1B, "Conflict");
codePages[0].AddToken(0x1C, "Collections");
codePages[0].AddToken(0x1D, "ApplicationData");
codePages[0].AddToken(0x1E, "DeletesAsMoves");
codePages[0].AddToken(0x20, "Supported");
codePages[0].AddToken(0x21, "SoftDelete");
codePages[0].AddToken(0x22, "MIMESupport");
codePages[0].AddToken(0x23, "MIMETruncation");
codePages[0].AddToken(0x24, "Wait");
codePages[0].AddToken(0x25, "Limit");
codePages[0].AddToken(0x26, "Partial");
codePages[0].AddToken(0x27, "ConversationMode");
codePages[0].AddToken(0x28, "MaxItems");
codePages[0].AddToken(0x29, "HeartbeatInterval");
#endregion
// Code Page 1: Contacts
#region Contacts Code Page
codePages[1] = new ASWBXMLCodePage();
codePages[1].Namespace = "Contacts";
codePages[1].Xmlns = "contacts";
codePages[1].AddToken(0x05, "Anniversary");
codePages[1].AddToken(0x06, "AssistantName");
codePages[1].AddToken(0x07, "AssistantPhoneNumber");
codePages[1].AddToken(0x08, "Birthday");
codePages[1].AddToken(0x0C, "Business2PhoneNumber");
codePages[1].AddToken(0x0D, "BusinessAddressCity");
codePages[1].AddToken(0x0E, "BusinessAddressCountry");
codePages[1].AddToken(0x0F, "BusinessAddressPostalCode");
codePages[1].AddToken(0x10, "BusinessAddressState");
codePages[1].AddToken(0x11, "BusinessAddressStreet");
codePages[1].AddToken(0x12, "BusinessFaxNumber");
codePages[1].AddToken(0x13, "BusinessPhoneNumber");
codePages[1].AddToken(0x14, "CarPhoneNumber");
codePages[1].AddToken(0x15, "Categories");
codePages[1].AddToken(0x16, "Category");
codePages[1].AddToken(0x17, "Children");
codePages[1].AddToken(0x18, "Child");
codePages[1].AddToken(0x19, "CompanyName");
codePages[1].AddToken(0x1A, "Department");
codePages[1].AddToken(0x1B, "Email1Address");
codePages[1].AddToken(0x1C, "Email2Address");
codePages[1].AddToken(0x1D, "Email3Address");
codePages[1].AddToken(0x1E, "FileAs");
codePages[1].AddToken(0x1F, "FirstName");
codePages[1].AddToken(0x20, "Home2PhoneNumber");
codePages[1].AddToken(0x21, "HomeAddressCity");
codePages[1].AddToken(0x22, "HomeAddressCountry");
codePages[1].AddToken(0x23, "HomeAddressPostalCode");
codePages[1].AddToken(0x24, "HomeAddressState");
codePages[1].AddToken(0x25, "HomeAddressStreet");
codePages[1].AddToken(0x26, "HomeFaxNumber");
codePages[1].AddToken(0x27, "HomePhoneNumber");
codePages[1].AddToken(0x28, "JobTitle");
codePages[1].AddToken(0x29, "LastName");
codePages[1].AddToken(0x2A, "MiddleName");
codePages[1].AddToken(0x2B, "MobilePhoneNumber");
codePages[1].AddToken(0x2C, "OfficeLocation");
codePages[1].AddToken(0x2D, "OtherAddressCity");
codePages[1].AddToken(0x2E, "OtherAddressCountry");
codePages[1].AddToken(0x2F, "OtherAddressPostalCode");
codePages[1].AddToken(0x30, "OtherAddressState");
codePages[1].AddToken(0x31, "OtherAddressStreet");
codePages[1].AddToken(0x32, "PagerNumber");
codePages[1].AddToken(0x33, "RadioPhoneNumber");
codePages[1].AddToken(0x34, "Spouse");
codePages[1].AddToken(0x35, "Suffix");
codePages[1].AddToken(0x36, "Title");
codePages[1].AddToken(0x37, "WebPage");
codePages[1].AddToken(0x38, "YomiCompanyName");
codePages[1].AddToken(0x39, "YomiFirstName");
codePages[1].AddToken(0x3A, "YomiLastName");
codePages[1].AddToken(0x3C, "Picture");
codePages[1].AddToken(0x3D, "Alias");
codePages[1].AddToken(0x3E, "WeightedRank");
#endregion
// Code Page 2: Email
#region Email Code Page
codePages[2] = new ASWBXMLCodePage();
codePages[2].Namespace = "Email";
codePages[2].Xmlns = "email";
codePages[2].AddToken(0x0F, "DateReceived");
codePages[2].AddToken(0x11, "DisplayTo");
codePages[2].AddToken(0x12, "Importance");
codePages[2].AddToken(0x13, "MessageClass");
codePages[2].AddToken(0x14, "Subject");
codePages[2].AddToken(0x15, "Read");
codePages[2].AddToken(0x16, "To");
codePages[2].AddToken(0x17, "Cc");
codePages[2].AddToken(0x18, "From");
codePages[2].AddToken(0x19, "ReplyTo");
codePages[2].AddToken(0x1A, "AllDayEvent");
codePages[2].AddToken(0x1B, "Categories");
codePages[2].AddToken(0x1C, "Category");
codePages[2].AddToken(0x1D, "DtStamp");
codePages[2].AddToken(0x1E, "EndTime");
codePages[2].AddToken(0x1F, "InstanceType");
codePages[2].AddToken(0x20, "BusyStatus");
codePages[2].AddToken(0x21, "Location");
codePages[2].AddToken(0x22, "MeetingRequest");
codePages[2].AddToken(0x23, "Organizer");
codePages[2].AddToken(0x24, "RecurrenceId");
codePages[2].AddToken(0x25, "Reminder");
codePages[2].AddToken(0x26, "ResponseRequested");
codePages[2].AddToken(0x27, "Recurrences");
codePages[2].AddToken(0x28, "Recurrence");
codePages[2].AddToken(0x29, "Type");
codePages[2].AddToken(0x2A, "Until");
codePages[2].AddToken(0x2B, "Occurrences");
codePages[2].AddToken(0x2C, "Interval");
codePages[2].AddToken(0x2D, "DayOfWeek");
codePages[2].AddToken(0x2E, "DayOfMonth");
codePages[2].AddToken(0x2F, "WeekOfMonth");
codePages[2].AddToken(0x30, "MonthOfYear");
codePages[2].AddToken(0x31, "StartTime");
codePages[2].AddToken(0x32, "Sensitivity");
codePages[2].AddToken(0x33, "TimeZone");
codePages[2].AddToken(0x34, "GlobalObjId");
codePages[2].AddToken(0x35, "ThreadTopic");
codePages[2].AddToken(0x39, "InternetCPID");
codePages[2].AddToken(0x3A, "Flag");
codePages[2].AddToken(0x3B, "Status");
codePages[2].AddToken(0x3C, "ContentClass");
codePages[2].AddToken(0x3D, "FlagType");
codePages[2].AddToken(0x3E, "CompleteTime");
codePages[2].AddToken(0x3F, "DisallowNewTimeProposal");
#endregion
// Code Page 3: AirNotify
#region AirNotify Code Page
codePages[3] = new ASWBXMLCodePage();
codePages[3].Namespace = "";
codePages[3].Xmlns = "";
#endregion
// Code Page 4: Calendar
#region Calendar Code Page
codePages[4] = new ASWBXMLCodePage();
codePages[4].Namespace = "Calendar";
codePages[4].Xmlns = "calendar";
codePages[4].AddToken(0x05, "TimeZone");
codePages[4].AddToken(0x06, "AllDayEvent");
codePages[4].AddToken(0x07, "Attendees");
codePages[4].AddToken(0x08, "Attendee");
codePages[4].AddToken(0x09, "Email");
codePages[4].AddToken(0x0A, "Name");
codePages[4].AddToken(0x0D, "BusyStatus");
codePages[4].AddToken(0x0E, "Categories");
codePages[4].AddToken(0x0F, "Category");
codePages[4].AddToken(0x11, "DtStamp");
codePages[4].AddToken(0x12, "EndTime");
codePages[4].AddToken(0x13, "Exception");
codePages[4].AddToken(0x14, "Exceptions");
codePages[4].AddToken(0x15, "Deleted");
codePages[4].AddToken(0x16, "ExceptionStartTime");
codePages[4].AddToken(0x17, "Location");
codePages[4].AddToken(0x18, "MeetingStatus");
codePages[4].AddToken(0x19, "OrganizerEmail");
codePages[4].AddToken(0x1A, "OrganizerName");
codePages[4].AddToken(0x1B, "Recurrence");
codePages[4].AddToken(0x1C, "Type");
codePages[4].AddToken(0x1D, "Until");
codePages[4].AddToken(0x1E, "Occurrences");
codePages[4].AddToken(0x1F, "Interval");
codePages[4].AddToken(0x20, "DayOfWeek");
codePages[4].AddToken(0x21, "DayOfMonth");
codePages[4].AddToken(0x22, "WeekOfMonth");
codePages[4].AddToken(0x23, "MonthOfYear");
codePages[4].AddToken(0x24, "Reminder");
codePages[4].AddToken(0x25, "Sensitivity");
codePages[4].AddToken(0x26, "Subject");
codePages[4].AddToken(0x27, "StartTime");
codePages[4].AddToken(0x28, "UID");
codePages[4].AddToken(0x29, "AttendeeStatus");
codePages[4].AddToken(0x2A, "AttendeeType");
codePages[4].AddToken(0x33, "DisallowNewTimeProposal");
codePages[4].AddToken(0x34, "ResponseRequested");
codePages[4].AddToken(0x35, "AppointmentReplyTime");
codePages[4].AddToken(0x36, "ResponseType");
codePages[4].AddToken(0x37, "CalendarType");
codePages[4].AddToken(0x38, "IsLeapMonth");
codePages[4].AddToken(0x39, "FirstDayOfWeek");
codePages[4].AddToken(0x3A, "OnlineMeetingConfLink");
codePages[4].AddToken(0x3B, "OnlineMeetingExternalLink");
#endregion
// Code Page 5: Move
#region Move Code Page
codePages[5] = new ASWBXMLCodePage();
codePages[5].Namespace = "Move";
codePages[5].Xmlns = "move";
codePages[5].AddToken(0x05, "MoveItems");
codePages[5].AddToken(0x06, "Move");
codePages[5].AddToken(0x07, "SrcMsgId");
codePages[5].AddToken(0x08, "SrcFldId");
codePages[5].AddToken(0x09, "DstFldId");
codePages[5].AddToken(0x0A, "Response");
codePages[5].AddToken(0x0B, "Status");
codePages[5].AddToken(0x0C, "DstMsgId");
#endregion
// Code Page 6: ItemEstimate
#region ItemEstimate Code Page
codePages[6] = new ASWBXMLCodePage();
codePages[6].Namespace = "GetItemEstimate";
codePages[6].Xmlns = "getitemestimate";
codePages[6].AddToken(0x05, "GetItemEstimate");
codePages[6].AddToken(0x06, "Version");
codePages[6].AddToken(0x07, "Collections");
codePages[6].AddToken(0x08, "Collection");
codePages[6].AddToken(0x09, "Class");
codePages[6].AddToken(0x0A, "CollectionId");
codePages[6].AddToken(0x0B, "DateTime");
codePages[6].AddToken(0x0C, "Estimate");
codePages[6].AddToken(0x0D, "Response");
codePages[6].AddToken(0x0E, "Status");
#endregion
// Code Page 7: FolderHierarchy
#region FolderHierarchy Code Page
codePages[7] = new ASWBXMLCodePage();
codePages[7].Namespace = "FolderHierarchy";
codePages[7].Xmlns = "folderhierarchy";
codePages[7].AddToken(0x07, "DisplayName");
codePages[7].AddToken(0x08, "ServerId");
codePages[7].AddToken(0x09, "ParentId");
codePages[7].AddToken(0x0A, "Type");
codePages[7].AddToken(0x0C, "Status");
codePages[7].AddToken(0x0E, "Changes");
codePages[7].AddToken(0x0F, "Add");
codePages[7].AddToken(0x10, "Delete");
codePages[7].AddToken(0x11, "Update");
codePages[7].AddToken(0x12, "SyncKey");
codePages[7].AddToken(0x13, "FolderCreate");
codePages[7].AddToken(0x14, "FolderDelete");
codePages[7].AddToken(0x15, "FolderUpdate");
codePages[7].AddToken(0x16, "FolderSync");
codePages[7].AddToken(0x17, "Count");
#endregion
// Code Page 8: MeetingResponse
#region MeetingResponse Code Page
codePages[8] = new ASWBXMLCodePage();
codePages[8].Namespace = "MeetingResponse";
codePages[8].Xmlns = "meetingresponse";
codePages[8].AddToken(0x05, "CalendarId");
codePages[8].AddToken(0x06, "CollectionId");
codePages[8].AddToken(0x07, "MeetingResponse");
codePages[8].AddToken(0x08, "RequestId");
codePages[8].AddToken(0x09, "Request");
codePages[8].AddToken(0x0A, "Result");
codePages[8].AddToken(0x0B, "Status");
codePages[8].AddToken(0x0C, "UserResponse");
codePages[8].AddToken(0x0E, "InstanceId");
#endregion
// Code Page 9: Tasks
#region Tasks Code Page
codePages[9] = new ASWBXMLCodePage();
codePages[9].Namespace = "Tasks";
codePages[9].Xmlns = "tasks";
codePages[9].AddToken(0x08, "Categories");
codePages[9].AddToken(0x09, "Category");
codePages[9].AddToken(0x0A, "Complete");
codePages[9].AddToken(0x0B, "DateCompleted");
codePages[9].AddToken(0x0C, "DueDate");
codePages[9].AddToken(0x0D, "UtcDueDate");
codePages[9].AddToken(0x0E, "Importance");
codePages[9].AddToken(0x0F, "Recurrence");
codePages[9].AddToken(0x10, "Type");
codePages[9].AddToken(0x11, "Start");
codePages[9].AddToken(0x12, "Until");
codePages[9].AddToken(0x13, "Occurrences");
codePages[9].AddToken(0x14, "Interval");
codePages[9].AddToken(0x15, "DayOfMonth");
codePages[9].AddToken(0x16, "DayOfWeek");
codePages[9].AddToken(0x17, "WeekOfMonth");
codePages[9].AddToken(0x18, "MonthOfYear");
codePages[9].AddToken(0x19, "Regenerate");
codePages[9].AddToken(0x1A, "DeadOccur");
codePages[9].AddToken(0x1B, "ReminderSet");
codePages[9].AddToken(0x1C, "ReminderTime");
codePages[9].AddToken(0x1D, "Sensitivity");
codePages[9].AddToken(0x1E, "StartDate");
codePages[9].AddToken(0x1F, "UtcStartDate");
codePages[9].AddToken(0x20, "Subject");
codePages[9].AddToken(0x22, "OrdinalDate");
codePages[9].AddToken(0x23, "SubOrdinalDate");
codePages[9].AddToken(0x24, "CalendarType");
codePages[9].AddToken(0x25, "IsLeapMonth");
codePages[9].AddToken(0x26, "FirstDayOfWeek");
#endregion
// Code Page 10: ResolveRecipients
#region ResolveRecipients Code Page
codePages[10] = new ASWBXMLCodePage();
codePages[10].Namespace = "ResolveRecipients";
codePages[10].Xmlns = "resolverecipients";
codePages[10].AddToken(0x05, "ResolveRecipients");
codePages[10].AddToken(0x06, "Response");
codePages[10].AddToken(0x07, "Status");
codePages[10].AddToken(0x08, "Type");
codePages[10].AddToken(0x09, "Recipient");
codePages[10].AddToken(0x0A, "DisplayName");
codePages[10].AddToken(0x0B, "EmailAddress");
codePages[10].AddToken(0x0C, "Certificates");
codePages[10].AddToken(0x0D, "Certificate");
codePages[10].AddToken(0x0E, "MiniCertificate");
codePages[10].AddToken(0x0F, "Options");
codePages[10].AddToken(0x10, "To");
codePages[10].AddToken(0x11, "CertificateRetrieval");
codePages[10].AddToken(0x12, "RecipientCount");
codePages[10].AddToken(0x13, "MaxCertificates");
codePages[10].AddToken(0x14, "MaxAmbiguousRecipients");
codePages[10].AddToken(0x15, "CertificateCount");
codePages[10].AddToken(0x16, "Availability");
codePages[10].AddToken(0x17, "StartTime");
codePages[10].AddToken(0x18, "EndTime");
codePages[10].AddToken(0x19, "MergedFreeBusy");
codePages[10].AddToken(0x1A, "Picture");
codePages[10].AddToken(0x1B, "MaxSize");
codePages[10].AddToken(0x1C, "Data");
codePages[10].AddToken(0x1D, "MaxPictures");
#endregion
// Code Page 11: ValidateCert
#region ValidateCert Code Page
codePages[11] = new ASWBXMLCodePage();
codePages[11].Namespace = "ValidateCert";
codePages[11].Xmlns = "validatecert";
codePages[11].AddToken(0x05, "ValidateCert");
codePages[11].AddToken(0x06, "Certificates");
codePages[11].AddToken(0x07, "Certificate");
codePages[11].AddToken(0x08, "CertificateChain");
codePages[11].AddToken(0x09, "CheckCRL");
codePages[11].AddToken(0x0A, "Status");
#endregion
// Code Page 12: Contacts2
#region Contacts2 Code Page
codePages[12] = new ASWBXMLCodePage();
codePages[12].Namespace = "Contacts2";
codePages[12].Xmlns = "contacts2";
codePages[12].AddToken(0x05, "CustomerId");
codePages[12].AddToken(0x06, "GovernmentId");
codePages[12].AddToken(0x07, "IMAddress");
codePages[12].AddToken(0x08, "IMAddress2");
codePages[12].AddToken(0x09, "IMAddress3");
codePages[12].AddToken(0x0A, "ManagerName");
codePages[12].AddToken(0x0B, "CompanyMainPhone");
codePages[12].AddToken(0x0C, "AccountName");
codePages[12].AddToken(0x0D, "NickName");
codePages[12].AddToken(0x0E, "MMS");
#endregion
// Code Page 13: Ping
#region Ping Code Page
codePages[13] = new ASWBXMLCodePage();
codePages[13].Namespace = "Ping";
codePages[13].Xmlns = "ping";
codePages[13].AddToken(0x05, "Ping");
codePages[13].AddToken(0x06, "AutdState"); // Per MS-ASWBXML, this tag is not used by protocol
codePages[13].AddToken(0x07, "Status");
codePages[13].AddToken(0x08, "HeartbeatInterval");
codePages[13].AddToken(0x09, "Folders");
codePages[13].AddToken(0x0A, "Folder");
codePages[13].AddToken(0x0B, "Id");
codePages[13].AddToken(0x0C, "Class");
codePages[13].AddToken(0x0D, "MaxFolders");
#endregion
// Code Page 14: Provision
#region Provision Code Page
codePages[14] = new ASWBXMLCodePage();
codePages[14].Namespace = "Provision";
codePages[14].Xmlns = "provision";
codePages[14].AddToken(0x05, "Provision");
codePages[14].AddToken(0x06, "Policies");
codePages[14].AddToken(0x07, "Policy");
codePages[14].AddToken(0x08, "PolicyType");
codePages[14].AddToken(0x09, "PolicyKey");
codePages[14].AddToken(0x0A, "Data");
codePages[14].AddToken(0x0B, "Status");
codePages[14].AddToken(0x0C, "RemoteWipe");
codePages[14].AddToken(0x0D, "EASProvisionDoc");
codePages[14].AddToken(0x0E, "DevicePasswordEnabled");
codePages[14].AddToken(0x0F, "AlphanumericDevicePasswordRequired");
codePages[14].AddToken(0x10, "RequireStorageCardEncryption");
codePages[14].AddToken(0x11, "PasswordRecoveryEnabled");
codePages[14].AddToken(0x13, "AttachmentsEnabled");
codePages[14].AddToken(0x14, "MinDevicePasswordLength");
codePages[14].AddToken(0x15, "MaxInactivityTimeDeviceLock");
codePages[14].AddToken(0x16, "MaxDevicePasswordFailedAttempts");
codePages[14].AddToken(0x17, "MaxAttachmentSize");
codePages[14].AddToken(0x18, "AllowSimpleDevicePassword");
codePages[14].AddToken(0x19, "DevicePasswordExpiration");
codePages[14].AddToken(0x1A, "DevicePasswordHistory");
codePages[14].AddToken(0x1B, "AllowStorageCard");
codePages[14].AddToken(0x1C, "AllowCamera");
codePages[14].AddToken(0x1D, "RequireDeviceEncryption");
codePages[14].AddToken(0x1E, "AllowUnsignedApplications");
codePages[14].AddToken(0x1F, "AllowUnsignedInstallationPackages");
codePages[14].AddToken(0x20, "MinDevicePasswordComplexCharacters");
codePages[14].AddToken(0x21, "AllowWiFi");
codePages[14].AddToken(0x22, "AllowTextMessaging");
codePages[14].AddToken(0x23, "AllowPOPIMAPEmail");
codePages[14].AddToken(0x24, "AllowBluetooth");
codePages[14].AddToken(0x25, "AllowIrDA");
codePages[14].AddToken(0x26, "RequireManualSyncWhenRoaming");
codePages[14].AddToken(0x27, "AllowDesktopSync");
codePages[14].AddToken(0x28, "MaxCalendarAgeFilter");
codePages[14].AddToken(0x29, "AllowHTMLEmail");
codePages[14].AddToken(0x2A, "MaxEmailAgeFilter");
codePages[14].AddToken(0x2B, "MaxEmailBodyTruncationSize");
codePages[14].AddToken(0x2C, "MaxEmailHTMLBodyTruncationSize");
codePages[14].AddToken(0x2D, "RequireSignedSMIMEMessages");
codePages[14].AddToken(0x2E, "RequireEncryptedSMIMEMessages");
codePages[14].AddToken(0x2F, "RequireSignedSMIMEAlgorithm");
codePages[14].AddToken(0x30, "RequireEncryptionSMIMEAlgorithm");
codePages[14].AddToken(0x31, "AllowSMIMEEncryptionAlgorithmNegotiation");
codePages[14].AddToken(0x32, "AllowSMIMESoftCerts");
codePages[14].AddToken(0x33, "AllowBrowser");
codePages[14].AddToken(0x34, "AllowConsumerEmail");
codePages[14].AddToken(0x35, "AllowRemoteDesktop");
codePages[14].AddToken(0x36, "AllowInternetSharing");
codePages[14].AddToken(0x37, "UnapprovedInROMApplicationList");
codePages[14].AddToken(0x38, "ApplicationName");
codePages[14].AddToken(0x39, "ApprovedApplicationList");
codePages[14].AddToken(0x3A, "Hash");
#endregion
// Code Page 15: Search
#region Search Code Page
codePages[15] = new ASWBXMLCodePage();
codePages[15].Namespace = "Search";
codePages[15].Xmlns = "search";
codePages[15].AddToken(0x05, "Search");
codePages[15].AddToken(0x07, "Store");
codePages[15].AddToken(0x08, "Name");
codePages[15].AddToken(0x09, "Query");
codePages[15].AddToken(0x0A, "Options");
codePages[15].AddToken(0x0B, "Range");
codePages[15].AddToken(0x0C, "Status");
codePages[15].AddToken(0x0D, "Response");
codePages[15].AddToken(0x0E, "Result");
codePages[15].AddToken(0x0F, "Properties");
codePages[15].AddToken(0x10, "Total");
codePages[15].AddToken(0x11, "EqualTo");
codePages[15].AddToken(0x12, "Value");
codePages[15].AddToken(0x13, "And");
codePages[15].AddToken(0x14, "Or");
codePages[15].AddToken(0x15, "FreeText");
codePages[15].AddToken(0x17, "DeepTraversal");
codePages[15].AddToken(0x18, "LongId");
codePages[15].AddToken(0x19, "RebuildResults");
codePages[15].AddToken(0x1A, "LessThan");
codePages[15].AddToken(0x1B, "GreaterThan");
codePages[15].AddToken(0x1E, "UserName");
codePages[15].AddToken(0x1F, "Password");
codePages[15].AddToken(0x20, "ConversationId");
codePages[15].AddToken(0x21, "Picture");
codePages[15].AddToken(0x22, "MaxSize");
codePages[15].AddToken(0x23, "MaxPictures");
#endregion
// Code Page 16: GAL
#region GAL Code Page
codePages[16] = new ASWBXMLCodePage();
codePages[16].Namespace = "GAL";
codePages[16].Xmlns = "gal";
codePages[16].AddToken(0x05, "DisplayName");
codePages[16].AddToken(0x06, "Phone");
codePages[16].AddToken(0x07, "Office");
codePages[16].AddToken(0x08, "Title");
codePages[16].AddToken(0x09, "Company");
codePages[16].AddToken(0x0A, "Alias");
codePages[16].AddToken(0x0B, "FirstName");
codePages[16].AddToken(0x0C, "LastName");
codePages[16].AddToken(0x0D, "HomePhone");
codePages[16].AddToken(0x0E, "MobilePhone");
codePages[16].AddToken(0x0F, "EmailAddress");
codePages[16].AddToken(0x10, "Picture");
codePages[16].AddToken(0x11, "Status");
codePages[16].AddToken(0x12, "Data");
#endregion
// Code Page 17: AirSyncBase
#region AirSyncBase Code Page
codePages[17] = new ASWBXMLCodePage();
codePages[17].Namespace = "AirSyncBase";
codePages[17].Xmlns = "airsyncbase";
codePages[17].AddToken(0x05, "BodyPreference");
codePages[17].AddToken(0x06, "Type");
codePages[17].AddToken(0x07, "TruncationSize");
codePages[17].AddToken(0x08, "AllOrNone");
codePages[17].AddToken(0x0A, "Body");
codePages[17].AddToken(0x0B, "Data");
codePages[17].AddToken(0x0C, "EstimatedDataSize");
codePages[17].AddToken(0x0D, "Truncated");
codePages[17].AddToken(0x0E, "Attachments");
codePages[17].AddToken(0x0F, "Attachment");
codePages[17].AddToken(0x10, "DisplayName");
codePages[17].AddToken(0x11, "FileReference");
codePages[17].AddToken(0x12, "Method");
codePages[17].AddToken(0x13, "ContentId");
codePages[17].AddToken(0x14, "ContentLocation");
codePages[17].AddToken(0x15, "IsInline");
codePages[17].AddToken(0x16, "NativeBodyType");
codePages[17].AddToken(0x17, "ContentType");
codePages[17].AddToken(0x18, "Preview");
codePages[17].AddToken(0x19, "BodyPartPreference");
codePages[17].AddToken(0x1A, "BodyPart");
codePages[17].AddToken(0x1B, "Status");
#endregion
// Code Page 18: Settings
#region Settings Code Page
codePages[18] = new ASWBXMLCodePage();
codePages[18].Namespace = "Settings";
codePages[18].Xmlns = "settings";
codePages[18].AddToken(0x05, "Settings");
codePages[18].AddToken(0x06, "Status");
codePages[18].AddToken(0x07, "Get");
codePages[18].AddToken(0x08, "Set");
codePages[18].AddToken(0x09, "Oof");
codePages[18].AddToken(0x0A, "OofState");
codePages[18].AddToken(0x0B, "StartTime");
codePages[18].AddToken(0x0C, "EndTime");
codePages[18].AddToken(0x0D, "OofMessage");
codePages[18].AddToken(0x0E, "AppliesToInternal");
codePages[18].AddToken(0x0F, "AppliesToExternalKnown");
codePages[18].AddToken(0x10, "AppliesToExternalUnknown");
codePages[18].AddToken(0x11, "Enabled");
codePages[18].AddToken(0x12, "ReplyMessage");
codePages[18].AddToken(0x13, "BodyType");
codePages[18].AddToken(0x14, "DevicePassword");
codePages[18].AddToken(0x15, "Password");
codePages[18].AddToken(0x16, "DeviceInformation");
codePages[18].AddToken(0x17, "Model");
codePages[18].AddToken(0x18, "IMEI");
codePages[18].AddToken(0x19, "FriendlyName");
codePages[18].AddToken(0x1A, "OS");
codePages[18].AddToken(0x1B, "OSLanguage");
codePages[18].AddToken(0x1C, "PhoneNumber");
codePages[18].AddToken(0x1D, "UserInformation");
codePages[18].AddToken(0x1E, "EmailAddresses");
codePages[18].AddToken(0x1F, "SMTPAddress");
codePages[18].AddToken(0x20, "UserAgent");
codePages[18].AddToken(0x21, "EnableOutboundSMS");
codePages[18].AddToken(0x22, "MobileOperator");
codePages[18].AddToken(0x23, "PrimarySmtpAddress");
codePages[18].AddToken(0x24, "Accounts");
codePages[18].AddToken(0x25, "Account");
codePages[18].AddToken(0x26, "AccountId");
codePages[18].AddToken(0x27, "AccountName");
codePages[18].AddToken(0x28, "UserDisplayName");
codePages[18].AddToken(0x29, "SendDisabled");
codePages[18].AddToken(0x2B, "RightsManagementInformation");
#endregion
// Code Page 19: DocumentLibrary
#region DocumentLibrary Code Page
codePages[19] = new ASWBXMLCodePage();
codePages[19].Namespace = "DocumentLibrary";
codePages[19].Xmlns = "documentlibrary";
codePages[19].AddToken(0x05, "LinkId");
codePages[19].AddToken(0x06, "DisplayName");
codePages[19].AddToken(0x07, "IsFolder");
codePages[19].AddToken(0x08, "CreationDate");
codePages[19].AddToken(0x09, "LastModifiedDate");
codePages[19].AddToken(0x0A, "IsHidden");
codePages[19].AddToken(0x0B, "ContentLength");
codePages[19].AddToken(0x0C, "ContentType");
#endregion
// Code Page 20: ItemOperations
#region ItemOperations Code Page
codePages[20] = new ASWBXMLCodePage();
codePages[20].Namespace = "ItemOperations";
codePages[20].Xmlns = "itemoperations";
codePages[20].AddToken(0x05, "ItemOperations");
codePages[20].AddToken(0x06, "Fetch");
codePages[20].AddToken(0x07, "Store");
codePages[20].AddToken(0x08, "Options");
codePages[20].AddToken(0x09, "Range");
codePages[20].AddToken(0x0A, "Total");
codePages[20].AddToken(0x0B, "Properties");
codePages[20].AddToken(0x0C, "Data");
codePages[20].AddToken(0x0D, "Status");
codePages[20].AddToken(0x0E, "Response");
codePages[20].AddToken(0x0F, "Version");
codePages[20].AddToken(0x10, "Schema");
codePages[20].AddToken(0x11, "Part");
codePages[20].AddToken(0x12, "EmptyFolderContents");
codePages[20].AddToken(0x13, "DeleteSubFolders");
codePages[20].AddToken(0x14, "UserName");
codePages[20].AddToken(0x15, "Password");
codePages[20].AddToken(0x16, "Move");
codePages[20].AddToken(0x17, "DstFldId");
codePages[20].AddToken(0x18, "ConversationId");
codePages[20].AddToken(0x19, "MoveAlways");
#endregion
// Code Page 21: ComposeMail
#region ComposeMail Code Page
codePages[21] = new ASWBXMLCodePage();
codePages[21].Namespace = "ComposeMail";
codePages[21].Xmlns = "composemail";
codePages[21].AddToken(0x05, "SendMail");
codePages[21].AddToken(0x06, "SmartForward");
codePages[21].AddToken(0x07, "SmartReply");
codePages[21].AddToken(0x08, "SaveInSentItems");
codePages[21].AddToken(0x09, "ReplaceMime");
codePages[21].AddToken(0x0B, "Source");
codePages[21].AddToken(0x0C, "FolderId");
codePages[21].AddToken(0x0D, "ItemId");
codePages[21].AddToken(0x0E, "LongId");
codePages[21].AddToken(0x0F, "InstanceId");
codePages[21].AddToken(0x10, "Mime");
codePages[21].AddToken(0x11, "ClientId");
codePages[21].AddToken(0x12, "Status");
codePages[21].AddToken(0x13, "AccountId");
#endregion
// Code Page 22: Email2
#region Email2 Code Page
codePages[22] = new ASWBXMLCodePage();
codePages[22].Namespace = "Email2";
codePages[22].Xmlns = "email2";
codePages[22].AddToken(0x05, "UmCallerID");
codePages[22].AddToken(0x06, "UmUserNotes");
codePages[22].AddToken(0x07, "UmAttDuration");
codePages[22].AddToken(0x08, "UmAttOrder");
codePages[22].AddToken(0x09, "ConversationId");
codePages[22].AddToken(0x0A, "ConversationIndex");
codePages[22].AddToken(0x0B, "LastVerbExecuted");
codePages[22].AddToken(0x0C, "LastVerbExecutionTime");
codePages[22].AddToken(0x0D, "ReceivedAsBcc");
codePages[22].AddToken(0x0E, "Sender");
codePages[22].AddToken(0x0F, "CalendarType");
codePages[22].AddToken(0x10, "IsLeapMonth");
codePages[22].AddToken(0x11, "AccountId");
codePages[22].AddToken(0x12, "FirstDayOfWeek");
codePages[22].AddToken(0x13, "MeetingMessageType");
#endregion
// Code Page 23: Notes
#region Notes Code Page
codePages[23] = new ASWBXMLCodePage();
codePages[23].Namespace = "Notes";
codePages[23].Xmlns = "notes";
codePages[23].AddToken(0x05, "Subject");
codePages[23].AddToken(0x06, "MessageClass");
codePages[23].AddToken(0x07, "LastModifiedDate");
codePages[23].AddToken(0x08, "Categories");
codePages[23].AddToken(0x09, "Category");
#endregion
// Code Page 24: RightsManagement
#region RightsManagement Code Page
codePages[24] = new ASWBXMLCodePage();
codePages[24].Namespace = "RightsManagement";
codePages[24].Xmlns = "rightsmanagement";
codePages[24].AddToken(0x05, "RightsManagementSupport");
codePages[24].AddToken(0x06, "RightsManagementTemplates");
codePages[24].AddToken(0x07, "RightsManagementTemplate");
codePages[24].AddToken(0x08, "RightsManagementLicense");
codePages[24].AddToken(0x09, "EditAllowed");
codePages[24].AddToken(0x0A, "ReplyAllowed");
codePages[24].AddToken(0x0B, "ReplyAllAllowed");
codePages[24].AddToken(0x0C, "ForwardAllowed");
codePages[24].AddToken(0x0D, "ModifyRecipientsAllowed");
codePages[24].AddToken(0x0E, "ExtractAllowed");
codePages[24].AddToken(0x0F, "PrintAllowed");
codePages[24].AddToken(0x10, "ExportAllowed");
codePages[24].AddToken(0x11, "ProgrammaticAccessAllowed");
codePages[24].AddToken(0x12, "Owner");
codePages[24].AddToken(0x13, "ContentExpiryDate");
codePages[24].AddToken(0x14, "TemplateID");
codePages[24].AddToken(0x15, "TemplateName");
codePages[24].AddToken(0x16, "TemplateDescription");
codePages[24].AddToken(0x17, "ContentOwner");
codePages[24].AddToken(0x18, "RemoveRightsManagementDistribution");
#endregion
#endregion
}
public void LoadXml(string strXML)
{
XmlReader xmlReader = XmlReader.Create(new StringReader(strXML));
xmlDoc.Load(xmlReader);
}
public string GetXml()
{
StringWriter sw = new StringWriter();
XmlTextWriter xmlw = new XmlTextWriter(sw);
xmlw.Formatting = Formatting.Indented;
xmlDoc.WriteTo(xmlw);
xmlw.Flush();
return sw.ToString();
}
public void LoadBytes(byte[] byteWBXML)
{
xmlDoc = new XmlDocument();
ASWBXMLByteQueue bytes = new ASWBXMLByteQueue(byteWBXML);
// Version is ignored
byte version = bytes.Dequeue();
// Public Identifier is ignored
int publicIdentifier = bytes.DequeueMultibyteInt();
// Character set
// Currently only UTF-8 is supported, throw if something else
int charset = bytes.DequeueMultibyteInt();
if (charset != 0x6A)
throw new InvalidDataException("ASWBXML only supports UTF-8 encoded XML.");
// String table length
// This should be 0, MS-ASWBXML does not use string tables
int stringTableLength = bytes.DequeueMultibyteInt();
if (stringTableLength != 0)
throw new InvalidDataException("WBXML data contains a string table.");
// Now we should be at the body of the data.
// Add the declaration
XmlDeclaration xmlDec = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
xmlDoc.InsertBefore(xmlDec, null);
XmlNode currentNode = xmlDoc;
while (bytes.Count > 0)
{
byte currentByte = bytes.Dequeue();
switch ((GlobalTokens)currentByte)
{
// Check for a global token that we actually implement
case GlobalTokens.SWITCH_PAGE:
int newCodePage = (int)bytes.Dequeue();
if (newCodePage >= 0 && newCodePage < 25)
{
currentCodePage = newCodePage;
}
else
{
throw new InvalidDataException(string.Format("Unknown code page ID 0x{0:X} encountered in WBXML", currentByte));
}
break;
case GlobalTokens.END:
if (currentNode.ParentNode != null)
{
currentNode = currentNode.ParentNode;
}
else
{
throw new InvalidDataException("END global token encountered out of sequence");
}
break;
case GlobalTokens.OPAQUE:
int CDATALength = bytes.DequeueMultibyteInt();
XmlCDataSection newOpaqueNode = xmlDoc.CreateCDataSection(bytes.DequeueString(CDATALength));
currentNode.AppendChild(newOpaqueNode);
break;
case GlobalTokens.STR_I:
XmlNode newTextNode = xmlDoc.CreateTextNode(bytes.DequeueString());
currentNode.AppendChild(newTextNode);
break;
// According to MS-ASWBXML, these features aren't used
case GlobalTokens.ENTITY:
case GlobalTokens.EXT_0:
case GlobalTokens.EXT_1:
case GlobalTokens.EXT_2:
case GlobalTokens.EXT_I_0:
case GlobalTokens.EXT_I_1:
case GlobalTokens.EXT_I_2:
case GlobalTokens.EXT_T_0:
case GlobalTokens.EXT_T_1:
case GlobalTokens.EXT_T_2:
case GlobalTokens.LITERAL:
case GlobalTokens.LITERAL_A:
case GlobalTokens.LITERAL_AC:
case GlobalTokens.LITERAL_C:
case GlobalTokens.PI:
case GlobalTokens.STR_T:
throw new InvalidDataException(string.Format("Encountered unknown global token 0x{0:X}.", currentByte));
// If it's not a global token, it should be a tag
default:
bool hasAttributes = false;
bool hasContent = false;
hasAttributes = (currentByte & 0x80) > 0;
hasContent = (currentByte & 0x40) > 0;
byte token = (byte)(currentByte & 0x3F);
if (hasAttributes)
// Maybe use Trace.Assert here?
throw new InvalidDataException(string.Format("Token 0x{0:X} has attributes.", token));
string strTag = codePages[currentCodePage].GetTag(token);
if (strTag == null)
{
strTag = string.Format("UNKNOWN_TAG_{0,2:X}", token);
}
XmlNode newNode = xmlDoc.CreateElement(codePages[currentCodePage].Xmlns, strTag, codePages[currentCodePage].Namespace);
newNode.Prefix = codePages[currentCodePage].Xmlns;
currentNode.AppendChild(newNode);
if (hasContent)
{
currentNode = newNode;
}
break;
}
}
}
public byte[] GetBytes()
{
List<byte> byteList = new List<byte>();
byteList.Add(versionByte);
byteList.Add(publicIdentifierByte);
byteList.Add(characterSetByte);
byteList.Add(stringTableLengthByte);
foreach (XmlNode node in xmlDoc.ChildNodes)
{
byteList.AddRange(EncodeNode(node));
}
return byteList.ToArray();
}
private byte[] EncodeNode(XmlNode node)
{
List<byte> byteList = new List<byte>();
switch (node.NodeType)
{
case XmlNodeType.Element:
if (node.Attributes.Count > 0)
{
ParseXmlnsAttributes(node);
}
if (SetCodePageByXmlns(node.Prefix))
{
byteList.Add((byte)GlobalTokens.SWITCH_PAGE);
byteList.Add((byte)currentCodePage);
}
byte token = codePages[currentCodePage].GetToken(node.LocalName);
if (node.HasChildNodes)
{
token |= 0x40;
}
byteList.Add(token);
if (node.HasChildNodes)
{
foreach (XmlNode child in node.ChildNodes)
{
byteList.AddRange(EncodeNode(child));
}
byteList.Add((byte)GlobalTokens.END);
}
break;
case XmlNodeType.Text:
byteList.Add((byte)GlobalTokens.STR_I);
byteList.AddRange(EncodeString(node.Value));
break;
case XmlNodeType.CDATA:
byteList.Add((byte)GlobalTokens.OPAQUE);
byteList.AddRange(EncodeOpaque(node.Value));
break;
default:
break;
}
return byteList.ToArray();
}
private int GetCodePageByXmlns(string xmlns)
{
for (int i = 0; i < codePages.Length; i++)
{
if (codePages[i].Xmlns.ToUpper() == xmlns.ToUpper())
{
return i;
}
}
return -1;
}
private int GetCodePageByNamespace(string nameSpace)
{
for (int i = 0; i < codePages.Length; i++)
{
if (codePages[i].Namespace.ToUpper() == nameSpace.ToUpper())
{
return i;
}
}
return -1;
}
private bool SetCodePageByXmlns(string xmlns)
{
if (xmlns == null || xmlns == "")
{
// Try default namespace
if (currentCodePage != defaultCodePage)
{
currentCodePage = defaultCodePage;
return true;
}
return false;
}
// Try current first
if (codePages[currentCodePage].Xmlns.ToUpper() == xmlns.ToUpper())
{
return false;
}
for (int i = 0; i < codePages.Length; i++)
{
if (codePages[i].Xmlns.ToUpper() == xmlns.ToUpper())
{
currentCodePage = i;
return true;
}
}
throw new InvalidDataException(string.Format("Unknown Xmlns: {0}.", xmlns));
}
private void ParseXmlnsAttributes(XmlNode node)
{
foreach (XmlAttribute attribute in node.Attributes)
{
int codePage = GetCodePageByNamespace(attribute.Value);
if (attribute.Name.ToUpper() == "XMLNS")
{
defaultCodePage = codePage;
}
else if (attribute.Prefix.ToUpper() == "XMLNS")
{
codePages[codePage].Xmlns = attribute.LocalName;
}
}
}
private byte[] EncodeString(string value)
{
List<byte> byteList = new List<byte>();
char[] charArray = value.ToCharArray();
for (int i = 0; i < charArray.Length; i++)
{
byteList.Add((byte)charArray[i]);
}
byteList.Add(0x00);
return byteList.ToArray();
}
private byte[] EncodeOpaque(string value)
{
List<byte> byteList = new List<byte>();
char[] charArray = value.ToCharArray();
byteList.AddRange(EncodeMultiByteInteger(charArray.Length));
for (int i = 0; i < charArray.Length; i++)
{
byteList.Add((byte)charArray[i]);
}
return byteList.ToArray();
}
private byte[] EncodeMultiByteInteger(int value)
{
List<byte> byteList = new List<byte>();
int shiftedValue = value;
while (value > 0)
{
byte addByte = (byte)(value & 0x7F);
if (byteList.Count > 0)
{
addByte |= 0x80;
}
byteList.Insert(0, addByte);
value >>= 7;
}
return byteList.ToArray();
}
}
}
ASWBXMLByteQueue.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestApp
{
class ASWBXMLByteQueue : Queue<byte>
{
public ASWBXMLByteQueue(byte[] bytes)
: base(bytes)
{
}
public int DequeueMultibyteInt()
{
int iReturn = 0;
byte singleByte = 0xFF;
do
{
iReturn <<= 7;
singleByte = this.Dequeue();
iReturn += (int)(singleByte & 0x7F);
}
while (CheckContinuationBit(singleByte));
return iReturn;
}
private bool CheckContinuationBit(byte byteval)
{
byte continuationBitmask = 0x80;
return (continuationBitmask & byteval) != 0;
}
public string DequeueString()
{
StringBuilder strReturn = new StringBuilder();
byte currentByte = 0x00;
do
{
// TODO: Improve this handling. We are technically UTF-8, meaning
// that characters could be more than one byte long. This will fail if we have
// characters outside of the US-ASCII range
currentByte = this.Dequeue();
if (currentByte != 0x00)
{
strReturn.Append((char)currentByte);
}
}
while (currentByte != 0x00);
return strReturn.ToString();
}
public string DequeueString(int length)
{
StringBuilder strReturn = new StringBuilder();
byte currentByte = 0x00;
for (int i = 0; i < length; i++)
{
// TODO: Improve this handling. We are technically UTF-8, meaning
// that characters could be more than one byte long. This will fail if we have
// characters outside of the US-ASCII range
currentByte = this.Dequeue();
strReturn.Append((char)currentByte);
}
return strReturn.ToString();
}
}
}
ASWBXMLCodePage.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestApp
{
class ASWBXMLCodePage
{
private string strNamespace = "";
private string strXmlns = "";
private Dictionary<byte, string> tokenLookup = new Dictionary<byte,string>();
private Dictionary<string, byte> tagLookup = new Dictionary<string, byte>();
public string Namespace
{
get
{
return strNamespace;
}
set
{
strNamespace = value;
}
}
public string Xmlns
{
get
{
return strXmlns;
}
set
{
strXmlns = value;
}
}
public void AddToken(byte token, string tag)
{
tokenLookup.Add(token, tag);
tagLookup.Add(tag, token);
}
public byte GetToken(string tag)
{
if (tagLookup.ContainsKey(tag))
return tagLookup[tag];
return 0xFF;
}
public string GetTag(byte token)
{
if (tokenLookup.ContainsKey(token))
return tokenLookup[token];
return null;
}
}
}
ASOptionsRequest.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
namespace TestApp
{
class ASOptionsRequest
{
private NetworkCredential credential = null;
private string server = null;
private bool useSSL = true;
public NetworkCredential Credentials
{
get
{
return credential;
}
set
{
credential = value;
}
}
public string Server
{
get
{
return server;
}
set
{
server = value;
}
}
public bool UseSSL
{
get
{
return useSSL;
}
set
{
useSSL = value;
}
}
public ASOptionsResponse GetOptions()
{
if (credential == null || server == null)
throw new InvalidDataException("ASOptionsRequest not initialized.");
string strURI = string.Format("{0}//{1}/Microsoft-Server-ActiveSync", UseSSL ? "https:" : "http:", Server);
Uri serverUri = new Uri(strURI);
CredentialCache creds = new CredentialCache();
// Using Basic authentication
creds.Add(serverUri, "Basic", Credentials);
HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(strURI);
httpReq.Credentials = creds;
httpReq.Method = "OPTIONS";
try
{
HttpWebResponse httpResp = (HttpWebResponse)httpReq.GetResponse();
ASOptionsResponse response = new ASOptionsResponse(httpResp);
httpResp.Close();
return response;
}
catch (Exception ex)
{
VSError.ReportException(ex);
return null;
}
}
}
}
ASOptionsResponse.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
namespace TestApp
{
class ASOptionsResponse
{
private string commands = null;
private string versions = null;
public ASOptionsResponse(HttpWebResponse httpResponse)
{
commands = httpResponse.GetResponseHeader("MS-ASProtocolCommands");
versions = httpResponse.GetResponseHeader("MS-ASProtocolVersions");
}
public string SupportedCommands
{
get
{
return commands;
}
}
public string SupportedVersions
{
get
{
return versions;
}
}
public string HighestSupportedVersion
{
get
{
char[] chDelimiters = { ',' };
string[] strVersions = SupportedVersions.Split(chDelimiters);
string strHighestVersion = "0";
foreach (string strVersion in strVersions)
{
if (Convert.ToSingle(strVersion) > Convert.ToSingle(strHighestVersion))
{
strHighestVersion = strVersion;
}
}
return strHighestVersion;
}
}
}
}
ASCommandRequest.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
namespace TestApp
{
struct CommandParameter
{
public string Parameter;
public string Value;
}
// Base class for ActiveSync command requests
class ASCommandRequest
{
private NetworkCredential credential = null;
private string server = null;
private bool useSSL = true;
private byte[] wbxmlBytes = null;
private string xmlString = null;
private string protocolVersion = null;
private string requestLine = null;
private bool useEncodedRequestLine = true;
private string command = null;
private string user = null;
private string deviceID = null;
private string deviceType = null;
private UInt32 policyKey = 0;
private CommandParameter[] parameters = null;
#region Property Accessors
public NetworkCredential Credentials
{
get
{
return credential;
}
set
{
credential = value;
}
}
public string Server
{
get
{
return server;
}
set
{
server = value;
}
}
public bool UseSSL
{
get
{
return useSSL;
}
set
{
useSSL = value;
}
}
public byte[] WbxmlBytes
{
get
{
return wbxmlBytes;
}
set
{
wbxmlBytes = value;
// Loading WBXML bytes causes immediate decoding
xmlString = DecodeWBXML(wbxmlBytes);
}
}
public string XmlString
{
get
{
return xmlString;
}
set
{
xmlString = value;
// Loading XML causes immediate encoding
wbxmlBytes = EncodeXMLString(xmlString);
}
}
public string ProtocolVersion
{
get
{
return protocolVersion;
}
set
{
protocolVersion = value;
}
}
public string RequestLine
{
get
{
// Generate on demand
BuildRequestLine();
return requestLine;
}
set
{
requestLine = value;
}
}
public bool UseEncodedRequestLine
{
get
{
return useEncodedRequestLine;
}
set
{
useEncodedRequestLine = value;
}
}
public string Command
{
get
{
return command;
}
set
{
command = value;
}
}
public string User
{
get
{
return user;
}
set
{
user = value;
}
}
public string DeviceID
{
get
{
return deviceID;
}
set
{
deviceID = value;
}
}
public string DeviceType
{
get
{
return deviceType;
}
set
{
deviceType = value;
}
}
public Uint32 PolicyKey
{
get
{
return policyKey;
}
set
{
policyKey = value;
}
}
public CommandParameter[] CommandParameters
{
get
{
return parameters;
}
set
{
parameters = value;
}
}
#endregion
public ASCommandResponse GetResponse()
{
GenerateXMLPayload();
if (Credentials == null || Server == null || ProtocolVersion == null || WbxmlBytes == null)
throw new InvalidDataException("ASCommandRequest not initialized.");
string uriString = string.Format("{0}//{1}/Microsoft-Server-ActiveSync?{2}",
useSSL ? "https:" : "http:", server, RequestLine);
Uri serverUri = new Uri(uriString);
CredentialCache creds = new CredentialCache();
// Using Basic authentication
creds.Add(serverUri, "Basic", credential);
HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(uriString);
httpReq.Credentials = creds;
httpReq.Method = "POST";
httpReq.ContentType = "application/vnd.ms-sync.wbxml";
if (!UseEncodedRequestLine)
{
httpReq.Headers.Add("MS-ASProtocolVersion", ProtocolVersion);
httpReq.Headers.Add("X-MS-PolicyKey", PolicyKey.ToString());
}
try
{
Stream requestStream = httpReq.GetRequestStream();
requestStream.Write(WbxmlBytes, 0, WbxmlBytes.Length);
requestStream.Close();
HttpWebResponse httpResp = (HttpWebResponse)httpReq.GetResponse();
ASCommandResponse response = WrapHttpResponse(httpResp);
httpResp.Close();
return response;
}
catch (Exception ex)
{
VSError.ReportException(ex);
return null;
}
}
protected virtual ASCommandResponse WrapHttpResponse(HttpWebResponse httpResp)
{
return new ASCommandResponse(httpResp);
}
protected virtual void BuildRequestLine()
{
if (Command == null || User == null || DeviceID == null || DeviceType == null)
throw new InvalidDataException("ASCommandRequest not initialized.");
if (UseEncodedRequestLine == true)
{
EncodedRequest encRequest = new EncodedRequest();
encRequest.ProtocolVersion = Convert.ToByte(Convert.ToSingle(ProtocolVersion) * 10);
encRequest.SetCommandCode(Command);
encRequest.SetLocale("en-us");
encRequest.DeviceId = DeviceID;
encRequest.DeviceType = DeviceType;
encRequest.PolicyKey = PolicyKey;
encRequest.AddCommandParameter("User", user);
if (CommandParameters != null)
{
for (int i = 0; i < parameters.Length; i++)
{
encRequest.AddCommandParameter(CommandParameters[i].Parameter, CommandParameters[i].Value);
}
}
RequestLine = encRequest.GetBase64EncodedString();
}
else
{
RequestLine = string.Format("Cmd={0}&User={1}&DeviceId={2}&DeviceType={3}",
Command, User, DeviceID, DeviceType);
if (CommandParameters != null)
{
for (int i = 0; i < parameters.Length; i++)
{
RequestLine = string.Format("{0}&{1}={2}", RequestLine,
CommandParameters[i].Parameter, CommandParameters[i].Value);
}
}
}
}
protected virtual void GenerateXMLPayload()
{
// For the base class, this is a no-op.
// Classes that extend this class to implement
// commands override this function to generate
// the XML payload based on the command's request schema.
}
private string DecodeWBXML(byte[] wbxml)
{
try
{
ASWBXML decoder = new ASWBXML();
decoder.LoadBytes(wbxml);
return decoder.GetXml();
}
catch (Exception ex)
{
VSError.ReportException(ex);
return "";
}
}
private byte[] EncodeXMLString(string stringXML)
{
try
{
ASWBXML encoder = new ASWBXML();
encoder.LoadXml(stringXML);
return encoder.GetBytes();
}
catch (Exception ex)
{
VSError.ReportException(ex);
return null;
}
}
}
}
ASCommandResponse.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
namespace VisualSync
{
class ASCommandResponse
{
private byte[] wbxmlBytes = null;
private string xmlString = null;
private HttpStatusCode httpStatus = HttpStatusCode.OK;
public byte[] WbxmlBytes
{
get
{
return wbxmlBytes;
}
}
public string XmlString
{
get
{
return xmlString;
}
}
public HttpStatusCode HttpStatus
{
get
{
return httpStatus;
}
}
public ASCommandResponse(HttpWebResponse httpResponse)
{
httpStatus = httpResponse.StatusCode;
Stream responseStream = httpResponse.GetResponseStream();
List<byte> bytes = new List<byte>();
byte[] byteBuffer = new byte[256];
int count = 0;
count = responseStream.Read(byteBuffer, 0, 256);
while (count > 0)
{
bytes.AddRange(byteBuffer);
if (count < 256)
{
int excess = 256 - count;
bytes.RemoveRange(bytes.Count - excess, excess);
}
count = responseStream.Read(byteBuffer, 0, 256);
}
wbxmlBytes = bytes.ToArray();
xmlString = DecodeWBXML(wbxmlBytes);
}
private string DecodeWBXML(byte[] wbxml)
{
try
{
ASWBXML decoder = new ASWBXML();
decoder.LoadBytes(wbxml);
return decoder.GetXml();
}
catch (Exception ex)
{
VSError.ReportException(ex);
return "";
}
}
}
}
EncodedRequest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestApp
{
struct EncodedParameter
{
public byte tag;
public byte length;
public string value;
}
class EncodedRequest
{
// MS-ASHTTP section 2.2.1.1.1.1
private byte protocolVersion = 0;
private byte commandCode = 0;
private Int32 locale = 0;
private byte deviceIdLength = 0;
private string deviceId = "";
private byte policyKeyLength = 0;
private UInt32 policyKey = 0;
private byte deviceTypeLength = 0;
private string deviceType = "";
private EncodedParameter[] commandParameters = null;
private Dictionary<string,byte> commandDictionary = new Dictionary<string,byte>();
private Dictionary<string,byte> parameterDictionary = new Dictionary<string,byte>();
private Dictionary<string, Int32> localeDictionary = new Dictionary<string, Int32>();
public EncodedRequest()
{
// Load command dictionary
commandDictionary.Add("SYNC", 0);
commandDictionary.Add("SENDMAIL", 1);
commandDictionary.Add("SMARTFORWARD", 2);
commandDictionary.Add("SMARTREPLY", 3);
commandDictionary.Add("GETATTACHMENT", 4);
commandDictionary.Add("FOLDERSYNC", 9);
commandDictionary.Add("FOLDERCREATE", 10);
commandDictionary.Add("FOLDERDELETE", 11);
commandDictionary.Add("FOLDERUPDATE", 12);
commandDictionary.Add("MOVEITEMS", 13);
commandDictionary.Add("GETITEMESTIMATE", 14);
commandDictionary.Add("MEETINGRESPONSE", 15);
commandDictionary.Add("SEARCH", 16);
commandDictionary.Add("SETTINGS", 17);
commandDictionary.Add("PING", 18);
commandDictionary.Add("ITEMOPERATIONS", 19);
commandDictionary.Add("PROVISION", 20);
commandDictionary.Add("RESOLVERECIPIENTS", 21);
commandDictionary.Add("VALIDATECERT", 22);
// Load parameter dictionary
parameterDictionary.Add("ATTACHMENTNAME", 0);
parameterDictionary.Add("ITEMID", 3);
parameterDictionary.Add("LONGID", 4);
parameterDictionary.Add("OCCURRENCE", 6);
parameterDictionary.Add("OPTIONS", 7);
parameterDictionary.Add("USER", 8);
// Load locale dictionary
// TODO: Add other locales
localeDictionary.Add("EN-US", 0x0409);
}
public string GetBase64EncodedString()
{
List<byte> bytes = new List<byte>();
// Fill in the byte array
bytes.Add(protocolVersion);
bytes.Add(commandCode);
bytes.AddRange(BitConverter.GetBytes((short)locale));
bytes.Add(deviceIdLength);
if (deviceIdLength > 0)
{
bytes.AddRange(ASCIIEncoding.ASCII.GetBytes(deviceId));
}
bytes.Add(policyKeyLength);
if (policyKeyLength > 0)
{
bytes.AddRange(BitConverter.GetBytes(policyKey));
}
bytes.Add(deviceTypeLength);
if (deviceTypeLength > 0)
{
bytes.AddRange(ASCIIEncoding.ASCII.GetBytes(deviceType));
}
if (commandParameters != null)
{
for (int i = 0; i < commandParameters.Length; i++)
{
bytes.Add(commandParameters[i].tag);
bytes.Add(commandParameters[i].length);
bytes.AddRange(ASCIIEncoding.ASCII.GetBytes(commandParameters[i].value));
}
}
return Convert.ToBase64String(bytes.ToArray());
}
public byte ProtocolVersion
{
get
{
return protocolVersion;
}
set
{
protocolVersion = value;
}
}
public byte CommandCode
{
get
{
return commandCode;
}
}
public bool SetCommandCode(string strCommand)
{
return commandDictionary.TryGetValue(strCommand.ToUpper(), out commandCode);
}
public Int32 Locale
{
get
{
return locale;
}
}
public bool SetLocale(string strLocale)
{
return localeDictionary.TryGetValue(strLocale.ToUpper(), out locale);
}
public Int32 DeviceIdLength
{
get
{
return Convert.ToInt32(deviceIdLength);
}
}
public string DeviceId
{
get
{
return deviceId;
}
set
{
deviceId = value;
deviceIdLength = Convert.ToByte(deviceId.Length);
}
}
public Int32 PolicyKeyLength
{
get
{
return Convert.ToInt32(policyKeyLength);
}
}
public UInt32 PolicyKey
{
get
{
return policyKey;
}
set
{
policyKey = value;
policyKeyLength = Convert.ToByte(sizeof(Int32));
}
}
public Int32 DeviceTypeLength
{
get
{
return Convert.ToInt32(deviceTypeLength);
}
}
public string DeviceType
{
get
{
return deviceType;
}
set
{
deviceType = value;
deviceTypeLength = Convert.ToByte(deviceType.Length);
}
}
public EncodedParameter[] CommandParameters
{
get
{
return commandParameters;
}
}
public bool AddCommandParameter(string parameterName, string parameterValue)
{
EncodedParameter newParameter;
byte parameterTag;
if (parameterDictionary.TryGetValue(parameterName.ToUpper(), out parameterTag))
{
newParameter.tag = parameterTag;
newParameter.value = parameterValue;
newParameter.length = Convert.ToByte(parameterValue.Length);
if (commandParameters == null)
{
commandParameters = new EncodedParameter[1];
commandParameters[0] = newParameter;
}
else
{
Array.Resize(ref commandParameters, commandParameters.Length + 1);
commandParameters[commandParameters.Length - 1] = newParameter;
}
return true;
}
return false;
}
}
}
Related resources
Exploring the Microsoft Exchange Server Open Specifications
[MS-ASHTTP]: ActiveSync HTTP Protocol Specification
[MS-ASPROV]: ActiveSync Provisioning Protocol Specification
[MS-ASWBXML]: ActiveSync WAP Binary XML (WBXML) Protocol Specification