private static void SyncAppointmentBothExists(AppointmentMatch m, AppointmentsSynchronizer sync) { //ToDo: Check how to overcome appointment recurrences, which need more than 60 seconds to update and therefore get updated again and again because of time tolerance 60 seconds violated again and again //merge appointment details //determine if this appointment pair were synchronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookAppointment, sync.OutlookPropertyNameUpdated); DateTime?lastSynced = AppointmentPropertiesUtils.GetOutlookLastSync(sync, m.OutlookAppointment); if (lastSynced.HasValue) { //appointment pair was syncronysed before. //determine if Google appointment was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = m.OutlookAppointment.LastModificationTime.AddSeconds(-m.OutlookAppointment.LastModificationTime.Second); DateTime lastUpdatedGoogle = m.GoogleAppointment.Updated.Value.AddSeconds(-m.GoogleAppointment.Updated.Value.Second); //consider GoogleAppointmentExceptions, because if they are updated, the master appointment doesn't have a new Saved TimeStamp foreach (var e in m.GoogleAppointmentExceptions) { if (e.Updated != null)//happens for cancelled events { DateTime lastUpdatedGoogleException = e.Updated.Value.AddSeconds(-e.Updated.Value.Second); if (lastUpdatedGoogleException > lastUpdatedGoogle) { lastUpdatedGoogle = lastUpdatedGoogleException; } } else if (m.OutlookAppointment.IsRecurring && m.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster) { Outlook.AppointmentItem outlookRecurrenceException = null; try { var slaveRecurrence = m.OutlookAppointment.GetRecurrencePattern(); if (e.OriginalStartTime != null && !string.IsNullOrEmpty(e.OriginalStartTime.Date)) { outlookRecurrenceException = slaveRecurrence.GetOccurrence(DateTime.Parse(e.OriginalStartTime.Date)); } else if (e.OriginalStartTime != null && e.OriginalStartTime.DateTime != null) { outlookRecurrenceException = slaveRecurrence.GetOccurrence(e.OriginalStartTime.DateTime.Value); } } catch (Exception ignored) { Logger.Log("Google Appointment with OriginalEvent found, but Outlook occurrence not found: " + e.Summary + " - " + e.OriginalStartTime.DateTime + ": " + ignored, EventType.Debug); } if (outlookRecurrenceException != null && outlookRecurrenceException.MeetingStatus != Outlook.OlMeetingStatus.olMeetingCanceled) { lastUpdatedGoogle = DateTime.Now; break; //no need to search further, already newest date set } } } //check if both outlok and Google appointments where updated sync last sync if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both appointments were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both appointments were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment Logger.Log("Outlook and Google appointment have been updated, Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + m.OutlookAppointment.Subject + ".", EventType.Information); sync.UpdateAppointment(m.OutlookAppointment, ref m.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment Logger.Log("Outlook and Google appointment have been updated, Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + m.GoogleAppointment.Summary + ".", EventType.Information); sync.UpdateAppointment(ref m.GoogleAppointment, m.OutlookAppointment, m.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { using (var r = new ConflictResolver()) { sync.ConflictResolution = r.Resolve(m.OutlookAppointment, m.GoogleAppointment, sync, false); } } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped appointment ({0}).", m.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(m.OutlookAppointment, ref m.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref m.GoogleAppointment, m.OutlookAppointment, m.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Cancelled"); } break; } return; } //check if Outlook appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //Outlook appointment was changed or changed Google appointment will be overwritten if ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) { Logger.Log("Google appointment has been updated since last sync, but Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + m.OutlookAppointment.Subject + ".", EventType.Information); } sync.UpdateAppointment(m.OutlookAppointment, ref m.GoogleAppointment); //at the moment use Outlook as "master" source of appointments - in the event of a conflict Google appointment will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if Google appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google appointment was changed or changed Outlook appointment will be overwritten if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) { Logger.Log("Outlook appointment has been updated since last sync, but Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + m.OutlookAppointment.Subject + ".", EventType.Information); } sync.UpdateAppointment(ref m.GoogleAppointment, m.OutlookAppointment, m.GoogleAppointmentExceptions); } } else { //appointments were never synced. //merge appointments. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment sync.UpdateAppointment(m.OutlookAppointment, ref m.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment sync.UpdateAppointment(ref m.GoogleAppointment, m.OutlookAppointment, m.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { using (var r = new ConflictResolver()) { sync.ConflictResolution = r.Resolve(m.OutlookAppointment, m.GoogleAppointment, sync, true); } } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Appointments.Add(new AppointmentMatch(m.OutlookAppointment, null)); sync.Appointments.Add(new AppointmentMatch(null, m.GoogleAppointment)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(m.OutlookAppointment, ref m.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref m.GoogleAppointment, m.OutlookAppointment, m.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Canceled"); } break; } } }
public static void SyncAppointment(AppointmentMatch match, Synchronizer sync) { if (match.GoogleAppointment == null && match.OutlookAppointment != null) { //no Google appointment string googleAppointmentId = AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); if (!string.IsNullOrEmpty(googleAppointmentId)) { //if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster && // (Syncronizer.MonthsInPast == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.End.Month, match.OutlookAppointment.End.Day) >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) && // (Syncronizer.MonthsInFuture == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.Start.Month, match.OutlookAppointment.Start.Day) <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture)) // || // (Syncronizer.MonthsInPast == 0 || match.OutlookAppointment.End >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) && // (Syncronizer.MonthsInFuture == 0 || match.OutlookAppointment.Start <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture)) // ) //{ //Redundant check if exist, but in case an error occurred in MatchAppointments or not all appointments have been loaded (e.g. because months before/after constraint) Event matchingGoogleAppointment = null; if (sync.AllGoogleAppointments != null) { matchingGoogleAppointment = sync.GetGoogleAppointmentById(googleAppointmentId); } else { matchingGoogleAppointment = sync.LoadGoogleAppointments(googleAppointmentId, 0, 0, null, null); } if (matchingGoogleAppointment == null) { if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete) { return; } else if (!sync.PromptDelete && match.OutlookAppointment.Recipients.Count == 0) { sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; } else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { var r = new ConflictResolver(); sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookAppointment); } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: if (match.OutlookAppointment.Recipients.Count > 1) { //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment Logger.Log("Outlook Appointment not deleted, because multiple participants found, invitation maybe NOT sent by Google: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information); AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); break; } else { //Avoid recreating a GoogleAppointment already existing //==> Delete this OutlookAppointment instead if previous match existed but no match exists anymore return; } default: throw new ApplicationException("Cancelled"); } } else { sync.SkippedCount++; match.GoogleAppointment = matchingGoogleAppointment; Logger.Log("Outlook Appointment not deleted, because still existing on Google side, maybe because months restriction: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information); return; } } //} if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook appointment not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookAppointment.Subject), EventType.Information); return; } //create a Google appointment from Outlook appointment match.GoogleAppointment = Factory.NewEvent(); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); } else if (match.OutlookAppointment == null && match.GoogleAppointment != null) { //no Outlook appointment string outlookAppointmenttId = AppointmentPropertiesUtils.GetGoogleOutlookAppointmentId(sync.SyncProfile, match.GoogleAppointment); if (!string.IsNullOrEmpty(outlookAppointmenttId)) { if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete) { return; } else if (!sync.PromptDelete) { sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways; } else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways && sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways) { var r = new ConflictResolver(); sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleAppointment); } switch (sync.DeleteGoogleResolution) { case DeleteResolution.KeepGoogle: case DeleteResolution.KeepGoogleAlways: AppointmentPropertiesUtils.ResetGoogleOutlookAppointmentId(sync.SyncProfile, match.GoogleAppointment); break; case DeleteResolution.DeleteGoogle: case DeleteResolution.DeleteGoogleAlways: //Avoid recreating a OutlookAppointment already existing //==> Delete this googleAppointment instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.OutlookToGoogleOnly) { sync.SkippedCount++; Logger.Log(string.Format("Google appointment not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleAppointment.Summary), EventType.Information); return; } //create a Outlook appointment from Google appointment match.OutlookAppointment = Synchronizer.CreateOutlookAppointmentItem(Synchronizer.SyncAppointmentsFolder); sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); } else if (match.OutlookAppointment != null && match.GoogleAppointment != null) { //ToDo: Check how to overcome appointment recurrences, which need more than 60 seconds to update and therefore get updated again and again because of time tolerance 60 seconds violated again and again //merge appointment details //determine if this appointment pair were synchronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookAppointment, sync.OutlookPropertyNameUpdated); DateTime?lastSynced = AppointmentPropertiesUtils.GetOutlookLastSync(sync, match.OutlookAppointment); if (lastSynced.HasValue) { //appointment pair was syncronysed before. //determine if Google appointment was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = match.OutlookAppointment.LastModificationTime.AddSeconds(-match.OutlookAppointment.LastModificationTime.Second); DateTime lastUpdatedGoogle = match.GoogleAppointment.Updated.Value.AddSeconds(-match.GoogleAppointment.Updated.Value.Second); //consider GoogleAppointmentExceptions, because if they are updated, the master appointment doesn't have a new Saved TimeStamp foreach (Event googleAppointment in match.GoogleAppointmentExceptions) { if (googleAppointment.Updated != null)//happens for cancelled events { DateTime lastUpdatedGoogleException = googleAppointment.Updated.Value.AddSeconds(-googleAppointment.Updated.Value.Second); if (lastUpdatedGoogleException > lastUpdatedGoogle) { lastUpdatedGoogle = lastUpdatedGoogleException; } } else if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster) { Outlook.AppointmentItem outlookRecurrenceException = null; try { var slaveRecurrence = match.OutlookAppointment.GetRecurrencePattern(); if (googleAppointment.OriginalStartTime != null && !string.IsNullOrEmpty(googleAppointment.OriginalStartTime.Date)) { outlookRecurrenceException = slaveRecurrence.GetOccurrence(DateTime.Parse(googleAppointment.OriginalStartTime.Date)); } else if (googleAppointment.OriginalStartTime != null && googleAppointment.OriginalStartTime.DateTime != null) { outlookRecurrenceException = slaveRecurrence.GetOccurrence(googleAppointment.OriginalStartTime.DateTime.Value); } } catch (Exception ignored) { Logger.Log("Google Appointment with OriginalEvent found, but Outlook occurrence not found: " + googleAppointment.Summary + " - " + googleAppointment.OriginalStartTime.DateTime + ": " + ignored, EventType.Debug); } if (outlookRecurrenceException != null && outlookRecurrenceException.MeetingStatus != Outlook.OlMeetingStatus.olMeetingCanceled) { lastUpdatedGoogle = DateTime.Now; break; //no need to search further, already newest date set } } } //check if both outlok and Google appointments where updated sync last sync if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both appointments were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both appointments were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment Logger.Log("Outlook and Google appointment have been updated, Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment Logger.Log("Outlook and Google appointment have been updated, Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.GoogleAppointment.Summary + ".", EventType.Information); sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, false); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped appointment ({0}).", match.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Cancelled"); } break; } return; } //check if Outlook appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //Outlook appointment was changed or changed Google appointment will be overwritten if ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) { Logger.Log("Google appointment has been updated since last sync, but Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); } sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); //at the moment use Outlook as "master" source of appointments - in the event of a conflict Google appointment will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if Google appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google appointment was changed or changed Outlook appointment will be overwritten if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) { Logger.Log("Outlook appointment has been updated since last sync, but Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); } sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); } } else { //appointments were never synced. //merge appointments. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, true); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Appointments.Add(new AppointmentMatch(match.OutlookAppointment, null)); sync.Appointments.Add(new AppointmentMatch(null, match.GoogleAppointment)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Canceled"); } break; } } } else { throw new ArgumentNullException("AppointmenttMatch has all peers null."); } //} //finally //{ //if (outlookAppointment != null && // match.OutlookAppointment != null) //{ // match.OutlookAppointment.Update(outlookAppointment, sync); // Marshal.ReleaseComObject(outlookAppointment); // outlookAppointment = null; //} //} }
public static void SyncContact(ContactMatch match, Synchronizer sync) { Outlook.ContactItem outlookContactItem = match.OutlookContact != null ? match.OutlookContact.GetOriginalItemFromOutlook() : null; try { if (match.GoogleContact == null && match.OutlookContact != null) { //no google contact string googleContactId = match.OutlookContact.UserProperties.GoogleContactId; if (!string.IsNullOrEmpty(googleContactId)) { //Redundant check if exist, but in case an error occurred in MatchContacts Contact matchingGoogleContact = sync.GetGoogleContactById(googleContactId); if (matchingGoogleContact == null) { if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete) sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { var r = new ConflictResolver(); sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookContact); } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: ContactPropertiesUtils.ResetOutlookGoogleContactId(sync, outlookContactItem); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: //Avoid recreating a GoogleContact already existing //==> Delete this outlookContact instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } } if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook Contact not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookContact.FileAs), EventType.Information); return; } //create a Google contact from Outlook contact match.GoogleContact = new Contact(); sync.UpdateContact(outlookContactItem, match.GoogleContact); } else if (match.OutlookContact == null && match.GoogleContact != null) { // no outlook contact string outlookId = ContactPropertiesUtils.GetGoogleOutlookContactId(sync.SyncProfile, match.GoogleContact); if (outlookId != null) { if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete) sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways; else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways && sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways) { var r = new ConflictResolver(); sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleContact); } switch (sync.DeleteGoogleResolution) { case DeleteResolution.KeepGoogle: case DeleteResolution.KeepGoogleAlways: ContactPropertiesUtils.ResetGoogleOutlookContactId(sync.SyncProfile, match.GoogleContact); break; case DeleteResolution.DeleteGoogle: case DeleteResolution.DeleteGoogleAlways: //Avoid recreating a OutlookContact already existing //==> Delete this googleContact instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.OutlookToGoogleOnly) { sync.SkippedCount++; Logger.Log(string.Format("Google Contact not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleContact.Title), EventType.Information); return; } //create a Outlook contact from Google contact outlookContactItem = Synchronizer.CreateOutlookContactItem(Synchronizer.SyncContactsFolder); sync.UpdateContact(match.GoogleContact, outlookContactItem); match.OutlookContact = new OutlookContactInfo(outlookContactItem, sync); } else if (match.OutlookContact != null && match.GoogleContact != null) { //merge contact details //determine if this contact pair were synchronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookContact, sync.OutlookPropertyNameUpdated); DateTime? lastSynced = match.OutlookContact.UserProperties.LastSync; if (lastSynced.HasValue) { //contact pair was syncronysed before. //determine if google contact was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = match.OutlookContact.LastModificationTime.AddSeconds(-match.OutlookContact.LastModificationTime.Second); DateTime lastUpdatedGoogle = match.GoogleContact.Updated.AddSeconds(-match.GoogleContact.Updated.Second); //check if both outlok and google contacts where updated sync last sync if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both contacts were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both contacts were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google contact Logger.Log("Outlook and Google contact have been updated, Outlook contact is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information); sync.UpdateContact(outlookContactItem, match.GoogleContact); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook contact Logger.Log("Outlook and Google contact have been updated, Google contact is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information); sync.UpdateContact(match.GoogleContact, outlookContactItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match, false); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped contact ({0}).", match.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateContact(outlookContactItem, match.GoogleContact); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateContact(match.GoogleContact, outlookContactItem); break; default: throw new ApplicationException("Cancelled"); } break; } return; } //check if outlook contact was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //outlook contact was changed or changed Google contact will be overwritten if (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) Logger.Log("Google contact has been updated since last sync, but Outlook contact is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information); sync.UpdateContact(outlookContactItem, match.GoogleContact); //at the moment use outlook as "master" source of contacts - in the event of a conflict google contact will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if google contact was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google contact was changed or changed Outlook contact will be overwritten if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) Logger.Log("Outlook contact has been updated since last sync, but Google contact is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information); sync.UpdateContact(match.GoogleContact, outlookContactItem); } } else { //contacts were never synced. //merge contacts. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google contact sync.UpdateContact(outlookContactItem, match.GoogleContact); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook contact sync.UpdateContact(match.GoogleContact, outlookContactItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match, true); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Contacts.Add(new ContactMatch(match.OutlookContact, null)); sync.Contacts.Add(new ContactMatch(null, match.GoogleContact)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateContact(outlookContactItem, match.GoogleContact); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateContact(match.GoogleContact, outlookContactItem); break; default: throw new ApplicationException("Cancelled"); } break; } } } else throw new ArgumentNullException("ContactMatch has all peers null."); } catch (ArgumentNullException e) { throw e; } catch (Exception e) { throw new Exception("Error syncing contact " + (match.OutlookContact != null ? match.OutlookContact.FileAs : match.GoogleContact.Title) + ": " + e.Message, e); } finally { if (outlookContactItem != null && match.OutlookContact != null) { match.OutlookContact.Update(outlookContactItem, sync); Marshal.ReleaseComObject(outlookContactItem); outlookContactItem = null; } } }
private static void SyncAppointmentNoGoogle(AppointmentMatch match, AppointmentsSynchronizer sync) { string googleAppointmentId = AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); if (!string.IsNullOrEmpty(googleAppointmentId)) { //Redundant check if exist, but in case an error occurred in MatchAppointments or not all appointments have been loaded (e.g. because months before/after constraint) Event matchingGoogleAppointment = null; if (sync.AllGoogleAppointments != null) { matchingGoogleAppointment = sync.GetGoogleAppointmentById(googleAppointmentId); } else { matchingGoogleAppointment = sync.LoadGoogleAppointments(googleAppointmentId, null, null, null, null); } if (matchingGoogleAppointment == null) { if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete) { return; } else if (!sync.PromptDelete && match.OutlookAppointment.Recipients.Count == 0) { sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; } else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { using (var r = new ConflictResolver()) { sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookAppointment); } } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: if (match.OutlookAppointment.Recipients.Count > 1) { //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment Logger.Log("Outlook Appointment not deleted, because multiple participants found, invitation maybe NOT sent by Google: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information); AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); break; } else { //Avoid recreating a GoogleAppointment already existing //==> Delete this OutlookAppointment instead if previous match existed but no match exists anymore return; } default: throw new ApplicationException("Cancelled"); } } else { sync.SkippedCount++; match.GoogleAppointment = matchingGoogleAppointment; Logger.Log("Outlook Appointment not deleted, because still existing on Google side, maybe because months restriction: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information); return; } } if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook appointment not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookAppointment.Subject), EventType.Information); return; } //create a Google appointment from Outlook appointment match.GoogleAppointment = Factory.NewEvent(); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); }
public static void SyncAppointment(AppointmentMatch match, Synchronizer sync) { if (match.GoogleAppointment == null && match.OutlookAppointment != null) { //no Google appointment string googleAppointmentId = AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); if (!string.IsNullOrEmpty(googleAppointmentId)) { //if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster && // (Syncronizer.MonthsInPast == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.End.Month, match.OutlookAppointment.End.Day) >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) && // (Syncronizer.MonthsInFuture == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.Start.Month, match.OutlookAppointment.Start.Day) <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture)) // || // (Syncronizer.MonthsInPast == 0 || match.OutlookAppointment.End >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) && // (Syncronizer.MonthsInFuture == 0 || match.OutlookAppointment.Start <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture)) // ) //{ //Redundant check if exist, but in case an error occurred in MatchAppointments or not all appointments have been loaded (e.g. because months before/after constraint) Event matchingGoogleAppointment = null; if (sync.AllGoogleAppointments != null) matchingGoogleAppointment = sync.GetGoogleAppointmentById(googleAppointmentId); else matchingGoogleAppointment = sync.LoadGoogleAppointments(googleAppointmentId, 0, 0, null, null); if (matchingGoogleAppointment == null) { if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete && match.OutlookAppointment.Recipients.Count == 0) sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { var r = new ConflictResolver(); sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookAppointment); } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: if (match.OutlookAppointment.Recipients.Count > 1) { //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment Logger.Log("Outlook Appointment not deleted, because multiple participants found, invitation maybe NOT sent by Google: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information); AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); break; } else //Avoid recreating a GoogleAppointment already existing //==> Delete this OutlookAppointment instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } else { sync.SkippedCount++; match.GoogleAppointment = matchingGoogleAppointment; Logger.Log("Outlook Appointment not deleted, because still existing on Google side, maybe because months restriction: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information); return; } } //} if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook appointment not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookAppointment.Subject), EventType.Information); return; } //create a Google appointment from Outlook appointment match.GoogleAppointment = Factory.NewEvent(); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); } else if (match.OutlookAppointment == null && match.GoogleAppointment != null) { //no Outlook appointment string outlookAppointmenttId = AppointmentPropertiesUtils.GetGoogleOutlookAppointmentId(sync.SyncProfile,match.GoogleAppointment); if (!string.IsNullOrEmpty(outlookAppointmenttId)) { if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete) sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways; else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways && sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways) { var r = new ConflictResolver(); sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleAppointment); } switch (sync.DeleteGoogleResolution) { case DeleteResolution.KeepGoogle: case DeleteResolution.KeepGoogleAlways: AppointmentPropertiesUtils.ResetGoogleOutlookAppointmentId(sync.SyncProfile,match.GoogleAppointment); break; case DeleteResolution.DeleteGoogle: case DeleteResolution.DeleteGoogleAlways: //Avoid recreating a OutlookAppointment already existing //==> Delete this googleAppointment instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.OutlookToGoogleOnly) { sync.SkippedCount++; Logger.Log(string.Format("Google appointment not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleAppointment.Summary), EventType.Information); return; } //create a Outlook appointment from Google appointment match.OutlookAppointment = Synchronizer.CreateOutlookAppointmentItem(Synchronizer.SyncAppointmentsFolder); sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); } else if (match.OutlookAppointment != null && match.GoogleAppointment != null) { //ToDo: Check how to overcome appointment recurrences, which need more than 60 seconds to update and therefore get updated again and again because of time tolerance 60 seconds violated again and again //merge appointment details //determine if this appointment pair were synchronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookAppointment, sync.OutlookPropertyNameUpdated); DateTime? lastSynced = AppointmentPropertiesUtils.GetOutlookLastSync(sync, match.OutlookAppointment); if (lastSynced.HasValue) { //appointment pair was syncronysed before. //determine if Google appointment was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = match.OutlookAppointment.LastModificationTime.AddSeconds(-match.OutlookAppointment.LastModificationTime.Second); DateTime lastUpdatedGoogle = match.GoogleAppointment.Updated.Value.AddSeconds(-match.GoogleAppointment.Updated.Value.Second); //consider GoogleAppointmentExceptions, because if they are updated, the master appointment doesn't have a new Saved TimeStamp foreach (Event googleAppointment in match.GoogleAppointmentExceptions) { if (googleAppointment.Updated != null)//happens for cancelled events { DateTime lastUpdatedGoogleException = googleAppointment.Updated.Value.AddSeconds(-googleAppointment.Updated.Value.Second); if (lastUpdatedGoogleException > lastUpdatedGoogle) lastUpdatedGoogle = lastUpdatedGoogleException; } else if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster) { Outlook.AppointmentItem outlookRecurrenceException = null; try { var slaveRecurrence = match.OutlookAppointment.GetRecurrencePattern(); if (googleAppointment.OriginalStartTime != null && !string.IsNullOrEmpty(googleAppointment.OriginalStartTime.Date)) outlookRecurrenceException = slaveRecurrence.GetOccurrence(DateTime.Parse(googleAppointment.OriginalStartTime.Date)); else if (googleAppointment.OriginalStartTime != null && googleAppointment.OriginalStartTime.DateTime != null) outlookRecurrenceException = slaveRecurrence.GetOccurrence(googleAppointment.OriginalStartTime.DateTime.Value); } catch (Exception ignored) { Logger.Log("Google Appointment with OriginalEvent found, but Outlook occurrence not found: " + googleAppointment.Summary + " - " + googleAppointment.OriginalStartTime.DateTime + ": " + ignored, EventType.Debug); } if (outlookRecurrenceException != null && outlookRecurrenceException.MeetingStatus != Outlook.OlMeetingStatus.olMeetingCanceled) { lastUpdatedGoogle = DateTime.Now; break; //no need to search further, already newest date set } } } //check if both outlok and Google appointments where updated sync last sync if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both appointments were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both appointments were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment Logger.Log("Outlook and Google appointment have been updated, Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment Logger.Log("Outlook and Google appointment have been updated, Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.GoogleAppointment.Summary + ".", EventType.Information); sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, false); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped appointment ({0}).", match.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Cancelled"); } break; } return; } //check if Outlook appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //Outlook appointment was changed or changed Google appointment will be overwritten if ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) Logger.Log("Google appointment has been updated since last sync, but Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); //at the moment use Outlook as "master" source of appointments - in the event of a conflict Google appointment will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if Google appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google appointment was changed or changed Outlook appointment will be overwritten if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) Logger.Log("Outlook appointment has been updated since last sync, but Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); } } else { //appointments were never synced. //merge appointments. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, true); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Appointments.Add(new AppointmentMatch(match.OutlookAppointment, null)); sync.Appointments.Add(new AppointmentMatch(null, match.GoogleAppointment)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Canceled"); } break; } } } else throw new ArgumentNullException("AppointmenttMatch has all peers null."); //} //finally //{ //if (outlookAppointment != null && // match.OutlookAppointment != null) //{ // match.OutlookAppointment.Update(outlookAppointment, sync); // Marshal.ReleaseComObject(outlookAppointment); // outlookAppointment = null; //} //} }
/// <summary> /// Matches outlook and google contact by a) google id b) properties. /// </summary> /// <param name="sync">Syncronizer instance</param> /// <param name="duplicatesFound">Exception returned, if duplicates have been found (null else)</param> /// <returns>Returns a list of match pairs (outlook contact + google contact) for all contact. Those that weren't matche will have it's peer set to null</returns> public static List<ContactMatch> MatchContacts(Synchronizer sync, out DuplicateDataException duplicatesFound) { Logger.Log("Matching Outlook and Google contacts...", EventType.Information); var result = new List<ContactMatch>(); var duplicateGoogleMatches = string.Empty; var duplicateOutlookContacts = string.Empty; sync.GoogleContactDuplicates = new Collection<ContactMatch>(); sync.OutlookContactDuplicates = new Collection<ContactMatch>(); var skippedOutlookIds = new List<string>(); //for each outlook contact try to get google contact id from user properties //if no match - try to match by properties //if no match - create a new match pair without google contact. //foreach (Outlook._ContactItem olc in outlookContacts) var outlookContactsWithoutOutlookGoogleId = new Collection<OutlookContactInfo>(); #region Match first all outlookContacts by sync id for (int i = 1; i <= sync.OutlookContacts.Count; i++) { Outlook.ContactItem olc = null; try { olc = sync.OutlookContacts[i] as Outlook.ContactItem; if (olc == null) { Logger.Log("Empty Outlook contact found (maybe distribution list). Skipping", EventType.Warning); sync.SkippedCount++; sync.SkippedCountNotMatches++; continue; } } catch (Exception ex) { //this is needed because some contacts throw exceptions Logger.Log("Accessing Outlook contact threw and exception. Skipping: " + ex.Message, EventType.Warning); sync.SkippedCount++; sync.SkippedCountNotMatches++; continue; } try { // sometimes contacts throw Exception when accessing their properties, so we give it a controlled try first. try { string email1Address = olc.Email1Address; } catch (Exception ex) { string message = string.Format("Can't access contact details for outlook contact, got {0} - '{1}'. Skipping", ex.GetType().ToString(), ex.Message); try { message = string.Format("{0} {1}.", message, olc.FileAs); //remember skippedOutlookIds to later not delete them if found on Google side skippedOutlookIds.Add(string.Copy(olc.EntryID)); } catch { //e.g. if olc.FileAs also fails, ignore, because messge already set //message = null; } //if (olc != null && message != null) // it's useless to say "we couldn't access some contacts properties //{ Logger.Log(message, EventType.Warning); //} sync.SkippedCount++; sync.SkippedCountNotMatches++; continue; } if (!IsContactValid(olc)) { Logger.Log(string.Format("Invalid outlook contact ({0}). Skipping", olc.FileAs), EventType.Warning); skippedOutlookIds.Add(string.Copy(olc.EntryID)); sync.SkippedCount++; sync.SkippedCountNotMatches++; continue; } if (olc.Body != null && olc.Body.Length > 62000) { // notes field too large Logger.Log(string.Format("Skipping outlook contact ({0}). Reduce the notes field to a maximum of 62.000 characters.", olc.FileAs), EventType.Warning); skippedOutlookIds.Add(string.Copy(olc.EntryID)); sync.SkippedCount++; sync.SkippedCountNotMatches++; continue; } if (NotificationReceived != null) NotificationReceived(String.Format("Matching contact {0} of {1} by id: {2} ...", i, sync.OutlookContacts.Count, olc.FileAs)); // Create our own info object to go into collections/lists, so we can free the Outlook objects and not run out of resources / exceed policy limits. var olci = new OutlookContactInfo(olc, sync); //try to match this contact to one of google contacts Outlook.UserProperties userProperties = olc.UserProperties; Outlook.UserProperty idProp = userProperties[sync.OutlookPropertyNameId]; try { if (idProp != null) { string googleContactId = string.Copy((string)idProp.Value); Contact foundContact = sync.GetGoogleContactById(googleContactId); var match = new ContactMatch(olci, null); //Check first, that this is not a duplicate //e.g. by copying an existing Outlook contact //or by Outlook checked this as duplicate, but the user selected "Add new" Collection<OutlookContactInfo> duplicates = sync.OutlookContactByProperty(sync.OutlookPropertyNameId, googleContactId); if (duplicates.Count > 1) { foreach (OutlookContactInfo duplicate in duplicates) { if (!string.IsNullOrEmpty(googleContactId)) { Logger.Log("Duplicate Outlook contact found, resetting match and trying to match again: " + duplicate.FileAs, EventType.Warning); Outlook.ContactItem item = duplicate.GetOriginalItemFromOutlook(); try { ContactPropertiesUtils.ResetOutlookGoogleContactId(sync, item); item.Save(); } finally { if (item != null) { Marshal.ReleaseComObject(item); item = null; } } } } if (foundContact != null && !foundContact.Deleted) { ContactPropertiesUtils.ResetGoogleOutlookContactId(sync.SyncProfile, foundContact); } outlookContactsWithoutOutlookGoogleId.Add(olci); } else { if (foundContact != null && !foundContact.Deleted) { //we found a match by google id, that is not deleted yet match.AddGoogleContact(foundContact); result.Add(match); //Remove the contact from the list to not sync it twice sync.GoogleContacts.Remove(foundContact); } else { outlookContactsWithoutOutlookGoogleId.Add(olci); } } } else outlookContactsWithoutOutlookGoogleId.Add(olci); } finally { if (idProp != null) Marshal.ReleaseComObject(idProp); Marshal.ReleaseComObject(userProperties); } } finally { Marshal.ReleaseComObject(olc); olc = null; } } #endregion #region Match the remaining contacts by properties for (int i = 0; i < outlookContactsWithoutOutlookGoogleId.Count; i++) { OutlookContactInfo olci = outlookContactsWithoutOutlookGoogleId[i]; if (NotificationReceived != null) NotificationReceived(String.Format("Matching contact {0} of {1} by unique properties: {2} ...", i + 1, outlookContactsWithoutOutlookGoogleId.Count, olci.FileAs)); //no match found by id => match by common properties //create a default match pair with just outlook contact. var match = new ContactMatch(olci, null); //foreach google contact try to match and create a match pair if found some match(es) for (int j=sync.GoogleContacts.Count-1;j>=0;j--) { Contact entry = sync.GoogleContacts[j]; if (entry.Deleted) continue; // only match if there is either an email or telephone or else // a matching google contact will be created at each sync //1. try to match by FileAs //1.1 try to match by FullName //2. try to match by primary email //3. try to match by mobile phone number, don't match by home or business bumbers, because several people may share the same home or business number //4. try to math Company, if Google Title is null, i.e. the contact doesn't have a name and title, only a company string entryTitleFirstLastAndSuffix = OutlookContactInfo.GetTitleFirstLastAndSuffix(entry); if (!string.IsNullOrEmpty(olci.FileAs) && !string.IsNullOrEmpty(entry.Title) && olci.FileAs.Equals(entry.Title.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) || //Replace twice to not replace a \r\n by \r\r\n. This is necessary because \r\n are saved as \n only to google !string.IsNullOrEmpty(olci.FileAs) && !string.IsNullOrEmpty(entry.Name.FullName) && olci.FileAs.Equals(entry.Name.FullName.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) || !string.IsNullOrEmpty(olci.FullName) && !string.IsNullOrEmpty(entry.Name.FullName) && olci.FullName.Equals(entry.Name.FullName.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) || !string.IsNullOrEmpty(olci.TitleFirstLastAndSuffix) && !string.IsNullOrEmpty(entryTitleFirstLastAndSuffix) && olci.TitleFirstLastAndSuffix.Equals(entryTitleFirstLastAndSuffix.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) || !string.IsNullOrEmpty(olci.Email1Address) && entry.Emails.Count > 0 && olci.Email1Address.Equals(entry.Emails[0].Address, StringComparison.InvariantCultureIgnoreCase) || //!string.IsNullOrEmpty(olci.Email2Address) && FindEmail(olci.Email2Address, entry.Emails) != null || //!string.IsNullOrEmpty(olci.Email3Address) && FindEmail(olci.Email3Address, entry.Emails) != null || olci.MobileTelephoneNumber != null && FindPhone(olci.MobileTelephoneNumber, entry.Phonenumbers) != null || !string.IsNullOrEmpty(olci.FileAs) && string.IsNullOrEmpty(entry.Title) && entry.Organizations.Count > 0 && olci.FileAs.Equals(entry.Organizations[0].Name, StringComparison.InvariantCultureIgnoreCase) ) { match.AddGoogleContact(entry); sync.GoogleContacts.Remove(entry); } } #region find duplicates not needed now //if (match.GoogleContact == null && match.OutlookContact != null) //{//If GoogleContact, we have to expect a conflict because of Google insert of duplicates // foreach (Contact entry in sync.GoogleContacts) // { // if (!string.IsNullOrEmpty(olc.FullName) && olc.FullName.Equals(entry.Title, StringComparison.InvariantCultureIgnoreCase) || // !string.IsNullOrEmpty(olc.FileAs) && olc.FileAs.Equals(entry.Title, StringComparison.InvariantCultureIgnoreCase) || // !string.IsNullOrEmpty(olc.Email1Address) && FindEmail(olc.Email1Address, entry.Emails) != null || // !string.IsNullOrEmpty(olc.Email2Address) && FindEmail(olc.Email1Address, entry.Emails) != null || // !string.IsNullOrEmpty(olc.Email3Address) && FindEmail(olc.Email1Address, entry.Emails) != null || // olc.MobileTelephoneNumber != null && FindPhone(olc.MobileTelephoneNumber, entry.Phonenumbers) != null // ) // } //// check for each email 1,2 and 3 if a duplicate exists with same email, because Google doesn't like inserting new contacts with same email //Collection<Outlook.ContactItem> duplicates1 = new Collection<Outlook.ContactItem>(); //Collection<Outlook.ContactItem> duplicates2 = new Collection<Outlook.ContactItem>(); //Collection<Outlook.ContactItem> duplicates3 = new Collection<Outlook.ContactItem>(); //if (!string.IsNullOrEmpty(olc.Email1Address)) // duplicates1 = sync.OutlookContactByEmail(olc.Email1Address); //if (!string.IsNullOrEmpty(olc.Email2Address)) // duplicates2 = sync.OutlookContactByEmail(olc.Email2Address); //if (!string.IsNullOrEmpty(olc.Email3Address)) // duplicates3 = sync.OutlookContactByEmail(olc.Email3Address); //if (duplicates1.Count > 1 || duplicates2.Count > 1 || duplicates3.Count > 1) //{ // if (string.IsNullOrEmpty(duplicatesEmailList)) // duplicatesEmailList = "Outlook contacts with the same email have been found and cannot be synchronized. Please delete duplicates of:"; // if (duplicates1.Count > 1) // foreach (Outlook.ContactItem duplicate in duplicates1) // { // string str = olc.FileAs + " (" + olc.Email1Address + ")"; // if (!duplicatesEmailList.Contains(str)) // duplicatesEmailList += Environment.NewLine + str; // } // if (duplicates2.Count > 1) // foreach (Outlook.ContactItem duplicate in duplicates2) // { // string str = olc.FileAs + " (" + olc.Email2Address + ")"; // if (!duplicatesEmailList.Contains(str)) // duplicatesEmailList += Environment.NewLine + str; // } // if (duplicates3.Count > 1) // foreach (Outlook.ContactItem duplicate in duplicates3) // { // string str = olc.FileAs + " (" + olc.Email3Address + ")"; // if (!duplicatesEmailList.Contains(str)) // duplicatesEmailList += Environment.NewLine + str; // } // continue; //} //else if (!string.IsNullOrEmpty(olc.Email1Address)) //{ // ContactMatch dup = result.Find(delegate(ContactMatch match) // { // return match.OutlookContact != null && match.OutlookContact.Email1Address == olc.Email1Address; // }); // if (dup != null) // { // Logger.Log(string.Format("Duplicate contact found by Email1Address ({0}). Skipping", olc.FileAs), EventType.Information); // continue; // } //} //// check for unique mobile phone, because this sync tool uses the also the mobile phone to identify matches between Google and Outlook //Collection<Outlook.ContactItem> duplicatesMobile = new Collection<Outlook.ContactItem>(); //if (!string.IsNullOrEmpty(olc.MobileTelephoneNumber)) // duplicatesMobile = sync.OutlookContactByProperty("MobileTelephoneNumber", olc.MobileTelephoneNumber); //if (duplicatesMobile.Count > 1) //{ // if (string.IsNullOrEmpty(duplicatesMobileList)) // duplicatesMobileList = "Outlook contacts with the same mobile phone have been found and cannot be synchronized. Please delete duplicates of:"; // foreach (Outlook.ContactItem duplicate in duplicatesMobile) // { // sync.OutlookContactDuplicates.Add(olc); // string str = olc.FileAs + " (" + olc.MobileTelephoneNumber + ")"; // if (!duplicatesMobileList.Contains(str)) // duplicatesMobileList += Environment.NewLine + str; // } // continue; //} //else if (!string.IsNullOrEmpty(olc.MobileTelephoneNumber)) //{ // ContactMatch dup = result.Find(delegate(ContactMatch match) // { // return match.OutlookContact != null && match.OutlookContact.MobileTelephoneNumber == olc.MobileTelephoneNumber; // }); // if (dup != null) // { // Logger.Log(string.Format("Duplicate contact found by MobileTelephoneNumber ({0}). Skipping", olc.FileAs), EventType.Information); // continue; // } //} #endregion if (match.AllGoogleContactMatches == null || match.AllGoogleContactMatches.Count == 0) { //Check, if this Outlook contact has a match in the google duplicates bool duplicateFound = false; foreach (ContactMatch duplicate in sync.GoogleContactDuplicates) { string entryTitleFirstLastAndSuffix = OutlookContactInfo.GetTitleFirstLastAndSuffix(duplicate.AllGoogleContactMatches[0]); if (duplicate.AllGoogleContactMatches.Count > 0 && (!string.IsNullOrEmpty(olci.FileAs) && !string.IsNullOrEmpty(duplicate.AllGoogleContactMatches[0].Title) && olci.FileAs.Equals(duplicate.AllGoogleContactMatches[0].Title.Replace("\r\n", "\n").Replace("\n","\r\n"), StringComparison.InvariantCultureIgnoreCase) || //Replace twice to not replace a \r\n by \r\r\n. This is necessary because \r\n are saved as \n only to google !string.IsNullOrEmpty(olci.FileAs) && !string.IsNullOrEmpty(duplicate.AllGoogleContactMatches[0].Name.FullName) && olci.FileAs.Equals(duplicate.AllGoogleContactMatches[0].Name.FullName.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) || !string.IsNullOrEmpty(olci.FullName) && !string.IsNullOrEmpty(duplicate.AllGoogleContactMatches[0].Name.FullName) && olci.FullName.Equals(duplicate.AllGoogleContactMatches[0].Name.FullName.Replace("\r\n", "\n").Replace("\n","\r\n"), StringComparison.InvariantCultureIgnoreCase) || !string.IsNullOrEmpty(olci.TitleFirstLastAndSuffix) && !string.IsNullOrEmpty(entryTitleFirstLastAndSuffix) && olci.TitleFirstLastAndSuffix.Equals(entryTitleFirstLastAndSuffix.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) || !string.IsNullOrEmpty(olci.Email1Address) && duplicate.AllGoogleContactMatches[0].Emails.Count > 0 && olci.Email1Address.Equals(duplicate.AllGoogleContactMatches[0].Emails[0].Address, StringComparison.InvariantCultureIgnoreCase) || //!string.IsNullOrEmpty(olci.Email2Address) && FindEmail(olci.Email2Address, duplicate.AllGoogleContactMatches[0].Emails) != null || //!string.IsNullOrEmpty(olci.Email3Address) && FindEmail(olci.Email3Address, duplicate.AllGoogleContactMatches[0].Emails) != null || olci.MobileTelephoneNumber != null && FindPhone(olci.MobileTelephoneNumber, duplicate.AllGoogleContactMatches[0].Phonenumbers) != null || !string.IsNullOrEmpty(olci.FileAs) && string.IsNullOrEmpty(duplicate.AllGoogleContactMatches[0].Title) && duplicate.AllGoogleContactMatches[0].Organizations.Count > 0 && olci.FileAs.Equals(duplicate.AllGoogleContactMatches[0].Organizations[0].Name, StringComparison.InvariantCultureIgnoreCase) ) || !string.IsNullOrEmpty(olci.FileAs) && olci.FileAs.Equals(duplicate.OutlookContact.FileAs, StringComparison.InvariantCultureIgnoreCase) || !string.IsNullOrEmpty(olci.FullName) && olci.FullName.Equals(duplicate.OutlookContact.FullName, StringComparison.InvariantCultureIgnoreCase) || !string.IsNullOrEmpty(olci.TitleFirstLastAndSuffix) && olci.TitleFirstLastAndSuffix.Equals(duplicate.OutlookContact.TitleFirstLastAndSuffix, StringComparison.InvariantCultureIgnoreCase) || !string.IsNullOrEmpty(olci.Email1Address) && olci.Email1Address.Equals(duplicate.OutlookContact.Email1Address, StringComparison.InvariantCultureIgnoreCase) || // olci.Email1Address.Equals(duplicate.OutlookContact.Email2Address, StringComparison.InvariantCultureIgnoreCase) || // olci.Email1Address.Equals(duplicate.OutlookContact.Email3Address, StringComparison.InvariantCultureIgnoreCase) // ) || //!string.IsNullOrEmpty(olci.Email2Address) && (olci.Email2Address.Equals(duplicate.OutlookContact.Email1Address, StringComparison.InvariantCultureIgnoreCase) || // olci.Email2Address.Equals(duplicate.OutlookContact.Email2Address, StringComparison.InvariantCultureIgnoreCase) || // olci.Email2Address.Equals(duplicate.OutlookContact.Email3Address, StringComparison.InvariantCultureIgnoreCase) // ) || //!string.IsNullOrEmpty(olci.Email3Address) && (olci.Email3Address.Equals(duplicate.OutlookContact.Email1Address, StringComparison.InvariantCultureIgnoreCase) || // olci.Email3Address.Equals(duplicate.OutlookContact.Email2Address, StringComparison.InvariantCultureIgnoreCase) || // olci.Email3Address.Equals(duplicate.OutlookContact.Email3Address, StringComparison.InvariantCultureIgnoreCase) // ) || olci.MobileTelephoneNumber != null && olci.MobileTelephoneNumber.Equals(duplicate.OutlookContact.MobileTelephoneNumber) || !string.IsNullOrEmpty(olci.FileAs) && string.IsNullOrEmpty(duplicate.GoogleContact.Title) && duplicate.GoogleContact.Organizations.Count > 0 && olci.FileAs.Equals(duplicate.GoogleContact.Organizations[0].Name, StringComparison.InvariantCultureIgnoreCase) ) { duplicateFound = true; duplicate.AddOutlookContact(olci); sync.OutlookContactDuplicates.Add(match); if (string.IsNullOrEmpty(duplicateOutlookContacts)) duplicateOutlookContacts = "Outlook contact found that has been already identified as duplicate Google contact (either same email, Mobile or FullName) and cannot be synchronized. Please delete or resolve duplicates of:"; string str = olci.FileAs + " (" + olci.Email1Address + ", " + olci.MobileTelephoneNumber + ")"; if (!duplicateOutlookContacts.Contains(str)) duplicateOutlookContacts += Environment.NewLine + str; break; } } if (!duplicateFound) Logger.Log(string.Format("No match found for outlook contact ({0}) => {1}", olci.FileAs, (olci.UserProperties.GoogleContactId != null?"Delete from Outlook":"Add to Google")), EventType.Information); } else { //Remember Google duplicates to later react to it when resetting matches or syncing //ResetMatches: Also reset the duplicates //Sync: Skip duplicates (don't sync duplicates to be fail safe) if (match.AllGoogleContactMatches.Count > 1) { sync.GoogleContactDuplicates.Add(match); foreach (Contact entry in match.AllGoogleContactMatches) { //Create message for duplicatesFound exception if (string.IsNullOrEmpty(duplicateGoogleMatches)) duplicateGoogleMatches = "Outlook contacts matching with multiple Google contacts have been found (either same email, Mobile, FullName or company) and cannot be synchronized. Please delete or resolve duplicates of:"; string str = olci.FileAs + " (" + olci.Email1Address + ", " + olci.MobileTelephoneNumber + ")"; if (!duplicateGoogleMatches.Contains(str)) duplicateGoogleMatches += Environment.NewLine + str; } } } result.Add(match); } #endregion if (!string.IsNullOrEmpty(duplicateGoogleMatches) || !string.IsNullOrEmpty(duplicateOutlookContacts)) duplicatesFound = new DuplicateDataException(duplicateGoogleMatches + Environment.NewLine + Environment.NewLine + duplicateOutlookContacts); else duplicatesFound = null; //return result; //for each google contact that's left (they will be nonmatched) create a new match pair without outlook contact. for (int i=0; i< sync.GoogleContacts.Count;i++) { Contact entry = sync.GoogleContacts[i]; if (NotificationReceived != null) NotificationReceived(String.Format("Adding new Google contact {0} of {1} by unique properties: {2} ...", i+1, sync.GoogleContacts.Count, entry.Title)); // only match if there is either an email or mobile phone or a name or a company // otherwise a matching google contact will be created at each sync bool mobileExists = false; foreach (PhoneNumber phone in entry.Phonenumbers) { if (phone.Rel == ContactsRelationships.IsMobile) { mobileExists = true; break; } } string googleOutlookId = ContactPropertiesUtils.GetGoogleOutlookContactId(sync.SyncProfile, entry); if (!String.IsNullOrEmpty(googleOutlookId) && skippedOutlookIds.Contains(googleOutlookId)) { Logger.Log("Skipped GoogleContact because Outlook contact couldn't be matched because of previous problem (see log): " + entry.Title, EventType.Warning); } else if (entry.Emails.Count == 0 && !mobileExists && string.IsNullOrEmpty(entry.Title) && (entry.Organizations.Count == 0 || string.IsNullOrEmpty(entry.Organizations[0].Name))) { // no telephone and email //ToDo: For now I use the ResolveDelete function, because it is almost the same, maybe we introduce a separate function for this ans also include DeleteGoogleAlways checkbox var r = new ConflictResolver(); DeleteResolution res = r.ResolveDelete(entry); if (res == DeleteResolution.DeleteGoogle || res == DeleteResolution.DeleteGoogleAlways) { ContactPropertiesUtils.SetGoogleOutlookContactId(sync.SyncProfile, entry, "-1"); //just set a dummy Id to delete this entry later on sync.SaveContact(new ContactMatch(null, entry)); } else { sync.SkippedCount++; sync.SkippedCountNotMatches++; Logger.Log("Skipped GoogleContact because no unique property found (Email1 or mobile or name or company):" + ContactMatch.GetSummary(entry), EventType.Warning); } } else { Logger.Log(string.Format("No match found for Google contact ({0}) => {1}", entry.Title, (!string.IsNullOrEmpty(googleOutlookId) ? "Delete from Google" : "Add to Outlook")), EventType.Information); var match = new ContactMatch(null, entry); result.Add(match); } } return result; }
private void ResolveDuplicateContact(ContactMatch match) { if (Contacts.Contains(match)) { if (_syncOption == SyncOption.MergePrompt) { //For each OutlookDuplicate: Ask user for the GoogleContact to be synced with for (int j = match.AllOutlookContactMatches.Count - 1; j >= 0 && match.AllGoogleContactMatches.Count > 0; j--) { OutlookContactInfo olci = match.AllOutlookContactMatches[j]; Outlook.ContactItem outlookContactItem = olci.GetOriginalItemFromOutlook(); try { Contact googleContact; ConflictResolver r = new ConflictResolver(); switch (r.ResolveDuplicate(olci, match.AllGoogleContactMatches, out googleContact)) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both entries and sync it to both sides match.AllGoogleContactMatches.Remove(googleContact); match.AllOutlookContactMatches.Remove(olci); Contacts.Add(new ContactMatch(null, googleContact)); Contacts.Add(new ContactMatch(olci, null)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: //Keep Outlook and overwrite Google match.AllGoogleContactMatches.Remove(googleContact); match.AllOutlookContactMatches.Remove(olci); UpdateContact(outlookContactItem, googleContact); SaveContact(new ContactMatch(olci, googleContact)); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: //Keep Google and overwrite Outlook match.AllGoogleContactMatches.Remove(googleContact); match.AllOutlookContactMatches.Remove(olci); UpdateContact(googleContact, outlookContactItem); SaveContact(new ContactMatch(olci, googleContact)); break; default: throw new ApplicationException("Cancelled"); } } finally { if (outlookContactItem != null) { Marshal.ReleaseComObject(outlookContactItem); outlookContactItem = null; } } //Cleanup the match, i.e. assign a proper OutlookContact and GoogleContact, because can be deleted before if (match.AllOutlookContactMatches.Count == 0) match.OutlookContact = null; else match.OutlookContact = match.AllOutlookContactMatches[0]; } } //Cleanup the match, i.e. assign a proper OutlookContact and GoogleContact, because can be deleted before if (match.AllGoogleContactMatches.Count == 0) match.GoogleContact = null; else match.GoogleContact = match.AllGoogleContactMatches[0]; if (match.AllOutlookContactMatches.Count == 0) { //If all OutlookContacts have been assigned by the users ==> Create one match for each remaining Google Contact to sync them to Outlook Contacts.Remove(match); foreach (Contact googleContact in match.AllGoogleContactMatches) Contacts.Add(new ContactMatch(null, googleContact)); } else if (match.AllGoogleContactMatches.Count == 0) { //If all GoogleContacts have been assigned by the users ==> Create one match for each remaining Outlook Contact to sync them to Google Contacts.Remove(match); foreach (OutlookContactInfo outlookContact in match.AllOutlookContactMatches) Contacts.Add(new ContactMatch(outlookContact, null)); } else // if (match.AllGoogleContactMatches.Count > 1 || // match.AllOutlookContactMatches.Count > 1) { SkippedCount++; Contacts.Remove(match); } //else //{ // //If there remains a modified ContactMatch with only a single OutlookContact and GoogleContact // //==>Remove all outlookContactDuplicates for this Outlook Contact to not remove it later from the Contacts to sync // foreach (ContactMatch duplicate in OutlookContactDuplicates) // { // if (duplicate.OutlookContact.EntryID == match.OutlookContact.EntryID) // { // OutlookContactDuplicates.Remove(duplicate); // break; // } // } //} } }
/// <summary> /// Updates Outlook appointment from master to slave (including groups/categories) /// </summary> public bool UpdateAppointment(ref Google.Apis.Calendar.v3.Data.Event master, Outlook.AppointmentItem slave, List<Google.Apis.Calendar.v3.Data.Event> googleAppointmentExceptions) { //if (master.Participants.Count > 1) //{ // bool organizerIsGoogle = AppointmentSync.IsOrganizer(AppointmentSync.GetOrganizer(master)); // if (organizerIsGoogle || AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(this, slave) == null) // {//Only update, if this appointment was organized on Google side or freshly created during tis sync // updated = true; // } // else // { // //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment // SkippedCount++; // Logger.Log("Skipped Updating appointment from Google to Outlook because multiple participants found and invitations NOT sent by Google: \"" + master.Summary + " - " + Syncronizer.GetTime(master) + "\".", EventType.Information); // } //} //else // updated = true; bool updated = false; if (slave.Recipients.Count > 1 && AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(this, slave) != null) { //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment switch (this.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment Logger.Log("Multiple participants found, invitation maybe NOT sent by Google. Outlook appointment is overwriting Google because of SyncOption " + SyncOption + ": " + master.Summary + " - " + Synchronizer.GetTime(master) + ". ", EventType.Information); UpdateAppointment(slave, ref master); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment Logger.Log("Multiple participants found, invitation maybe NOT sent by Google, but Google appointment is overwriting Outlook because of SyncOption " + SyncOption + ": " + master.Summary + " - " + Synchronizer.GetTime(master) + ".", EventType.Information); updated = true; break; case SyncOption.MergePrompt: //promp for sync option if ( //ConflictResolution != ConflictResolution.GoogleWinsAlways && //Shouldn't be used, because Outlook seems to be the master of the appointment ConflictResolution != ConflictResolution.OutlookWinsAlways && ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); ConflictResolution = r.Resolve("Cannot update appointment from Google to Outlook because multiple participants found, invitation maybe NOT sent by Google: \"" + master.Summary + " - " + Synchronizer.GetTime(master) + "\". Do you want to update it back from Outlook to Google?", slave, master, this); } switch (ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Skip SkippedCount++; Logger.Log("Skipped Updating appointment from Google to Outlook because multiple participants found, invitation maybe NOT sent by Google: \"" + master.Summary + " - " + Synchronizer.GetTime(master) + "\".", EventType.Information); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: //Keep Outlook and overwrite Google UpdateAppointment(slave, ref master); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: //Keep Google and overwrite Outlook updated = true; break; default: throw new ApplicationException("Cancelled"); } break; } //if (MessageBox.Show("Cannot update appointment from Google to Outlook because multiple participants found, invitation maybe NOT sent by Google: \"" + master.Summary + " - " + Syncronizer.GetTime(master) + "\". Do you want to update it back from Outlook to Google?", "Outlook appointment cannot be overwritten from Google", MessageBoxButtons.YesNo) == DialogResult.Yes) // UpdateAppointment(slave, ref master); //else // SkippedCount++; // Logger.Log("Skipped Updating appointment from Google to Outlook because multiple participants found, invitation maybe NOT sent by Google: \"" + master.Summary + " - " + Syncronizer.GetTime(master) + "\".", EventType.Information); } else //Only update, if invitation was not sent on Outlook side or freshly created during this sync updated = true; if (updated) { AppointmentSync.UpdateAppointment(master, slave); AppointmentPropertiesUtils.SetOutlookGoogleAppointmentId(this, slave, master); try { //Try to save 2 times, because sometimes the first save fails with a COMException (Outlook aborted) slave.Save(); } catch (Exception) { try { slave.Save(); } catch (COMException ex) { Logger.Log("Error saving Outlook appointment: \"" + master.Summary + " - " + GetTime(master) + "\".\n"+ex.StackTrace, EventType.Warning); return false; } } if (master.Creator == null || AppointmentSync.IsOrganizer(master.Creator.Email)) { //only update Google, if I am the organizer, otherwise an error will be thrown AppointmentPropertiesUtils.SetGoogleOutlookAppointmentId(SyncProfile, master, slave); master = SaveGoogleAppointment(master); } SyncedCount++; Logger.Log("Updated appointment from Google to Outlook: \"" + master.Summary + " - " + GetTime(master) + "\".", EventType.Information); //After saving Outlook Appointment => also sync recurrence exceptions and increase SyncCount if (master.Recurrence != null && googleAppointmentExceptions != null && AppointmentSync.UpdateRecurrenceExceptions(googleAppointmentExceptions, slave, this)) SyncedCount++; } return true; }
/// <summary> /// Updates Outlook appointment from master to slave (including groups/categories) /// </summary> public void UpdateAppointment(Outlook.AppointmentItem master, ref Google.Apis.Calendar.v3.Data.Event slave) { bool updated = false; if (slave.Creator != null && !AppointmentSync.IsOrganizer(slave.Creator.Email)) // && AppointmentPropertiesUtils.GetGoogleOutlookAppointmentId(this.SyncProfile, slave) != null) { //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment switch (this.SyncOption) { case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite Outlook appointment Logger.Log("Different Organizer found on Google, invitation maybe NOT sent by Outlook. Google appointment is overwriting Outlook because of SyncOption " + SyncOption + ": " + master.Subject + " - " + master.Start + ". ", EventType.Information); UpdateAppointment(ref slave, master, null); break; case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment Logger.Log("Different Organizer found on Google, invitation maybe NOT sent by Outlook, but Outlook appointment is overwriting Google because of SyncOption " + SyncOption + ": " + master.Subject + " - " + master.Start + ".", EventType.Information); updated = true; break; case SyncOption.MergePrompt: //promp for sync option if ( //ConflictResolution != ConflictResolution.OutlookWinsAlways && //Shouldn't be used, because Google seems to be the master of the appointment ConflictResolution != ConflictResolution.GoogleWinsAlways && ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); ConflictResolution = r.Resolve("Cannot update appointment from Outlook to Google because different Organizer found on Google, invitation maybe NOT sent by Outlook: \"" + master.Subject + " - " + master.Start + "\". Do you want to update it back from Google to Outlook?", slave, master, this); } switch (ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Skip SkippedCount++; Logger.Log("Skipped Updating appointment from Outlook to Google because different Organizer found on Google, invitation maybe NOT sent by Outlook: \"" + master.Subject + " - " + master.Start + "\".", EventType.Information); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: //Keep Google and overwrite Outlook UpdateAppointment(ref slave, master, null); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: //Keep Outlook and overwrite Google updated = true; break; default: throw new ApplicationException("Cancelled"); } break; } } else //Only update, if invitation was not sent on Google side or freshly created during this sync updated = true; //if (master.Recipients.Count == 0 || // master.Organizer == null || // AppointmentSync.IsOrganizer(AppointmentSync.GetOrganizer(master), master)|| // slave.Id.Uri == null // ) //{//Only update, if this appointment was organized on Outlook side or freshly created during this sync if (updated) { AppointmentSync.UpdateAppointment(master, slave); if (slave.Creator == null || AppointmentSync.IsOrganizer(slave.Creator.Email)) { AppointmentPropertiesUtils.SetGoogleOutlookAppointmentId(SyncProfile, slave, master); slave = SaveGoogleAppointment(slave); } //ToDo: Doesn'T work for newly created recurrence appointments before save, because Event.Reminder is throwing NullPointerException and Reminders cannot be initialized, therefore moved to after saving //if (slave.Recurrence != null && slave.Reminders != null) //{ // if (slave.Reminders.Overrides != null) // { // slave.Reminders.Overrides.Clear(); // if (master.ReminderSet) // { // var reminder = new Google.Apis.Calendar.v3.Data.EventReminder(); // reminder.Minutes = master.ReminderMinutesBeforeStart; // if (reminder.Minutes > 40300) // { // //ToDo: Check real limit, currently 40300 // Logger.Log("Reminder Minutes to big (" + reminder.Minutes + "), set to maximum of 40300 minutes for appointment: " + master.Subject + " - " + master.Start, EventType.Warning); // reminder.Minutes = 40300; // } // reminder.Method = "popup"; // slave.Reminders.Overrides.Add(reminder); // } // } // slave = SaveGoogleAppointment(slave); //} AppointmentPropertiesUtils.SetOutlookGoogleAppointmentId(this, master, slave); master.Save(); //After saving Google Appointment => also sync recurrence exceptions and save again if ((slave.Creator == null || AppointmentSync.IsOrganizer(slave.Creator.Email)) && master.IsRecurring && master.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster && AppointmentSync.UpdateRecurrenceExceptions(master, slave, this)) { slave = SaveGoogleAppointment(slave); } SyncedCount++; Logger.Log("Updated appointment from Outlook to Google: \"" + master.Subject + " - " + master.Start + "\".", EventType.Information); //} //else //{ // //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment // SkippedCount++; // //Logger.Log("Skipped Updating appointment from Outlook to Google because multiple recipients found and invitations NOT sent by Outlook: \"" + master.Subject + " - " + master.Start + "\".", EventType.Information); // Logger.Log("Skipped Updating appointment from Outlook to Google because meeting was received by Outlook: \"" + master.Subject + " - " + master.Start + "\".", EventType.Information); //} } }
public static void SyncNote(NoteMatch match, Syncronizer sync) { Outlook.NoteItem outlookNoteItem = match.OutlookNote; //try //{ if (match.GoogleNote == null && match.OutlookNote != null) { //no google note string googleNotetId = NotePropertiesUtils.GetOutlookGoogleNoteId(sync, outlookNoteItem); if (!string.IsNullOrEmpty(googleNotetId)) { //Redundant check if exist, but in case an error occurred in MatchNotes Document matchingGoogleNote = sync.GetGoogleNoteById(googleNotetId); if (matchingGoogleNote == null) { if (!sync.PromptDelete) { sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; } else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { var r = new ConflictResolver(); sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookNote); } } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: NotePropertiesUtils.ResetOutlookGoogleNoteId(sync, match.OutlookNote); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: //Avoid recreating a GoogleNote already existing //==> Delete this outlookNote instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook Note not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookNote.Subject), EventType.Information); return; } //create a Google note from Outlook note match.GoogleNote = new Document(); match.GoogleNote.Type = Document.DocumentType.Document; //match.GoogleNote.Categories.Add(new AtomCategory("http://schemas.google.com/docs/2007#document")); //match.GoogleNote.Categories.Add(new AtomCategory("document")); sync.UpdateNote(outlookNoteItem, match.GoogleNote); } else if (match.OutlookNote == null && match.GoogleNote != null) { // no outlook note if (NotePropertiesUtils.NoteFileExists(match.GoogleNote.Id, sync.SyncProfile)) { if (!sync.PromptDelete) { sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways; } else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways && sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways) { var r = new ConflictResolver(); sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleNote, sync); } switch (sync.DeleteGoogleResolution) { case DeleteResolution.KeepGoogle: case DeleteResolution.KeepGoogleAlways: System.IO.File.Delete(NotePropertiesUtils.GetFileName(match.GoogleNote.Id, sync.SyncProfile)); break; case DeleteResolution.DeleteGoogle: case DeleteResolution.DeleteGoogleAlways: //Avoid recreating a OutlookNote already existing //==> Delete this googleNote instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.OutlookToGoogleOnly) { sync.SkippedCount++; Logger.Log(string.Format("Google Note not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleNote.Title), EventType.Information); return; } //create a Outlook note from Google note outlookNoteItem = Syncronizer.CreateOutlookNoteItem(Syncronizer.SyncNotesFolder); sync.UpdateNote(match.GoogleNote, outlookNoteItem); match.OutlookNote = outlookNoteItem; } else if (match.OutlookNote != null && match.GoogleNote != null) { //merge note details //determine if this note pair were syncronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookNote, sync.OutlookPropertyNameUpdated); DateTime?lastSynced = NotePropertiesUtils.GetOutlookLastSync(sync, outlookNoteItem); if (lastSynced.HasValue) { //note pair was syncronysed before. //determine if google note was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = match.OutlookNote.LastModificationTime.AddSeconds(-match.OutlookNote.LastModificationTime.Second); DateTime lastUpdatedGoogle = match.GoogleNote.Updated.AddSeconds(-match.GoogleNote.Updated.Second); //check if both outlok and google notes where updated sync last sync if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both notes were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both notes were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google note Logger.Log("Outlook and Google note have been updated, Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook note Logger.Log("Outlook and Google note have been updated, Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, false); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped note ({0}).", match.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; default: throw new ApplicationException("Canceled"); } break; } return; } //check if outlook note was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //outlook note was changed or changed Google note will be overwritten if (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) { Logger.Log("Google note has been updated since last sync, but Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); } sync.UpdateNote(outlookNoteItem, match.GoogleNote); //at the moment use outlook as "master" source of notes - in the event of a conflict google note will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if google note was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google note was changed or changed Outlook note will be overwritten if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) { Logger.Log("Outlook note has been updated since last sync, but Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); } sync.UpdateNote(match.GoogleNote, outlookNoteItem); } } else { //notes were never synced. //merge notes. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google note sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook note sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, true); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Notes.Add(new NoteMatch(match.OutlookNote, null)); sync.Notes.Add(new NoteMatch(null, match.GoogleNote)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; default: throw new ApplicationException("Canceled"); } break; } } } else { throw new ArgumentNullException("NotetMatch has all peers null."); } //} //finally //{ //if (outlookNoteItem != null && // match.OutlookNote != null) //{ // match.OutlookNote.Update(outlookNoteItem, sync); // Marshal.ReleaseComObject(outlookNoteItem); // outlookNoteItem = null; //} //} }
public static void SyncNote(NoteMatch match, Synchronizer sync) { Outlook.NoteItem outlookNoteItem = match.OutlookNote; //try //{ if (match.GoogleNote == null && match.OutlookNote != null) { //no Google note string googleNotetId = NotePropertiesUtils.GetOutlookGoogleNoteId(sync, outlookNoteItem); if (!string.IsNullOrEmpty(googleNotetId)) { //Redundant check if exist, but in case an error occurred in MatchNotes Document matchingGoogleNote = sync.GetGoogleNoteById(googleNotetId); if (matchingGoogleNote == null) { if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete) sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { var r = new ConflictResolver(); sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookNote); } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: NotePropertiesUtils.ResetOutlookGoogleNoteId(sync, match.OutlookNote); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: //Avoid recreating a GoogleNote already existing //==> Delete this outlookNote instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } } if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook Note not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookNote.Subject), EventType.Information); return; } //create a Google note from Outlook note match.GoogleNote = new Document(); match.GoogleNote.Type = Document.DocumentType.Document; //match.GoogleNote.Categories.Add(new AtomCategory("http://schemas.google.com/docs/2007#document")); //match.GoogleNote.Categories.Add(new AtomCategory("document")); sync.UpdateNote(outlookNoteItem, match.GoogleNote); } else if (match.OutlookNote == null && match.GoogleNote != null) { // no outlook note if (NotePropertiesUtils.NoteFileExists(match.GoogleNote.Id, sync.SyncProfile)) { if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete) sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways; else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways && sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways) { var r = new ConflictResolver(); sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleNote, sync); } switch (sync.DeleteGoogleResolution) { case DeleteResolution.KeepGoogle: case DeleteResolution.KeepGoogleAlways: System.IO.File.Delete(NotePropertiesUtils.GetFileName(match.GoogleNote.Id, sync.SyncProfile)); break; case DeleteResolution.DeleteGoogle: case DeleteResolution.DeleteGoogleAlways: //Avoid recreating a OutlookNote already existing //==> Delete this googleNote instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.OutlookToGoogleOnly) { sync.SkippedCount++; Logger.Log(string.Format("Google Note not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleNote.Title), EventType.Information); return; } //create a Outlook note from Google note outlookNoteItem = Synchronizer.CreateOutlookNoteItem(Synchronizer.SyncNotesFolder); sync.UpdateNote(match.GoogleNote, outlookNoteItem); match.OutlookNote = outlookNoteItem; } else if (match.OutlookNote != null && match.GoogleNote != null) { //merge note details //determine if this note pair were synchronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookNote, sync.OutlookPropertyNameUpdated); DateTime? lastSynced = NotePropertiesUtils.GetOutlookLastSync(sync,outlookNoteItem); if (lastSynced.HasValue) { //note pair was syncronysed before. //determine if google note was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = match.OutlookNote.LastModificationTime.AddSeconds(-match.OutlookNote.LastModificationTime.Second); DateTime lastUpdatedGoogle = match.GoogleNote.Updated.AddSeconds(-match.GoogleNote.Updated.Second); //check if both outlok and google notes where updated sync last sync if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both notes were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both notes were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google note Logger.Log("Outlook and Google note have been updated, Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook note Logger.Log("Outlook and Google note have been updated, Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, false); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped note ({0}).", match.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; default: throw new ApplicationException("Canceled"); } break; } return; } //check if outlook note was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //outlook note was changed or changed Google note will be overwritten if (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) Logger.Log("Google note has been updated since last sync, but Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(outlookNoteItem, match.GoogleNote); //at the moment use outlook as "master" source of notes - in the event of a conflict google note will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if google note was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google note was changed or changed Outlook note will be overwritten if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) Logger.Log("Outlook note has been updated since last sync, but Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(match.GoogleNote, outlookNoteItem); } } else { //notes were never synced. //merge notes. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google note sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook note sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, true); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Notes.Add(new NoteMatch(match.OutlookNote, null)); sync.Notes.Add(new NoteMatch(null, match.GoogleNote)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; default: throw new ApplicationException("Canceled"); } break; } } } else throw new ArgumentNullException("NotetMatch has all peers null."); //} //finally //{ //if (outlookNoteItem != null && // match.OutlookNote != null) //{ // match.OutlookNote.Update(outlookNoteItem, sync); // Marshal.ReleaseComObject(outlookNoteItem); // outlookNoteItem = null; //} //} }