Esempio n. 1
0
        private SyncResult synchronize()
        {
            Console console = Forms.Main.Instance.Console;

            console.Update("Finding Calendar Entries", Console.Markup.mag_right, newLine: false);

            List <AppointmentItem> outlookEntries = null;
            List <Event>           googleEntries  = null;

            try {
                #region Read Outlook items
                console.Update("Scanning Outlook calendar...");
                outlookEntries = OutlookOgcs.Calendar.Instance.GetCalendarEntriesInRange(false);
                console.Update(outlookEntries.Count + " Outlook calendar entries found.", Console.Markup.sectionEnd, newLine: false);

                if (CancellationPending)
                {
                    return(SyncResult.UserCancelled);
                }
                #endregion

                #region Read Google items
                console.Update("Scanning Google calendar...");
                try {
                    googleEntries = GoogleOgcs.Calendar.Instance.GetCalendarEntriesInRange();
                } catch (AggregateException agex) {
                    OGCSexception.AnalyseAggregate(agex);
                } catch (Google.Apis.Auth.OAuth2.Responses.TokenResponseException ex) {
                    OGCSexception.AnalyseTokenResponse(ex, false);
                    return(SyncResult.Fail);
                } catch (System.Net.Http.HttpRequestException ex) {
                    OGCSexception.Analyse(ex);
                    ex.Data.Add("OGCS", "ERROR: Unable to connect to the Google calendar. Please try again.");
                    throw ex;
                } catch (System.Exception ex) {
                    OGCSexception.Analyse(ex);
                    ex.Data.Add("OGCS", "ERROR: Unable to connect to the Google calendar.");
                    if (OGCSexception.GetErrorCode(ex) == "0x8013153B") //ex.Message == "A task was canceled." - likely timed out.
                    {
                        ex.Data["OGCS"] += " Please try again.";
                    }
                    throw ex;
                }
                Recurrence.Instance.SeparateGoogleExceptions(googleEntries);
                if (Recurrence.Instance.GoogleExceptions != null && Recurrence.Instance.GoogleExceptions.Count > 0)
                {
                    console.Update(googleEntries.Count + " Google calendar entries found.");
                    console.Update(Recurrence.Instance.GoogleExceptions.Count + " are exceptions to recurring events.", Console.Markup.sectionEnd, newLine: false);
                }
                else
                {
                    console.Update(googleEntries.Count + " Google calendar entries found.", Console.Markup.sectionEnd, newLine: false);
                }

                if (CancellationPending)
                {
                    return(SyncResult.UserCancelled);
                }
                #endregion

                #region Normalise recurring items in sync window
                console.Update("Total inc. recurring items spanning sync date range...");
                //Outlook returns recurring items that span the sync date range, Google doesn't
                //So check for master Outlook items occurring before sync date range, and retrieve Google equivalent
                for (int o = outlookEntries.Count - 1; o >= 0; o--)
                {
                    log.Fine("Processing " + (o + 1) + "/" + outlookEntries.Count);
                    AppointmentItem ai = null;
                    try {
                        if (outlookEntries[o] is AppointmentItem)
                        {
                            ai = outlookEntries[o];
                        }
                        else if (outlookEntries[o] is MeetingItem)
                        {
                            log.Info("Calendar object appears to be a MeetingItem, so retrieving associated AppointmentItem.");
                            MeetingItem mi = outlookEntries[o] as MeetingItem;
                            outlookEntries[o] = mi.GetAssociatedAppointment(false);
                            ai = outlookEntries[o];
                        }
                        else
                        {
                            log.Warn("Unknown calendar object type - cannot sync it.");
                            skipCorruptedItem(ref outlookEntries, outlookEntries[o], "Unknown object type.");
                            outlookEntries[o] = (AppointmentItem)OutlookOgcs.Calendar.ReleaseObject(outlookEntries[o]);
                            continue;
                        }
                    } catch (System.Exception ex) {
                        log.Warn("Encountered error casting calendar object to AppointmentItem - cannot sync it.");
                        log.Debug(ex.Message);
                        skipCorruptedItem(ref outlookEntries, outlookEntries[o], ex.Message);
                        outlookEntries[o] = (AppointmentItem)OutlookOgcs.Calendar.ReleaseObject(outlookEntries[o]);
                        ai = (AppointmentItem)OutlookOgcs.Calendar.ReleaseObject(ai);
                        continue;
                    }

                    //Now let's check there's a start/end date - sometimes it can be missing, even though this shouldn't be possible!!
                    String entryID;
                    try {
                        entryID = outlookEntries[o].EntryID;
                        DateTime checkDates = ai.Start;
                        checkDates = ai.End;
                    } catch (System.Exception ex) {
                        log.Warn("Calendar item does not have a proper date range - cannot sync it.");
                        log.Debug(ex.Message);
                        skipCorruptedItem(ref outlookEntries, outlookEntries[o], ex.Message);
                        outlookEntries[o] = (AppointmentItem)OutlookOgcs.Calendar.ReleaseObject(outlookEntries[o]);
                        ai = (AppointmentItem)OutlookOgcs.Calendar.ReleaseObject(ai);
                        continue;
                    }

                    if (ai.IsRecurring && ai.Start.Date < Settings.Instance.SyncStart && ai.End.Date < Settings.Instance.SyncStart)
                    {
                        //We won't bother getting Google master event if appointment is yearly reoccurring in a month outside of sync range
                        //Otherwise, every sync, the master event will have to be retrieved, compared, concluded nothing's changed (probably) = waste of API calls
                        RecurrencePattern oPattern = ai.GetRecurrencePattern();
                        try {
                            if (oPattern.RecurrenceType.ToString().Contains("Year"))
                            {
                                log.Fine("It's an annual event.");
                                Boolean  monthInSyncRange = false;
                                DateTime monthMarker      = Settings.Instance.SyncStart;
                                while (Convert.ToInt32(monthMarker.ToString("yyyyMM")) <= Convert.ToInt32(Settings.Instance.SyncEnd.ToString("yyyyMM")) &&
                                       !monthInSyncRange)
                                {
                                    if (monthMarker.Month == ai.Start.Month)
                                    {
                                        monthInSyncRange = true;
                                    }
                                    monthMarker = monthMarker.AddMonths(1);
                                }
                                log.Fine("Found it to be " + (monthInSyncRange ? "inside" : "outside") + " sync range.");
                                if (!monthInSyncRange)
                                {
                                    outlookEntries.Remove(ai); log.Fine("Removed."); continue;
                                }
                            }
                            Event masterEv = Recurrence.Instance.GetGoogleMasterEvent(ai);
                            if (masterEv != null && masterEv.Status != "cancelled")
                            {
                                Event cachedEv = googleEntries.Find(x => x.Id == masterEv.Id);
                                if (cachedEv == null)
                                {
                                    googleEntries.Add(masterEv);
                                }
                                else
                                {
                                    if (masterEv.Updated > cachedEv.Updated)
                                    {
                                        log.Debug("Refreshing cache for this Event.");
                                        googleEntries.Remove(cachedEv);
                                        googleEntries.Add(masterEv);
                                    }
                                }
                            }
                        } catch (System.Exception ex) {
                            console.Update("Failed to retrieve master for Google recurring event outside of sync range.", Console.Markup.error);
                            throw ex;
                        } finally {
                            oPattern = (RecurrencePattern)OutlookOgcs.Calendar.ReleaseObject(oPattern);
                        }
                    }
                    //Completely dereference object and retrieve afresh (due to GetRecurrencePattern earlier)
                    ai = (AppointmentItem)OutlookOgcs.Calendar.ReleaseObject(ai);
                    OutlookOgcs.Calendar.Instance.IOutlook.GetAppointmentByID(entryID, out ai);
                    outlookEntries[o] = ai;
                }
                console.Update("Outlook " + outlookEntries.Count + ", Google " + googleEntries.Count);

                GoogleOgcs.Calendar.ExportToCSV("Outputting all Events.", "google_events.csv", googleEntries);
                OutlookOgcs.Calendar.ExportToCSV("Outputting all Appointments.", "outlook_appointments.csv", outlookEntries);
                if (CancellationPending)
                {
                    return(SyncResult.UserCancelled);
                }
                #endregion

                Boolean success    = true;
                String  bubbleText = "";
                if (Settings.Instance.SyncDirection != Direction.GoogleToOutlook)
                {
                    success = outlookToGoogle(outlookEntries, googleEntries, ref bubbleText);
                    if (CancellationPending)
                    {
                        return(SyncResult.UserCancelled);
                    }
                }
                if (!success)
                {
                    return(SyncResult.Fail);
                }
                if (Settings.Instance.SyncDirection != Direction.OutlookToGoogle)
                {
                    if (bubbleText != "")
                    {
                        bubbleText += "\r\n";
                    }
                    success = googleToOutlook(googleEntries, outlookEntries, ref bubbleText);
                    if (CancellationPending)
                    {
                        return(SyncResult.UserCancelled);
                    }
                }
                if (bubbleText != "")
                {
                    Forms.Main.Instance.NotificationTray.ShowBubbleInfo(bubbleText);
                }

                return(SyncResult.OK);
            } finally {
                for (int o = outlookEntries.Count() - 1; o >= 0; o--)
                {
                    outlookEntries[o] = (AppointmentItem)OutlookOgcs.Calendar.ReleaseObject(outlookEntries[o]);
                    outlookEntries.RemoveAt(o);
                }
            }
        }
Esempio n. 2
0
        public void Start(Boolean updateSyncSchedule = true)
        {
            Forms.Main mainFrm = Forms.Main.Instance;
            try {
                DateTime syncStarted   = DateTime.Now;
                String   cacheNextSync = mainFrm.lNextSyncVal.Text;

                mainFrm.Console.Clear();

                if (Settings.Instance.UseGoogleCalendar == null ||
                    Settings.Instance.UseGoogleCalendar.Id == null ||
                    Settings.Instance.UseGoogleCalendar.Id == "")
                {
                    MessageBox.Show("You need to select a Google Calendar first on the 'Settings' tab.");
                    return;
                }

                if (Settings.Instance.MuteClickSounds)
                {
                    Console.MuteClicks(true);
                }

                //Check network availability
                if (!System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
                {
                    mainFrm.Console.Update("There does not appear to be any network available! Sync aborted.", Console.Markup.error, notifyBubble: true);
                    setNextSync(syncStarted, false, updateSyncSchedule, cacheNextSync);
                    return;
                }
                //Check if Outlook is Online
                try {
                    if (OutlookOgcs.Calendar.Instance.IOutlook.Offline() && Settings.Instance.AddAttendees)
                    {
                        mainFrm.Console.Update("<p>You have selected to sync attendees but Outlook is currently offline.</p>" +
                                               "<p>Either put Outlook online or do not sync attendees.</p>", Console.Markup.error, notifyBubble: true);
                        setNextSync(syncStarted, false, updateSyncSchedule, cacheNextSync);
                        return;
                    }
                } catch (System.Exception ex) {
                    mainFrm.Console.UpdateWithError(null, ex, notifyBubble: true);
                    OGCSexception.Analyse(ex, true);
                    return;
                }
                GoogleOgcs.Calendar.APIlimitReached_attendee = false;
                Forms.Main.Instance.SyncNote(Forms.Main.SyncNotes.QuotaExhaustedInfo, null, false);
                Forms.Main.Instance.bSyncNow.Text = "Stop Sync";
                Forms.Main.Instance.NotificationTray.UpdateItem("sync", "&Stop Sync");

                Forms.Main.Instance.lNextSyncVal.Text = "In progress...";

                StringBuilder sb = new StringBuilder();
                Forms.Main.Instance.Console.BuildOutput("Sync version: " + System.Windows.Forms.Application.ProductVersion, ref sb);
                Forms.Main.Instance.Console.BuildOutput((ManualForceCompare ? "Full s" : "S") + "ync started at " + syncStarted.ToString(), ref sb);
                Forms.Main.Instance.Console.BuildOutput("Syncing from " + Settings.Instance.SyncStart.ToShortDateString() +
                                                        " to " + Settings.Instance.SyncEnd.ToShortDateString(), ref sb);
                mainFrm.Console.BuildOutput(Settings.Instance.SyncDirection.Name, ref sb);

                //Make the clock emoji show the right time
                int minsPastHour = DateTime.Now.Minute;
                minsPastHour = (int)minsPastHour - (minsPastHour % 30);
                sb.Insert(0, ":clock" + DateTime.Now.ToString("hh").TrimStart('0') + (minsPastHour == 00 ? "" : "30") + ":");
                mainFrm.Console.Update(sb);

                if (Settings.Instance.OutlookPush)
                {
                    OutlookOgcs.Calendar.Instance.DeregisterForPushSync();
                }

                SyncResult syncResult     = SyncResult.Fail;
                int        failedAttempts = 0;
                Social.TrackSync();
                try {
                    GoogleOgcs.Calendar.Instance.GetCalendarSettings();
                } catch (System.AggregateException ae) {
                    OGCSexception.AnalyseAggregate(ae);
                    syncResult = SyncResult.AutoRetry;
                } catch (System.Exception ex) {
                    log.Warn(ex.Message);
                    syncResult = SyncResult.AutoRetry;
                }
                while (syncResult == SyncResult.Fail)
                {
                    if (failedAttempts > 0)
                    {
                        if (MessageBox.Show("The synchronisation failed - check the Sync tab for further details.\r\nDo you want to try again?", "Sync Failed",
                                            MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == System.Windows.Forms.DialogResult.No)
                        {
                            syncResult = SyncResult.Abandon;
                            break;
                        }
                        else
                        {
                            log.Info("User opted to retry sync straight away.");
                        }
                    }

                    //Set up a separate thread for the sync to operate in. Keeps the UI responsive.
                    bwSync = new AbortableBackgroundWorker();
                    //Don't need thread to report back. The logbox is updated from the thread anyway.
                    bwSync.WorkerReportsProgress      = false;
                    bwSync.WorkerSupportsCancellation = true;

                    //Kick off the sync in the background thread
                    bwSync.DoWork += new DoWorkEventHandler(
                        delegate(object o, DoWorkEventArgs args) {
                        BackgroundWorker b = o as BackgroundWorker;
                        try {
                            syncResult = synchronize();
                        } catch (System.Exception ex) {
                            sb = new StringBuilder();
                            mainFrm.Console.BuildOutput("The following error was encountered during sync:-", ref sb);
                            if (ex.Data.Count > 0 && ex.Data.Contains("OGCS"))
                            {
                                mainFrm.Console.BuildOutput(ex.Data["OGCS"].ToString(), ref sb);
                                mainFrm.Console.Update(sb, Console.Markup.error, notifyBubble: true);
                                if (ex.Data["OGCS"].ToString().Contains("Please try again"))
                                {
                                    syncResult = SyncResult.AutoRetry;
                                }
                            }
                            else
                            {
                                OGCSexception.Analyse(ex, true);
                                mainFrm.Console.UpdateWithError(null, ex, notifyBubble: true);
                                syncResult = SyncResult.Fail;
                            }
                        }
                    }
                        );

                    bwSync.RunWorkerAsync();
                    while (bwSync != null && (bwSync.IsBusy || bwSync.CancellationPending))
                    {
                        System.Windows.Forms.Application.DoEvents();
                        System.Threading.Thread.Sleep(100);
                    }
                    try {
                        //Get Logbox text - this is a little bit dirty!
                        if (syncResult != SyncResult.OK && mainFrm.Console.DocumentText.Contains("The RPC server is unavailable."))
                        {
                            mainFrm.Console.Update("Attempting to reconnect to Outlook...");
                            try { OutlookOgcs.Calendar.Instance.Reset(); } catch { }
                        }
                    } finally {
                        failedAttempts += (syncResult != SyncResult.OK) ? 1 : 0;
                    }
                }

                if (syncResult == SyncResult.OK)
                {
                    Settings.Instance.CompletedSyncs++;
                    consecutiveSyncFails = 0;
                    mainFrm.Console.Update("Sync finished with success!", Console.Markup.checkered_flag);
                }
                else if (syncResult == SyncResult.AutoRetry)
                {
                    consecutiveSyncFails++;
                    mainFrm.Console.Update("Sync encountered a problem and did not complete successfully.<br/>" + consecutiveSyncFails + " consecutive syncs failed.", Console.Markup.error, notifyBubble: true);
                }
                else
                {
                    consecutiveSyncFails += failedAttempts;
                    mainFrm.Console.Update("Operation aborted after " + failedAttempts + " failed attempts!", Console.Markup.error);
                }

                setNextSync(syncStarted, syncResult == SyncResult.OK, updateSyncSchedule, cacheNextSync);
                mainFrm.CheckSyncMilestone();
            } finally {
                mainFrm.bSyncNow.Text = "Start Sync";
                mainFrm.NotificationTray.UpdateItem("sync", "&Sync Now");
                if (Settings.Instance.MuteClickSounds)
                {
                    Console.MuteClicks(false);
                }

                if (Settings.Instance.OutlookPush)
                {
                    OutlookOgcs.Calendar.Instance.RegisterForPushSync();
                }

                //Release Outlook reference if GUI not available.
                //Otherwise, tasktray shows "another program is using outlook" and it doesn't send and receive emails
                OutlookOgcs.Calendar.Instance.Disconnect(onlyWhenNoGUI: true);
            }
        }