예제 #1
0
        public async Task <ExchangeSyncResult> ExecuteFirstSyncAsync(ExchangeConnectionInfo connectionInfo, ExchangeChangeSet changeSet)
        {
            ExchangeSyncResult loginResult = await this.EnsureLoginAsync(this.taskFolderId, connectionInfo);

            if (loginResult != null)
            {
                return(loginResult);
            }

            ActiveSyncServer server = CreateExchangeServer(connectionInfo);

            // Start sync with sync state = 0
            // In this case we only obtain a non ero syncKey to use with future queries
            SyncCommandResult syncCommandResult = await server.Sync(InitialSyncKey, this.taskFolderId, new ExchangeChangeSet());

            connectionInfo.PolicyKey = server.PolicyKey;

            if (syncCommandResult.Status != StatusOk)
            {
                var result = new ExchangeSyncResult
                {
                    AuthorizationResult = this.GetFailedAuthResult("ExecuteFirstSync", syncCommandResult)
                };

                return(result);
            }

            // As it is first sync, we have just picked a new SyncId
            // We have to re-sync with this new SyncId
            return(await this.ExecuteSyncAsync(connectionInfo, changeSet, syncCommandResult.SyncKey, this.taskFolderId, true));
        }
예제 #2
0
        private void ProcessSyncResult(ExchangeChangeSet sourceChangeSet, ExchangeSyncResult result, bool sendCompletedNotification = true)
        {
            if (this.isCanceled)
            {
                throw new OperationCanceledException();
            }

            if (result.AuthorizationResult.IsOperationSuccess)
            {
                this.Metadata.Reset();

                var changeSet = result.ChangeSet;

                this.Changes.WebAdd    = result.TaskAddedCount;
                this.Changes.WebEdit   = result.TaskEditedCount;
                this.Changes.WebDelete = result.TaskDeletedCount;

                // update task sent in the sourceChangeSet
                foreach (var exchangeTask in sourceChangeSet.AddedTasks)
                {
                    // find the task in the workbook
                    var task = this.Workbook.Tasks.FirstOrDefault(t => t.Id == exchangeTask.LocalId);
                    if (task != null)
                    {
                        var map = result.MapId.FirstOrDefault(m => m.LocalId == task.Id);
                        if (map != null)
                        {
                            this.PrepareTaskUpdate(task);

                            task.SyncId = map.ExchangeId;
                        }
                    }
                }

                LogService.LogFormat(
                    "ExchangeSync", "Processing result sent by server changeset with {0} added tasks {1} deleted tasks and {2} edited tasks",
                    changeSet.AddedTasks.Count, changeSet.DeletedTasks.Count, changeSet.ModifiedTasks.Count);

                foreach (var exchangeTask in changeSet.AddedTasks.Where(t => !string.IsNullOrEmpty(t.Subject)))
                {
                    // find the target folder by looking using its name
                    var folder = this.GetFolderForExchangeTask(exchangeTask);

                    var candidate = folder.Tasks.FirstOrDefault(t => t.SyncId == exchangeTask.Id);
                    if (candidate == null)
                    {
                        // check that we don't have already the "same" task
                        candidate = this.FindTask(folder.Tasks, exchangeTask.Subject, exchangeTask.Due);
                        if (candidate == null)
                        {
                            // create the task and add it to its folder
                            var task = exchangeTask.ToTask(this.Workbook);
                            this.AttachTaskToFolder(task, folder);

                            this.Changes.LocalAdd++;
                        }
                        else if (string.IsNullOrEmpty(candidate.SyncId))
                        {
                            candidate.SyncId = exchangeTask.Id;
                        }
                    }
                }

                foreach (var deletedAsset in changeSet.DeletedTasks)
                {
                    var task = this.Workbook.Tasks.FirstOrDefault(t => t.SyncId == deletedAsset.Id);
                    if (task != null)
                    {
                        this.DeleteTask(task);
                        this.Changes.LocalDelete++;
                    }
                }

                foreach (var exchangeTask in changeSet.ModifiedTasks)
                {
                    var task = this.Workbook.Tasks.FirstOrDefault(t => this.HaveSameId(t, exchangeTask));
                    if (task != null)
                    {
                        this.PrepareTaskUpdate(task);
                        task.UpdateFromExchange(exchangeTask);

                        var requiredFolder = this.GetFolderForExchangeTask(exchangeTask);
                        if (task.Folder != requiredFolder)
                        {
                            task.Folder = requiredFolder;
                        }

                        this.Changes.LocalEdit++;
                    }
                    else
                    {
                        LogService.LogFormat("ExchangeSync", "Task {0} has been modified on the server but cannot be found in the workbook", exchangeTask.Subject);
                    }
                }

                if (result.OperationResult.IsOperationSuccess)
                {
                    this.SyncState = result.SyncState;

                    if (sendCompletedNotification)
                    {
                        this.OnSynchronizationCompleted("Exchange");
                    }
                }
                else
                {
                    this.OnSynchronizationFailed(string.Format(ExchangeResources.Exchange_SyncErrorFormat, result.OperationResult.ErrorMessage));
                }
            }
            else
            {
                if (result.AuthorizationResult.Status != null && result.AuthorizationResult.Status.Equals("3", StringComparison.OrdinalIgnoreCase))
                {
                    this.OnSynchronizationFailed(StringResources.Exchange_InvalidSyncKeyWorkaround);
                }
                else
                {
                    this.OnSynchronizationFailed(string.Format(ExchangeResources.Exchange_AuthProblemFormat, result.AuthorizationResult.AuthorizationStatus, result.AuthorizationResult.ErrorMessage));
                }
            }
        }
예제 #3
0
 private static bool IsExchangeSyncResultValid(ExchangeSyncResult exchangeSyncResult)
 {
     return(exchangeSyncResult != null && exchangeSyncResult.AuthorizationResult != null && exchangeSyncResult.AuthorizationResult.AuthorizationStatus == ExchangeAuthorizationStatus.OK);
 }
예제 #4
0
        private async Task <ExchangeSyncResult> EnsureAutoDiscover(ExchangeConnectionInfo connectionInfo)
        {
            var success = new ExchangeSyncResult
            {
                AuthorizationResult = new ExchangeAuthorizationResult
                {
                    AuthorizationStatus = ExchangeAuthorizationStatus.OK,
                    IsOperationSuccess  = true
                },
                OperationResult = new ServiceOperationResult
                {
                    IsOperationSuccess = true
                },
            };

            // check Office 365 server uri is correct (ie. https://outlook.office365.com/EWS/Exchange.asmx)
            if (connectionInfo.ServerUri != null)
            {
                string uri = connectionInfo.ServerUri.ToString().ToLowerInvariant();
                if (uri.Contains("outlook.office365.com") && !uri.EndsWith("/ews/exchange.asmx"))
                {
                    connectionInfo.ServerUri = SafeUri.Get("https://outlook.office365.com/EWS/Exchange.asmx");
                }

                if (uri.Contains("/ews/exchange.asmx/ews/exchange.asmx"))
                {
                    connectionInfo.ServerUri = SafeUri.Get(uri.Replace("/ews/exchange.asmx/ews/exchange.asmx", "/EWS/Exchange.asmx"));
                }

                // check if the server uri looks correct, if it doesn't, run autodiscover
                var server = new EwsSyncServer(connectionInfo.CreateEwsSettings());
                try
                {
                    var identifiers = await server.GetRootFolderIdentifiersAsync(useCache : false);

                    if (identifiers != null)
                    {
                        return(success);
                    }
                }
                catch (Exception ex)
                {
                    if (connectionInfo.ServerUri != null && !connectionInfo.ServerUri.ToString().EndsWith("/ews/exchange.asmx", StringComparison.OrdinalIgnoreCase))
                    {
                        // one more try with the /ews/exchange.asmx path
                        connectionInfo.ServerUri = SafeUri.Get(uri.TrimEnd('/') + "/ews/exchange.asmx");
                        var secondResult = await this.EnsureAutoDiscover(connectionInfo);

                        if (IsExchangeSyncResultValid(secondResult))
                        {
                            return(success);
                        }
                    }
                    LogService.Log("EwsSyncService", $"Server uri is {connectionInfo.ServerUri} but GetRootFolderIds failed ({ex.Message}), fallbacking to classic autodiscover");
                }
            }

            LogService.Log("EwsSyncService", "Server uri is empty, performing auto discover");

            var engine             = new AutoDiscoverEngine();
            var autoDiscoverResult = await engine.AutoDiscoverAsync(connectionInfo.Email, connectionInfo.Username, connectionInfo.Password, connectionInfo.Version);

            if (autoDiscoverResult != null)
            {
                if (autoDiscoverResult.ServerUri != null)
                {
                    LogService.Log("EwsSyncService", "Now using server uri: " + autoDiscoverResult.ServerUri);
                    connectionInfo.ServerUri = autoDiscoverResult.ServerUri;
                }

                if (!string.IsNullOrWhiteSpace(autoDiscoverResult.RedirectEmail))
                {
                    LogService.Log("EwsSyncService", "Now using email redirect: " + autoDiscoverResult.RedirectEmail);
                    connectionInfo.Email = autoDiscoverResult.RedirectEmail;
                }
                return(success);
            }

            LogService.Log("EwsSyncService", "Auto discovery failed");

            return(new ExchangeSyncResult
            {
                AuthorizationResult = new ExchangeAuthorizationResult
                {
                    AuthorizationStatus = ExchangeAuthorizationStatus.AutodiscoveryServiceNotFound,
                    ErrorMessage = "Check credentials are valid and/or try to specify manually a valid server address"
                }
            });
        }
예제 #5
0
        public async Task <ExchangeSyncResult> ExecuteSyncAsync(ExchangeConnectionInfo connectionInfo, ExchangeChangeSet changeSet, string syncState, string folderId)
        {
            if (connectionInfo == null)
            {
                throw new ArgumentNullException(nameof(connectionInfo));
            }
            if (changeSet == null)
            {
                throw new ArgumentNullException(nameof(changeSet));
            }

            var outChangeSet = new ExchangeChangeSet();

            var autoDiscoverResult = await this.EnsureAutoDiscover(connectionInfo);

            if (!IsExchangeSyncResultValid(autoDiscoverResult))
            {
                return(autoDiscoverResult);
            }

            var server = new EwsSyncServer(connectionInfo.CreateEwsSettings());

            var identifiers = await server.GetRootFolderIdentifiersAsync();

            //var subFolders = await server.GetSubFoldersAsync(identifiers.TaskFolderIdentifier);

            //var flaggedItemFolders = await this.EnsureSearchFolder(server);

            /*
             * var a = await server.EnumerateFolderContentAsync(flaggedItemFolders);
             * var d = await server.DownloadFolderContentAsync(a);
             */

            var mapId = new List <ExchangeMapId>();

            await this.PushLocalChanges(changeSet, server, mapId, identifiers.TaskFolderIdentifier);

            var remoteIdentifiers = await this.ApplyRemoteChanges(outChangeSet, server, mapId, identifiers.TaskFolderIdentifier, EwsItemType.Task);

            /*foreach (var subFolder in subFolders.Folders)
             * {
             *  await this.ApplyRemoteChanges(outChangeSet, server, mapId, subFolder);
             * }*/

            if (connectionInfo.SyncFlaggedItems)
            {
                try
                {
                    var identifier = await this.EnsureSearchFolderAsync(server);

                    var otherRemoteIdentifiers = await this.ApplyRemoteChanges(outChangeSet, server, mapId, identifier, EwsItemType.Item);

                    remoteIdentifiers.AddRange(otherRemoteIdentifiers);
                }
                catch (Exception ex)
                {
                    LogService.Log("EwsSyncService", $"Exception while syncing flagged items: {ex}");
                    TrackingManagerHelper.Exception(ex, "Exception while syncing flagged items");
                }
            }

            // check for deleted items
            foreach (var task in this.workbook.Tasks.Where(t => t.SyncId != null))
            {
                var remoteId = remoteIdentifiers.FirstOrDefault(i => i.Id == task.SyncId.GetId());
                if (remoteId == null)
                {
                    // local item not found on server because it was deleted => delete
                    outChangeSet.DeletedTasks.Add(new ServerDeletedAsset(task.SyncId));
                }
            }

            var result = new ExchangeSyncResult
            {
                AuthorizationResult = new ExchangeAuthorizationResult
                {
                    Status = "OK",
                    AuthorizationStatus = ExchangeAuthorizationStatus.OK,
                    IsOperationSuccess  = true,
                    ServerUri           = connectionInfo.ServerUri
                },
                ChangeSet       = outChangeSet,
                MapId           = mapId,
                SyncState       = identifiers.TaskFolderIdentifier.ChangeKey,
                OperationResult = new ServiceOperationResult
                {
                    IsOperationSuccess = true
                }
            };

            return(result);
        }
예제 #6
0
        private async Task <ExchangeSyncResult> ExecuteSyncAsync(ExchangeConnectionInfo connectionInfo, ExchangeChangeSet changeSet, string syncState, string folderId, bool isFirstSync)
        {
            if (connectionInfo == null)
            {
                throw new ArgumentNullException("connectionInfo");
            }
            if (changeSet == null)
            {
                throw new ArgumentNullException("changeSet");
            }
            if (string.IsNullOrEmpty(syncState))
            {
                throw new ArgumentNullException("syncState");
            }

            if (string.IsNullOrEmpty(folderId) || connectionInfo.ServerUri == null || string.IsNullOrWhiteSpace(connectionInfo.ServerUri.ToString()))
            {
                ExchangeSyncResult loginResult = await this.EnsureLoginAsync(folderId, connectionInfo);

                if (loginResult != null)
                {
                    return(loginResult);
                }
            }
            else
            {
                this.taskFolderId = folderId;
            }

            ActiveSyncServer server = CreateExchangeServer(connectionInfo);

            ExchangeSyncResult returnValue = new ExchangeSyncResult
            {
                SyncState = syncState,
                ChangeSet = new ExchangeChangeSet()
            };

            bool mustSync = true;

            while (mustSync)
            {
                SyncCommandResult result = await server.Sync(returnValue.SyncState, this.taskFolderId, changeSet);

                if (result.Status != StatusOk)
                {
                    returnValue.AuthorizationResult = this.GetFailedAuthResult("Sync", result);
                    mustSync = false;
                }
                else
                {
                    returnValue.AuthorizationResult = new ExchangeAuthorizationResult
                    {
                        AuthorizationStatus = ExchangeAuthorizationStatus.OK,
                        IsOperationSuccess  = true,
                        ServerUri           = connectionInfo.ServerUri
                    };

                    if (result.SyncKey != null)
                    {
                        returnValue.SyncState = result.SyncKey;
                    }

                    connectionInfo.PolicyKey = server.PolicyKey;

                    // If we don't have any syncstate (nothing has changed) we return the old one
                    returnValue.OperationResult.IsOperationSuccess = true;

                    returnValue.ChangeSet.AddedTasks.AddRange(result.AddedTasks);
                    returnValue.ChangeSet.ModifiedTasks.AddRange(result.ModifiedTasks);
                    returnValue.ChangeSet.DeletedTasks.AddRange(result.DeletedTasks);
                    returnValue.TaskAddedCount  += result.ServerAddedTasks;
                    returnValue.TaskEditedCount += result.ServerModifiedTasks;

                    foreach (var map in result.ClientServerMapIds)
                    {
                        returnValue.AddMap(map.Key, map.Value);
                    }

                    returnValue.TaskDeletedCount += changeSet.DeletedTasks.Count;
                    mustSync = result.MoreAvailable;

                    changeSet = new ExchangeChangeSet(); // changeSet has been pushed to server, reset it !
                }
            }
            return(returnValue);
        }