Wrappers/WatchedWindow.cs
branchOUT-497
changeset 2359 0e6191d039e4
child 2360 f01523e581fa
equal deleted inserted replaced
2358:5d193f09df27 2359:0e6191d039e4
       
     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 }