// This function updates a folder tree based on the // changes received from the server in the response. public void UpdateFolderTree(Folder rootFolder) { rootFolder.LastSyncTime = DateTime.Now.ToUniversalTime(); try { // Get sync key XmlNode syncKeyNode = responseXml.SelectSingleNode(".//folderhierarchy:SyncKey", xmlNsMgr); rootFolder.SyncKey = syncKeyNode.InnerText; // Process adds (new folders) first XmlNodeList addNodes = responseXml.SelectNodes(".//folderhierarchy:Add", xmlNsMgr); foreach (XmlNode addNode in addNodes) { XmlNode nameNode = addNode.SelectSingleNode(".//folderhierarchy:DisplayName", xmlNsMgr); XmlNode serverIdNode = addNode.SelectSingleNode(".//folderhierarchy:ServerId", xmlNsMgr); XmlNode parentIdNode = addNode.SelectSingleNode(".//folderhierarchy:ParentId", xmlNsMgr); XmlNode typeNode = addNode.SelectSingleNode(".//folderhierarchy:Type", xmlNsMgr); rootFolder.AddFolder(nameNode.InnerText, serverIdNode.InnerText, parentIdNode.InnerText, (Folder.FolderType)XmlConvert.ToInt32(typeNode.InnerText)); } // Then process deletes XmlNodeList deleteNodes = responseXml.SelectNodes(".//folderhierarchy:Delete", xmlNsMgr); foreach (XmlNode deleteNode in deleteNodes) { XmlNode serverIdNode = deleteNode.SelectSingleNode(".//folderhierarchy:ServerId", xmlNsMgr); Folder removeFolder = rootFolder.FindFolderById(serverIdNode.InnerText); removeFolder.Remove(); } // Finally process any updates to existing folders XmlNodeList updateNodes = responseXml.SelectNodes(".//folderhierarchy:Update", xmlNsMgr); foreach (XmlNode updateNode in updateNodes) { XmlNode nameNode = updateNode.SelectSingleNode(".//folderhierarchy:DisplayName", xmlNsMgr); XmlNode serverIdNode = updateNode.SelectSingleNode(".//folderhierarchy:ServerId", xmlNsMgr); XmlNode parentIdNode = updateNode.SelectSingleNode(".//folderhierarchy:ParentId", xmlNsMgr); XmlNode typeNode = updateNode.SelectSingleNode(".//folderhierarchy:Type", xmlNsMgr); Folder updateFolder = rootFolder.FindFolderById(serverIdNode.InnerText); Folder updateParent = rootFolder.FindFolderById(parentIdNode.InnerText); updateFolder.Update(nameNode.InnerText, updateParent, (Folder.FolderType)XmlConvert.ToInt32(typeNode.InnerText)); } } catch (Exception e) { // Rather than attempting to recover, reset sync key // and empty folders. The next FolderSync should // re-sync folders. rootFolder.SyncKey = "0"; rootFolder.RemoveAllSubFolders(); } rootFolder.SaveFolderInfo(); }
static void Main(string[] args) { NetworkCredential userCredentials = new NetworkCredential( string.Format("{0}\\{1}", domainName, userName), password); #region OPTIONS Request Console.WriteLine("Sending OPTIONS request to server..."); ASOptionsRequest optionsRequest = new ASOptionsRequest(); optionsRequest.Server = activeSyncServer; optionsRequest.UseSSL = useSSL; optionsRequest.Credentials = userCredentials; // Send the OPTIONS request ASOptionsResponse optionsResponse = optionsRequest.GetOptions(); if (optionsResponse == null) { ReportError("Unable to connect to server."); return; } Console.WriteLine("Supported ActiveSync versions: {0}", optionsResponse.SupportedVersions); Console.WriteLine("Supported ActiveSync commands: {0}\n", optionsResponse.SupportedCommands); // Make sure version 14.1 is supported if (optionsResponse.HighestSupportedVersion != "14.1") { ReportError("+++ The server does not support ActiveSync version 14.1. +++"); return; } #endregion #region Provisioning // Initialize the policy key uint policyKey = 0; // Provide details about the device Device deviceToProvision = new Device(); deviceToProvision.DeviceID = "EX2010activesyncfoldercs"; deviceToProvision.DeviceType = "Sample Application"; deviceToProvision.Model = "Sample Model"; deviceToProvision.FriendlyName = "FolderSync/Sync Example"; deviceToProvision.OperatingSystem = "Sample OS 1.0"; deviceToProvision.OperatingSystemLanguage = "English"; deviceToProvision.MobileOperator = "Phone Company"; deviceToProvision.UserAgent = "EX2010_activesyncfolder_cs_1.0"; deviceToProvision.PhoneNumber = "425-555-1000"; // Phase 1: Initial provision request // During this phase, the client requests the policy // and the server sends the policy details, along with a temporary // policy key. ASProvisionRequest initialProvisionRequest = new ASProvisionRequest(); // Initialize the request with information // that applies to all requests. initialProvisionRequest.Credentials = userCredentials; initialProvisionRequest.Server = activeSyncServer; initialProvisionRequest.User = userName; initialProvisionRequest.DeviceID = deviceToProvision.DeviceID; initialProvisionRequest.DeviceType = deviceToProvision.DeviceType; initialProvisionRequest.ProtocolVersion = "14.1"; initialProvisionRequest.PolicyKey = policyKey; initialProvisionRequest.UseEncodedRequestLine = useBase64RequestLine; // Initialize the Provision command-specific // information. initialProvisionRequest.ProvisionDevice = deviceToProvision; Console.WriteLine("Sending initial provision request..."); // Send the request ASProvisionResponse initialProvisionResponse = (ASProvisionResponse)initialProvisionRequest.GetResponse(); Console.WriteLine("Initial Provision Request:"); Console.WriteLine(initialProvisionRequest.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); Console.WriteLine("Initial Provision Response:"); Console.WriteLine(initialProvisionResponse.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); // Check the result of the initial request. // The server may have requested a remote wipe, or // may have returned an error. Console.WriteLine("Response Status: {0}", initialProvisionResponse.Status); if (initialProvisionResponse.Status == (Int32)ASProvisionResponse.ProvisionStatus.Success) { if (initialProvisionResponse.IsPolicyLoaded) { if (initialProvisionResponse.Policy.Status == (Int32)ASProvisionResponse.ProvisionStatus.Success) { if (initialProvisionResponse.Policy.RemoteWipeRequested) { // The server requested a remote wipe. // The client must acknowledge it. Console.WriteLine("+++ The server has requested a remote wipe. +++\n"); ASProvisionRequest remoteWipeAcknowledgment = new ASProvisionRequest(); // Initialize the request with information // that applies to all requests. remoteWipeAcknowledgment.Credentials = userCredentials; remoteWipeAcknowledgment.Server = activeSyncServer; remoteWipeAcknowledgment.User = userName; remoteWipeAcknowledgment.DeviceID = deviceToProvision.DeviceID; remoteWipeAcknowledgment.DeviceType = deviceToProvision.DeviceType; remoteWipeAcknowledgment.ProtocolVersion = "14.1"; remoteWipeAcknowledgment.PolicyKey = policyKey; remoteWipeAcknowledgment.UseEncodedRequestLine = useBase64RequestLine; // Initialize the Provision command-specific // information. remoteWipeAcknowledgment.IsRemoteWipe = true; // Indicate successful wipe remoteWipeAcknowledgment.Status = (int)ASProvisionResponse.ProvisionStatus.Success; Console.WriteLine("Sending remote wipe acknowledgment..."); // Send the acknowledgment ASProvisionResponse remoteWipeAckResponse = (ASProvisionResponse)remoteWipeAcknowledgment.GetResponse(); Console.WriteLine("Remote Wipe Acknowledgment Request:"); Console.WriteLine(remoteWipeAcknowledgment.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); Console.WriteLine("Remote Wipe Acknowledgment Response:"); Console.WriteLine(remoteWipeAckResponse.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); Console.WriteLine("Remote wipe acknowledgment response status: {0}", remoteWipeAckResponse.Status); } else { // The server has provided a policy // and a temporary policy key. // The client must acknowledge this policy // in order to get a permanent policy // key. Console.WriteLine("Policy retrieved from the server."); Console.WriteLine("Temporary policy key: {0}\n", initialProvisionResponse.Policy.PolicyKey); ASProvisionRequest policyAcknowledgment = new ASProvisionRequest(); // Initialize the request with information // that applies to all requests. policyAcknowledgment.Credentials = userCredentials; policyAcknowledgment.Server = activeSyncServer; policyAcknowledgment.User = userName; policyAcknowledgment.DeviceID = deviceToProvision.DeviceID; policyAcknowledgment.DeviceType = deviceToProvision.DeviceType; policyAcknowledgment.ProtocolVersion = "14.1"; // Set the policy key to the temporary policy key from // the previous response. policyAcknowledgment.PolicyKey = initialProvisionResponse.Policy.PolicyKey; policyAcknowledgment.UseEncodedRequestLine = useBase64RequestLine; // Initialize the Provision command-specific // information. policyAcknowledgment.IsAcknowledgement = true; // Indicate successful application of the policy. policyAcknowledgment.Status = (int)ASProvisionRequest.PolicyAcknowledgement.Success; Console.WriteLine("Sending policy acknowledgment..."); // Send the request ASProvisionResponse policyAckResponse = (ASProvisionResponse)policyAcknowledgment.GetResponse(); Console.WriteLine("Policy Acknowledgment Request:"); Console.WriteLine(policyAcknowledgment.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); Console.WriteLine("Policy Acknowledgment Response:"); Console.WriteLine(policyAckResponse.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); Console.WriteLine("Response status: {0}", policyAckResponse.Status); if (policyAckResponse.Status == (int)ASProvisionResponse.ProvisionStatus.Success && policyAckResponse.IsPolicyLoaded) { Console.WriteLine("Policy acknowledgment successful."); Console.WriteLine("Permanent Policy Key: {0}", policyAckResponse.Policy.PolicyKey); // Save the permanent policy key for use // in subsequent command requests. policyKey = policyAckResponse.Policy.PolicyKey; } else { Console.WriteLine("Error returned from policy acknowledgment request: {0}", policyAckResponse.Status); } } } else { Console.WriteLine("Policy Error returned from initial provision request: {0}", initialProvisionResponse.Policy.Status); } } } else { Console.WriteLine("Error returned from initial provision request: {0}", initialProvisionResponse.Status); } // If we did not end up with a permanent policy // key, then exit. if (policyKey == 0) { Console.WriteLine("Provisioning failed, cannot continue."); Console.WriteLine("+++ HIT ENTER TO EXIT +++"); Console.ReadLine(); return; } #endregion #region Sync Folder Hierarchy // Initialize local storage of mailbox Folder rootFolder = new Folder(mailboxCacheLocation); ASFolderSyncRequest folderSyncRequest = new ASFolderSyncRequest(); // Initialize the request with information // that applies to all requests. folderSyncRequest.Credentials = userCredentials; folderSyncRequest.Server = activeSyncServer; folderSyncRequest.User = userName; folderSyncRequest.DeviceID = deviceToProvision.DeviceID; folderSyncRequest.DeviceType = deviceToProvision.DeviceType; folderSyncRequest.ProtocolVersion = "14.1"; folderSyncRequest.PolicyKey = policyKey; folderSyncRequest.UseEncodedRequestLine = useBase64RequestLine; // Initialize the FolderSync command-specific // information. folderSyncRequest.SyncKey = rootFolder.SyncKey; // Send the request Console.WriteLine("Sending FolderSync request..."); ASFolderSyncResponse folderSyncResponse = (ASFolderSyncResponse)folderSyncRequest.GetResponse(); if (folderSyncResponse == null) { ReportError("Unable to connect to server."); return; } Console.WriteLine("FolderSync Request:"); Console.WriteLine(folderSyncRequest.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); Console.WriteLine("FolderSync Response:"); Console.WriteLine(folderSyncResponse.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); Console.WriteLine("Response Status: {0}", folderSyncResponse.Status); if (folderSyncResponse.Status == (int)ASFolderSyncResponse.FolderSyncStatus.Success) { // Populate the folder hierarchy from the // response folderSyncResponse.UpdateFolderTree(rootFolder); } else { Console.WriteLine("Error returned from FolderSync request: {0}", folderSyncResponse.Status); } // Check that we have subfolders and // that "Inbox" is one of them. Folder inboxFolder = null; foreach (Folder subFolder in rootFolder.SubFolders) { if (subFolder.Name == "Inbox") { inboxFolder = subFolder; break; } } if (inboxFolder == null) { // Folders didn't sync or somehow // Inbox wasn't one of them, exit. ReportError("+++ Inbox not found in hierarchy. +++"); return; } Console.WriteLine("Inbox found, Id: {0}, Sync Key: {1}, Last sync: {2}\n", inboxFolder.Id, inboxFolder.SyncKey, inboxFolder.LastSyncTime); #endregion #region Sync Inbox Contents // If this is the first time syncing Inbox // (Sync Key = 0), then you must // send an initial sync request to "prime" // the sync state. if (inboxFolder.SyncKey == "0") { ASSyncRequest initialSyncRequest = new ASSyncRequest(); // Initialize the request with information // that applies to all requests. initialSyncRequest.Credentials = userCredentials; initialSyncRequest.Server = activeSyncServer; initialSyncRequest.User = userName; initialSyncRequest.DeviceID = deviceToProvision.DeviceID; initialSyncRequest.DeviceType = deviceToProvision.DeviceType; initialSyncRequest.ProtocolVersion = "14.1"; initialSyncRequest.PolicyKey = policyKey; initialSyncRequest.UseEncodedRequestLine = useBase64RequestLine; // Initialize the Sync command-specific // information. // Add the Inbox to the folders to be synced initialSyncRequest.Folders.Add(inboxFolder); Console.WriteLine("Sending initial Sync request to prime the sync state for Inbox..."); // Send the request ASSyncResponse initialSyncResponse = (ASSyncResponse)initialSyncRequest.GetResponse(); if (initialSyncResponse == null) { ReportError("Unable to connect to server."); return; } Console.WriteLine("Response status: {0}", initialSyncResponse.Status); Console.WriteLine("Initial Sync Request:"); Console.WriteLine(initialSyncRequest.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); Console.WriteLine("Initial Sync Response:"); Console.WriteLine(initialSyncResponse.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); if (initialSyncResponse.Status == (int)ASSyncResponse.SyncStatus.Success) { inboxFolder.SyncKey = initialSyncResponse.GetSyncKeyForFolder(inboxFolder.Id); } else { string errorMessage = string.Format("Error returned from empty sync reqeust: {0}", initialSyncResponse.Status); ReportError(errorMessage); return; } } ASSyncRequest inboxSyncRequest = new ASSyncRequest(); // Initialize the request with information // that applies to all requests. inboxSyncRequest.Credentials = userCredentials; inboxSyncRequest.Server = activeSyncServer; inboxSyncRequest.User = userName; inboxSyncRequest.DeviceID = deviceToProvision.DeviceID; inboxSyncRequest.DeviceType = deviceToProvision.DeviceType; inboxSyncRequest.ProtocolVersion = "14.1"; inboxSyncRequest.PolicyKey = policyKey; inboxSyncRequest.UseEncodedRequestLine = useBase64RequestLine; // Initialize the Sync command-specific // information. // Add the Inbox to the folders to be synced inboxSyncRequest.Folders.Add(inboxFolder); // You can specify a Wait or HeartBeatInterval // to direct the server to delay a response // until something changes. // NOTE: Per MS-ASCMD, don't include both // a Wait and a HeartBeatInterval. // The value of HeartBeatInterval is in seconds inboxSyncRequest.HeartBeatInterval = 60; // The value of Wait is in minutes //inboxSyncRequest.Wait = 5; // You can specify a maximum number of changes // that the server should send in the response. // Uncomment the line below to specify a maximum. //inboxSyncRequest.WindowSize = 50; // You can also specify sync options on a specific // folder. This is useful if you are syncing // multiple folders in a single Sync command request // and need to specify different options for each. inboxFolder.UseConversationMode = true; inboxFolder.AreDeletesPermanent = false; // Further options are available in the <Options> // element as a child of the <Colletion> element. // (See MS-ASCMD section 2.2.3.115.5) inboxFolder.Options = new FolderSyncOptions(); inboxFolder.Options.BodyPreference = new BodyPreferences[1]; inboxFolder.Options.BodyPreference[0] = new BodyPreferences(); inboxFolder.Options.BodyPreference[0].Type = BodyType.HTML; inboxFolder.Options.BodyPreference[0].TruncationSize = 500; inboxFolder.Options.BodyPreference[0].AllOrNone = false; inboxFolder.Options.BodyPreference[0].Preview = 100; Console.WriteLine("Sending Inbox Sync request..."); // Send the request ASSyncResponse inboxSyncResponse = (ASSyncResponse)inboxSyncRequest.GetResponse(); if (inboxSyncResponse == null) { ReportError("Unable to connect to server."); return; } Console.WriteLine("Response status: {0}", inboxSyncResponse.Status); Console.WriteLine("Inbox Sync Request:"); Console.WriteLine(inboxSyncRequest.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); Console.WriteLine("Inbox Sync Response:"); Console.WriteLine(inboxSyncResponse.XmlString); Console.WriteLine("+++ HIT ENTER TO CONTINUE +++"); Console.ReadLine(); if (inboxSyncResponse.Status == (int)ASSyncResponse.SyncStatus.Success) { List <ServerSyncCommand> addCommands = inboxSyncResponse.GetServerAddsForFolder(inboxFolder.Id); if (addCommands != null) { Console.WriteLine("Adding {0} items...", addCommands.Count); SaveItemsInLocalFolder(inboxFolder, addCommands); } inboxFolder.SyncKey = inboxSyncResponse.GetSyncKeyForFolder(inboxFolder.Id); inboxFolder.LastSyncTime = DateTime.Now; // Call SaveFolderInfo to update the last sync time // and sync key of the inbox. rootFolder.SaveFolderInfo(); } else if (inboxSyncResponse.HttpStatus == HttpStatusCode.OK) { // The server had no changes // to send. At this point you could // repeat the last sync request by sending // an empty sync request (MS-ASCMD section 2.2.2.19.1.1) // This sample does not implement repeating the request, // so just notify the user. Console.WriteLine("No changes available on server."); inboxFolder.LastSyncTime = DateTime.Now; // Call SaveFolderInfo to update the last sync time rootFolder.SaveFolderInfo(); } else { Console.WriteLine("Error returned from Inbox sync request: {0}", inboxSyncResponse.Status); } #endregion Console.WriteLine("+++ HIT ENTER TO EXIT +++"); Console.ReadLine(); }
// This function updates an existing folder by either // renaming it or moving it. public void Update(string folderName, Folder newParent, Folder.FolderType folderType) { // Set the new name name = folderName; // Check to see if we are moving the folder. if (newParent != parentFolder) { // Remove this folder from the current parent's // subfolders parentFolder.RemoveSubFolder(this); parentFolder = newParent; // Add this folder to the new parent's subfolders newParent.AddSubFolder(this); } // Call MoveLocalFolder to move the local data to a new // directory. MoveLocalFolder(newParent.SaveLocation + "\\" + name); }
// This function removes a folder from the list of // subfolders. public bool RemoveSubFolder(Folder removeFolder) { return subFolders.Remove(removeFolder); }
// This function adds a Folder object to the list of subfolders. public void AddSubFolder(Folder newFolder) { subFolders.Add(newFolder); }
// This function creates a new Folder object and adds it to the list of subfolders. public Folder AddSubFolder(string folderName, string folderId, FolderType folderType) { Folder newFolder = new Folder(folderName, folderId, folderType, this); subFolders.Add(newFolder); return newFolder; }
// Constructor that creates a Folder object based on the basic // properties of the folder. This form is used when building // the folder hierarchy under the root. public Folder(string folderName, string folderId, FolderType folderType, Folder parent) { name = folderName; id = folderId; parentFolder = parent; type = folderType; saveLocation = parent.SaveLocation + "\\" + folderName; Directory.CreateDirectory(saveLocation); subFolders = new List<Folder>(); }