Wrappers/WatchedWindow.cs
author Thomas
Thu, 06 Sep 2018 09:29:19 +0200
branchOUT-497
changeset 2359 0e6191d039e4
child 2360 f01523e581fa
permissions -rw-r--r--
Major restructuration:
- Remove FormRegionPrivacyStatus
- Create new WatchedWindow as base class for WatchedExplorers and WatchedInspectors
- Move all logic from FormRegionPrivacy to WatchedWindow
- Use Explorer.SelectionChanged and Inspectors.Newinspector events instead of FormRegionPrivacyStatus.FormRegionShowing to detect opening of mail item.
- Rework of RibbonCustomizations. State of pEp button now stored in WatchedWindow instead of FormRegionPrivacyStatus
     1 ´╗┐using pEp.UI;
     2 using pEpCOMServerAdapterLib;
     3 using System;
     4 using System.ComponentModel;
     5 using System.Windows.Forms;
     6 using Outlook = Microsoft.Office.Interop.Outlook;
     7 
     8 namespace pEp
     9 {
    10     /// <summary>
    11     /// Stores an Outlook Window with connected events.
    12     /// </summary>
    13     public abstract class WatchedWindow : IDisposable
    14     {
    15         private CryptableMailItem               cryptableMailItem           = null;
    16         private bool                            displayMirrorRequested      = false;
    17         private HandshakeDialog                 handshakeDialog             = null;
    18         private bool                            isEnabled                   = true;
    19         private bool                            isStarted                   = false;
    20         private bool                            processingOngoing           = false;
    21         private bool                            refreshOngoing              = false;
    22         private bool                            repeatProcessing            = false;
    23         private int                             repeatCounter               = 0;
    24         private const int                       maxRepeatCount              = 5;
    25 
    26         private Outlook.MailItem                _CurrentMailItem            = null;
    27 
    28         public enum WindowTypes
    29         {
    30             Undefined,
    31             Inspector,
    32             Explorer
    33         }
    34 
    35         #region Properties
    36         /// <summary>
    37         /// The rating of the currently selected message.
    38         /// </summary>
    39         private pEpRating _Rating = pEpRating.pEpRatingUndefined;
    40 
    41         /// <summary>
    42         /// The mail item that is connected to this Inspector/Explorer window.
    43         /// </summary>
    44         public Outlook.MailItem CurrentMailItem
    45         {
    46             get
    47             {
    48                 return this._CurrentMailItem;
    49             }
    50             protected set
    51             {
    52                 this._CurrentMailItem = value;
    53             }
    54         }
    55 
    56         /// <summary>
    57         /// Gets or sets whether to disable the Force Protection option.
    58         /// </summary>
    59         public bool DisableForceProtection { get; set; } = false;
    60 
    61         /// <summary>
    62         /// Gets or sets whether to send this message forcefully protected.
    63         /// </summary>
    64         public bool ForceProtection { get; set; } = false;
    65 
    66         /// <summary>
    67         /// Gets or sets whether to send this message forcefully unencrypted.
    68         /// </summary>
    69         public bool ForceUnencrypted { get; set; } = false;
    70 
    71         /// <summary>
    72         /// Gets or sets whether the message is a draft message.
    73         /// </summary>
    74         public bool IsDraft { get; set; } = false;
    75 
    76         /// <summary>
    77         /// Gets or sets whether to always store this message as if on an untrusted server.
    78         /// </summary>
    79         public bool NeverUnsecure { get; set; } = false;
    80 
    81         /// <summary>
    82         /// Gets the rating of this message.
    83         /// </summary>
    84         public pEpRating Rating
    85         {
    86             get { return this._Rating; }
    87         }
    88 
    89         /// <summary>
    90         /// Gets the associated Explorer/Inspector window. To be overwritten by child class.
    91         /// </summary>
    92         public abstract dynamic Window { get; }
    93 
    94         /// <summary>
    95         /// Sets the rating of this message and updates the UI.
    96         /// </summary>
    97         /// <param name="rating">The message rating.</param>
    98         public void SetRating(pEpRating rating)
    99         {
   100             this._Rating = rating;
   101             RibbonCustomizations.Invalidate();
   102         }
   103 
   104         /// <summary>
   105         /// The timer that schedules the updates of this window.
   106         /// </summary>
   107         public System.Windows.Forms.Timer TimerRefresh { get; set; } = new System.Windows.Forms.Timer();
   108         #endregion
   109 
   110         #region Constructors / Destructors
   111 
   112         /// <summary>
   113         /// Destructor.
   114         /// </summary>
   115         ~WatchedWindow()
   116         {
   117             this.Dispose(true);
   118         }
   119         #endregion
   120 
   121         #region Methods
   122         /**************************************************************
   123          * 
   124          * Methods
   125          * 
   126          *************************************************************/
   127 
   128         /// <summary>
   129         /// Releases all resources and disconnects internal events.
   130         /// </summary>
   131         public void Dispose()
   132         {
   133             this.Dispose(true);
   134         }
   135 
   136         /// <summary>
   137         /// Clean up any resources being used.
   138         /// </summary>
   139         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
   140         protected virtual void Dispose(bool disposing)
   141         {
   142             if (disposing)
   143             {
   144                 // Set Outlook objects to null
   145                 this._CurrentMailItem = null;
   146             }
   147         }
   148               
   149         /// <summary>
   150         /// Builds the latest state of the encryption status manager then shows the UI.
   151         /// </summary>
   152         public void BuildAndShowManager()
   153         {
   154             /* Resolve all recipients -- this ensures the identities list is correctly populated
   155              * 
   156              * Note: The PropertyChanged changed event must be disconnected before trying to resolve.
   157              * This is because the resolve process can modify the contents of the mail item which triggers an event.
   158              * The PropertyChanged event would then trigger a UI refresh cycle. However, because the GetManagerState itself 
   159              * is called within the UI refresh, an infinite loop could occur trying to resolve a recipient that
   160              * cannot be resolved (no address).
   161              */
   162             try
   163             {
   164                 this.ResolveAllRecipients();
   165             }
   166             catch (Exception ex)
   167             {
   168                 Log.Verbose("BuildAndShowManager: Error resolving recipients. " + ex.ToString());
   169             }
   170 
   171             // Build the dialog
   172             try
   173             {
   174                 // The PEPMessage to build the dialog with
   175                 PEPMessage message = null;
   176 
   177                 // The own identity to build the dialog with
   178                 PEPIdentity myself = this.cryptableMailItem?.Myself;
   179 
   180                 // If message is a draft, create it directly from the Outlook mail item
   181                 if (this.IsDraft)
   182                 {
   183                     Log.Verbose("BuildAndShowManager: Creating PEPMessage from draft.");
   184 
   185                     if (PEPMessage.Create(this._CurrentMailItem, out message) != Globals.ReturnStatus.Success)
   186                     {
   187                         Log.Error("BuildAndShowManager: Error creating PEPMessage from draft.");
   188                         message = null;
   189                     }
   190                     else
   191                     {
   192                         // Calculate rating
   193                         message.Rating = message.GetOutgoingRating();
   194 
   195                         // If Force Protection is set, assign a random GUID
   196                         if ((this.ForceProtection) &&
   197                             (this.DisableForceProtection == false))
   198                         {
   199                             message.ForceProtectionId = Guid.NewGuid().ToString();
   200                         }
   201 
   202                         // If message is Force Unencrypted, assign it
   203                         message.ForceUnencrypted = this.ForceUnencrypted;
   204                     }
   205                 }
   206                 else
   207                 {
   208                     // Else, use either the cryptable mail item's associated mirror or message
   209                     message = this.cryptableMailItem?.Mirror ?? this.cryptableMailItem?.Message;
   210                     Log.Verbose(string.Format("BuildAndShowManager: Message {0} retrieved from CryptableMailItem", ((message != null) ? "successfully" : "could not be")));
   211 
   212                     // As fallback, if we don't have a message yet, create it
   213                     if (message == null)
   214                     {
   215                         Log.Verbose("BuildAndShowManager: Using fallback method to get message.");
   216 
   217                         Outlook.MailItem mirror = null;
   218 
   219                         try
   220                         {
   221                             // For securely stored mails, try to look up mirror
   222                             if (this._CurrentMailItem?.GetIsSecurelyStored() == true)
   223                             {
   224                                 mirror = this._CurrentMailItem?.GetMirror();
   225 
   226                                 if (mirror != null)
   227                                 {
   228                                     // If mirror is found, use it to create PEPMessage
   229                                     Log.Verbose("BuildAndShowManager: Mirror found.");
   230 
   231                                     if (PEPMessage.Create(mirror, out message) != Globals.ReturnStatus.Success)
   232                                     {
   233                                         message = null;
   234                                         Log.Error("BuildAndShowManager: Error creating PEPMessage.");
   235                                     }
   236                                     else
   237                                     {
   238                                         message.Rating = AdapterExtensions.ReevaluateMessageRating(message);
   239                                     }
   240                                 }
   241                                 else
   242                                 {
   243                                     // If no mirror is found, decrypt message and use decrypted one (do not 
   244                                     // decrypt forcefully protected messages).
   245                                     if ((PEPMessage.Create(this._CurrentMailItem, out message) == Globals.ReturnStatus.Success) &&
   246                                         (string.IsNullOrEmpty(message.ForceProtectionId)))
   247                                     {
   248                                         var msgProcessor = new MsgProcessor();
   249                                         PEPMessage decryptedMsg = null;
   250                                         string[] keyList;
   251                                         pEpDecryptFlags flags = pEpDecryptFlags.pEpDecryptFlagsNone;
   252                                         pEpRating rating = msgProcessor.Decrypt(ref message, out decryptedMsg, out keyList, ref flags);
   253                                         if (decryptedMsg != null)
   254                                         {
   255                                             message = decryptedMsg;
   256                                         }
   257                                         message.Rating = rating;
   258                                     }
   259                                     else
   260                                     {
   261                                         message = null;
   262                                         Log.Error("BuildAndShowManager: Error creating PEPMessage from mirror.");
   263                                     }
   264                                 }
   265                             }
   266 
   267                             // If we don't have a PEPMessage yet, create it
   268                             if (message == null)
   269                             {
   270                                 Log.Verbose("BuildAndShowManager: Creating PEPMessage.");
   271 
   272                                 if (PEPMessage.Create(this._CurrentMailItem, out message) != Globals.ReturnStatus.Success)
   273                                 {
   274                                     message = null;
   275                                     Log.Error("BuildAndShowManager: Error creating PEPMessage.");
   276                                 }
   277                                 else
   278                                 {
   279                                     message.Rating = AdapterExtensions.ReevaluateMessageRating(message);
   280                                 }
   281                             }
   282 
   283                             // Get myself identiy if we don't have it yet
   284                             if (myself == null)
   285                             {
   286                                 myself = this._CurrentMailItem.GetMyselfIdentity();
   287                             }
   288                         }
   289                         catch (Exception ex)
   290                         {
   291                             Log.Error("FormRegionPrivacyStatus.Message: Error converting message. " + ex.ToString());
   292                         }
   293                         finally
   294                         {
   295                             mirror = null;
   296                         }
   297                     }
   298                 }
   299 
   300                 // Build dialog
   301                 if (message != null)
   302                 {
   303                     handshakeDialog = new HandshakeDialog(message, myself, this.IsDraft);
   304                     handshakeDialog.OnUpdateStatus += HandshakeDialog_Updated;
   305                     handshakeDialog.Closed += HandshakeDialog_Closed;
   306                     handshakeDialog.Show();
   307                 }
   308                 else
   309                 {
   310                     throw new Exception("Could not build handshake dialog. Message is null.");
   311                 }
   312             }
   313             catch (Exception ex)
   314             {
   315                 Globals.StopAndSendCrashReport(ex);
   316             }
   317         }
   318 
   319         /// <summary>
   320         /// Schedules for the pEp rating and UI (including displayed mirror) to be updated.
   321         /// This can be called many times with no issue as the update is only run every n milliseconds.
   322         /// </summary>
   323         public void RequestRatingAndUIUpdate()
   324         {
   325             if (this.isEnabled)
   326             {
   327                 this.TimerRefresh.Enabled = true;
   328             }
   329         }
   330 
   331         /// <summary>
   332         /// Immediately starts the update of UI rating based on the associated mail item.
   333         /// This will start decryption as necessary and also will update the displayed mirror.
   334         /// This method by-passes the refresh timer completely.
   335         /// WARNING: This method assumes the message is fully downloaded already.
   336         /// </summary>
   337         private void ImmediateRatingAndUIUpdate()
   338         {
   339             if ((this.isEnabled) &&
   340                 (this.cryptableMailItem != null))
   341             {
   342                 Log.Verbose("ImmediateRatingAndUIUpdate: Starting processing.");
   343 
   344                 // Start the rating calculation/decryption process
   345                 this.cryptableMailItem.StartProcessing();
   346                 this.processingOngoing = true;
   347             }
   348         }
   349 
   350         /// <summary>
   351         /// Resets the Enabled status of this form region and recalculates the rating if necessary.
   352         /// </summary>
   353         /// <param name="enable">Whether to enable the form region.</param>
   354         public void UpdateFormRegion(bool enable)
   355         {
   356             this.SetIsEnabled(enable);
   357 
   358             // Refresh the UI
   359             if (enable)
   360             {
   361                 this.ResolveAllRecipients();
   362                 this.RequestRatingAndUIUpdate();
   363             }
   364         }
   365 
   366         /// <summary>
   367         /// Workaround method to update the current inspector window. This is done by moving the mail item
   368         /// to a temporary folder first and then back to the current folder. Both folders CANNOT be the same.
   369         /// As a fallback, the mail item stays in the temporary folder if moving back to the current folder
   370         /// fails.
   371         /// </summary>
   372         private void UpdateInspector()
   373         {
   374             Outlook.Application application = null;
   375             Outlook.Folder currentFolder = null;
   376             Outlook.Folder tempFolder = null;
   377             Outlook.Inspector currentInspector = null;
   378             Outlook.Inspector newInspector = null;
   379             Outlook.MailItem tempMailItem = null;
   380             Outlook.Store store = null;
   381             Outlook.MailItem omi = this.CurrentMailItem;
   382 
   383             if (omi != null)
   384             {
   385                 try
   386                 {
   387                     application = omi.Application;
   388                     currentInspector = omi.GetInspector;
   389                     currentFolder = omi.Parent as Outlook.Folder;
   390 
   391                     if ((currentInspector != null) &&
   392                         (application != null) &&
   393                         (currentFolder != null))
   394                     {
   395                         var left = currentInspector.Left;
   396                         var top = currentInspector.Top;
   397                         var width = currentInspector.Width;
   398                         var height = currentInspector.Height;
   399                         var windowState = currentInspector.WindowState;
   400 
   401                         /* Check, if in trusted store. In that case, use the default drafts folder
   402                          * as temporary folder. If the store is untrusted, use the pEp drafts folder.
   403                          */
   404                         store = currentFolder.Store;
   405                         if (store?.GetIsSecureStorageEnabled() ?? false)
   406                         {
   407                             tempFolder = Globals.ThisAddIn.GetPEPStoreDraftsFolder();
   408                         }
   409                         else
   410                         {
   411                             tempFolder = store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDrafts) as Outlook.Folder;
   412                         }
   413 
   414                         if (tempFolder != null)
   415                         {
   416                             tempMailItem = omi.Move(tempFolder) as Outlook.MailItem;
   417 
   418                             if (tempMailItem != null)
   419                             {
   420                                 try
   421                                 {
   422                                     omi = tempMailItem.Move(currentFolder) as Outlook.MailItem;
   423                                 }
   424                                 catch
   425                                 {
   426                                     omi = tempMailItem.Copy();
   427                                 }
   428 
   429                                 newInspector = application.Inspectors.Add(omi);
   430 
   431                                 if (windowState == Outlook.OlWindowState.olNormalWindow)
   432                                 {
   433                                     newInspector.Left = left;
   434                                     newInspector.Top = top;
   435                                     newInspector.Width = width;
   436                                     newInspector.Height = height;
   437                                 }
   438 
   439                                 newInspector.Display();
   440                                 newInspector.WindowState = windowState;
   441 
   442                                 repeatProcessing = false;
   443                             }
   444                         }
   445                         else
   446                         {
   447                             Log.Error("UpdateInspector: Cannot get temporary folder.");
   448                         }
   449                     }
   450                     else
   451                     {
   452                         Log.Verbose("UpdateInspector: Error retrieving inspector window or application.");
   453                     }
   454                 }
   455                 catch (Exception ex)
   456                 {
   457                     Log.Verbose("UpdateInspector: Error updating inspector window. " + ex.ToString());
   458                 }
   459                 finally
   460                 {
   461                     application = null;
   462                     currentInspector = null;
   463                     newInspector = null;
   464                     omi = null;
   465                     tempMailItem = null;
   466                 }
   467             }
   468         }
   469 
   470         /// <summary>
   471         /// Clears the associated unencrypted preview and displays the given note (if any).
   472         /// </summary>
   473         /// <param name="note">The note to display.</param>
   474         private void SetNote(string note = null)
   475         {
   476             WindowFormRegionCollection formRegions = Globals.FormRegions[Globals.ThisAddIn.Application.ActiveWindow()];
   477 
   478             if ((formRegions != null) &&
   479                 (string.IsNullOrEmpty(note) == false))
   480             {
   481                 if ((formRegions.FormRegionPreviewUnencrypted != null) &&
   482                     (formRegions.FormRegionPreviewUnencrypted.Visible))
   483                 {
   484                     formRegions.FormRegionPreviewUnencrypted.DisplayState.SetNote(note);
   485                 }
   486             }
   487         }
   488 
   489         /// <summary>
   490         /// Sets whether processing of the mail item is enabled.
   491         /// This should commonly be set from the .GetIsPEPEnabled() value.
   492         /// </summary>
   493         private void SetIsEnabled(bool enabled)
   494         {
   495             PEPIdentity currIdent;
   496             Globals.ReturnStatus sts;
   497             Outlook.Recipient currUser = null;
   498             Outlook.Account currAccount = null;
   499             Outlook.Account sendingAccount = null;
   500             Outlook.NameSpace ns = null;
   501 
   502             this.isEnabled = enabled;
   503 
   504             if (enabled)
   505             {
   506                 // Do not allow initialization more than once
   507                 if (this.isStarted == false)
   508                 {
   509                     /* It's possible for new draft MailItems to be created outside the context of an account.
   510                      * In this situation the SendUsingAccount will always be null which of course breaks several pEp operations.
   511                      * The operations themselves cannot make the assumption about what account information to use.
   512                      * Therefore, the situation is detected here and for draft mail items a null SendUsingAccount will be 
   513                      * set with the session's default account.
   514                      */
   515                     try
   516                     {
   517                         ns = Globals.ThisAddIn.Application.Session;
   518 
   519                         if (this.IsDraft)
   520                         {
   521                             sendingAccount = this._CurrentMailItem.SendUsingAccount;
   522                             currUser = ns.CurrentUser;
   523 
   524                             if (sendingAccount == null)
   525                             {
   526                                 sts = PEPIdentity.Create(currUser, out currIdent);
   527 
   528                                 if (sts == Globals.ReturnStatus.Success)
   529                                 {
   530                                     sendingAccount = AccountExtensions.GetDefaultAccount(Outlook.OlDefaultFolders.olFolderDrafts);
   531                                     if (sendingAccount != null)
   532                                     {
   533                                         this._CurrentMailItem.SendUsingAccount = currAccount;
   534                                     }
   535                                 }
   536                             }
   537 
   538                             // Check if account is already registered in pEp list and add it if necessary
   539                             var acctSettings = Globals.ThisAddIn.Settings.GetAccountSettings(sendingAccount?.SmtpAddress);
   540                             if (acctSettings == null)
   541                             {
   542                                 Log.Verbose("SetIsEnabled: New account detected. Creating pEp settings.");
   543 
   544                                 // Create pEp settings for the new account
   545                                 acctSettings = sendingAccount.CreatePEPSettings();
   546 
   547                                 // Add account to list
   548                                 Globals.ThisAddIn.Settings.AccountsAddedWhileRunningList.Add(acctSettings?.SmtpAddress);
   549                                 Log.Verbose("SetIsEnabled: New account registered in pEp settings.");
   550 
   551                                 // Generate key
   552                                 Globals.ThisAddIn.RegisterMyself(acctSettings);
   553                                 Log.Verbose("SetIsEnabled: Myself registered.");
   554 
   555                                 // Set rules and view filters
   556                                 Globals.ThisAddIn.SetRulesAndViewFilters(sendingAccount);
   557                             }
   558                         }
   559                     }
   560                     catch (Exception ex)
   561                     {
   562                         Log.Error("SetIsEnabled: Error occured. " + ex.ToString());
   563                     }
   564                     finally
   565                     {
   566                         currAccount = null;
   567                         currUser = null;
   568                         ns = null;
   569                         sendingAccount = null;
   570                     }
   571 
   572                     // Connect refresh timer
   573                     this.TimerRefresh.Tick += TimerRefresh_Tick;
   574                     this.isStarted = true;
   575                 }
   576             }
   577             else
   578             {
   579                 // Stop and disconnect the refresh timer
   580                 this.TimerRefresh.Stop();
   581                 this.TimerRefresh.Enabled = false;
   582                 this.TimerRefresh.Tick -= TimerRefresh_Tick;
   583 
   584                 this.isStarted = false;
   585             }
   586         }
   587 
   588         /// <summary>
   589         /// Resolves all recipients of the Outlook mail item.
   590         /// </summary>
   591         private void ResolveAllRecipients()
   592         {
   593             if ((this.isEnabled) &&
   594                 (this.cryptableMailItem != null))
   595             {
   596                 /*
   597                  * Note: The PropertyChanged changed event must be disconnected before trying to resolve.
   598                  * This is because the resolve process can modify the contents of the mail item which triggers an event.
   599                  * The PropertyChanged event would then trigger a UI refresh cycle. However, because the GetManagerState itself 
   600                  * is called within the UI refresh, an infinite loop could occur trying to resolve a recipient that
   601                  * cannot be resolved(no address).
   602                  */
   603                 try
   604                 {
   605                     this.cryptableMailItem.PropertyChanged -= MailItem_PropertyChanged;
   606                     this.cryptableMailItem.ResolveAllRecipients();
   607                     this.cryptableMailItem.PropertyChanged += MailItem_PropertyChanged;
   608                 }
   609                 catch (Exception ex)
   610                 {
   611                     Log.Error("Error resolving recipients. " + ex.ToString());
   612                 }
   613             }
   614         }
   615         #endregion
   616 
   617         #region Event Handling
   618         /**************************************************************
   619          * 
   620          * Event Handling
   621          * 
   622          *************************************************************/
   623 
   624         protected void InitializeMailItem(bool isInlineResponse = false)
   625         {
   626             bool enableFormRegion = false;
   627             bool cancelOpenEvent = false;
   628             bool isSecureAttachedMail = false;
   629             string messageId = null;
   630 
   631             try
   632             {      
   633                 // Check if draft
   634                 this.IsDraft = this._CurrentMailItem.GetIsDraft();
   635 
   636                 /* Set immediately a provisional rating in order to have a less flickery
   637                  * UI experience.
   638                  * The provisional rating is either a stored rating or, in case we have a
   639                  * reply message, the rating of the original (the item we reply to).
   640                  * This provisional rating will be replace with the real one once the full
   641                  * processing is complete.
   642                  */
   643                 pEpRating provisionalRating = pEpRating.pEpRatingUndefined;
   644 
   645                 // Try to get an original rating (in case of reply messages)
   646                 if ((this.IsDraft) &&
   647                     (this._CurrentMailItem?.Recipients?.Count > 0))
   648                 {
   649                     string originalRatingString = this._CurrentMailItem.GetUserProperty(CryptableMailItem.USER_PROPERTY_KEY_ORIGINAL_RATING) as string;
   650 
   651                     // If we have an original rating, parse it and set it.
   652                     if (string.IsNullOrEmpty(originalRatingString) == false)
   653                     {
   654                         provisionalRating = AdapterExtensions.ParseRatingString(originalRatingString);
   655                     }
   656                 }
   657                 else
   658                 {
   659                     // Try to get rating from db
   660                     provisionalRating = PEPDatabase.GetRating(this._CurrentMailItem.EntryID);
   661 
   662                     // If there is no rating in the db, use stored or default rating
   663                     if (provisionalRating == pEpRating.pEpRatingUndefined)
   664                     {
   665                         provisionalRating = this._CurrentMailItem.GetStoredRating() ?? pEpRating.pEpRatingUndefined;
   666                     }
   667                 }
   668 
   669                 // Only set rating if one has been retrieved
   670                 if (provisionalRating != pEpRating.pEpRatingUndefined)
   671                 {
   672                     this.SetRating(provisionalRating);
   673                     Log.Verbose("FormRegionPrivacyStatus_FormRegionShowing: Provisional rating {0} shown.", Enum.GetName(typeof(pEpRating), provisionalRating));
   674                 }
   675 
   676 
   677                 // Do not process S/MIME messages
   678                 if (this._CurrentMailItem.GetIsSMIMEEnabled())
   679                 {
   680                     Log.Verbose("FormRegionPrivacyStatus_FormRegionShowing: S/MIME message detected. Won't be processed.");
   681 
   682                     // Set unencrypted rating
   683                     this.SetRating(pEpRating.pEpRatingUnencrypted);
   684 
   685                     // Set icon(s) if necessary     
   686                     if ((IsDraft == false) &&
   687                         (this._CurrentMailItem.SetEncryptionIcons()))
   688                     {
   689                         try
   690                         {
   691                             this._CurrentMailItem.Save();
   692                         }
   693                         catch (Exception ex)
   694                         {
   695                             Log.Error("FormRegionPrivacyStatus_FormRegionShowing: Error saving message after changing icon. " + ex.ToString());
   696                         }
   697                     }
   698 
   699                     return;
   700                 }
   701 
   702                 /* Check if item is attached mail. For performance reasons,
   703                  * only do the whole check if an item has been loaded to the
   704                  * cache of attached mails and if item might be secure.
   705                  */
   706                 if ((PEPAttachment.AttachedMailsCache.Count > 0) &&
   707                     (this._CurrentMailItem.Attachments?.Count == 2))
   708                 {
   709                     try
   710                     {
   711                         // Check if mail item is an attached mail
   712                         if (this._CurrentMailItem.GetIsAttachedMail(out messageId))
   713                         {
   714                             Outlook.MailItem mirror = null;
   715                             PEPMessage pEpMessage;
   716 
   717                             // Try to get the mirror
   718                             mirror = this._CurrentMailItem.GetMirror(messageId);
   719 
   720                             // If mirror was not found, decrypt and create mirror
   721                             if (mirror != null)
   722                             {
   723                                 isSecureAttachedMail = true;
   724                             }
   725                             else
   726                             {
   727                                 if ((PEPMessage.Create(this._CurrentMailItem, out pEpMessage) == Globals.ReturnStatus.Success) &&
   728                                     (pEpMessage.IsSecure))
   729                                 {
   730                                     PEPMessage decryptedMessage;
   731                                     MsgProcessor msgProcessor = new MsgProcessor();
   732                                     if (msgProcessor.Decrypt(pEpMessage, out decryptedMessage))
   733                                     {
   734                                         isSecureAttachedMail = true;
   735                                         mirror = this._CurrentMailItem.CreateMirrorOMI(messageId);
   736                                         decryptedMessage.ApplyTo(mirror, true, false);
   737                                         mirror?.Save();
   738                                     }
   739                                     else
   740                                     {
   741                                         Log.Error("FormRegionPrivacyStatus_FormRegionShowing: Decryption of attached mail was not successful.");
   742                                     }
   743                                 }
   744                             }
   745 
   746                             // Check if attachment is being opened or if only the preview is needed
   747                             if (CryptableMailItem.PreviewAttachedMailId?.Equals(messageId) != true)
   748                             {
   749                                 // Display mirror and cancel opening of original
   750                                 cancelOpenEvent = true;
   751                                 mirror?.Display();
   752                             }
   753 
   754                             // Not needed anymore after this point
   755                             CryptableMailItem.PreviewAttachedMailId = null;
   756                         }
   757                     }
   758                     catch (Exception ex)
   759                     {
   760                         messageId = null;
   761                         Log.Error("FormRegionPrivacyStatus_FormRegionShowing: Error checking for attached mail. " + ex.ToString());
   762                     }
   763                 }
   764 
   765                 this.cryptableMailItem = new CryptableMailItem(this._CurrentMailItem, provisionalRating);
   766 
   767                 // Set inline response property
   768                 this.cryptableMailItem.IsInlineResponse = isInlineResponse;
   769 
   770                 // Set properties for encrypted attached mail
   771                 if (isSecureAttachedMail)
   772                 {
   773                     this.cryptableMailItem.MessageId = messageId;
   774                     this.cryptableMailItem.IsSecureAttachedMail = true;
   775                 }
   776 
   777                 // Connect cryptable mail item events
   778                 if (this.cryptableMailItem != null)
   779                 {
   780                     // If we don't open the original, set property and return
   781                     if (cancelOpenEvent)
   782                     {
   783                         this.cryptableMailItem.Open += MailItem_Open;
   784                         this.cryptableMailItem.CancelOpen = true;
   785                         return;
   786                     }
   787                     else
   788                     {
   789                         try
   790                         {
   791                             this.cryptableMailItem.PropertyChanged += MailItem_PropertyChanged;
   792                             this.cryptableMailItem.ProcessingCompleted += MailItem_ProcessingCompleted;
   793                             this.cryptableMailItem.GetMirrorCompleted += MailItem_GetMirrorCompleted;
   794                             this.cryptableMailItem.Send += MailItem_Send;
   795 
   796                             if (this.cryptableMailItem.IsSecurelyStored)
   797                             {
   798                                 this.cryptableMailItem.Open += MailItem_Open;
   799                             }
   800                         }
   801                         catch (Exception ex)
   802                         {
   803                             Log.Error("FormRegionPrivacyStatus_FormRegionShowing: Error occured. " + ex.ToString());
   804                         }
   805 
   806                         if (this._CurrentMailItem.GetIsPEPEnabled())
   807                         {
   808                             // If pEp is enabled, show the form region
   809                             enableFormRegion = true;
   810                         }
   811                         else
   812                         {
   813                             /* If pEp is disabled, process item and show form region in the following cases:
   814                              * 1. Incoming items: if decrypt always option is set
   815                              * 2. Outgoing items: if EnableProtection option is set
   816                              *                    Note: if this property isn't set, subscribe to originally 
   817                              *                          encrypted status update event handler in case the 
   818                              *                          current code runs before CryptableMailItem.MailItem_Reply
   819                              *                          or CryptableMailItem.MailItem_Forward.
   820                              */
   821                             if (this.IsDraft)
   822                             {
   823                                 enableFormRegion = true;
   824                                 this.cryptableMailItem.OriginallyEncryptedStatusUpdated += CryptableMailItem_OriginallyEncryptedStatusUpdated;
   825                             }
   826                             else
   827                             {
   828                                 enableFormRegion = this._CurrentMailItem.GetIsDecryptAlwaysEnabled();
   829                             }
   830                         }
   831                     }
   832                 }
   833 
   834                 // Update pEp enabled status
   835                 this.SetIsEnabled(enableFormRegion);
   836 
   837                 if (this.isEnabled)
   838                 {
   839                     // If forcefully protected, run dedicated decryption
   840                     if (this._CurrentMailItem.GetIsForcefullyProtected())
   841                     {
   842                         FPPMessage fppMessage = new FPPMessage(this._CurrentMailItem);
   843                         if ((fppMessage?.GetMessageType() != null) ||
   844                             (fppMessage?.CurrentMessage?.IsSecure == true))
   845                         {
   846                             fppMessage.ProcessIncoming(true);
   847                         }
   848                         else
   849                         {
   850                             this.TimerRefresh_Tick(null, new EventArgs());
   851                         }
   852                     }
   853                     else
   854                     {
   855                         // Call the timer tick method manually to refresh data with no delay
   856                         this.TimerRefresh_Tick(null, new EventArgs());
   857                     }
   858                 }
   859             }
   860             catch (Exception e)
   861             {
   862                 Log.Error("Window_SelectionChange: Error. " + e.Message);
   863             }
   864         }
   865 
   866         /// <summary>
   867         /// Event handler for when the form region is closed.
   868         /// </summary>
   869         protected void FormRegionPrivacyStatus_FormRegionClosed(object sender, EventArgs e)
   870         {
   871             // Disconnect cryptable mail item
   872             if (this.cryptableMailItem != null)
   873             {
   874                 try
   875                 {
   876                     this.cryptableMailItem.PropertyChanged -= MailItem_PropertyChanged;
   877                     this.cryptableMailItem.ProcessingCompleted -= MailItem_ProcessingCompleted;
   878                     this.cryptableMailItem.GetMirrorCompleted -= MailItem_GetMirrorCompleted;
   879                     this.cryptableMailItem.Open -= MailItem_Open;
   880                     this.cryptableMailItem.Send -= MailItem_Send;
   881                     this.cryptableMailItem.OriginallyEncryptedStatusUpdated -= CryptableMailItem_OriginallyEncryptedStatusUpdated;
   882                 }
   883                 catch { }
   884 
   885                 this.cryptableMailItem.Dispose();
   886                 this.cryptableMailItem = null;
   887             }
   888 
   889             // Disconnect other
   890             this.SetIsEnabled(false);
   891         }
   892 
   893         /// <summary>
   894         /// Event handler for when the processing is completed in the associated mail item.
   895         /// This will then update the form region UI and the privacy status window as needed.
   896         /// </summary>
   897         private void MailItem_ProcessingCompleted(object sender, MsgProcessor.ProcessingCompletedEventArgs e)
   898         {
   899             Log.Verbose("MailItem_ProcessingComplete: Decryption completed.");
   900 
   901             try
   902             {
   903                 // Marshal code back to UI thread as necessary
   904                 KeySyncWizard.Wizard.Dispatcher.Invoke(new Action(() =>
   905                 {
   906                     if (this.isEnabled)
   907                     {
   908                         try
   909                         {
   910                             this.SetRating(e.ProcessedRating);
   911 
   912                             // Set MAPI properties if needed
   913                             if (e.PropertiesToSet?.Count > 0)
   914                             {
   915                                 this._CurrentMailItem?.SetMAPIProperties(e.PropertiesToSet);
   916                             }
   917                             Log.Verbose("MailItem_ProcessingComplete: Status bar updated with rating " + Enum.GetName(typeof(pEpRating), e.ProcessedRating));
   918                         }
   919                         catch (Exception ex)
   920                         {
   921                             Log.Verbose("MailItem_ProcessingComplete: Error. " + ex.ToString());
   922                         }
   923 
   924                         if (repeatProcessing &&
   925                             (repeatCounter++ < maxRepeatCount))
   926                         {
   927                             repeatProcessing = false;
   928                             this.RequestRatingAndUIUpdate();
   929                         }
   930                         else
   931                         {
   932                             /* Check if the mail item is in Outbox and update the inspector window if possible.
   933                              * This is the case when a submitted, but not yet sent email is opened again when working 
   934                              * offline or without internet connection. Without this update, Outlook removes the message class 
   935                              * "IPM.Note.SMIME.MultipartSigned" at the next send event and the message gets invalid and can't be
   936                              * opened again (i.e. is lost).
   937                              */
   938                             try
   939                             {
   940                                 if ((this._CurrentMailItem?.GetIsSubmitted() == true) &&
   941                                     (this.IsDraft))
   942                                 {
   943                                     UpdateInspector();
   944                                 }
   945                             }
   946                             catch (Exception ex)
   947                             {
   948                                 Log.Verbose("MailItem_ProcessingComplete: Error while checking if mail item is in outbox or updating inspector. " + ex.Message);
   949                             }
   950 
   951                             /* Create the unencrypted preview if the mail item is encrypted and
   952                              * it is in an encrypted (untrusted) store
   953                              * 
   954                              * This is done here because FormRegionPrivacyStatus has the cryptable mail item and
   955                              * it also is initialized after FormRegionPreviewUnencrypted.
   956                              */
   957                             try
   958                             {
   959                                 if (this.cryptableMailItem.IsSecurelyStored || this.cryptableMailItem.IsSecureAttachedMail)
   960                                 {
   961                                     Log.Verbose("MailItem_ProcessingComplete: Starting mirror location.");
   962                                     this.cryptableMailItem.StartGetMirror(this.cryptableMailItem.MessageId);
   963                                 }
   964                                 else
   965                                 {
   966                                     this.SetNote();
   967                                 }
   968                             }
   969                             catch (Exception ex)
   970                             {
   971                                 // Error is possible in some situations where the mail item was deleted or moved while decryption was ongoing.
   972                                 // While rare, just log the issue and stop the process
   973                                 Log.Warning("MailItem_ProcessingComplete: Failed to start mirror location, " + ex.ToString());
   974                             }
   975 
   976                             /* OUT-470: Workaround until this is implemented in the engine:
   977                              * Only enable Force Protection if all recipients are grey
   978                              */
   979                             if (this.IsDraft)
   980                             {
   981                                 bool disableForceProtection = false;
   982                                 Outlook.Recipients recipients = null;
   983                                 Outlook.Recipient recipient = null;
   984                                 PEPIdentity pEpIdentity = null;
   985 
   986                                 try
   987                                 {
   988                                     recipients = this._CurrentMailItem?.Recipients;
   989                                     if (recipients?.Count > 0)
   990                                     {
   991                                         for (int i = 1; i <= recipients.Count; i++)
   992                                         {
   993                                             recipient = recipients[i];
   994                                             if ((PEPIdentity.Create(recipient, out pEpIdentity, false) == Globals.ReturnStatus.Success) &&
   995                                                 ((PEPIdentity.GetIsOwnIdentity(pEpIdentity.Address) ||
   996                                                  (ThisAddIn.PEPEngine.IdentityRating(pEpIdentity.ToCOMType()) >= pEpRating.pEpRatingReliable))))
   997                                             {
   998                                                 disableForceProtection = true;
   999                                                 Log.Verbose("ToggleButtonForceProtection_GetEnabled: Secure recipient found. Disabling force protection.");
  1000                                                 break;
  1001                                             }
  1002                                         }
  1003                                     }
  1004                                     else
  1005                                     {
  1006                                         // If there aren't any recipients (anymore), reset values
  1007                                         this.DisableForceProtection = false;
  1008                                         this.ForceProtection = false;
  1009                                         this.NeverUnsecure = false;
  1010                                         this.ForceUnencrypted = false;
  1011                                     }
  1012                                 }
  1013                                 catch (Exception ex)
  1014                                 {
  1015                                     Log.Error("ToggleButtonForceProtection_GetEnabled: Error occured. " + ex.ToString());
  1016                                 }
  1017                                 finally
  1018                                 {
  1019                                     recipient = null;
  1020                                     recipients = null;
  1021                                 }
  1022 
  1023                                 this.DisableForceProtection = disableForceProtection;
  1024                             }
  1025                         }
  1026                     }
  1027                 }));
  1028             }
  1029             catch (Exception ex)
  1030             {
  1031                 Log.Error("MailItem_ProcessingComplete: Error setting UI state, " + ex.ToString());
  1032             }
  1033 
  1034             this.processingOngoing = false;
  1035         }
  1036 
  1037         /// <summary>
  1038         /// Event handler for when the get mirror locating process is complete for the associated mail item.
  1039         /// This will then update the unencrypted preview in the UI.
  1040         /// </summary>
  1041         private void MailItem_GetMirrorCompleted(object sender, CryptableMailItem.GetMirrorCompletedEventArgs e)
  1042         {
  1043             try
  1044             {
  1045                 // Marshal code back to UI thread as necessary
  1046                 KeySyncWizard.Wizard.Dispatcher.Invoke(new Action(() =>
  1047                 {
  1048                     if (this.isEnabled)
  1049                     {
  1050                         WindowFormRegionCollection formRegions = Globals.FormRegions[Globals.ThisAddIn.Application.ActiveWindow()];
  1051 
  1052                         // If the message was forcefully protected, there is no mirror and we show the actual message
  1053                         if ((e.Mirror == null) &&
  1054                             (this._CurrentMailItem?.GetIsForcefullyProtected() == true))
  1055                         {
  1056                             PEPMessage mirror = null;
  1057                             if (PEPMessage.Create(this._CurrentMailItem, out mirror) == Globals.ReturnStatus.Success)
  1058                             {
  1059                                 e.Mirror = mirror;
  1060                             }
  1061                         }
  1062 
  1063                         if ((e.Mirror == null) &&
  1064                             (this.cryptableMailItem.LastProcessedStatus == Globals.ReturnStatus.Failure))
  1065                         {
  1066                             this.SetNote(Properties.Resources.Message_OpenError);
  1067                             Log.Verbose("MailItem_GetMirrorComplete: Cannot display mirror, failure during decryption.");
  1068                         }
  1069                         else if ((e.Mirror == null) &&
  1070                                  (this.cryptableMailItem.LastProcessedStatus == Globals.ReturnStatus.FailureNoConnection))
  1071                         {
  1072                             this.SetNote(Properties.Resources.Message_DecryptionNoConnection);
  1073                             Log.Verbose("MailItem_GetMirrorComplete: Cannot display mirror, connection failure during decryption.");
  1074                         }
  1075                         else
  1076                         {
  1077                             if (formRegions != null)
  1078                             {
  1079                                 if ((formRegions.FormRegionPreviewUnencrypted != null) &&
  1080                                     (formRegions.FormRegionPreviewUnencrypted.Visible))
  1081                                 {
  1082                                     formRegions.FormRegionPreviewUnencrypted.DisplayState.OriginalEntryId = this._CurrentMailItem?.EntryID;
  1083                                     formRegions.FormRegionPreviewUnencrypted.DisplayState.SetMessage(e.Mirror);
  1084                                 }
  1085 
  1086                                 Log.Verbose("MailItem_GetMirrorComplete: Mirror found and displayed.");
  1087                             }
  1088                         }
  1089 
  1090                         // Display the mirror if necessary
  1091                         if ((this.cryptableMailItem != null) &&
  1092                             (this.displayMirrorRequested))
  1093                         {
  1094                             this.cryptableMailItem.DisplayMirror();
  1095                             this.displayMirrorRequested = false;
  1096                         }
  1097                     }
  1098                 }));
  1099             }
  1100             catch (Exception ex)
  1101             {
  1102                 Log.Error("MailItem_GetMirrorComplete: Error displaying preview, " + ex.ToString());
  1103             }
  1104         }
  1105 
  1106         /// <summary>
  1107         /// Event handler for the mail item originally encrypted status updated.
  1108         /// This event is fired after the status has been updated with parent information following a
  1109         /// forward or reply mail item event (on a different, likely parent, mail item).
  1110         /// </summary>
  1111         protected void CryptableMailItem_OriginallyEncryptedStatusUpdated(object sender, EventArgs e)
  1112         {
  1113             // Process the mail item now that the cache is updated
  1114             if (this.cryptableMailItem.IsOriginallyEncrypted)
  1115             {
  1116                 this.cryptableMailItem.StartProcessing();
  1117             }
  1118         }
  1119 
  1120         /// <summary>
  1121         /// Event handler for when a mail item is being opened in an inspector.
  1122         /// See: https://msdn.microsoft.com/en-us/library/office/ff865989.aspx
  1123         /// </summary>
  1124         /// <param name="cancel">Whether to cancel the event: Value is False when the event occurs. 
  1125         /// If the event procedure sets this argument to True, the open operation is not completed 
  1126         /// and the inspector is not displayed.</param>
  1127         protected void MailItem_Open(ref bool cancel)
  1128         {
  1129             bool result;
  1130 
  1131             if ((this.isEnabled) &&
  1132                 (this.cryptableMailItem != null))
  1133             {
  1134                 // If cryptable mail item is not to be opened, cancel opening
  1135                 if (this.cryptableMailItem.CancelOpen)
  1136                 {
  1137                     cancel = true;
  1138                 }
  1139                 else if ((this.cryptableMailItem.IsSecurelyStored) &&
  1140                          (this.cryptableMailItem.IsIncoming || this.cryptableMailItem.IsOriginallyEncrypted || !this.cryptableMailItem.IsDraft))
  1141                 {
  1142                     // Try to open the mirror
  1143                     result = this.cryptableMailItem.DisplayMirror();
  1144 
  1145                     if (result == false)
  1146                     {
  1147                         // Set flag to open after decryption/mirror location
  1148                         this.displayMirrorRequested = true;
  1149                     }
  1150 
  1151                     // Always cancel opening the original
  1152                     cancel = true;
  1153                 }
  1154             }
  1155         }
  1156 
  1157         /// <summary>
  1158         /// Event handler for when a mail item is sent.
  1159         /// See: https://msdn.microsoft.com/en-us/library/office/ff865379.aspx
  1160         /// </summary>
  1161         /// <param name="cancel">Whether to cancel the event: Value is False when the event occurs. 
  1162         /// If the event procedure sets this argument to True, the send operation is not completed 
  1163         /// and the inspector is left open.</param>
  1164         protected void MailItem_Send(ref bool cancel)
  1165         {
  1166             DialogResult result;
  1167 
  1168             if (this.isEnabled)
  1169             {
  1170                 if ((this.cryptableMailItem.IsBeingProcessed) ||
  1171                     (this.processingOngoing))
  1172                 {
  1173                     Log.Verbose("MailItem_Send cancelled. Mail item still being processed.");
  1174                     cancel = true;
  1175                     return;
  1176                 }
  1177 
  1178                 // Show warning message if needed
  1179                 if ((Globals.ThisAddIn.Settings.IsSecurityLossWarningEnabled) &&
  1180                     (this.Rating < pEpRating.pEpRatingUnreliable) &&
  1181                     (this.cryptableMailItem.IsOriginallyEncrypted))
  1182                 {
  1183 
  1184 #if READER_RELEASE_MODE
  1185                     FormReaderSplash warningMessage = new FormReaderSplash(true);
  1186                     result = warningMessage.ShowDialog();
  1187 
  1188                     if (result != DialogResult.OK)
  1189                     {
  1190                         // Cancel sending
  1191                         cancel = true;
  1192                     }
  1193 #else
  1194                     result = MessageBox.Show(Properties.Resources.Message_WarningSecurityLoss,
  1195                                              Properties.Resources.Message_TitleConfirmOperation,
  1196                                              MessageBoxButtons.YesNo,
  1197                                              MessageBoxIcon.Warning);
  1198 
  1199                     if (result == DialogResult.No)
  1200                     {
  1201                         // Cancel sending
  1202                         cancel = true;
  1203                     }
  1204 #endif
  1205                 }
  1206 
  1207                 if (cancel == false)
  1208                 {
  1209                     // Set pEp options if needed
  1210                     if ((this.ForceProtection) &&
  1211                         (this.DisableForceProtection == false))
  1212                     {
  1213                         this._CurrentMailItem?.SetPEPProperty(MailItemExtensions.PEPProperty.ForceProtection, Guid.NewGuid().ToString());
  1214                     }
  1215                     else if (this.ForceUnencrypted)
  1216                     {
  1217                         this._CurrentMailItem?.SetPEPProperty(MailItemExtensions.PEPProperty.ForceUnencrypted, true);
  1218                     }
  1219 
  1220                     if (this.NeverUnsecure)
  1221                     {
  1222                         this._CurrentMailItem?.SetPEPProperty(MailItemExtensions.PEPProperty.NeverUnsecure, true);
  1223                     }
  1224 
  1225                     // Stop and disconnect the refresh timer
  1226                     // This is necessary so an ongoing refresh doesn't try to access a mail item as it's being moved
  1227                     this.TimerRefresh.Stop();
  1228                     this.TimerRefresh.Enabled = false;
  1229                     this.TimerRefresh.Tick -= TimerRefresh_Tick;
  1230                 }
  1231             }
  1232         }
  1233 
  1234         /// <summary>
  1235         /// Event handler for when a mail item property is changed.
  1236         /// See: https://msdn.microsoft.com/en-us/library/office/ff866739.aspx
  1237         /// </summary>
  1238         /// <param name="propertyName">The name of the property that was changed.</param>
  1239         protected void MailItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
  1240         {
  1241             switch (e.PropertyName.ToUpperInvariant())
  1242             {
  1243                 case "BCC":
  1244                     {
  1245                         // Outlook always fires Bcc, Cc and To together so only "TO" is used
  1246                         break;
  1247                     }
  1248                 case "CC":
  1249                     {
  1250                         // Outlook always fires Bcc, Cc and To together so only "TO" is used
  1251                         break;
  1252                     }
  1253                 case "SENTONBEHALFOFNAME":
  1254                     {
  1255                         // Always fired with "SENDUSINGACCOUNT" so is ignored
  1256                         break;
  1257                     }
  1258                 case "SENDUSINGACCOUNT":
  1259                     {
  1260                         // Update pEp enabled status
  1261                         this.isStarted = false;
  1262                         this.SetIsEnabled((this.CurrentMailItem)?.GetEnableFormRegion() ?? false);
  1263 
  1264                         // Refresh the UI
  1265                         this.ResolveAllRecipients();
  1266                         this.RequestRatingAndUIUpdate();
  1267                         RibbonCustomizations.Invalidate();
  1268 
  1269                         break;
  1270                     }
  1271                 case "TO":
  1272                     {
  1273                         if (this.isEnabled)
  1274                         {
  1275                             this.RequestRatingAndUIUpdate();
  1276                         }
  1277                         break;
  1278                     }
  1279             }
  1280         }
  1281 
  1282         /// <summary>
  1283         /// Event handler for when a handshake dialog was updated.
  1284         /// </summary>
  1285         protected void HandshakeDialog_Updated(object sender, EventArgs e)
  1286         {
  1287             // Update current form region
  1288             this.RequestRatingAndUIUpdate();
  1289             
  1290             /* If a handshake is performed while having the same message open both in an inspector
  1291              * and an Window window, the one that didn't trigger the handshake won't get updated
  1292              * automatically. Therefore, after a handshake, we also update all other open windows.
  1293              */
  1294             try
  1295             {
  1296                 Globals.ThisAddIn.RecalculateAllWindows(this);                
  1297             }
  1298             catch (Exception ex)
  1299             {
  1300                 Log.Error("HandshakeDialog_Updated: Error updating other windows. " + ex.Message);
  1301             }
  1302         }
  1303 
  1304         /// <summary>
  1305         /// Event handler for when a handshake dialog was closed.
  1306         /// </summary>
  1307         protected void HandshakeDialog_Closed(object sender, EventArgs e)
  1308         {
  1309             if (this.handshakeDialog != null)
  1310             {
  1311                 this.handshakeDialog.OnUpdateStatus -= HandshakeDialog_Updated;
  1312                 this.handshakeDialog.Closed -= HandshakeDialog_Closed;
  1313                 this.handshakeDialog = null;
  1314             }
  1315         }
  1316 
  1317         /// <summary>
  1318         /// Event handler called after the refresh timer has elapsed.
  1319         /// </summary>
  1320         private void TimerRefresh_Tick(object sender, EventArgs e)
  1321         {
  1322             bool tryAgain = false;
  1323 #pragma warning disable 219
  1324             bool markForDownload = false;
  1325 #pragma warning restore 219
  1326             this.TimerRefresh.Enabled = false; // Only once
  1327             Outlook.OlDownloadState dlState;
  1328 
  1329             /* The Refresh/UI_Update process:
  1330              * There are the following components:
  1331              *   1. TimerRefresh_Tick
  1332              *        This is the main timer tick event handler called any time
  1333              *        a refresh was requested (RequestRatingAndUIUpdate) and hasn't been run yet. 
  1334              *        A refresh is requested either at initialization or when a property changes
  1335              *        (such as MailItem_PropertyChanged). It is on a timer as many 
  1336              *        property change events could occur rapidy, but only one refresh should
  1337              *        occur for performance reasons.
  1338              * 
  1339              *   2. ImmediateRatingAndUIUpdate
  1340              *        This will re-calculate the mail item's rating.
  1341              *        This internally just calls CryptableMailItem.StartProcessing.
  1342              * 
  1343              *   3. MailItem_ProcessingCompleted
  1344              *        When processing of the mail item is complete, this even handler will be called.
  1345              *        This will update the UI with the latest rating then call StartGetMirror.
  1346              *        The CopyStateToUI method is used which means any open privacy status form will also
  1347              *        be updated.
  1348              * 
  1349              *   4. MailItem_GetMirrorComplete
  1350              *        This is the final step in updating the UI, after a mirror is located, it's contents
  1351              *        will be shown in the unencrypted preview.
  1352              * 
  1353              * The general calling sequence is as shown above 1->4 with each component calling the next.
  1354              * However, for methods that update the mail item directly, commonly only 2->4 is needed.
  1355              */
  1356 
  1357             // Ensure the tick method is not called more than once
  1358             if ((this.isEnabled) &&
  1359                 (refreshOngoing == false))
  1360             {
  1361                 this.refreshOngoing = true;
  1362 
  1363                 if ((this.cryptableMailItem != null) &&
  1364                     (this.cryptableMailItem.IsBeingProcessed == false))
  1365                 {
  1366                     // Attempt to get the download state
  1367                     try
  1368                     {
  1369                         dlState = this.cryptableMailItem.DownloadState;
  1370                     }
  1371                     catch (Exception ex)
  1372                     {
  1373                         Log.Warning("TimerRefresh_Tick: Get DownloadState failed, " + ex.ToString());
  1374 
  1375                         // Assume everything is downloaded, but try to download again as well
  1376                         dlState = Outlook.OlDownloadState.olFullItem;
  1377                         markForDownload = true;
  1378                     }
  1379 
  1380                     if (dlState == Outlook.OlDownloadState.olFullItem)
  1381                     {
  1382                         this.ImmediateRatingAndUIUpdate();
  1383                     }
  1384                     else
  1385                     {
  1386                         markForDownload = true;
  1387                     }
  1388 
  1389                     /* 12/16/2016: It could be verified via testing that setting the MarkForDownload property
  1390                      * can be a way to crash Outlook under certain circumstances (e.g. with IMAP on Outlook 2010 or on Windows 7). 
  1391                      * This then is not caught by a try/catch block and therefore has to be considered an Outlook bug.
  1392                      * It seems that in newer versions of Outlook/Windows, they fixed it, causing exceptions, if at all.
  1393                      * For now, the following is commented out to prevent a crash. If at any point we see that header-only
  1394                      * messages cause bigger issues, this decision has to be reevaluated.
  1395                      */
  1396                     //if (markForDownload)
  1397                     //{
  1398                     //    // Try to mark the message for full download
  1399                     //    try
  1400                     //    {
  1401                     //        this.cryptableMailItem.MarkForDownload = Outlook.OlRemoteStatus.olMarkedForDownload;
  1402                     //        tryAgain = true;
  1403                     //    }
  1404                     //    catch (Exception ex)
  1405                     //    {
  1406                     //        Log.Warning("TimerRefresh_Tick: MarkForDownload failed, " + ex.ToString());
  1407                     //    }
  1408                     //}
  1409                 }
  1410                 else
  1411                 {
  1412                     repeatProcessing = true;
  1413                 }
  1414 
  1415                 // Set the timer to refresh again later automatically
  1416                 if (tryAgain)
  1417                 {
  1418                     this.TimerRefresh.Interval = 100;
  1419                     this.TimerRefresh.Enabled = true;
  1420                 }
  1421 
  1422                 this.refreshOngoing = false;
  1423             }
  1424         }
  1425 
  1426         #endregion
  1427     }
  1428 }