// binds the two events together public static void BindEvents(AppointmentItem oitem, Event gitem, string event_property_key) { // make sure to tag the user property of the google id UserProperty oitem_google_prop = oitem.UserProperties.Find(event_property_key); if (oitem_google_prop != null) { oitem_google_prop.Value = gitem.Id; System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem_google_prop); } else { oitem.UserProperties.Add(event_property_key, OlUserPropertyType.olText).Value = gitem.Id; } try { // save the outlook event oitem.Save(); } catch (System.Exception) { // unable to save the outlook item... // should report an error, but this will get handled by the main form... } // make sure to tag the private property of the outlook id if (gitem.ExtendedProperties == null) { gitem.ExtendedProperties = new Event.ExtendedPropertiesData(); gitem.ExtendedProperties.Shared = new Dictionary <string, string>(); } else if (gitem.ExtendedProperties.Shared == null) { gitem.ExtendedProperties.Shared = new Dictionary <string, string>(); } gitem.ExtendedProperties.Shared[event_property_key] = OutlookCalendar.FormatEventID(oitem); }
bool synchronize(List <AppointmentItem> outlook_items, List <Event> google_items) { // TODO: consider description updates (only needs to be considered when checked... come back and fix this) // TODO: optimize comparison algorithms - this one not mine, but should consider for larger sets of data // TODO: outlook to google recurrence 90% complete // getting weird error message from outlook: "The operation cannot be performed because the message has changed" // need to determine what happens when deleting just the master recurrence object // TODO: google to outlook recurrence 0% complete... need to read up on http://www.ietf.org/rfc/rfc2445 // TODO: testing on a larger scale... // TODO: refactor this function, as it is getting out of control... // indicates the number of entries added / updated / removed.... uint google_entries_added = 0; uint google_entries_updated = 0; uint google_entries_removed = 0; uint outlook_entries_added = 0; uint outlook_entries_updated = 0; uint outlook_entries_removed = 0; uint bound_entries_found = 0; uint errors = 0; // first synchronize outlook -> google // run over all the office items and add or update on google for (int i = outlook_items.Count - 1; i >= 0; --i) { // obtain a reference to the outlook item... var oitem = outlook_items[i]; // determine first if this outlook item is associated with a google calendar item... UserProperty oitem_google_prop = oitem.UserProperties.Find(EventPropertyKey); if (oitem_google_prop == null) { // run across the google events to see if there is one that matches Event gitem = null; foreach (var g in google_items) { // lowercase match the signature and // the property is not set or the property matches the oitem, match found... // the user has to type it the same in both cases to make a match... // is there something better here? if (signature(g).ToLower() == signature(oitem).ToLower() && (g.ExtendedProperties == null || g.ExtendedProperties.Shared == null || g.ExtendedProperties.Shared.ContainsKey(EventPropertyKey) == false || g.ExtendedProperties.Shared[EventPropertyKey] == OutlookCalendar.FormatEventID(oitem))) { gitem = g; break; } } if (gitem != null) { // give some indication of what will take place logboxout("Binding Outlook and Google event: " + oitem.Subject + " (" + oitem.Start + ")"); // bind the properties together OutlookCalendar.Instance.Bind(oitem, gitem); // update the instance on the google calendar GoogleCalendar.Instance.updateEntry(gitem); // remove the outlook item and the google item from the lists... // do not need to process them again for future iterations... google_items.Remove(gitem); outlook_items.Remove(oitem); // save and close the outlook item ((_AppointmentItem)oitem).Close(OlInspectorClose.olSave); // remove the com reference System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem); // update the stats ++bound_entries_found; } else { // give some indication of what will take place logboxout("Creating Google event: " + oitem.Subject + " (" + oitem.Start + ")"); // the property does not exist... this mean that google calendar // does not have this outlook entry... after this call, the outlook item // and the google item should be tied together by the use of properties... gitem = GoogleCalendar.Instance.addEntry(oitem, cbAddDescription.Checked, cbAddReminders.Checked, cbAddAttendees.Checked); // updated the stats ++google_entries_added; } } else { // the property exists, so we need to determine if the event should be updated // first, find the event in the list of google items... Event gitem = null; foreach (var g in google_items) { if (oitem_google_prop.Value == g.Id) { gitem = g; break; } } if (gitem == null) { // give some indication of what will take place logboxout("Removing Outlook event: " + oitem.Subject + " (" + oitem.Start + ")"); // the item does not exist, so it was removed from google calendar // since it was removed from google, remove it from outlook OutlookCalendar.Instance.deleteCalendarEntry(oitem); // outlook item can no longer be used after it is deleted outlook_items.RemoveAt(i); // need to release the com reference System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem); // update the stats ++outlook_entries_removed; } else { // the item does exist... // determine if the event should be updated... if ((signature(oitem) != signature(gitem) || OutlookGoogleSync.Utilities.ObtainUserBodyData(oitem.Body) != OutlookGoogleSync.Utilities.ObtainUserBodyData(gitem.Description)) && oitem.LastModificationTime > gitem.Updated) { // the google event can only be updated if owned by the calendar... if (gitem.Organizer.Self.HasValue && gitem.Organizer.Self.Value) { // give some indication of what will take place if (signature(oitem) != signature(gitem)) { logboxout("Updating Google event: " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ") ==> " + oitem.Subject + " (" + oitem.Start + ")"); } else { logboxout("Updating Google event description: " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ")"); } // update the event based on the outlook item GoogleCalendar.Instance.updateEntry(gitem, oitem, cbAddDescription.Checked, cbAddReminders.Checked, cbAddAttendees.Checked); // update the status ++google_entries_updated; } // this google item has been processed google_items.Remove(gitem); } } // need to release the com reference System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem_google_prop); } } // second synchronize google -> outlook // run over all the google items and add or update on outlook for (int i = google_items.Count - 1; i >= 0; --i) { // obtain a reference to the google item... var gitem = google_items[i]; // determine first if this google item is associated with an outlook calendar item... string outlook_id = null; if (gitem.ExtendedProperties != null && gitem.ExtendedProperties.Shared != null && gitem.ExtendedProperties.Shared.ContainsKey(EventPropertyKey)) { outlook_id = gitem.ExtendedProperties.Shared[EventPropertyKey]; } if (outlook_id == null) { // run across the outlook events to see if there is one that matches... // this may not need to be done, as the google pass should have found them all... // lets be on the safe side here and do it anyway... AppointmentItem oitem = null; foreach (var o in outlook_items) { // lowercase match the signature and // the property is not set or the property matches the gitem, match found... // the user has to type it the same in both cases to make a match... // is there something better here? if (signature(o).ToLower() == signature(gitem).ToLower() && (o.UserProperties == null || o.UserProperties.Find(EventPropertyKey) == null || o.UserProperties.Find(EventPropertyKey).Value == gitem.Id)) { oitem = o; break; } } if (oitem != null) { // give some indication of what will take place logboxout("Binding Outlook and Google event: " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ")"); // bind the properties together OutlookCalendar.Instance.Bind(oitem, gitem); // update the instance on the google calendar GoogleCalendar.Instance.updateEntry(gitem); // remove the outlook item and the google item from the lists... // do not need to process them again for future iterations... google_items.Remove(gitem); outlook_items.Remove(oitem); // save and close the outlook item ((_AppointmentItem)oitem).Close(OlInspectorClose.olSave); // need to release the com reference System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem); // update the stats ++bound_entries_found; } else { // give some indication of what will take place logboxout("Creating Outlook event: " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ")"); // the property does not exist... this means that the outlook calendar // does not have this google entry... after this call, the outlook item // and the google item should be tied together by the use of properties... oitem = OutlookCalendar.Instance.addEntry(gitem, cbAddDescription.Checked, cbAddReminders.Checked, cbAddAttendees.Checked); try { // google currently does not have an updated id... this needs to be reflected on the server... GoogleCalendar.Instance.updateEntry(gitem); } catch (System.Exception ex) { // unable to update the google entry for some reason... // let the user know and the reason why... logboxout(""); logboxout("Error: Google event cannot be bound to Outlook event!"); logboxout("Reason: " + ex.Message); logboxout(""); // update the stats ++errors; } // need to release the com reference System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem); // update the stats ++outlook_entries_added; } } else { // the property exists, so we need to determine if the event should be updated // first, find the event in the list of outlook items... AppointmentItem oitem = null; foreach (var o in outlook_items) { if (outlook_id == OutlookCalendar.FormatEventID(o)) { oitem = o; break; } } if (oitem == null) { // give some indication of what will take place logboxout("Removing Google event: " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ")"); // the item does not exist, so it was removed from outlook calendar // since it was removed from outlook, remove it from google GoogleCalendar.Instance.deleteCalendarEntry(gitem); // update the stats ++google_entries_removed; } else { // this outlook item is about to be processed... no matter the action // taken, we can remove it from the list of items, so as not // to need to look at it again in future iterations... outlook_items.Remove(oitem); // the item does exist... // determine if the event should be updated... if ((signature(gitem) != signature(oitem) || OutlookGoogleSync.Utilities.ObtainUserBodyData(oitem.Body) != OutlookGoogleSync.Utilities.ObtainUserBodyData(gitem.Description)) && gitem.Updated > oitem.LastModificationTime) { // give some indication of what will take place if (signature(gitem) != signature(oitem)) { logboxout("Updating Outlook event: " + oitem.Subject + " (" + oitem.Start + ") ==> " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ")"); } else { logboxout("Updating Outlook event description: " + oitem.Subject + " (" + oitem.Start + ")"); } // update the event based on the google item OutlookCalendar.Instance.updateEntry(oitem, gitem, cbAddDescription.Checked, cbAddReminders.Checked, cbAddAttendees.Checked); // update the status ++outlook_entries_updated; } try { // save and close the outlook item ((_AppointmentItem)oitem).Close(OlInspectorClose.olSave); } catch (System.Exception ex) { // unable to save the outlook entry for some reason... // let the user know and the reason why... logboxout(""); logboxout("Error: Outlook event cannot be saved!"); logboxout("Reason: " + ex.Message); logboxout(""); // update the stats ++errors; } // need to release the com reference System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem); } } } // close all the outlook items foreach (var o in outlook_items) { ((_AppointmentItem)o).Close(OlInspectorClose.olSave); System.Runtime.InteropServices.Marshal.ReleaseComObject(o); } // clear out both lists... these items have been processed... outlook_items.Clear(); google_items.Clear(); // summarize the changes made logboxout("--------------------------------------------------"); logboxout("Google entries added: " + google_entries_added); logboxout("Google entries updated: " + google_entries_updated); logboxout("Google entries removed: " + google_entries_removed); logboxout(""); logboxout("Outlook entries added: " + outlook_entries_added); logboxout("Outlook entries updated: " + outlook_entries_updated); logboxout("Outlook entries removed: " + outlook_entries_removed); logboxout(""); logboxout("Bound entries found: " + bound_entries_found); logboxout(""); logboxout("Errors found: " + errors); return(errors == 0); }