ThisAddIn.cs
author Volker Birk <vb@pep.foundation>
Sun, 11 Mar 2018 19:02:18 +0100
changeset 2053 054906b5e067
parent 2037 c2a6c0ee9a56
child 2058 ecee220c7d7d
permissions -rw-r--r--
automating time logging
     1 using pEp.UI;
     2 using pEpCOMServerAdapterLib;
     3 using System;
     4 using System.Collections.Generic;
     5 using System.ComponentModel;
     6 using System.Diagnostics;
     7 using System.Globalization;
     8 using System.IO;
     9 using System.Runtime.InteropServices;
    10 using System.Threading;
    11 using System.Windows.Forms;
    12 using Office = Microsoft.Office.Core;
    13 using Outlook = Microsoft.Office.Interop.Outlook;
    14 
    15 namespace pEp
    16 {
    17     public partial class ThisAddIn
    18     {
    19         #region VSTO generated code
    20 
    21         /// <summary>
    22         /// Required method for Designer support - do not modify
    23         /// the contents of this method with the code editor.
    24         /// </summary>
    25         private void InternalStartup()
    26         {
    27             this.Startup += new System.EventHandler(ThisAddIn_Startup);
    28             this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
    29         }
    30 
    31         #endregion
    32 
    33         internal delegate void NewAccountEventHandler(object sender, NewAccountEventArgs e);
    34 
    35         /// <summary>
    36         /// Custom event for when the add-in is starting for the first time.
    37         /// </summary>
    38         internal event EventHandler FirstStartup;
    39 
    40         /// <summary>
    41         /// Custom event for when a new account is detected by the add-in.
    42         /// </summary>
    43         internal event NewAccountEventHandler NewAccount;
    44 
    45         private List<KeyValuePair<CultureInfo, string>> _LanguageList       = null;
    46         private static pEpEngine                        _PEPEngine          = null;
    47         private static Exception                        _PEPEngineException = null;
    48         private Outlook.Folder                          _PEPStoreRootFolder = null;
    49         private PEPSettings                             _Settings           = null;
    50         private OutlookOptions                          _OutlookOptions     = null;
    51         private static DecryptionStack                  _DecryptionStack    = null;
    52 
    53 #if !READER_RELEASE_MODE
    54         private AdapterCallbacks adapterCallbacks = null;
    55 #endif
    56 
    57         private bool                       initialized               = false;
    58         private bool                       isItemSendHandlerEnabled  = true;
    59         private FormControlOptions.State   lastOptionsState          = null;
    60         private Dictionary<string, string> userIdCache               = new Dictionary<string, string>();
    61         private List<WatchedFolder>        watchedFolders            = new List<WatchedFolder>();
    62         private System.Windows.Forms.Timer inboxCleaner              = null;
    63 
    64         /**************************************************************
    65          * 
    66          * Property Accessors
    67          * 
    68          *************************************************************/
    69 
    70         /// <summary>
    71         /// Gets the list of languages supported in the pEp engine.
    72         /// The list of language includes a key-value pair of the culture as well as a 
    73         /// display phrase in the native language for the trustwords.
    74         /// (phrase example: "I want to display the trustwords in English language")
    75         /// </summary>
    76         internal List<KeyValuePair<CultureInfo, string>> LanguageList
    77         {
    78             get
    79             {
    80                 string code;
    81                 string phrase;
    82                 string engineList;
    83                 string[] languages;
    84                 string[] language;
    85 
    86                 if (this._LanguageList == null)
    87                 {
    88                     this._LanguageList = new List<KeyValuePair<CultureInfo, string>>();
    89 
    90                     try
    91                     {
    92                         engineList = ThisAddIn.PEPEngine.GetLanguageList();
    93                     }
    94                     catch
    95                     {
    96                         engineList = "";
    97                         Log.Error("LanguageList: Failed to get list from engine.");
    98                     }
    99 
   100                     languages = engineList.Split('\n');
   101 
   102                     // Go through each language group
   103                     for (int i = 0; i < languages.Length; i++)
   104                     {
   105                         language = languages[i].Split(',');
   106 
   107                         if (language.Length == 3)
   108                         {
   109                             code = language[0].Trim();
   110                             code = code.Replace("\"", "");
   111 
   112                             phrase = language[1].Trim();
   113                             phrase = phrase.Replace("\"", "");
   114 
   115                             this._LanguageList.Add(new KeyValuePair<CultureInfo, string>(new CultureInfo(code),
   116                                                                                          phrase));
   117                         }
   118                     }
   119                 }
   120 
   121                 return (this._LanguageList);
   122             }
   123         }
   124 
   125         /// <summary>
   126         /// Gets the static pEp engine reference.
   127         /// </summary>
   128         internal static pEpEngine PEPEngine
   129         {
   130             get
   131             {
   132                 if (ThisAddIn._PEPEngine == null)
   133                 {
   134                     try
   135                     {
   136                         ThisAddIn._PEPEngine = new pEpEngine();
   137                     }
   138                     catch (Exception ex)
   139                     {
   140                         // As Globals.StopAndSendCrashReport below internally tries to access the engine
   141                         // several times, we need to prevent infinite recursion in case of engine
   142                         // initializaition failure.
   143                         if (_PEPEngineException != null)
   144                             return null;
   145                         _PEPEngineException = ex;
   146 
   147                         string summaryMessage = Properties.Resources.Message_InitError + " " +
   148                                                 Properties.Resources.Message_Reinstall;
   149                         Globals.StopAndSendCrashReport(ex, summaryMessage, false, true);
   150 
   151                         return (null);
   152                     }
   153                 }
   154 
   155                 return (ThisAddIn._PEPEngine);
   156             }
   157         }
   158 
   159         /// <summary>
   160         /// Gets the root folder of the pEp data store.
   161         /// This is primarily used to save unencrypted outlook mail items (mirrors).
   162         /// </summary>
   163         internal Outlook.Folder PEPStoreRootFolder
   164         {
   165             get { return (this._PEPStoreRootFolder); }
   166         }
   167 
   168         /// <summary>
   169         /// Gets the current pEp settings for the add-in.
   170         /// </summary>
   171         internal PEPSettings Settings
   172         {
   173             get { return (this._Settings); }
   174         }
   175 
   176         /// <summary>
   177         /// Gets the Outlook options set in the program.
   178         /// </summary>
   179         internal OutlookOptions OutlookOptions
   180         {
   181             get { return (this._OutlookOptions); }
   182         }
   183 
   184         /// <summary>
   185         /// Gets the decryption stack.
   186         /// </summary>
   187         internal static DecryptionStack DecryptionStack
   188         {
   189             get
   190             {
   191                 if (ThisAddIn._DecryptionStack == null)
   192                 {
   193                     ThisAddIn._DecryptionStack = new DecryptionStack();
   194                 }
   195 
   196                 return ThisAddIn._DecryptionStack;
   197             }
   198         }
   199 
   200         /**************************************************************
   201          * 
   202          * Methods
   203          * 
   204          *************************************************************/
   205 
   206         /// <summary>
   207         /// Required override to add ribbon extensions.
   208         /// </summary>
   209         protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
   210         {
   211             return new RibbonCustomizations();
   212         }
   213 
   214         /// <summary>
   215         /// This updates the accounts list in pEp settings with the ones found in Outlook, adding new accounts and removing
   216         /// all accounts that are not there anymore.
   217         /// </summary>
   218         internal void SyncAccountsList()
   219         {
   220             bool exists;
   221             string dialogMessage = string.Empty;
   222             Outlook.NameSpace ns = null;
   223             Outlook.Account account = null;
   224             Outlook.Accounts accounts = null;
   225             PEPSettings.PEPAccountSettings acctSettings;
   226             NewAccountEventArgs args;
   227 
   228             try
   229             {
   230                 ns = Application.Session;
   231                 accounts = ns?.Accounts;
   232 
   233                 if (this._Settings != null)
   234                 {
   235                     // Add any new Outlook accounts to the account settings list
   236                     for (int i = 1; i <= accounts.Count; i++)
   237                     {
   238                         account = accounts[i];
   239 
   240                         if (string.IsNullOrWhiteSpace(account.SmtpAddress) == false)
   241                         {
   242                             // Check if it is already in the list
   243                             if (this._Settings.GetAccountSettings(account.SmtpAddress) == null)
   244                             {
   245                                 acctSettings = new PEPSettings.PEPAccountSettings();
   246                                 acctSettings.SmtpAddress = account.SmtpAddress;
   247                                 acctSettings.Type = account.AccountType.ToString();
   248                                 acctSettings.UserName = account.UserName;
   249 
   250                                 // Qualify server trust if not ActiveSync (always trusted as mails don't get synced back to server)
   251                                 if (account.AccountType != Outlook.OlAccountType.olEas)
   252                                 {
   253                                     acctSettings.IsSecureStorageEnabled = (account.GetIsInLocalDomain() == false);
   254                                 }
   255 
   256                                 // Deactivate key sync for IMAP accounts or if global sync setting is disabled
   257                                 if ((account.AccountType == Outlook.OlAccountType.olImap) ||
   258                                     (this._Settings.IsSyncEnabledForAllAccounts == false))
   259                                 {
   260                                     acctSettings.IsSyncEnabled = false;
   261                                 }
   262 
   263                                 // Add account settings
   264                                 this._Settings.AccountSettingsList.Add(acctSettings);
   265 
   266                                 // Raise the new account event
   267                                 args = new NewAccountEventArgs();
   268                                 args.AccountType = account.AccountType;
   269                                 args.DisplayName = account.DisplayName;
   270                                 args.SmtpAddress = account.SmtpAddress;
   271                                 args.UserName = account.UserName;
   272 
   273                                 this.NewAccount?.Invoke(this, args);
   274                             }
   275                         }
   276                         else
   277                         {
   278                             Log.Warning("SyncWithSettings: Invalid Smtp address detected, skipping account.");
   279                         }
   280 
   281                         account = null;
   282                     }
   283 
   284                     // Remove any entries of the account settings list not in Outlook
   285                     // This is needed to skip any entries in the registry that may be invalid, user created, or no longer in Outlook
   286                     for (int i = (this._Settings.AccountSettingsList.Count - 1); i >= 0; i--)
   287                     {
   288                         acctSettings = this._Settings.AccountSettingsList[i];
   289 
   290                         // Check if it exists in Outlook
   291                         exists = false;
   292                         for (int j = 1; j <= accounts.Count; j++)
   293                         {
   294                             account = accounts[j];
   295 
   296                             if (acctSettings.EqualsByAddress(account.SmtpAddress))
   297                             {
   298                                 exists = true;
   299                             }
   300 
   301                             account = null;
   302 
   303                             if (exists) { break; }
   304                         }
   305 
   306                         // Delete if necessary
   307                         if (exists == false)
   308                         {
   309                             this._Settings.AccountSettingsList.RemoveAt(i);
   310                         }
   311                     }
   312                 }
   313             }
   314             catch (Exception ex)
   315             {
   316                 Log.Error("SyncAccountsList: Error occured. " + ex.ToString());
   317             }
   318             finally
   319             {
   320                 ns = null;
   321                 account = null;
   322                 accounts = null;
   323             }
   324         }
   325 
   326         /// <summary>
   327         /// Syncronizes the current settings class with the pEp for Outlook instance and the pEp engine.
   328         /// Warning: This code should be kept synchronized with the Settings_PropertyChanged event.
   329         /// <param name="syncAccountsList">Whether to also update the accounts list in pEp settings. 
   330         /// </summary>
   331         internal void SyncWithSettings(bool syncAccountsList = false)
   332         {
   333             bool exists;
   334             string dialogMessage = string.Empty;
   335             Outlook.NameSpace ns = null;
   336             Outlook.Account account = null;
   337             Outlook.Accounts accounts = null;
   338             pEpIdentity[] ownIdentities;
   339             List<KeyValuePair<CultureInfo, string>> languages;
   340 
   341             try
   342             {
   343                 ns = Application.Session;
   344                 accounts = ns.Accounts;
   345 
   346                 if (this._Settings != null)
   347                 {
   348                     // Update the Outlook accounts list with the one maintained in pEp settings if needed.
   349                     if (syncAccountsList)
   350                     {
   351                         this.SyncAccountsList();
   352                     }
   353 
   354                     /* Sync the account settings with the engine's own identity flags.
   355                      * Warning: RegisterMyself() must already have been called so the engine has all own identities.
   356                      * The engine can commonly have more own identities than what is in the accounts list.
   357                      * This isn't a problem though and engine-only identities will not be changed here.
   358                      */
   359                     ownIdentities = ThisAddIn.PEPEngine.OwnIdentitiesRetrieve();
   360                     foreach (PEPSettings.PEPAccountSettings acctSettings in this._Settings.AccountSettingsList)
   361                     {
   362                         // Find the own identity that matches with the account settings
   363                         foreach (pEpIdentity ident in ownIdentities)
   364                         {
   365                             pEpIdentity ownIdent = ident;
   366 
   367                             if (acctSettings.EqualsByAddress(ownIdent.Address))
   368                             {
   369                                 // Sync the engine's own identity flags with the account or global settings
   370                                 if (this._Settings.IsSyncEnabledForAllAccounts)
   371                                 {
   372                                     if (ownIdent.GetIsSyncEnabled() == false)
   373                                     {
   374                                         ThisAddIn.PEPEngine.UnsetIdentityFlags(ref ownIdent, pEpIdentityFlags.pEpIdfNotForSync);
   375                                     }
   376                                 }
   377                                 else if (ownIdent.GetIsSyncEnabled() != acctSettings.IsSyncEnabled)
   378                                 {
   379                                     if (acctSettings.IsSyncEnabled)
   380                                     {
   381                                         ThisAddIn.PEPEngine.UnsetIdentityFlags(ref ownIdent, pEpIdentityFlags.pEpIdfNotForSync);
   382                                     }
   383                                     else
   384                                     {
   385                                         ThisAddIn.PEPEngine.SetIdentityFlags(ref ownIdent, pEpIdentityFlags.pEpIdfNotForSync);
   386                                     }
   387                                 }
   388                                 break;
   389                             }
   390                         }
   391                     }
   392 
   393                     // Sync IsKeyServerUsed with engine
   394                     if (this._Settings.IsKeyServerUsed)
   395                     {
   396                         ThisAddIn.PEPEngine.StartKeyserverLookup();
   397                     }
   398                     else
   399                     {
   400                         ThisAddIn.PEPEngine.StopKeyserverLookup();
   401                     }
   402 
   403                     // Sync IsPassiveModeEnabled with engine
   404                     ThisAddIn.PEPEngine.PassiveMode(this._Settings.IsPassiveModeEnabled);
   405 
   406                     // Sync IsPEPFolderVisible with Outlook
   407                     this.SetPEPStoreRootFolderVisibility(this._Settings.IsPEPFolderVisible);
   408 
   409                     // Sync IsUnencryptedSubjectEnabled with engine
   410                     ThisAddIn.PEPEngine.UnencryptedSubject(this._Settings.IsUnencryptedSubjectEnabled);
   411 
   412                     // Sync IsVerboseLoggingEnabled with engine
   413                     ThisAddIn.PEPEngine.VerboseLogging(this._Settings.IsVerboseLoggingEnabled);
   414 
   415                     // Sync TrustwordsCulture with engine
   416                     if (this._Settings.TrustwordsCulture != null)
   417                     {
   418                         // Validate it exists in the engine list
   419                         exists = false;
   420                         languages = this.LanguageList;
   421                         foreach (KeyValuePair<CultureInfo, string> entry in languages)
   422                         {
   423                             if (entry.Key.LCID == this._Settings.TrustwordsCulture.LCID)
   424                             {
   425                                 exists = true;
   426                                 break;
   427                             }
   428                         }
   429 
   430                         if (exists == false)
   431                         {
   432                             // Reset to default
   433                             this._Settings.TrustwordsCulture = this.GetActiveUICulture();
   434                             Log.Warning("SyncWithSettings: Invalid TrustwordsCulture detected, setting to default.");
   435                         }
   436                     }
   437                     else
   438                     {
   439                         // Reset to default
   440                         this._Settings.TrustwordsCulture = this.GetActiveUICulture();
   441                     }
   442                 }
   443             }
   444             catch (Exception ex)
   445             {
   446                 Log.Error("SyncWithSettings: Failure occured, " + ex.ToString());
   447             }
   448             finally
   449             {
   450                 if (ns != null)
   451                 {
   452                     // Marshal.ReleaseComObject(ns);
   453                     ns = null;
   454                 }
   455 
   456                 if (account != null)
   457                 {
   458                     // Marshal.ReleaseComObject(account);
   459                     account = null;
   460                 }
   461 
   462                 if (accounts != null)
   463                 {
   464                     // Marshal.ReleaseComObject(accounts);
   465                     accounts = null;
   466                 }
   467             }
   468 
   469             return;
   470         }
   471 
   472         /// <summary>
   473         /// Compares the given settings with the current state of Outlook and detects any changes.
   474         /// This is currently only used to identify first startup.
   475         /// </summary>
   476         /// <param name="lastSettings">The last settings to compare with. 
   477         /// This should be the settings just loaded from the registry at startup (last saved settings).</param>
   478         private void DetectChangesFromLastSettings(PEPSettings lastSettings)
   479         {
   480             // Detect first startup
   481             if (lastSettings.IsFirstStartupComplete == false)
   482             {
   483                 this.FirstStartup?.Invoke(this, new EventArgs());
   484             }
   485 
   486             return;
   487         }
   488 
   489         /// <summary>
   490         /// Detects and disables GPG for Windows GpgOL Outlook add-in.
   491         /// </summary>
   492         /// <returns>True if the GpgOL add-in was disabled.</returns>
   493         private bool DisableGpgOL()
   494         {
   495             bool wasDisabled = false;
   496             Office.COMAddIns comAddIns = null;
   497 
   498             try
   499             {
   500                 comAddIns = Globals.ThisAddIn.Application.COMAddIns;
   501                 foreach (Office.COMAddIn addin in comAddIns)
   502                 {
   503                     if (string.Equals(addin.Description, "GpgOL - The GnuPG Outlook Plugin", StringComparison.OrdinalIgnoreCase))
   504                     {
   505                         addin.Connect = false;
   506                         wasDisabled = true;
   507 
   508                         Log.Info("DisableGpgOL: GpgOL was detected and disabled.");
   509                     }
   510                 }
   511             }
   512             catch (Exception ex)
   513             {
   514                 Log.Error("DisableGpgOL: Error trying to disable GpgOL, " + ex.ToString());
   515             }
   516             finally
   517             {
   518                 if (comAddIns != null)
   519                 {
   520                     // Marshal.ReleaseComObject(comAddIns);
   521                     comAddIns = null;
   522                 }
   523             }
   524 
   525             return (wasDisabled);
   526         }
   527 
   528         /// <summary>
   529         /// Registers each own identity in the pEp engine.
   530         /// An own identity represents each account in Outlook.
   531         /// </summary>
   532         internal void RegisterMyself()
   533         {
   534             pEpIdentity ownIdentity;
   535 
   536             if (Globals.ThisAddIn.Settings.AccountSettingsList != null)
   537             {
   538                 foreach (var acctSetting in Globals.ThisAddIn.Settings.AccountSettingsList)
   539                 {
   540                     // Create pEpIdentity
   541                     ownIdentity = new pEpIdentity
   542                     {
   543                         Address = acctSetting.SmtpAddress,
   544                         UserId = PEPSettings.PEP_OWN_USER_ID,
   545                         UserName = acctSetting.UserName
   546                     };
   547 
   548                     // Add to userId cache
   549                     this.userIdCache[ownIdentity.Address] = ownIdentity.UserId;
   550 
   551                     // Set not for sync flag if necessary
   552                     if ((acctSetting.Type.Contains("Imap")) ||
   553                         ((Globals.ThisAddIn.Settings.IsSyncEnabledForAllAccounts == false) &&
   554                          (acctSetting.IsSyncEnabled == false)))
   555                     {
   556                         ownIdentity.Flags = pEpIdentityFlags.pEpIdfNotForSync;
   557                     }
   558 
   559                     // Log information if invalid
   560                     if (string.IsNullOrWhiteSpace(ownIdentity.Address))
   561                     {
   562                         Log.Warning("RegisterMyself: Myself doesn't have an address.");
   563                     }
   564 
   565                     if (string.IsNullOrWhiteSpace(ownIdentity.UserName))
   566                     {
   567                         Log.Warning("RegisterMyself: Myself doesn't have a user name.");
   568                     }
   569 
   570                     /* Call engine to register myself (doesn't matter if already done in a past instance)
   571                      * Note that key generation can take place during the call to Myself if it's a new installation
   572                      * and the engine is unable to elect a key. In this situation key generation can take some time.
   573                      * It takes long-enough for the call to the adapter to timeout here and throw an exception.
   574                      * However, since the fingerprint of the own identity is not actually needed here, the assumption is 
   575                      * made that the engine will be OK and continue processing.
   576                      * The exception is simply ignored and we go to the next identity. 
   577                      */
   578                     try
   579                     {
   580                         ThisAddIn.PEPEngine.Myself(ownIdentity);
   581                     }
   582                     catch (COMException ex)
   583                     {
   584                         Log.Warning("RegisterMyself: Engine returned exception, " + ex.ToString());
   585                     }
   586                 }
   587             }
   588         }
   589 
   590         /// <summary>
   591         /// Gets the pEp user ID for the given address and contact.
   592         /// </summary>
   593         /// <param name="address">The address to get the user ID for.</param>
   594         /// <param name="contact">The contact associated with the address (if any).
   595         /// This value can be null and is not required for 'myself' user IDs.</param>
   596         /// <returns>The user ID (Outlook EntryID) for the given address</returns>
   597         internal string GetUserId(string address,
   598                                   Outlook.ContactItem contact = null)
   599         {
   600             string id = null;
   601             Outlook.Folder contactFolder = null;
   602 
   603             try
   604             {
   605                 if (string.IsNullOrEmpty(address) == false)
   606                 {
   607                     // Search cache
   608                     if (id == null)
   609                     {
   610                         try
   611                         {
   612                             id = this.userIdCache[address];
   613                         }
   614                         catch
   615                         {
   616                             id = null;
   617                         }
   618                     }
   619 
   620                     // Handle myself user ID calculation
   621                     if (id == null)
   622                     {
   623                         // Check if the address is myself
   624                         if (PEPIdentity.GetIsOwnIdentity(address))
   625                         {
   626                             id = PEPSettings.PEP_OWN_USER_ID;
   627                             this.userIdCache[address] = id;
   628                         }
   629                     }
   630 
   631                     // Use a contact EntryID if available
   632                     if ((id == null) &&
   633                         (contact != null))
   634                     {
   635                         try
   636                         {
   637                             id = contact.EntryID;
   638                             this.userIdCache[address] = id;
   639                         }
   640                         catch
   641                         {
   642                             Log.Error("GetUserId: Attempted to use contact but failed.");
   643                         }
   644                     }
   645                 }
   646             }
   647             catch (Exception ex)
   648             {
   649                 Globals.StopAndSendCrashReport(ex);
   650             }
   651             finally
   652             {
   653                 if (contactFolder != null)
   654                 {
   655                     Marshal.FinalReleaseComObject(contactFolder);
   656                     contactFolder = null;
   657                 }
   658             }
   659 
   660             return (id);
   661         }
   662 
   663         /// <summary>
   664         /// Create a new Outlook MailItem from the given PEPMessage and send it.
   665         /// The sent message will not be processed by the 'Application_ItemSend' event.
   666         /// WARNING: Exchange ActiveSync accounts ignore the deleteAfterSend parameter (always false).
   667         /// </summary>
   668         /// <param name="message">The message to send.</param>
   669         /// <param name="deleteAfterSend">Whether the message is deleted after sending (true) or a copy is saved (false).</param>
   670         /// <param name="validateSendingAccount">Validates that the SendingAccount matches the From identity of the given message.
   671         /// This can catch situations (and throw exceptions) where the default account would be used instead.</param>
   672         /// <param name="processMessage">Whether or not to process this message through the pEp engine.</param>
   673         internal void CreateAndSendMessage(PEPMessage message,
   674                                            bool deleteAfterSend,
   675                                            bool validateSendingAccount,
   676                                            bool processMessage = false)
   677         {
   678             Outlook.MailItem newItem;
   679             Globals.ReturnStatus sts;
   680 
   681             this.isItemSendHandlerEnabled = processMessage;
   682 
   683             try
   684             {
   685                 newItem = Application.CreateItem(Outlook.OlItemType.olMailItem);
   686 
   687                 /* Notes:
   688                  * Do NOT include internal Header Fields (MAPI properties) on outgoing messages!
   689                  * The sender must be set from the from identity (setSender=true)
   690                  */
   691                 sts = message.ApplyTo(newItem, false, true);
   692 
   693                 if (sts == Globals.ReturnStatus.Success)
   694                 {
   695                     // Check that the sending account is the From identity
   696                     if ((validateSendingAccount) &&
   697                         (message.From != null))
   698                     {
   699                         if (message.From.EqualsByAddress(newItem.GetSendUsingAccountIdentity()) == false)
   700                         {
   701                             throw new Exception("Sending account does not match from identity");
   702                         }
   703                     }
   704 
   705                     // Do not allow TNEF/RTF format with 'winmail.dat' attachment
   706                     MapiHelper.SetProperty(newItem, MapiProperty.PidLidUseTnef, false);
   707 
   708                     /* Send
   709                      * 
   710                      * Note: For ActiveSync accounts, the DeleteAfterSubmit property is ignored.
   711                      * This means the message will always appear in the sent folder by default.
   712                      * The reason for this is unknown.
   713                      * 
   714                      * It's possible this is related to the ActiveSync specification version
   715                      * and therefore may behave differently depending on Outlook version.
   716                      * More research is needed here.
   717                      */
   718                     newItem.DeleteAfterSubmit = deleteAfterSend;
   719                     ((Outlook._MailItem)newItem).Send();
   720 
   721                     newItem = null;
   722                 }
   723                 else
   724                 {
   725                     newItem.PermanentlyDelete();
   726                     newItem = null;
   727 
   728                     throw new Exception("Failed to create MailItem to send");
   729                 }
   730             }
   731             catch
   732             {
   733                 throw new Exception("CreateMessageAndSend: Failed to send MailItem.");
   734             }
   735             finally
   736             {
   737                 this.isItemSendHandlerEnabled = true;
   738             }
   739 
   740             return;
   741         }
   742 
   743         /// <summary>
   744         /// Create a new 'sent' Outlook MailItem and place it in the given store.
   745         /// WARNING: Exchange ActiveSync accounts are not supported by this method. The sent
   746         /// message will always appear in the drafts folder.
   747         /// </summary>
   748         /// <param name="sendingStore">The store to save the sent message to.</param>
   749         /// <param name="acctSettings">The sending account settings.</param>
   750         /// <param name="sentMessage">The message that is sent.</param>
   751         /// <param name="storedRating">The rating to store to the sent message mail item.
   752         /// Will only be set if not undefined (default). Warning: Only use this on trusted servers.</param>
   753         private void CreateNewSentMail(Outlook.Store sendingStore,
   754                                        PEPSettings.PEPAccountSettings acctSettings,
   755                                        PEPMessage sentMessage,
   756                                        pEpRating storedRating = pEpRating.pEpRatingUndefined)
   757         {
   758             Int32 messageFlags;
   759             Outlook.MailItem sentMailItem = null;
   760             Outlook.Folder sentFolder = null;
   761             Outlook.NameSpace ns = Application.Session;
   762             Outlook.Folder rootFolder = null;
   763             Outlook.Folders folders = null;
   764             Outlook.Folder folder = null;
   765 
   766             /* The process is:
   767              * 1. Use the Store.GetDefaultFolder(olFolderSentMail) to try to get the default Sent folder. 
   768              *    However, this can fail for a number of reasons, depending on server and folder configuration.
   769              * 2. As a fallback, search for the Sent folder by matching the Sent folder Entry ID
   770              *    However, this can also fail if no Sent folder Entry ID has been recorded.
   771              * 3. As another fallback, search by Folder name ("SENT") for an existing Sent folder
   772              *    If found, record the folder's Entry ID. If not, goto point 4.
   773              * 4. If everything of the above fails, create a new Sent folder and record the Entry ID.     
   774              */
   775 
   776             try
   777             {
   778                 if ((sendingStore != null) &&
   779                     (acctSettings != null) &&
   780                     (sentMessage != null))
   781                 {
   782                     rootFolder = (Outlook.Folder)sendingStore.GetRootFolder();
   783                     if (rootFolder != null)
   784                     {
   785                         folders = rootFolder.Folders;
   786                     }
   787 
   788                     // Step 1
   789                     try
   790                     {
   791                         sentFolder = (Outlook.Folder)sendingStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail);
   792                     }
   793                     catch
   794                     {
   795                         if (sentFolder != null)
   796                         {
   797                             // Marshal.ReleaseComObject(sentFolder);
   798                             sentFolder = null;
   799                         }
   800                     }
   801 
   802                     // Step 2
   803                     if (sentFolder == null)
   804                     {
   805                         try
   806                         {
   807                             /* Search for folder by entry ID
   808                              * Normally, the way to go would be the following line:
   809                              * sentFolder = (Outlook.Folder)ns.GetItemFromID(acctSettings.SentFolderEntryId, sendingStore.StoreID);
   810                              * However, for some reason, this can come up with the following error: "Could not open the item. Try again."
   811                              * Therefore, the below solution is used:
   812                              */
   813                             if (folders != null)
   814                             {
   815                                 for (int i = 1; i <= folders.Count; i++)
   816                                 {
   817                                     folder = (Outlook.Folder)folders[i];
   818 
   819                                     if (folder != null)
   820                                     {
   821                                         if ((string.IsNullOrEmpty(folder.EntryID) == false) &&
   822                                             (string.Equals(folder.EntryID, acctSettings.SentFolderEntryId)))
   823                                         {
   824                                             sentFolder = folder;
   825                                             break;
   826                                         }
   827 
   828                                         // Marshal.ReleaseComObject(folder);
   829                                         folder = null;
   830                                     }
   831                                 }
   832                             }
   833                         }
   834                         catch
   835                         {
   836                             if (sentFolder != null)
   837                             {
   838                                 // Marshal.ReleaseComObject(sentFolder);
   839                                 sentFolder = null;
   840                             }
   841                         }
   842                     }
   843 
   844                     // Step 3
   845                     if (sentFolder == null)
   846                     {
   847                         try
   848                         {
   849                             //Search for folder by name
   850                             if (folders != null)
   851                             {
   852                                 for (int i = 1; i <= folders.Count; i++)
   853                                 {
   854                                     folder = (Outlook.Folder)folders[i];
   855 
   856                                     if (folder != null)
   857                                     {
   858                                         if ((string.IsNullOrEmpty(folder.Name) == false) &&
   859                                             (folder.Name.Trim().StartsWith(Globals.CUSTOM_SENT_FOLDER_NAME, StringComparison.OrdinalIgnoreCase)))
   860                                         {
   861                                             sentFolder = folder;
   862                                             break;
   863                                         }
   864 
   865                                         // Marshal.ReleaseComObject(folder);
   866                                         folder = null;
   867                                     }
   868                                 }
   869                             }
   870                         }
   871                         catch
   872                         {
   873                             if (sentFolder != null)
   874                             {
   875                                 // Marshal.ReleaseComObject(sentFolder);
   876                                 sentFolder = null;
   877                             }
   878                         }
   879                     }
   880 
   881                     // Step 4
   882                     if (sentFolder == null)
   883                     {
   884                         try
   885                         {
   886                             // Create folder
   887                             if (folders != null)
   888                             {
   889                                 sentFolder = (Outlook.Folder)folders.Add(Globals.CUSTOM_SENT_FOLDER_NAME);
   890                             }
   891                         }
   892                         catch (Exception ex)
   893                         {
   894                             if (sentFolder != null)
   895                             {
   896                                 // Marshal.ReleaseComObject(sentFolder);
   897                                 sentFolder = null;
   898                             }
   899 
   900                             Log.Error("CreateNewSentMail: No Sent folder found and no new Sent folder could be created because of the following error: " + ex.ToString());
   901                         }
   902                     }
   903 
   904                     if (sentFolder != null)
   905                     {
   906                         acctSettings.SentFolderEntryId = sentFolder.EntryID;
   907 
   908                         // Create the basic sent mail item
   909                         sentMailItem = (Outlook.MailItem)sentFolder.Items.Add(Outlook.OlItemType.olMailItem);
   910                         sentMessage.ApplyTo(sentMailItem, false, true); // Do NOT include internal Header Fields (MAPI properties)!
   911 
   912                         // Set the date and time the mail item was sent
   913                         try
   914                         {
   915                             MapiHelper.SetProperty(sentMailItem, MapiProperty.PidTagClientSubmitTime, DateTime.Now.ToUniversalTime());
   916                         }
   917                         catch { }
   918 
   919                         // Set flags
   920                         try
   921                         {
   922                             messageFlags = (System.Int32)MapiHelper.GetProperty(sentMailItem, MapiProperty.PidTagMessageFlags);
   923                             messageFlags &= ~((System.Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfUnsent);  // Clear UNSENT flag -- must be done before save
   924                             messageFlags |= ((System.Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfRead);     // Mark as read
   925                             MapiHelper.SetProperty(sentMailItem, MapiProperty.PidTagMessageFlags, messageFlags);
   926                         }
   927                         catch { }
   928 
   929                         // Save pEp rating
   930                         try
   931                         {
   932                             sentMailItem.SetPEPProperty(MailItemExtensions.PEPProperty.Rating, storedRating);
   933                         }
   934                         catch { }
   935 
   936                         // Save changes to the mail item (must be done last for some properties)
   937                         sentMailItem.Save();
   938 
   939                         /* Attempt to move to the sent folder.
   940                          * For ActiveSync accounts, this will fail with the error:
   941                          * 'Sorry, Exchange ActiveSync doesn't support what you're trying to do.'
   942                          * This is because the .Items.Add(folder) will ignore the specified folder and create in local Drafts.
   943                          * Then "Exchange ActiveSync doesn’t support the Drafts folder" so it can't be moved out.
   944                          * Even in the Outlook UI a draft cannot be 'dragged' out of the drafts folder.
   945                          */
   946                         try
   947                         {
   948                             sentMailItem.Move(sentFolder);
   949                         }
   950                         catch { }
   951                     }
   952                 }
   953             }
   954             catch (Exception ex)
   955             {
   956                 Log.Error("CreateNewSentMail: Error occured, " + ex.ToString());
   957                 throw;
   958             }
   959             finally
   960             {
   961                 if (sentMailItem != null)
   962                 {
   963                     // Marshal.ReleaseComObject(sentMailItem);
   964                     sentMailItem = null;
   965                 }
   966 
   967                 if (ns != null)
   968                 {
   969                     //Marshal.ReleaseComObject(ns);
   970                     ns = null;
   971                 }
   972 
   973                 if (sentFolder != null)
   974                 {
   975                     //Marshal.ReleaseComObject(sentFolder);
   976                     sentFolder = null;
   977                 }
   978 
   979                 if (rootFolder != null)
   980                 {
   981                     //Marshal.ReleaseComObject(rootFolder);
   982                     rootFolder = null;
   983                 }
   984 
   985                 if (folders != null)
   986                 {
   987                     // Marshal.ReleaseComObject(folders);
   988                     folders = null;
   989                 }
   990 
   991                 if (folder != null)
   992                 {
   993                     // Marshal.ReleaseComObject(folder);
   994                     folder = null;
   995                 }
   996             }
   997 
   998             return;
   999         }
  1000 
  1001         /// <summary>
  1002         /// Gets whether the given account is trusted (on the account whitelist).
  1003         /// Character case and whitespace is ignored during comparison.
  1004         /// </summary>
  1005         /// <param name="address">The address of the account.</param>
  1006         /// <returns>True if the account with the given address is trusted by default (on the whitelist), otherwise false.</returns>
  1007         private bool GetIsAccountTrusted(string address)
  1008         {
  1009             bool isTrusted = false;
  1010             string searchAddress = address.ToUpperInvariant().Trim();
  1011             string searchDomain = searchAddress.Contains("@") ? searchAddress.Substring(searchAddress.LastIndexOf("@")) : null;
  1012             string whitelistAccount;
  1013 
  1014             for (int i = 0; i < this._Settings.AccountWhitelist.Length; i++)
  1015             {
  1016                 whitelistAccount = this._Settings.AccountWhitelist[i].ToUpperInvariant().Trim();
  1017 
  1018                 // Check for exact match
  1019                 if (searchAddress == whitelistAccount)
  1020                 {
  1021                     isTrusted = true;
  1022                     break;
  1023                 }
  1024 
  1025                 // Check for domain match
  1026                 if ((searchDomain != null) &&
  1027                     whitelistAccount.StartsWith("@") &&
  1028                     (searchDomain == whitelistAccount))
  1029                 {
  1030                     isTrusted = true;
  1031                     break;
  1032                 }
  1033             }
  1034 
  1035             return (isTrusted);
  1036         }
  1037 
  1038         /// <summary>
  1039         /// Copies any necessary data from the given options form state back into this add-ins current state.
  1040         /// The registry is also updated by this method.
  1041         /// </summary>
  1042         /// <param name="state">The state to copy data from.</param>
  1043         internal void SetOptionsState(FormControlOptions.State state)
  1044         {
  1045             bool isBlacklisted;
  1046             string fpr1;
  1047             string fpr2;
  1048             string[] blacklist;
  1049             PEPSettings.PEPAccountSettings acctSettings;
  1050 
  1051             if (state != null)
  1052             {
  1053                 // Get the blacklist
  1054                 try
  1055                 {
  1056                     blacklist = ThisAddIn.PEPEngine.BlacklistRetrieve();
  1057                 }
  1058                 catch (COMException ex)
  1059                 {
  1060                     blacklist = new string[0];
  1061                     Log.Error("SetOptionsState: Error getting blacklist from engine. " + ex.ToString());
  1062                 }
  1063 
  1064                 // Remove any fingerprints no longer in the engine blacklist
  1065                 if (blacklist != null)
  1066                 {
  1067                     for (int i = (blacklist.Length - 1); i >= 0; i--)
  1068                     {
  1069                         fpr1 = this.RemoveFprFormatting(blacklist[i]);
  1070                         isBlacklisted = false;
  1071 
  1072                         if (string.IsNullOrEmpty(fpr1) == false)
  1073                         {
  1074                             // Check if fingerprint is still blacklisted
  1075                             foreach (KVPair<PEPIdentity, bool> entry in state.Blacklist)
  1076                             {
  1077                                 fpr2 = this.RemoveFprFormatting(entry.Key.Fingerprint);
  1078 
  1079                                 // Value is true if the entry is blacklisted
  1080                                 if ((string.Equals(fpr1, fpr2, StringComparison.OrdinalIgnoreCase)) &&
  1081                                     (entry.Value))
  1082                                 {
  1083                                     isBlacklisted = true;
  1084                                     break;
  1085                                 }
  1086                             }
  1087 
  1088                             if (isBlacklisted == false)
  1089                             {
  1090                                 try
  1091                                 {
  1092                                     ThisAddIn.PEPEngine.BlacklistDelete(fpr1);
  1093                                 }
  1094                                 catch (COMException ex)
  1095                                 {
  1096                                     Log.Error("SetOptionsState: Failed to delete blacklist entry, " + ex.ToString());
  1097                                 }
  1098                             }
  1099                         }
  1100                     }
  1101                 }
  1102 
  1103                 // Add any new fingerprints to the engine blacklist
  1104                 foreach (KVPair<PEPIdentity, bool> entry in state.Blacklist)
  1105                 {
  1106                     fpr1 = this.RemoveFprFormatting(entry.Key.Fingerprint);
  1107 
  1108                     // Value is true if the entry is blacklisted
  1109                     if ((entry.Value) &&
  1110                         (string.IsNullOrEmpty(fpr1) == false))
  1111                     {
  1112                         try
  1113                         {
  1114                             if (ThisAddIn.PEPEngine.BlacklistIsListed(fpr1) == false)
  1115                             {
  1116                                 ThisAddIn.PEPEngine.BlacklistAdd(fpr1);
  1117                             }
  1118                         }
  1119                         catch (COMException ex)
  1120                         {
  1121                             Log.Error("SetOptionsState: Failed to add new blacklist entry, " + ex.ToString());
  1122                         }
  1123                     }
  1124                 }
  1125 
  1126                 foreach (FormControlOptions.AccountState entry in state.AccountSettingsList)
  1127                 {
  1128                     /* pEp internally uses account types as defined by microsoft.
  1129                      * This means they have 'ol' before the type (Example: 'olImap').
  1130                      * This isn't always user friendly so for the options UI the 'ol' is removed.
  1131                      * However, when comparing again with the settings list, this 'ol' must be added back.
  1132                      */
  1133                     acctSettings = this._Settings.GetAccountSettings(entry.SmtpAddress);
  1134 
  1135                     if (acctSettings != null)
  1136                     {
  1137                         // Modify existing value
  1138                         // The following are skipped: SentFolderEntryId, SmtpAddress & Type
  1139                         acctSettings.IsDecryptAlwaysEnabled = entry.IsDecryptAlwaysEnabled;
  1140                         acctSettings.IsPEPEnabled = entry.IsPEPEnabled;
  1141                         acctSettings.IsSecureStorageEnabled = entry.IsSecureStorageEnabled;
  1142                         acctSettings.IsSyncEnabled = entry.IsSyncEnabled;
  1143                     }
  1144                     else
  1145                     {
  1146                         Log.Warning("SetOptionsState: Existing account settings not found.");
  1147                     }
  1148                 }
  1149 
  1150                 this._Settings.IsAutoUpdateEnabled = state.IsAutoUpdateEnabled;
  1151                 this._Settings.IsEncryptAllAccountsEnabled = state.IsEncryptAllAccountsEnabled;
  1152                 this._Settings.IsKeyServerUsed = state.IsKeyServerUsed;
  1153                 this._Settings.IsNeverUnsecureOptionVisible = state.IsNeverUnsecureOptionVisible;
  1154                 this._Settings.IsPEPFolderVisible = state.IsPEPFolderVisible;
  1155                 this._Settings.IsSecurityLossWarningEnabled = state.IsSecurityLossWarningEnabled;
  1156                 this._Settings.IsSyncEnabledForAllAccounts = state.IsSyncEnabledForAllAccounts;
  1157                 this._Settings.IsUnencryptedSubjectEnabled = state.IsUnencryptedSubjectEnabled;
  1158                 this._Settings.IsVerboseLoggingEnabled = state.IsVerboseLoggingEnabled;
  1159                 this._Settings.TrustwordsCulture = ((state.TrustwordsCulture != null) ? new CultureInfo(state.TrustwordsCulture.LCID) : null);
  1160 
  1161                 // Save last state for next options opening
  1162                 this.lastOptionsState = state.Copy();
  1163 
  1164                 // Update registry (in case of unforseen app shutdown)
  1165                 this._Settings.SaveToRegistry();
  1166             }
  1167 
  1168             return;
  1169         }
  1170 
  1171         /// <summary>
  1172         /// Builds a new options form state using this add-ins current state.
  1173         /// </summary>
  1174         /// <returns>A new options form state.</returns>
  1175         internal FormControlOptions.State GetOptionsState()
  1176         {
  1177             bool isDefaultStore = false;
  1178             bool exists;
  1179             string fpr1;
  1180             string fpr2;
  1181             string[] blacklist;
  1182             StringPair[] keylist;
  1183             PEPIdentity newIdent;
  1184             PEPSettings.PEPAccountSettings newAcctSettings;
  1185             FormControlOptions.State state;
  1186             Outlook.NameSpace ns = this.Application.Session;
  1187             Outlook.Store defaultStore = null;
  1188 
  1189             // Check if pEp is the default store
  1190             try
  1191             {
  1192                 defaultStore = ns.DefaultStore;
  1193                 isDefaultStore = (defaultStore.StoreID == this._PEPStoreRootFolder.StoreID);
  1194             }
  1195             catch
  1196             {
  1197                 isDefaultStore = false;
  1198             }
  1199 
  1200             // Load from last state if possible
  1201             if (this.lastOptionsState != null)
  1202             {
  1203                 state = this.lastOptionsState.Copy();
  1204             }
  1205             else
  1206             {
  1207                 state = new FormControlOptions.State();
  1208             }
  1209 
  1210             // Get complete OpenPGP key list
  1211             try
  1212             {
  1213                 keylist = ThisAddIn.PEPEngine.OpenPGPListKeyinfo(null);
  1214             }
  1215             catch (COMException ex)
  1216             {
  1217                 keylist = new StringPair[0];
  1218                 Log.Error("GetOptionsState: Error getting OpenPGP keylist from engine. " + ex.ToString());
  1219             }
  1220 
  1221             // Get the blacklist
  1222             try
  1223             {
  1224                 blacklist = ThisAddIn.PEPEngine.BlacklistRetrieve();
  1225             }
  1226             catch (COMException ex)
  1227             {
  1228                 blacklist = new string[0];
  1229                 Log.Error("GetOptionsState: Error getting blacklist from engine. " + ex.ToString());
  1230             }
  1231 
  1232             // Add the OpenPGP keylist to the UI blacklist
  1233             state.Blacklist.Clear();
  1234             if (keylist != null)
  1235             {
  1236                 foreach (StringPair ident in keylist)
  1237                 {
  1238                     fpr1 = this.RemoveFprFormatting(ident.Name);
  1239                     exists = false;
  1240 
  1241                     if (string.IsNullOrEmpty(fpr1) == false)
  1242                     {
  1243                         // Check if it exists in the blacklist
  1244                         if (blacklist != null)
  1245                         {
  1246                             foreach (string fpr in blacklist)
  1247                             {
  1248                                 fpr2 = this.RemoveFprFormatting(fpr);
  1249 
  1250                                 if (string.Equals(fpr1, fpr2, StringComparison.OrdinalIgnoreCase))
  1251                                 {
  1252                                     exists = true;
  1253                                     break;
  1254                                 }
  1255                             }
  1256                         }
  1257 
  1258                         // Build entry to add
  1259                         newIdent = PEPIdentity.Parse(ident.Value);
  1260                         newIdent.Fingerprint = this.ToQuadruple(fpr1, false);
  1261 
  1262                         state.Blacklist.Add(new KVPair<PEPIdentity, bool>(newIdent, exists));
  1263                     }
  1264                 }
  1265             }
  1266 
  1267             // Add any fingerprints that exist only in the engine blacklist to the UI blacklist
  1268             if (blacklist != null)
  1269             {
  1270                 foreach (string fpr in blacklist)
  1271                 {
  1272                     fpr1 = this.RemoveFprFormatting(fpr);
  1273                     exists = false;
  1274 
  1275                     // Check if it already exists in the UI blacklist by fingerprint
  1276                     foreach (KVPair<PEPIdentity, bool> entry in state.Blacklist)
  1277                     {
  1278                         fpr2 = this.RemoveFprFormatting(entry.Key.Fingerprint);
  1279 
  1280                         if (string.Equals(fpr1, fpr2, StringComparison.OrdinalIgnoreCase))
  1281                         {
  1282                             exists = true;
  1283                             break;
  1284                         }
  1285                     }
  1286 
  1287                     if (exists == false)
  1288                     {
  1289                         newIdent = new PEPIdentity();
  1290                         newIdent.Fingerprint = this.ToQuadruple(fpr1, false);
  1291 
  1292                         // Always put at the beginning so it's easier for the user to find
  1293                         state.Blacklist.Insert(0, new KVPair<PEPIdentity, bool>(newIdent, true));
  1294                     }
  1295                 }
  1296             }
  1297 
  1298             state.BlacklistEnteredFingerprint = null;
  1299             state.BlacklistSelectedIndex = -1;
  1300 
  1301             // Build AccountSettingsList
  1302             state.AccountSettingsList.Clear();
  1303             for (int i = 0; i < this._Settings.AccountSettingsList.Count; i++)
  1304             {
  1305                 newAcctSettings = this._Settings.AccountSettingsList[i].Copy();
  1306 
  1307                 /* pEp internally uses account types as defined by microsoft.
  1308                  * This means they have 'ol' before the type (Example: 'olImap').
  1309                  * This isn't always user friendly so for the options UI the 'ol' is removed.
  1310                  */
  1311                 if ((string.IsNullOrEmpty(newAcctSettings.Type) == false) &&
  1312                     (newAcctSettings.Type.StartsWith("ol", StringComparison.OrdinalIgnoreCase)))
  1313                 {
  1314                     newAcctSettings.Type = newAcctSettings.Type.Substring(2);
  1315                 }
  1316 
  1317                 state.AccountSettingsList.Add(new FormControlOptions.AccountState(newAcctSettings));
  1318             }
  1319 
  1320             state.IsAutoUpdateEnabled = this._Settings.IsAutoUpdateEnabled;
  1321             state.IsDeveloperModeEnabled = this._Settings.IsDeveloperModeEnabled;
  1322             state.IsEncryptAllAccountsEnabled = this._Settings.IsEncryptAllAccountsEnabled;
  1323             state.IsKeyServerUsed = this._Settings.IsKeyServerUsed;
  1324             state.IsNeverUnsecureOptionVisible = this._Settings.IsNeverUnsecureOptionVisible;
  1325             state.IsPEPFolderDefaultStore = isDefaultStore;
  1326             state.IsPEPFolderVisible = (isDefaultStore ? true : this._Settings.IsPEPFolderVisible);
  1327             state.IsSecurityLossWarningEnabled = this._Settings.IsSecurityLossWarningEnabled;
  1328             state.IsSyncEnabledForAllAccounts = this._Settings.IsSyncEnabledForAllAccounts;
  1329             state.IsTNEFDisabled = this._Settings.IsTNEFDisabled;
  1330             state.IsUnencryptedSubjectEnabled = this._Settings.IsUnencryptedSubjectEnabled;
  1331             state.IsVerboseLoggingEnabled = this._Settings.IsVerboseLoggingEnabled;
  1332             state.PEPCopyright = Globals.PEP_COPYRIGHT;
  1333             state.PEPName = (Globals.RELEASE_MODE == Globals.ReleaseMode.Reader ? Globals.PEP_DISPLAY_NAME_READER : Globals.PEP_DISPLAY_NAME);
  1334             state.SystemInfo = Globals.GetSystemInfoList();
  1335             state.TrustwordsCulture = ((this._Settings.TrustwordsCulture != null) ? new CultureInfo(this._Settings.TrustwordsCulture.LCID) : null);
  1336 
  1337             // Release objects
  1338             if (ns != null)
  1339             {
  1340                 //Marshal.ReleaseComObject(ns);
  1341                 ns = null;
  1342             }
  1343 
  1344             if (defaultStore != null)
  1345             {
  1346                 //Marshal.ReleaseComObject(defaultStore);
  1347                 defaultStore = null;
  1348             }
  1349 
  1350             return (state);
  1351         }
  1352 
  1353         /// <summary>
  1354         /// Connects events for folder/item detection in Outlook.
  1355         /// Included folders are:
  1356         ///  • Default Sent folders for only ActiveSync accounts
  1357         ///  • Default Inbox folders for all accounts
  1358         /// </summary>
  1359         private void ConnectWatchedFolders()
  1360         {
  1361             Outlook.Store store = null;
  1362             Outlook.Stores stores = null;
  1363             Outlook.Folder folder = null;
  1364             Outlook.NameSpace ns = null;
  1365 
  1366             try
  1367             {
  1368                 ns = this.Application.Session;
  1369                 stores = ns.Stores;
  1370 
  1371                 for (int i = 1; i <= stores.Count; i++)
  1372                 {
  1373                     // Note: accessing the stores can fail if the data file is missing or is in use by another program. 
  1374                     try
  1375                     {
  1376                         store = stores[i];
  1377                     }
  1378                     catch (Exception ex)
  1379                     {
  1380                         Log.Warning("ConnectWatchedFolders: Failed to get store, " + ex.ToString());
  1381                     }
  1382 
  1383                     if (store != null)
  1384                     {
  1385                         // Add default inbox folder
  1386                         folder = null;
  1387                         try
  1388                         {
  1389                             folder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
  1390                         }
  1391                         catch (Exception ex)
  1392                         {
  1393                             if (folder != null)
  1394                             {
  1395                                 // Marshal.ReleaseComObject(folder);
  1396                                 folder = null;
  1397                             }
  1398 
  1399                             Log.Warning("ConnectWatchedFolders: Failure getting default inbox folder. " + ex.ToString());
  1400                         }
  1401 
  1402                         // Add the folder to the watched list (do not release it)
  1403                         if (folder != null)
  1404                         {
  1405                             this.watchedFolders.Add(new WatchedFolder(folder, Outlook.OlDefaultFolders.olFolderInbox));
  1406                         }
  1407 
  1408                         // Add Sent folder. This is needed so sent mails get decrypted on trusted servers to make the search function working
  1409                         try
  1410                         {
  1411                             folder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail);
  1412                         }
  1413                         catch (Exception ex)
  1414                         {
  1415                             if (folder != null)
  1416                             {
  1417                                 // Marshal.ReleaseComObject(folder);
  1418                                 folder = null;
  1419                             }
  1420 
  1421                             Log.Warning("ConnectWatchedFolders: Failure getting default sent folder. " + ex.ToString());
  1422                         }
  1423 
  1424                         // Add the folder to the watched list (do not release it)
  1425                         if (folder != null)
  1426                         {
  1427                             this.watchedFolders.Add(new WatchedFolder(folder, Outlook.OlDefaultFolders.olFolderSentMail));
  1428                         }
  1429 
  1430                         //Marshal.ReleaseComObject(store);
  1431                         store = null;
  1432                     }
  1433                 }
  1434             }
  1435             catch (Exception ex)
  1436             {
  1437                 Log.Error("ConnectWatchedFolders: Failure occured, " + ex.ToString());
  1438             }
  1439             finally
  1440             {
  1441                 // Release objects
  1442                 if (store != null)
  1443                 {
  1444                     //Marshal.ReleaseComObject(store);
  1445                     store = null;
  1446                 }
  1447 
  1448                 if (stores != null)
  1449                 {
  1450                     //Marshal.ReleaseComObject(stores);
  1451                     stores = null;
  1452                 }
  1453 
  1454                 if (ns != null)
  1455                 {
  1456                     //Marshal.ReleaseComObject(ns);
  1457                     ns = null;
  1458                 }
  1459             }
  1460 
  1461             return;
  1462         }
  1463 
  1464         /// <summary>
  1465         /// Opens the pEp data store root folder and saves the reference for further use.
  1466         /// If none already exists, a new one is created.
  1467         /// </summary>
  1468         private void OpenPEPStoreRootFolder()
  1469         {
  1470             string path;
  1471             Outlook.Store pEpStore = null;
  1472             Outlook.Store store = null;
  1473             Outlook.Stores stores = null;
  1474             Outlook.NameSpace ns = null;
  1475 
  1476             try
  1477             {
  1478                 ns = this.Application.Session;
  1479                 stores = ns.Stores;
  1480                 path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "pEp", (Globals.PEP_DATA_FILE_NAME + ".pst"));
  1481 
  1482                 // Try to find an existing store based on file name
  1483                 for (int i = 1; i <= stores.Count; i++)
  1484                 {
  1485                     /* Note: accessing the stores can fail if the data file is missing or is in use by another program. 
  1486                      * This is usually OK as long as it isn't the pEp file.
  1487                      * However, here the assumption is made that any error is not the pEp file and execution will continue.
  1488                      * If for some reason the error prevents detecting an existing pEp.pst file, this will be caught in the next step.
  1489                      */
  1490                     try
  1491                     {
  1492                         store = stores[i];
  1493                     }
  1494                     catch (Exception ex)
  1495                     {
  1496                         Log.Warning("OpenPEPStoreRootFolder: Failed to get store, " + ex.ToString());
  1497                     }
  1498 
  1499                     if (store?.FilePath == path)
  1500                     {
  1501                         pEpStore = store;
  1502                         break;
  1503                     }
  1504 
  1505                     store = null;
  1506                 }
  1507 
  1508                 // If no store was found, create new 
  1509                 if (pEpStore == null)
  1510                 {
  1511                     /* Will create the file and add it as a store, otherwise it will use the existing one.
  1512                      * This can throw an error if a user either modified the list of Outlook data stores
  1513                      * or the pEp.pst file itself.
  1514                      */
  1515                     try
  1516                     {
  1517                         ns.AddStoreEx(path, Outlook.OlStoreType.olStoreUnicode);
  1518                     }
  1519                     catch (Exception ex)
  1520                     {
  1521                         Log.Error("OpenPEPStoreRootFolder: Failes to add pEp store. " + ex.ToString());
  1522                     }
  1523 
  1524                     for (int i = 1; i <= stores.Count; i++)
  1525                     {
  1526                         /* Search again for the newly created store.
  1527                          * Note: As the creation itself might have failed, this is not guaranteed to work.
  1528                          * We then try again in the next step or throw an error if the store cannot be accessed.
  1529                          */
  1530                         try
  1531                         {
  1532                             store = stores[i];
  1533                         }
  1534                         catch (Exception ex)
  1535                         {
  1536                             Log.Warning("OpenPEPStoreRootFolder: Failed to get store, " + ex.ToString());
  1537                         }
  1538 
  1539                         if (store?.FilePath == path)
  1540                         {
  1541                             pEpStore = store;
  1542                             pEpStore.GetRootFolder().Name = Globals.PEP_DATA_FILE_NAME;
  1543                             break; // Break before releasing
  1544                         }
  1545                     }
  1546                 }
  1547 
  1548                 if (pEpStore != null)
  1549                 {
  1550                     this._PEPStoreRootFolder = (Outlook.Folder)pEpStore.GetRootFolder();
  1551                 }
  1552                 else
  1553                 {
  1554                     // Try again using fallback solution for file paths with prefixes
  1555                     Log.Warning("OpenPEPStoreRootFolder: No pEp store found. Trying fallback. \npEp path is " + path);
  1556 
  1557                     for (int i = 1; i <= stores.Count; i++)
  1558                     {
  1559                         try
  1560                         {
  1561                             store = stores[i];
  1562                             Log.Warning("OpenPEPStoreRootFolder: file path of store " + i + ": " + store?.FilePath ?? "<null>");
  1563                         }
  1564                         catch (Exception ex)
  1565                         {
  1566                             Log.Warning("OpenPEPStoreRootFolder: Failed to get store, " + ex.ToString());
  1567                         }
  1568 
  1569                         // Custom comparison of file paths independently of their (known) prefixes
  1570                         if (Comparisons.FilePathsAreEqual(store?.FilePath, path))
  1571                         {
  1572                             pEpStore = store;
  1573                             pEpStore.GetRootFolder().Name = Globals.PEP_DATA_FILE_NAME;
  1574                             break;
  1575                         }
  1576                     }
  1577 
  1578                     // If pEp store was found, use it. Else throw error.
  1579                     if (pEpStore != null)
  1580                     {
  1581                         this._PEPStoreRootFolder = (Outlook.Folder)pEpStore.GetRootFolder();
  1582                     }
  1583                     else
  1584                     {
  1585                         throw new Exception("Cannot open required pEp.pst file.");
  1586                     }
  1587                 }
  1588             }
  1589             catch (Exception ex)
  1590             {
  1591                 Globals.StopAndSendCrashReport(ex);
  1592             }
  1593             finally
  1594             {
  1595                 // Release objects
  1596                 if (store != null)
  1597                 {
  1598                     //Marshal.ReleaseComObject(store);
  1599                     store = null;
  1600                 }
  1601 
  1602                 if (stores != null)
  1603                 {
  1604                     //Marshal.ReleaseComObject(stores);
  1605                     stores = null;
  1606                 }
  1607 
  1608                 if (ns != null)
  1609                 {
  1610                     //Marshal.ReleaseComObject(ns);
  1611                     ns = null;
  1612                 }
  1613 
  1614                 if (pEpStore != null)
  1615                 {
  1616                     //Marshal.ReleaseComObject(pEpStore);
  1617                     pEpStore = null;
  1618                 }
  1619             }
  1620 
  1621             return;
  1622         }
  1623 
  1624         /// <summary>
  1625         /// Resets the pEp data store.
  1626         /// This will delete all files.
  1627         /// </summary>
  1628         internal void ResetPEPStore()
  1629         {
  1630             Outlook.Folder deletedFolder = null;
  1631             Outlook.Folders folders = null;
  1632             Outlook.Items items = null;
  1633             Outlook.Store store = null;
  1634             Outlook.Folder contacts = null;
  1635             Outlook.Folders contactsFolders = null;
  1636 
  1637             try
  1638             {
  1639                 if (this._PEPStoreRootFolder != null)
  1640                 {
  1641                     store = this._PEPStoreRootFolder.Store;
  1642 
  1643                     // Move all folders to deleted folder
  1644                     folders = this._PEPStoreRootFolder.Folders;
  1645                     for (int i = folders.Count; i > 0; i--)
  1646                     {
  1647                         // Default folders cannot be deleted and are skipped
  1648                         try
  1649                         {
  1650                             folders[i].Delete();
  1651                         }
  1652                         catch { }
  1653                     }
  1654 
  1655                     if (folders != null)
  1656                     {
  1657                         // Marshal.ReleaseComObject(folders);
  1658                         folders = null;
  1659                     }
  1660 
  1661                     // Move any contact folders/contacts to the deleted folder
  1662                     contacts = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts);
  1663                     contactsFolders = contacts.Folders;
  1664 
  1665                     for (int i = contactsFolders.Count; i > 0; i--)
  1666                     {
  1667                         try
  1668                         {
  1669                             contactsFolders[i].Delete();
  1670                         }
  1671                         catch { }
  1672                     }
  1673 
  1674                     // Delete items from deleted folder
  1675                     deletedFolder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDeletedItems);
  1676 
  1677                     // Permanently delete items
  1678                     items = deletedFolder.Items;
  1679                     for (int i = items.Count; i > 0; i--)
  1680                     {
  1681                         try
  1682                         {
  1683                             items[i].Delete();
  1684                         }
  1685                         catch { }
  1686                     }
  1687 
  1688                     if (items != null)
  1689                     {
  1690                         // Marshal.ReleaseComObject(items);
  1691                         items = null;
  1692                     }
  1693 
  1694                     // Permanently delete folders
  1695                     folders = deletedFolder.Folders;
  1696                     for (int i = folders.Count; i > 0; i--)
  1697                     {
  1698                         try
  1699                         {
  1700                             folders[i].Delete();
  1701                         }
  1702                         catch { }
  1703                     }
  1704 
  1705                     if (folders != null)
  1706                     {
  1707                         // Marshal.ReleaseComObject(folders);
  1708                         folders = null;
  1709                     }
  1710                 }
  1711             }
  1712             catch { }
  1713             finally
  1714             {
  1715                 // Release objects
  1716                 if (deletedFolder != null)
  1717                 {
  1718                     // Marshal.ReleaseComObject(deletedFolder);
  1719                     deletedFolder = null;
  1720                 }
  1721 
  1722                 if (folders != null)
  1723                 {
  1724                     // Marshal.ReleaseComObject(folders);
  1725                     folders = null;
  1726                 }
  1727 
  1728                 if (items != null)
  1729                 {
  1730                     // Marshal.ReleaseComObject(items);
  1731                     items = null;
  1732                 }
  1733 
  1734                 if (store != null)
  1735                 {
  1736                     //Marshal.ReleaseComObject(store);
  1737                     store = null;
  1738                 }
  1739 
  1740                 if (contacts != null)
  1741                 {
  1742                     // Marshal.ReleaseComObject(contacts);
  1743                     contacts = null;
  1744                 }
  1745 
  1746                 if (contactsFolders != null)
  1747                 {
  1748                     // Marshal.ReleaseComObject(contactsFolders);
  1749                     contactsFolders = null;
  1750                 }
  1751             }
  1752 
  1753             return;
  1754         }
  1755 
  1756         /// <summary>
  1757         /// Sets the visibility of the pEp data store in the navigation pane.
  1758         /// This works by carefully releasing and maintaining references outside of Outlook.
  1759         /// </summary>
  1760         /// <param name="visible">True if the pEp folder should be visible, otherwise false.</param>
  1761         private void SetPEPStoreRootFolderVisibility(bool visible)
  1762         {
  1763             Outlook.Folder folder = null;
  1764             Outlook.NameSpace ns = null;
  1765             Outlook.Store store = null;
  1766 
  1767             try
  1768             {
  1769                 ns = this.Application.Session;
  1770 
  1771                 if (this._PEPStoreRootFolder != null)
  1772                 {
  1773                     store = this._PEPStoreRootFolder.Store;
  1774 
  1775                     // Never show the default 'system' folders
  1776                     foreach (Outlook.OlDefaultFolders defaultFolder in Enum.GetValues(typeof(Outlook.OlDefaultFolders)))
  1777                     {
  1778                         // Allow deleted items
  1779                         if (defaultFolder != Outlook.OlDefaultFolders.olFolderDeletedItems)
  1780                         {
  1781                             try
  1782                             {
  1783                                 folder = (Outlook.Folder)store.GetDefaultFolder(defaultFolder);
  1784                                 MapiHelper.SetProperty(folder, MapiProperty.PidTagAttributeHidden, true);
  1785                             }
  1786                             catch { }
  1787                             finally
  1788                             {
  1789                                 if (folder != null)
  1790                                 {
  1791                                     // Marshal.ReleaseComObject(folder);
  1792                                     folder = null;
  1793                                 }
  1794                             }
  1795                         }
  1796                     }
  1797 
  1798                     // Attempt to add/remove the store from the session which sets visibility
  1799                     if (visible)
  1800                     {
  1801                         try
  1802                         {
  1803                             // Release the temp folder before attempting an open/re-open
  1804                             //Marshal.ReleaseComObject(this._PEPStoreRootFolder);
  1805                             this._PEPStoreRootFolder = null;
  1806 
  1807                             // After opening it will always be visible
  1808                             this.OpenPEPStoreRootFolder();
  1809                         }
  1810                         catch (Exception ex)
  1811                         {
  1812                             Log.Warning("SetPEPStoreRootFolderVisibility: Failed to make pEp root folder visible. " + ex.ToString());
  1813                         }
  1814                     }
  1815                     else
  1816                     {
  1817                         /* Remove the store from the session which will also hide it in the navigation pane.
  1818                          * Importantly, the pEp store's root folder reference is not released.
  1819                          * This allows the application to continue to use the store.
  1820                          * 
  1821                          * Also note that this call to remove the store will fail if the store is already removed.
  1822                          * This can happen when the visibility is set to false more than once.
  1823                          * Therefore, just try to remove it although don't log any errors.
  1824                          */
  1825                         try
  1826                         {
  1827                             ns.RemoveStore(this._PEPStoreRootFolder);
  1828                         }
  1829                         catch { }
  1830                     }
  1831                 }
  1832             }
  1833             catch (Exception ex)
  1834             {
  1835                 Log.Error("SetPEPStoreRootFolderVisibility: Failed, " + ex.ToString());
  1836             }
  1837             finally
  1838             {
  1839                 // Release objects
  1840                 if (folder != null)
  1841                 {
  1842                     // Marshal.ReleaseComObject(folder);
  1843                     folder = null;
  1844                 }
  1845 
  1846                 if (ns != null)
  1847                 {
  1848                     //Marshal.ReleaseComObject(ns);
  1849                     ns = null;
  1850                 }
  1851 
  1852                 if (store != null)
  1853                 {
  1854                     //Marshal.ReleaseComObject(store);
  1855                     store = null;
  1856                 }
  1857             }
  1858 
  1859             return;
  1860         }
  1861 
  1862         /// <summary>
  1863         /// Sets the rules to hide pEp internal category MailItems for the given account.
  1864         /// </summary>
  1865         /// <param name="account">The account to set the rules for.</param>
  1866         /// <returns>True, if the rules were correctly set for this account. Otherwise false.</returns>
  1867         internal bool SetRules(Outlook.Account account)
  1868         {
  1869             bool success = false;
  1870             bool categoriesSet = true;
  1871             bool ruleExists = false;
  1872             Outlook.AssignToCategoryRuleAction action = null;
  1873             Outlook.Rules rules = null;
  1874             Outlook.Rule rule = null;
  1875             Outlook.Store deliveryStore = null;
  1876             Outlook.TextRuleCondition condition = null;
  1877 
  1878             Log.Verbose("SetRules: Setting rules for single account");
  1879 
  1880             try
  1881             {
  1882                 // Getting delivery store can fail for new accounts when Outlook is not restarted
  1883                 try
  1884                 {
  1885                     deliveryStore = account.DeliveryStore;
  1886                 }
  1887                 catch
  1888                 {
  1889                     deliveryStore = null;
  1890                     Log.Warning("SetRules: Failure getting DeliveryStore");
  1891                 }
  1892 
  1893                 if (deliveryStore != null)
  1894                 {
  1895                     // Get the inbox
  1896                     try
  1897                     {
  1898                         rules = deliveryStore.GetRules();
  1899                     }
  1900                     catch
  1901                     {
  1902                         if (rules != null)
  1903                         {
  1904                             //Marshal.ReleaseComObject(rules);
  1905                             rules = null;
  1906                         }
  1907 
  1908                         Log.Warning("SetRules: Failure getting rules");
  1909                     }
  1910 
  1911                     // Adding rule to the rules collection
  1912                     if (rules != null)
  1913                     {
  1914                         try
  1915                         {
  1916                             // Check if rule already was created, create new otherwise
  1917                             try
  1918                             {
  1919                                 rule = rules[Globals.PEP_INTERNAL_CATEGORY_NAME];
  1920                                 ruleExists = true;
  1921                                 success = true;
  1922                             }
  1923                             catch
  1924                             {
  1925                                 rule = rules.Create(Globals.PEP_INTERNAL_CATEGORY_NAME, Outlook.OlRuleType.olRuleReceive);
  1926                             }
  1927                         }
  1928                         catch
  1929                         {
  1930                             if (rule != null)
  1931                             {
  1932                                 //Marshal.ReleaseComObject(rule);
  1933                                 rule = null;
  1934                             }
  1935 
  1936                             Log.Warning("SetRules: Failure creating rule");
  1937                         }
  1938 
  1939                         // Define rule if it didn't exist previously
  1940                         if ((rule != null) &&
  1941                             (ruleExists == false))
  1942                         {
  1943                             // Condition: message header includes defined string
  1944                             try
  1945                             {
  1946                                 condition = rule.Conditions?.MessageHeader;
  1947                                 condition.Text = new string[] { PEPMessage.PR_PEP_AUTO_CONSUME_NAME.ToLower() };
  1948                                 condition.Enabled = true;
  1949                             }
  1950                             catch
  1951                             {
  1952                                 if (condition != null)
  1953                                 {
  1954                                     // Marshal.ReleaseComObject(condition);
  1955                                     condition = null;
  1956                                 }
  1957 
  1958                                 Log.Warning("SetRules: Failure setting condition");
  1959                             }
  1960 
  1961                             if (condition != null)
  1962                             {
  1963                                 // Action: assign "pEp internal" category
  1964                                 try
  1965                                 {
  1966                                     if (this.CreatePEPInternalCategory() == false)
  1967                                     {
  1968                                         categoriesSet = false;
  1969                                     }
  1970 
  1971                                     action = rule.Actions.AssignToCategory;
  1972                                     action.Categories = new string[] { Globals.PEP_INTERNAL_CATEGORY_NAME };
  1973                                     action.Enabled = true;
  1974                                 }
  1975                                 catch
  1976                                 {
  1977                                     if (action != null)
  1978                                     {
  1979                                         // Marshal.ReleaseComObject(action);
  1980                                         action = null;
  1981                                     }
  1982 
  1983                                     Log.Warning("SetRules: Failure adding category");
  1984                                 }
  1985 
  1986                                 if ((condition != null) &&
  1987                                     (action != null))
  1988                                 {
  1989                                     rules.Save();
  1990                                     success = categoriesSet;
  1991                                 }
  1992                             }
  1993                         }
  1994                     }
  1995                 }
  1996             }
  1997             catch (Exception ex)
  1998             {
  1999                 Log.Error("SetRules: Failure occured, " + ex.ToString());
  2000             }
  2001             finally
  2002             {
  2003                 if (action != null)
  2004                 {
  2005                     // Marshal.ReleaseComObject(action);
  2006                     action = null;
  2007                 }
  2008 
  2009                 if (condition != null)
  2010                 {
  2011                     // Marshal.ReleaseComObject(condition);
  2012                     condition = null;
  2013                 }
  2014 
  2015                 if (deliveryStore != null)
  2016                 {
  2017                     //Marshal.ReleaseComObject(deliveryStore);
  2018                     deliveryStore = null;
  2019                 }
  2020 
  2021                 if (rule != null)
  2022                 {
  2023                     //Marshal.ReleaseComObject(rule);
  2024                     rule = null;
  2025                 }
  2026 
  2027                 if (rules != null)
  2028                 {
  2029                     //Marshal.ReleaseComObject(rules);
  2030                     rules = null;
  2031                 }
  2032             }
  2033 
  2034             return success;
  2035         }
  2036 
  2037         /// <summary>
  2038         /// Sets the rules for all accounts to hide pEp internal category MailItems.
  2039         /// </summary>
  2040         /// <returns>True, if the rules were correctly set. Otherwise false.</returns>
  2041         internal bool SetRules()
  2042         {
  2043             bool success = true;
  2044             Outlook.Account account = null;
  2045             Outlook.Accounts accounts = null;
  2046             Outlook.NameSpace ns = null;
  2047 
  2048             Log.Verbose("SetRules: Setting rules for all accounts");
  2049 
  2050             try
  2051             {
  2052                 ns = this.Application.Session;
  2053                 accounts = ns.Accounts;
  2054 
  2055                 for (int i = 1; i <= accounts.Count; i++)
  2056                 {
  2057                     account = accounts[i];
  2058 
  2059                     if (account != null)
  2060                     {
  2061                         if (this.SetRules(account) == false)
  2062                         {
  2063                             success = false;
  2064                         }
  2065 
  2066                         //Marshal.ReleaseComObject(account);
  2067                         account = null;
  2068                     }
  2069                 }
  2070             }
  2071             catch (Exception ex)
  2072             {
  2073                 success = false;
  2074                 Log.Error("SetRules: Failure occured, " + ex.ToString());
  2075             }
  2076             finally
  2077             {
  2078                 if (account != null)
  2079                 {
  2080                     //Marshal.ReleaseComObject(account);
  2081                     account = null;
  2082                 }
  2083 
  2084                 if (accounts != null)
  2085                 {
  2086                     //Marshal.ReleaseComObject(accounts);
  2087                     accounts = null;
  2088                 }
  2089 
  2090                 if (ns != null)
  2091                 {
  2092                     //Marshal.ReleaseComObject(ns);
  2093                     ns = null;
  2094                 }
  2095             }
  2096 
  2097             return success;
  2098         }
  2099 
  2100         /// <summary>
  2101         /// Sets the view filter settings of the inbox folder to hide pEp internal category MailItems for the given account.
  2102         /// </summary>
  2103         /// <param name="account">The account to set the inbox view filter for.</param>
  2104         /// <returns>True, if the filters were correctly set for this account. Otherwise false.</returns>
  2105         internal bool SetInboxViewFilters(Outlook.Account account)
  2106         {
  2107             bool success = false;
  2108             string currentViewName = null;
  2109             Outlook.Store deliveryStore = null;
  2110             Outlook.Folder folder = null;
  2111             Outlook.View view = null;
  2112             Outlook.Views views = null;
  2113 
  2114             Log.Verbose("SetInboxViewFilters: Setting inbox view filter for single account");
  2115 
  2116             try
  2117             {
  2118                 // Getting delivery store can fail for new accounts when Outlook is not restarted
  2119                 try
  2120                 {
  2121                     deliveryStore = account.DeliveryStore;
  2122                 }
  2123                 catch
  2124                 {
  2125                     deliveryStore = null;
  2126                     Log.Warning("SetInboxViewFilters: Failure getting DeliveryStore");
  2127                 }
  2128 
  2129                 if (deliveryStore != null)
  2130                 {
  2131                     // Get the inbox
  2132                     try
  2133                     {
  2134                         folder = (Outlook.Folder)deliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
  2135                     }
  2136                     catch
  2137                     {
  2138                         if (folder != null)
  2139                         {
  2140                             //Marshal.ReleaseComObject(folder);
  2141                             folder = null;
  2142                         }
  2143 
  2144                         Log.Warning("SetInboxViewFilters: Failure getting inbox folder");
  2145                     }
  2146 
  2147                     // Change the filter for all views in the inbox
  2148                     if (folder != null)
  2149                     {
  2150                         views = folder.Views;
  2151                         currentViewName = folder.CurrentView?.Name;
  2152 
  2153                         for (int j = 1; j <= views.Count; j++)
  2154                         {
  2155                             view = views[j];
  2156 
  2157                             try
  2158                             {
  2159                                 // Only set the filter on standard views, if the user creates their own don't modify it
  2160                                 if (view.Standard)
  2161                                 {
  2162                                     // Clear existing filter -- seems to work better
  2163                                     view.Filter = null;
  2164                                     view.Save();
  2165 
  2166                                     if ((currentViewName != null) &&
  2167                                         (string.Equals(view.Name, currentViewName, StringComparison.OrdinalIgnoreCase)))
  2168                                     {
  2169                                         view.Apply();
  2170                                     }
  2171 
  2172                                     // Set the filter
  2173                                     if (account.AccountType == Outlook.OlAccountType.olImap)
  2174                                     {
  2175                                         // (1) Hide any deleted IMAP items http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/85700003 = 0
  2176                                         // (2) Hide 'pEp internal' category items
  2177                                         view.Filter = "(\"" + MapiProperty.PidLidImapMarkedForDeletion.DaslName + "\" = 0" +
  2178                                                       " AND " +
  2179                                                       "\"urn:schemas-microsoft-com:office:office#Keywords\" <> '" + Globals.PEP_INTERNAL_CATEGORY_NAME + "')";
  2180                                     }
  2181                                     else
  2182                                     {
  2183                                         // Hide 'pEp internal' category items
  2184                                         view.Filter = "(\"urn:schemas-microsoft-com:office:office#Keywords\" <> '" + Globals.PEP_INTERNAL_CATEGORY_NAME + "')";
  2185                                     }
  2186 
  2187                                     if ((currentViewName != null) &&
  2188                                         (string.Equals(view.Name, currentViewName, StringComparison.OrdinalIgnoreCase)))
  2189                                     {
  2190                                         view.Save();
  2191                                         view.Apply();
  2192                                     }
  2193                                     else
  2194                                     {
  2195                                         view.Save();
  2196                                     }
  2197 
  2198                                 }
  2199 
  2200                                 success = true;
  2201                             }
  2202                             catch
  2203                             {
  2204                                 Log.Warning("SetInboxViewFilters: Failed to set view");
  2205                             }
  2206 
  2207                             if (view != null)
  2208                             {
  2209                                 //Marshal.ReleaseComObject(view);
  2210                                 view = null;
  2211                             }
  2212                         }
  2213                     }
  2214                 }
  2215             }
  2216             catch (Exception ex)
  2217             {
  2218                 Log.Error("SetInboxViewFilters: Failure occured, " + ex.ToString());
  2219             }
  2220             finally
  2221             {
  2222                 if (deliveryStore != null)
  2223                 {
  2224                     //Marshal.ReleaseComObject(deliveryStore);
  2225                     deliveryStore = null;
  2226                 }
  2227 
  2228                 if (folder != null)
  2229                 {
  2230                     //Marshal.ReleaseComObject(folder);
  2231                     folder = null;
  2232                 }
  2233 
  2234                 if (view != null)
  2235                 {
  2236                     // Marshal.ReleaseComObject(view);
  2237                     view = null;
  2238                 }
  2239 
  2240                 if (views != null)
  2241                 {
  2242                     // Marshal.ReleaseComObject(views);
  2243                     views = null;
  2244                 }
  2245             }
  2246 
  2247             return success;
  2248         }
  2249 
  2250         /// <summary>
  2251         /// Sets the view filter settings of all account's inbox folders to hide pEp internal category MailItems.
  2252         /// </summary>
  2253         /// <returns>True, if the filters were correctly set. Otherwise false.</returns>
  2254         internal bool SetInboxViewFilters()
  2255         {
  2256             bool success = true;
  2257             Outlook.Account account = null;
  2258             Outlook.Accounts accounts = null;
  2259             Outlook.NameSpace ns = null;
  2260 
  2261             Log.Verbose("SetInboxViewFilters: Setting inbox view filter for all accounts");
  2262 
  2263             try
  2264             {
  2265                 ns = this.Application.Session;
  2266                 accounts = ns.Accounts;
  2267 
  2268                 for (int i = 1; i <= accounts.Count; i++)
  2269                 {
  2270                     account = accounts[i];
  2271 
  2272                     if (account != null)
  2273                     {
  2274                         if (this.SetInboxViewFilters(account) == false)
  2275                         {
  2276                             success = false;
  2277                         }
  2278 
  2279                         //Marshal.ReleaseComObject(account);
  2280                         account = null;
  2281                     }
  2282                 }
  2283             }
  2284             catch (Exception ex)
  2285             {
  2286                 success = false;
  2287                 Log.Error("SetInboxViewFilters: Failure occured, " + ex.ToString());
  2288             }
  2289             finally
  2290             {
  2291                 if (account != null)
  2292                 {
  2293                     //Marshal.ReleaseComObject(account);
  2294                     account = null;
  2295                 }
  2296 
  2297                 if (accounts != null)
  2298                 {
  2299                     //Marshal.ReleaseComObject(accounts);
  2300                     accounts = null;
  2301                 }
  2302 
  2303                 if (ns != null)
  2304                 {
  2305                     //Marshal.ReleaseComObject(ns);
  2306                     ns = null;
  2307                 }
  2308             }
  2309 
  2310             return success;
  2311         }
  2312 
  2313         /// <summary>
  2314         /// Retrieves the pEp drafts folder and creates it if necessary.
  2315         /// </summary>
  2316         /// <returns>The pEp drafts folder or null if an error occurs.</returns>
  2317         internal Outlook.Folder GetPEPStoreDraftsFolder()
  2318         {
  2319             Outlook.Folder pEpDraftsFolder = null;
  2320             Outlook.Folders folders = null;
  2321 
  2322             try
  2323             {
  2324                 folders = PEPStoreRootFolder?.Folders;
  2325 
  2326                 try
  2327                 {
  2328                     pEpDraftsFolder = folders?[Globals.PEP_DRAFTS_FOLDER_NAME] as Outlook.Folder;
  2329                 }
  2330                 catch
  2331                 {
  2332                     pEpDraftsFolder = null;
  2333                 }
  2334 
  2335                 if (pEpDraftsFolder == null)
  2336                 {
  2337                     pEpDraftsFolder = folders?.Add(Globals.PEP_DRAFTS_FOLDER_NAME, Outlook.OlDefaultFolders.olFolderDrafts) as Outlook.Folder;
  2338                 }
  2339             }
  2340             catch (Exception ex)
  2341             {
  2342                 pEpDraftsFolder = null;
  2343                 Log.Error("Cannot create pEp drafts folder. " + ex.ToString());
  2344             }
  2345             finally
  2346             {
  2347                 folders = null;
  2348             }
  2349 
  2350             return pEpDraftsFolder;
  2351         }
  2352 
  2353         /// <summary>
  2354         /// Creates the pEp internal category for the specified account's store unless it already exists.
  2355         /// </summary>
  2356         internal bool CreatePEPInternalCategory(Outlook.Store deliveryStore)
  2357         {
  2358             bool exists = false;
  2359             bool success = false;
  2360             Outlook.Category category = null;
  2361             Outlook.Categories categories = null;
  2362 
  2363             if (deliveryStore != null)
  2364             {
  2365                 try
  2366                 {
  2367                     categories = deliveryStore.Categories;
  2368 
  2369                     // Check if the category already exists
  2370                     for (int i = 1; i <= categories?.Count; i++)
  2371                     {
  2372                         category = categories[i];
  2373 
  2374                         if (string.Equals(category?.Name, Globals.PEP_INTERNAL_CATEGORY_NAME, StringComparison.OrdinalIgnoreCase))
  2375                         {
  2376                             exists = true;
  2377                             success = true;
  2378                             break;
  2379                         }
  2380 
  2381                         category = null;
  2382                     }
  2383                 }
  2384                 catch (Exception ex)
  2385                 {
  2386                     Log.Error("CreatePEPInternalCategory: Error checking if category exists. " + ex.ToString());
  2387                     success = false;
  2388 
  2389                     // Just try to create it anyway, this will throw an exception if it already exists
  2390                     exists = false;
  2391                 }
  2392                 finally
  2393                 {
  2394                     category = null;
  2395                     categories = null;
  2396                 }
  2397 
  2398                 // If category was not found, create and add it to account master list
  2399                 if (exists == false)
  2400                 {
  2401                     try
  2402                     {
  2403                         // Add category
  2404                         categories = deliveryStore.Categories;
  2405                         categories?.Add(Globals.PEP_INTERNAL_CATEGORY_NAME, Outlook.OlCategoryColor.olCategoryColorDarkGreen);
  2406                         success = true;
  2407                     }
  2408                     catch (Exception ex)
  2409                     {
  2410                         Log.Error("CreatePEPInternalCategory: Error checking if category exists. " + ex.ToString());
  2411                         success = false;
  2412                     }
  2413                     finally
  2414                     {
  2415                         categories = null;
  2416                     }
  2417 
  2418                 }
  2419             }
  2420 
  2421             return success;
  2422         }
  2423 
  2424         /// <summary>
  2425         /// Creates the pEp internal category in Outlook for all accounts.
  2426         /// </summary>
  2427         internal bool CreatePEPInternalCategory()
  2428         {
  2429             bool success = true;
  2430             Outlook.Account account = null;
  2431             Outlook.Accounts accounts = null;
  2432             Outlook.NameSpace ns = null;
  2433             Outlook.Store deliveryStore = null;
  2434 
  2435             try
  2436             {
  2437                 ns = this.Application.Session;
  2438                 accounts = ns?.Accounts;
  2439 
  2440                 // Set the pEp internal category for all accounts
  2441                 for (int i = 1; i <= accounts?.Count; i++)
  2442                 {
  2443                     try
  2444                     {
  2445                         account = accounts[i];
  2446 
  2447                         // If the account doesn't have pEp enabled, don't create category
  2448                         if ((account?.GetIsPEPEnabled() ?? false) || (account?.GetIsDecryptAlwaysEnabled() ?? false))
  2449                         {
  2450                             deliveryStore = account?.DeliveryStore;
  2451                             if (deliveryStore != null)
  2452                             {
  2453                                 if (this.CreatePEPInternalCategory(deliveryStore) == false)
  2454                                 {
  2455                                     success = false;
  2456                                 }
  2457                             }
  2458                         }
  2459                     }
  2460                     catch (Exception ex)
  2461                     {
  2462                         Log.Error("SetPEPInternalCategory: Error setting category for account. " + ex.ToString());
  2463                         success = false;
  2464                     }
  2465                     finally
  2466                     {
  2467                         account = null;
  2468                         deliveryStore = null;
  2469                     }
  2470                 }
  2471             }
  2472             catch (Exception ex)
  2473             {
  2474                 Log.Error("SetPEPInternalCategory: Failure occured, " + ex.ToString());
  2475                 success = false;
  2476             }
  2477             finally
  2478             {
  2479                 account = null;
  2480                 accounts = null;
  2481                 ns = null;
  2482                 deliveryStore = null;
  2483             }
  2484 
  2485             return success;
  2486         }
  2487 
  2488         /// <summary>
  2489         /// Detects the current UI culture in Outlook and returns it as a CultureInfo object.
  2490         /// This will return English as the default language (never null).
  2491         /// </summary>
  2492         /// <returns>The active UI culture in Outlook.</returns>
  2493         internal CultureInfo GetActiveUICulture()
  2494         {
  2495             CultureInfo outlookCulture = null;
  2496             CultureInfo pEpCulture = null;
  2497             Office.LanguageSettings lang = null;
  2498             string languageName = null;
  2499             string defaultLanguageName = "en";
  2500 
  2501             // The current supported languages. Has to be synced with resources.
  2502             string[] pEpLanguages =
  2503             {
  2504                 "ca",
  2505                 "de",
  2506                 "en",
  2507                 "es",
  2508                 "fr",
  2509                 "hi",
  2510                 "it",
  2511                 "mr",
  2512                 "nl",
  2513                 "tr",
  2514                 "zh"
  2515              };
  2516 
  2517             try
  2518             {
  2519                 // Get Outlook culture
  2520                 lang = this.Application.LanguageSettings;
  2521                 outlookCulture = new CultureInfo(lang.LanguageID[Office.MsoAppLanguageID.msoLanguageIDUI]);
  2522 
  2523                 /* Get the two letter ISO language name.
  2524                  * This is needed because we have not yet country-specific localization.
  2525                  */
  2526                 languageName = outlookCulture.TwoLetterISOLanguageName;
  2527 
  2528                 // If language name matches a pEp supported language, use it
  2529                 foreach (var language in pEpLanguages)
  2530                 {
  2531                     if (languageName.Equals(language))
  2532                     {
  2533                         pEpCulture = new CultureInfo(language);
  2534                         break;
  2535                     }
  2536                 }
  2537 
  2538                 // Else, use English as default/fallback
  2539                 if (pEpCulture == null)
  2540                 {
  2541                     pEpCulture = new CultureInfo(defaultLanguageName);
  2542                 }
  2543             }
  2544             catch (Exception ex)
  2545             {
  2546                 pEpCulture = new CultureInfo(defaultLanguageName);
  2547                 Log.Verbose("GetActiveUICulture: Failed to get UI culture, " + ex.ToString());
  2548             }
  2549             finally
  2550             {
  2551                 if (lang != null)
  2552                 {
  2553                     //Marshal.ReleaseComObject(lang);
  2554                     lang = null;
  2555                 }
  2556             }
  2557 
  2558             return (pEpCulture);
  2559         }
  2560 
  2561         /// <summary>
  2562         /// Formats the given text string as separated 4-character groups.
  2563         /// The intention is for use when showing fingerprints.
  2564         /// Optionally, five 4-character groups are put on each line.
  2565         /// Example: 49422235FC99585B891C --> 4942 2235 FC99 585B 891C
  2566         /// </summary>
  2567         /// <param name="text">The text to format in 4-character groups.</param>
  2568         /// <param name="insertNewLine">Whether to insert a newline after each group.</param>
  2569         /// <returns>The re-formatted string.</returns>
  2570         internal string ToQuadruple(string text, bool insertNewLine)
  2571         {
  2572             List<string> groups = new List<string>();
  2573             string result = "";
  2574 
  2575             // Separate in sections of 4 characters
  2576             if (text != null)
  2577             {
  2578                 for (int i = 0; i < text.Length; i += 4)
  2579                 {
  2580                     try
  2581                     {
  2582                         groups.Add(text.Substring(i, 4));
  2583                     }
  2584                     catch (ArgumentOutOfRangeException)
  2585                     {
  2586                         groups.Add(text.Substring(i));
  2587                         break;
  2588                     }
  2589                 }
  2590             }
  2591 
  2592             // Separate in lines of 5 groups
  2593             if (insertNewLine)
  2594             {
  2595                 for (int i = 0; i < groups.Count; i += 5)
  2596                 {
  2597                     if (string.IsNullOrEmpty(result))
  2598                     {
  2599                         try
  2600                         {
  2601                             result = String.Join(" ", groups.GetRange(i, 5));
  2602                         }
  2603                         catch
  2604                         {
  2605                             result = String.Join(" ", groups.GetRange(i, (groups.Count - i)));
  2606                         }
  2607                     }
  2608                     else
  2609                     {
  2610                         try
  2611                         {
  2612                             result += Environment.NewLine + String.Join(" ", groups.GetRange(i, 5));
  2613                         }
  2614                         catch
  2615                         {
  2616                             result += Environment.NewLine + String.Join(" ", groups.GetRange(i, (groups.Count - i)));
  2617                         }
  2618                     }
  2619                 }
  2620             }
  2621             else
  2622             {
  2623                 result = string.Join(" ", groups);
  2624             }
  2625 
  2626             return result;
  2627         }
  2628 
  2629         /// <summary>
  2630         /// Removes any formatting from the given fingerprint string.
  2631         /// Example: 0x3fa1 32ca -> 3FA132CA
  2632         /// Warning: Null can be returned if the given fingerprint is null (default return value is the input).
  2633         /// </summary>
  2634         /// <param name="fingerprint">The fingerprint string to remove formatting from.</param>
  2635         /// <returns>A fingerprint string without formatting, otherwise the original input.</returns>
  2636         internal string RemoveFprFormatting(string fingerprint)
  2637         {
  2638             string result = fingerprint;
  2639 
  2640             if (string.IsNullOrEmpty(fingerprint) == false)
  2641             {
  2642                 result = fingerprint.Trim();
  2643                 result = result.ToUpperInvariant();
  2644                 result = result.Replace(" ", "");
  2645                 result = result.Replace("\r\n", "");
  2646 
  2647                 if (result.StartsWith("0X"))
  2648                 {
  2649                     result = result.Substring(2, (result.Length - 2));
  2650                 }
  2651             }
  2652 
  2653             return (result);
  2654         }
  2655 
  2656         /**************************************************************
  2657          * 
  2658          * Event Handling
  2659          * 
  2660          *************************************************************/
  2661 
  2662         /// <summary>
  2663         /// Event handler for when the add-in is starting up.
  2664         /// This is the first user code to execute in the add-in.
  2665         /// See: https://msdn.microsoft.com/en-us/library/7xy91eax.aspx
  2666         /// </summary>
  2667         private void ThisAddIn_Startup(object sender, EventArgs e)
  2668         {
  2669             CultureInfo culture;
  2670 
  2671             // Disable GPGOL
  2672             Log.Info("ThisAddIn_Startup: Disable GPGOL");
  2673             this.DisableGpgOL();
  2674 
  2675             // Init global error handling
  2676             Log.Info("ThisAddIn_Startup: Connect Events");
  2677             Globals.ConnectEvents(true);
  2678 
  2679             /* Open the pEp store
  2680              * Warning: This must be done BEFORE settings because settings will determine its visibility
  2681              */
  2682             Log.Info("ThisAddIn_Startup: Open pEp store root folder");
  2683             this.OpenPEPStoreRootFolder();
  2684 
  2685             // Connect custom ThisAddIn events (must be before DetectChangesFromLastSettings())
  2686             Log.Info("ThisAddIn_Startup: Connect ThisAddIn events");
  2687             this.FirstStartup += ThisAddIn_FirstStartup;
  2688             this.NewAccount += ThisAddIn_NewAccount;
  2689 
  2690             /* Load settings from Registry and update accounts list. This must be done before RegisterMyself()
  2691              * to be able to set the not for sync flag if necessary.
  2692              */
  2693             Log.Info("ThisAddIn_Startup: Loading settings");
  2694             this._Settings = new PEPSettings();
  2695             this._Settings.LoadFromRegistry();
  2696             this.SyncAccountsList();
  2697 
  2698 #if !READER_RELEASE_MODE
  2699             // Connect callbacks, use PEPEngine property accessor in case the engine is not already initialized
  2700             Log.Info("ThisAddIn_Startup: Register callbacks");
  2701             this.adapterCallbacks = new AdapterCallbacks();
  2702             ThisAddIn.PEPEngine.RegisterCallbacks(this.adapterCallbacks);
  2703 #endif
  2704 
  2705             /* Register all accounts as own identities in the engine
  2706              * Warning: This must be done BEFORE synchronizing settings because SyncWithSettings() will use 
  2707              * the engine's OwnIdentitiesRetrieve list to synchronize own identity flags such as IsSyncEnabled.
  2708              * The OwnIdentitiesRetrieve list will depend on what identities have been passed to Myself().
  2709              */
  2710             Log.Info("ThisAddIn_Startup: Register myself");
  2711             this.RegisterMyself();
  2712 
  2713             // Process and sync settings (Warning: sequence is very important here)
  2714             Log.Info("ThisAddIn_Startup: Sync settings");
  2715             this.DetectChangesFromLastSettings(this._Settings);
  2716             this._Settings.SaveUpdaterConfigToRegistry();
  2717             this.SyncWithSettings();
  2718             this._Settings.PropertyChanged += Settings_PropertyChanged;
  2719 
  2720             // Set the UI culture for resource translation
  2721             Log.Info("ThisAddIn_Startup: Set UI language");
  2722             culture = this.GetActiveUICulture();
  2723             Thread.CurrentThread.CurrentCulture = new CultureInfo(culture.LCID);
  2724             Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture.LCID);
  2725             Log.Info("ThisAddIn_Startup: " + culture.TwoLetterISOLanguageName + " language detected.");
  2726 
  2727             try
  2728             {
  2729                 /* Note: This is sensitive to time as pEp needs to start as fast as possible.
  2730                  * If there is too much delay, Outlook will automatically disable the add-in.
  2731                  * See: https://msdn.microsoft.com/en-us/library/office/jj228679.aspx#Anchor_7
  2732                  * 
  2733                  * If performance is proven to be an issue here, move to another location.
  2734                  */
  2735                 if (this.initialized == false)
  2736                 {
  2737                     // Get the Outlook options and set pEp required Outlook options
  2738                     Log.Info("ThisAddIn_Startup: Get Outlook options and set pEp required values.");
  2739                     this._OutlookOptions = new OutlookOptions();
  2740                     this._OutlookOptions.ReadOptionsFromRegistry();
  2741                     this._OutlookOptions.SetRegistryValues();
  2742 
  2743                     // Connect watched folders
  2744                     Log.Info("ThisAddIn_Startup: Connect Watched folders");
  2745                     this.ConnectWatchedFolders();
  2746 
  2747                     // Connect application events
  2748                     Log.Info("ThisAddIn_Startup: Connect Application Events");
  2749                     this.ConnectApplicationEvents(true);
  2750 
  2751                     // Initialize inbox cleaning timer
  2752                     if (this._Settings.IsInboxCleaningEnabled)
  2753                     {
  2754                         this.EnableInboxCleaning();
  2755                     }
  2756 
  2757                     // Initialization complete
  2758                     Log.Info("ThisAddIn_Startup: Main program started.");
  2759                     this.initialized = true;
  2760                 }
  2761             }
  2762             catch (Exception ex)
  2763             {
  2764                 string summaryMessage = Properties.Resources.Message_InitError + " " +
  2765                                         Properties.Resources.Message_Reinstall;
  2766                 Globals.StopAndSendCrashReport(ex, summaryMessage, false);
  2767             }
  2768 
  2769             return;
  2770         }
  2771 
  2772         /// <summary>
  2773         /// Subscribes to application events or unsubscribes from them.
  2774         /// </summary>
  2775         /// <param name="subscribe">Whether to subscribe or unsubscribe</param>
  2776         private void ConnectApplicationEvents(bool subscribe)
  2777         {
  2778             if (this.Application != null)
  2779             {
  2780                 try
  2781                 {
  2782                     if (subscribe)
  2783                     {
  2784                         ((Outlook.ApplicationEvents_11_Event)this.Application).Startup += Application_Startup;
  2785                         ((Outlook.ApplicationEvents_11_Event)this.Application).Quit += Application_Quit;
  2786                         ((Outlook.ApplicationEvents_11_Event)this.Application).NewMailEx += Application_NewMailEx;
  2787                         ((Outlook.ApplicationEvents_11_Event)this.Application).ItemSend += Application_ItemSend;
  2788                     }
  2789                     else
  2790                     {
  2791                         ((Outlook.ApplicationEvents_11_Event)this.Application).Startup -= Application_Startup;
  2792                         ((Outlook.ApplicationEvents_11_Event)this.Application).Quit -= Application_Quit;
  2793                         ((Outlook.ApplicationEvents_11_Event)this.Application).NewMailEx -= Application_NewMailEx;
  2794                         ((Outlook.ApplicationEvents_11_Event)this.Application).ItemSend -= Application_ItemSend;
  2795                     }
  2796                 }
  2797                 catch (Exception ex)
  2798                 {
  2799                     Log.Error("ConnectApplicationEvents: Error occured. " + ex.ToString());
  2800                 }
  2801             }
  2802         }
  2803 
  2804         /// <summary>
  2805         /// Enables the automatic deletion of old keysync messages if not already enabled.
  2806         /// This gets automatically disabled again if no keysync messages are found in the inbox.
  2807         /// </summary>
  2808         internal void EnableInboxCleaning()
  2809         {
  2810             if (inboxCleaner == null)
  2811             {
  2812                 inboxCleaner = new System.Windows.Forms.Timer();
  2813                 inboxCleaner.Interval = 1800000; // 30 minutes in ms
  2814                 inboxCleaner.Tick += InboxCleaner_Tick;
  2815                 inboxCleaner.Start();
  2816                 Log.Verbose("Automatic inbox cleaning enabled");
  2817 
  2818                 this._Settings.IsInboxCleaningEnabled = true;
  2819             }
  2820         }
  2821 
  2822         /// <summary>
  2823         /// Disables the automatic deletion of old keysync messages
  2824         /// </summary>
  2825         internal void DisableInboxCleaning()
  2826         {
  2827             if (inboxCleaner != null)
  2828             {
  2829                 inboxCleaner.Stop();
  2830                 inboxCleaner.Dispose();
  2831                 inboxCleaner = null;
  2832                 Log.Verbose("Automatic inbox cleaning disabled");
  2833 
  2834                 this._Settings.IsInboxCleaningEnabled = false;
  2835             }
  2836         }
  2837 
  2838         /// <summary>
  2839         /// Event handler for when the inbox cleaner timer has elapsed.
  2840         /// This searches in all inboxes for old keysync messages and deletes them.
  2841         /// </summary>
  2842         private void InboxCleaner_Tick(object sender, EventArgs e)
  2843         {
  2844             int keySyncMessagesCounter = 0;
  2845             const string searchFilter = "[Categories] ='" + Globals.PEP_INTERNAL_CATEGORY_NAME + "'";
  2846             Outlook.Folder folder = null;
  2847             Outlook.Items inboxItems = null;
  2848             Outlook.Items filteredInboxItems = null;
  2849             Outlook.MailItem mailItem = null;
  2850             Outlook.NameSpace ns = null;
  2851             Outlook.Store store = null;
  2852             Outlook.Stores stores = null;
  2853 
  2854 // Removing time logging for release versions
  2855 #if DEBUG
  2856             var sw = new Stopwatch();
  2857 #endif
  2858 
  2859             try
  2860             {
  2861 #if DEBUG
  2862                 sw.Restart();
  2863 #endif
  2864                 ns = this.Application.Session;
  2865                 stores = ns?.Stores;
  2866 
  2867                 for (int i = 1; i <= stores?.Count; i++)
  2868                 {
  2869                     // Note: accessing the stores can fail if the data file is missing or is in use by another program. 
  2870                     try
  2871                     {
  2872                         store = stores[i];
  2873                     }
  2874                     catch (Exception ex)
  2875                     {
  2876                         Log.Warning("InboxCleaner_Tick: Failed to get store, " + ex.ToString());
  2877                     }
  2878 
  2879                     if (store != null)
  2880                     {
  2881                         // Get default inbox folder
  2882                         folder = null;
  2883                         try
  2884                         {
  2885                             folder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
  2886                         }
  2887                         catch (Exception ex)
  2888                         {
  2889                             if (folder != null)
  2890                             {
  2891                                 // Marshal.ReleaseComObject(folder);
  2892                                 folder = null;
  2893                             }
  2894 
  2895                             Log.Warning("InboxCleaner_Tick: Failure getting default inbox folder. " + ex.Message);
  2896                         }
  2897 
  2898                         // Process all internal mail items again to check if consumed flag is set
  2899                         if (folder != null)
  2900                         {
  2901                             try
  2902                             {
  2903                                 inboxItems = folder.Items as Outlook.Items;
  2904 
  2905                                 if ((inboxItems != null) &&
  2906                                     (inboxItems.Count > 0))
  2907                                 {
  2908                                     // Get all mails with the "pEp Internal" category
  2909                                     filteredInboxItems = inboxItems.Restrict(searchFilter) as Outlook.Items;
  2910                                     Log.Verbose("InboxCleaner_Tick: " + inboxItems.Count + " items were found in the inbox.");
  2911 
  2912                                     if (filteredInboxItems != null)
  2913                                     {
  2914                                         Log.Verbose("InboxCleaner_Tick: " + filteredInboxItems.Count + " 'pEp Internal' items were found in the inbox.");
  2915 
  2916                                         keySyncMessagesCounter += filteredInboxItems.Count;
  2917 
  2918                                         for (int j = 1; j <= filteredInboxItems.Count; j++)
  2919                                         {
  2920                                             mailItem = filteredInboxItems[j] as Outlook.MailItem;
  2921 
  2922                                             if (mailItem != null)
  2923                                             {
  2924                                                 try
  2925                                                 {
  2926                                                     Log.Verbose("InboxCleaner_Tick: Processing 'pEp Internal' item " + j + "...");
  2927 
  2928                                                     // Check if mail item has timed out and delete it if necessary
  2929                                                     if (DateTime.UtcNow - (mailItem.ReceivedTime.ToUniversalTime()) > TimeSpan.FromSeconds(Globals.TIMEOUT_SYNC_MESSAGE))
  2930                                                     {
  2931                                                         mailItem.PermanentlyDelete();
  2932                                                     }
  2933                                                 }
  2934                                                 catch (Exception ex)
  2935                                                 {
  2936                                                     Log.Error("InboxCleaner_Tick: Error processing mail item. " + ex.Message);
  2937                                                 }
  2938                                             }
  2939 
  2940                                             mailItem = null;
  2941                                         }
  2942                                     }
  2943                                 }
  2944                                 else
  2945                                 {
  2946                                     Log.Verbose("InboxCleaner_Tick: No items were found in the inbox or inbox was null.");
  2947                                 }
  2948                             }
  2949                             catch (Exception ex)
  2950                             {
  2951                                 Log.Warning("InboxCleaner_Tick: Failure occured. " + ex.Message);
  2952                             }
  2953                         }
  2954 
  2955                         store = null;
  2956                         inboxItems = null;
  2957                         filteredInboxItems = null;
  2958                         mailItem = null;
  2959                     }
  2960                 }
  2961             }
  2962             catch (Exception ex)
  2963             {
  2964                 Log.Error("InboxCleaner_Tick: Failure occured, " + ex.ToString());
  2965             }
  2966             finally
  2967             {
  2968                 folder = null;
  2969                 inboxItems = null;
  2970                 filteredInboxItems = null;
  2971                 mailItem = null;
  2972                 ns = null;
  2973                 store = null;
  2974                 stores = null;
  2975             }
  2976 
  2977             // If no keys sync messages were found, disable the automatic cleaning.
  2978             if (keySyncMessagesCounter == 0)
  2979             {
  2980                 Log.Verbose("InboxCleaner_Tick: No more 'pEp Internal' messages were found in the inbox. Disabling cleaning...");
  2981                 Globals.ThisAddIn.DisableInboxCleaning();
  2982             }
  2983 #if DEBUG
  2984             sw.Stop();
  2985             Log.Verbose(string.Format("InboxCleaner_Tick: Cleaning inbox took {0} ms.", sw.ElapsedMilliseconds));
  2986 #endif
  2987         }
  2988 
  2989         /// <summary>
  2990         /// Event handler for when the add-in is shutdown.
  2991         /// Importantly: In Outlook, The Shutdown event is raised only when the user 
  2992         /// disables the VSTO Add-in by using the COM Add-ins dialog box in Outlook. 
  2993         /// It is not raised when Outlook exits. This is why Application_Quit is used instead.
  2994         /// See: https://msdn.microsoft.com/en-us/library/7xy91eax.aspx
  2995         /// </summary>
  2996         private void ThisAddIn_Shutdown(object sender, EventArgs e)
  2997         {
  2998             return;
  2999         }
  3000 
  3001         /// <summary>
  3002         /// Event handler for when first startup of the add-in is detected.
  3003         /// This is a custom event.
  3004         /// </summary>
  3005         private void ThisAddIn_FirstStartup(object sender, EventArgs e)
  3006         {
  3007             bool rulesSet = false;
  3008             bool filtersSet = false;
  3009 
  3010             Log.Info("ThisAddIn_FirstStartup: First startup detected");
  3011 
  3012             System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
  3013             timer.Interval = 10000; // Interval to ensure Outlook loads
  3014             timer.Tick += (s, ev) =>
  3015             {
  3016                 rulesSet = this.SetRules();
  3017                 filtersSet = this.SetInboxViewFilters();
  3018 
  3019                 // If a failure occured, do not mark first startup as completed (run again next time)
  3020                 this.Settings.IsFirstStartupComplete = (rulesSet && filtersSet);
  3021 
  3022                 timer.Stop();
  3023                 timer.Dispose();
  3024             };
  3025             timer.Start();
  3026 
  3027             return;
  3028         }
  3029 
  3030         /// <summary>
  3031         /// Event handler for when a new account is detected at startup.
  3032         /// This is a custom event.
  3033         /// </summary>
  3034         private void ThisAddIn_NewAccount(object sender, NewAccountEventArgs e)
  3035         {
  3036             bool rulesSet = false;
  3037             bool filtersSet = false;
  3038             Outlook.Account account = null;
  3039             Outlook.Accounts accounts = null;
  3040             Outlook.NameSpace ns = null;
  3041 
  3042             Log.Info("ThisAddIn_NewAccount: New account detected");
  3043 
  3044             // Locate the account
  3045             try
  3046             {
  3047                 ns = this.Application.Session;
  3048                 accounts = ns.Accounts;
  3049 
  3050                 for (int i = 1; i <= accounts.Count; i++)
  3051                 {
  3052                     account = accounts[i];
  3053 
  3054                     if (account != null)
  3055                     {
  3056                         if (object.Equals(account.AccountType, e.AccountType) &&
  3057                             object.Equals(account.SmtpAddress, e.SmtpAddress))
  3058                         {
  3059                             // Set view filter and rules for new account
  3060                             System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
  3061                             timer.Interval = 10000; // Interval to ensure Outlook loads
  3062                             timer.Tick += (s, ev) =>
  3063                             {
  3064                                 rulesSet = this.SetRules();
  3065                                 filtersSet = this.SetInboxViewFilters();
  3066 
  3067                                 // If a failure occured, set IsFirstStartupComplete to false to try again upon next startup
  3068                                 this.Settings.IsFirstStartupComplete = (rulesSet && filtersSet);
  3069 
  3070                                 timer.Stop();
  3071                                 timer.Dispose();
  3072                             };
  3073                             timer.Start();
  3074 
  3075                             break;
  3076                         }
  3077 
  3078                         //Marshal.ReleaseComObject(account);
  3079                         account = null;
  3080                     }
  3081                 }
  3082             }
  3083             catch { }
  3084             finally
  3085             {
  3086                 if (account != null)
  3087                 {
  3088                     //Marshal.ReleaseComObject(account);
  3089                     account = null;
  3090                 }
  3091 
  3092                 if (accounts != null)
  3093                 {
  3094                     //Marshal.ReleaseComObject(accounts);
  3095                     accounts = null;
  3096                 }
  3097 
  3098                 if (ns != null)
  3099                 {
  3100                     //Marshal.ReleaseComObject(ns);
  3101                     ns = null;
  3102                 }
  3103             }
  3104 
  3105             return;
  3106         }
  3107 
  3108         /// <summary>
  3109         /// Event handler for when the application is starting up.
  3110         /// This occurs after all add-in programs have been loaded.
  3111         /// See: https://msdn.microsoft.com/en-us/library/ff869298.aspx
  3112         /// </summary>
  3113         private void Application_Startup()
  3114         {
  3115             /* Open the Reader splash screen if the user has not disabled it.
  3116              * The splash is opened with a Timer in order to keep it from slowing initialization on this thread.
  3117              */
  3118             if ((Globals.RELEASE_MODE == Globals.ReleaseMode.Reader) &&
  3119                 (this._Settings.IsReaderSplashEnabled == true))
  3120             {
  3121                 System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
  3122                 timer.Interval = 6000; // Interval to ensure Outlook loads
  3123                 timer.Tick += (s, ev) =>
  3124                 {
  3125                     var splashScreen = new FormReaderSplash();
  3126                     splashScreen.Show();
  3127                     timer.Stop();
  3128                     timer.Dispose();
  3129                 };
  3130                 timer.Start();
  3131             }
  3132 
  3133             return;
  3134         }
  3135 
  3136         /// <summary>
  3137         /// Event handler for when the application is quit.
  3138         /// This is the last user code to execute in the add-in.
  3139         /// See: https://msdn.microsoft.com/en-us/library/ff869760.aspx
  3140         /// </summary>
  3141         private void Application_Quit()
  3142         {
  3143             // Disconnect global events
  3144             Globals.ConnectEvents(false);
  3145 
  3146             // Disconnect application events
  3147             this.ConnectApplicationEvents(false);
  3148 
  3149             // Save configuration
  3150             this._Settings.SaveToRegistry();
  3151             this._Settings.PropertyChanged -= Settings_PropertyChanged;
  3152 
  3153             // Disconnect custom ThisAddIn events
  3154             this.FirstStartup -= ThisAddIn_FirstStartup;
  3155             this.NewAccount -= ThisAddIn_NewAccount;
  3156 
  3157             // Log loaded items
  3158             // Log.Verbose("Application_Quit: Item loaded count=" + this.watchedMailItems.Count.ToString());
  3159 
  3160             if (ThisAddIn._PEPEngine != null)
  3161             {
  3162 #if !READER_RELEASE_MODE
  3163                 // Disconnect callbacks, DO NOT use the PEPEngine property accessor (only use existing instance)
  3164                 ThisAddIn._PEPEngine.UnregisterCallbacks();
  3165                 this.adapterCallbacks = null;
  3166 #endif
  3167                 // Release the pEp engine
  3168                 Marshal.FinalReleaseComObject(ThisAddIn._PEPEngine);
  3169                 ThisAddIn._PEPEngine = null;
  3170             }
  3171 
  3172             // Stop inbox cleaning timer
  3173             if (inboxCleaner != null)
  3174             {
  3175                 inboxCleaner.Stop();
  3176                 inboxCleaner.Dispose();
  3177             }
  3178 
  3179             Log.Archive();
  3180 
  3181             return;
  3182         }
  3183 
  3184         /// <summary>
  3185         /// Event handler for when one or more new mail items are received.
  3186         /// See: https://msdn.microsoft.com/en-us/library/office/ff869202.aspx
  3187         /// </summary>
  3188         /// <param name="entryIdCollection">A collection of EntryIDs (separated by ',')
  3189         /// for each new mail item.</param>
  3190         private void Application_NewMailEx(string entryIdCollection)
  3191         {
  3192             string[] idList;
  3193             Outlook.NameSpace ns = this.Application.Session;
  3194 
  3195             Log.Verbose("Application_NewMailEx: Started processing new items.");
  3196 
  3197             if (string.IsNullOrEmpty(entryIdCollection) == false)
  3198             {
  3199                 idList = entryIdCollection.Split(',');
  3200                 foreach (string eid in idList)
  3201                 {
  3202                     try
  3203                     {
  3204                         // Try to push to decryption stack
  3205                         ThisAddIn.DecryptionStack.TryPush(eid);
  3206                     }
  3207                     catch (Exception ex)
  3208                     {
  3209                         Log.Error("Application_NewMailEx: Error processing item entryId. " + ex.ToString());
  3210                     }
  3211                 }
  3212             }
  3213 
  3214             if (ns != null)
  3215             {
  3216                 //Marshal.ReleaseComObject(ns);
  3217                 ns = null;
  3218             }
  3219 
  3220             Log.Verbose("Application_NewMailEx: Complete (decryption likely ongoing in background threads).");
  3221 
  3222             return;
  3223         }
  3224 
  3225         // NOTE 08/30/2017: Removed this handler again. Tests showed that this event is occasionally called very often
  3226         // and is not suitable to replace the Items_ItemAdd event. Removed due to possible performance overhead during
  3227         // connection of new account with lots of mails.
  3228         /// <summary>
  3229         /// This event handler was added to test when the event is called and if it's more reliable than NewMailEx.
  3230         /// </summary>
  3231         /// <param name="item"></param>
  3232         //private void Application_ItemLoad(object item)
  3233         //{
  3234         //    Log.Verbose("Application_ItemLoad called.");
  3235         //}
  3236 
  3237         /// <summary>
  3238         /// Event handler that occurs whenever an Outlook item is loaded into memory.
  3239         /// See: https://msdn.microsoft.com/en-us/library/office/ff868544.aspx
  3240         /// </summary>
  3241         /// <param name="Item">A weak object reference for the loaded Outlook item.</param>
  3242         //private void Application_ItemLoad(object item)
  3243         //{
  3244         //    WatchedMailItem newItem;
  3245 
  3246         //    if (item is Outlook.MailItem)
  3247         //    {
  3248         //        newItem = new WatchedMailItem(item as Outlook.MailItem);
  3249         //        newItem.Disposed += WatchedMailItem_Disposed;
  3250 
  3251         //        this.watchedMailItems.Add(newItem);
  3252         //    }
  3253 
  3254         //    return;
  3255         //}
  3256 
  3257         /// <summary>
  3258         /// Event handler for when the application is about to send an item.
  3259         /// This event is called before the mail item is sent and will encrypt the data as necessary.
  3260         /// See: https://msdn.microsoft.com/en-us/library/office/ff865076.aspx
  3261         /// </summary>
  3262         /// <param name="item">The outlook item being sent.</param>
  3263         /// <param name="cancel">Whether to cancel the item send.</param>
  3264         private void Application_ItemSend(object item, ref bool cancel)
  3265         {
  3266             Outlook.MailItem omi;
  3267             CryptableMailItem cmi;
  3268 
  3269             Log.Verbose("Application_ItemSend: Send started");
  3270 
  3271             if (this.isItemSendHandlerEnabled)
  3272             {
  3273                 omi = item as Outlook.MailItem;
  3274 
  3275                 // Only process if pEp is enabled
  3276                 if (omi?.GetIsSendProcessingEnabled() == true)
  3277                 {
  3278                     // If ForceProtected, create special mail
  3279                     bool processMessage = true;
  3280                     bool isForceFullyProtected = (omi?.GetIsForcefullyProtected() == true);
  3281                     if (isForceFullyProtected)
  3282                     {
  3283                         Log.Info("Sending forcefully protected message.");
  3284 
  3285                         if (FPPMessage.ProcessOutgoing(omi) == Globals.ReturnStatus.Success)
  3286                         {
  3287                             Log.Verbose("Application_ItemSend: Successfully processed outgoing message.");
  3288                         }
  3289                         else
  3290                         {
  3291                             cancel = true;
  3292                             processMessage = false;
  3293                             Log.Error("Application_ItemSend: Error creating forcefully protected message.");
  3294                         }
  3295                     }
  3296 
  3297                     if (processMessage)
  3298                     {
  3299                         bool isInSecureStore;
  3300                         string sendUnencryptedWarning = Properties.Resources.Message_SendError + Environment.NewLine + Environment.NewLine + Properties.Resources.Message_SendUnencryptedConfirmation;
  3301                         DialogResult result;
  3302                         Outlook.Account sendingAccount = null;
  3303                         Outlook.Store sendingStore = null;
  3304                         Outlook.MailItem inlineItem = null;
  3305                         Outlook.Inspector insp;
  3306                         Outlook.Explorer exp = null;
  3307                         MsgProcessor processor;
  3308                         PEPMessage sentMessage;
  3309                         PEPMessage[] messages;
  3310                         PEPMessage savedSentMessage;
  3311                         pEpRating savedSentMessageStoredRating;
  3312                         Globals.ReturnStatus createSts;
  3313                         Globals.ReturnStatus processSts;
  3314                         cmi = new CryptableMailItem(omi);
  3315 
  3316                         try
  3317                         {
  3318                             // Encrypt unless the mail has BCC recipients
  3319                             if (cmi.HasBCC == false)
  3320                             {
  3321                                 Log.Verbose("Application_ItemSend: Starting encryption and message processing.");
  3322 
  3323                                 createSts = PEPMessage.Create(omi, out sentMessage);
  3324                                 isInSecureStore = omi.GetIsInSecureStore();
  3325 
  3326                                 if (createSts == Globals.ReturnStatus.Success)
  3327                                 {
  3328                                     processor = new MsgProcessor();
  3329                                     processSts = processor.ProcessSentMessage(sentMessage,
  3330                                                                               isInSecureStore,
  3331                                                                               this.Settings.ExtraKeys,
  3332                                                                               out messages,
  3333                                                                               out savedSentMessage,
  3334                                                                               out savedSentMessageStoredRating);
  3335 
  3336                                     // Importantly, check for processing failure before continuing
  3337                                     if (processSts != Globals.ReturnStatus.Success)
  3338                                     {
  3339                                         throw (new Exception("Failed while processing the sent message."));
  3340                                     }
  3341 
  3342                                     if (messages.Length == 1)
  3343                                     {
  3344                                         Log.Verbose("Application_ItemSend: Applying to original mail item for send.");
  3345 
  3346                                         /* If the processor produces only one message, it can be applied to and sent with
  3347                                          * the original Outlook MailItem. This allows outlook to do all the standard processing for
  3348                                          * send which greatly reduces the number of possible failure points.
  3349                                          * 
  3350                                          * If a failure occurs, the original mail item is applied back to the Outlook mail item
  3351                                          * and an error dialog enables the user to send the message unencrypted. We currently 
  3352                                          * differentiate between generic errors and the specific error where the unencrypted message
  3353                                          * size is below the allowable limit, but the PGP/MIME encrypted one exceeds it.
  3354                                          * 
  3355                                          * Notes:
  3356                                          * Do NOT include internal Header Fields (MAPI properties) on outgoing messages!
  3357                                          * The sender does not need to be set as the encrypted content is applied to the original
  3358                                          * MailItem (setSender=false)
  3359                                          */
  3360                                         try
  3361                                         {
  3362                                             messages[0].ApplyTo(omi, false, false, false);
  3363                                         }
  3364                                         catch (AttachmentSizeException ex)
  3365                                         {
  3366                                             // Adjust warning message for this concrete case
  3367                                             sendUnencryptedWarning = Properties.Resources.Message_SendErrorAttachmentSize + Environment.NewLine + Environment.NewLine + Properties.Resources.Message_SendUnencryptedConfirmation;
  3368                                             Log.Verbose("Application_ItemSend: Error with attachments while applying encrypted message. " + ex.ToString());
  3369                                             sentMessage.ApplyTo(omi, false, false);
  3370                                             throw;
  3371                                         }
  3372                                         catch (Exception ex)
  3373                                         {
  3374                                             Log.Verbose("Application_ItemSend: Error applying encrypted message. " + ex.ToString());
  3375                                             sentMessage.ApplyTo(omi, false, false);
  3376                                             throw;
  3377                                         }
  3378                                     }
  3379                                     else
  3380                                     {
  3381                                         // Create and send individual processed messages
  3382                                         Log.Verbose("Application_ItemSend: Sending message(s).");
  3383                                         for (int i = 0; i < messages.Length; i++)
  3384                                         {
  3385                                             /* Send the messages directly and catch errors the same way as in case of
  3386                                              * only one message being applied directly (see above).                                      
  3387                                              */
  3388                                             try
  3389                                             {
  3390                                                 this.CreateAndSendMessage(messages[i], true, false);
  3391                                             }
  3392                                             catch (AttachmentSizeException ex)
  3393                                             {
  3394                                                 // Adjust warning message for this concrete case
  3395                                                 sendUnencryptedWarning = Properties.Resources.Message_SendErrorAttachmentSize + Environment.NewLine + Environment.NewLine + Properties.Resources.Message_SendUnencryptedConfirmation;
  3396                                                 Log.Verbose("Application_ItemSend: Error with attachments while sending encrypted message. " + ex.ToString());
  3397                                                 throw;
  3398                                             }
  3399                                             catch (Exception ex)
  3400                                             {
  3401                                                 Log.Verbose("Application_ItemSend: Error sending encrypted message. " + ex.ToString());
  3402                                                 throw;
  3403                                             }
  3404                                         }
  3405 
  3406                                         // Save the sent message
  3407                                         Log.Verbose("Application_ItemSend: Saving sent message.");
  3408                                         try
  3409                                         {
  3410                                             sendingAccount = omi.SendUsingAccount;
  3411 
  3412                                             /* Note: Do not save the sent message for ActiveSync accounts which are handled specially.
  3413                                              * Outlook/ActiveSync specification doesn't allow the necessary manual control.
  3414                                              * Alternatively ActiveSync sent folders are monitored for new sent items then decrypted as needed.
  3415                                              */
  3416                                             if (sendingAccount.AccountType != Outlook.OlAccountType.olEas)
  3417                                             {
  3418                                                 // Getting delivery store can fail for new accounts when Outlook is not restarted
  3419                                                 try
  3420                                                 {
  3421                                                     sendingStore = sendingAccount.DeliveryStore;
  3422                                                 }
  3423                                                 catch
  3424                                                 {
  3425                                                     sendingStore = null;
  3426                                                     Log.Warning("Application_ItemSend: Failure getting DeliveryStore to save sent message.");
  3427                                                 }
  3428 
  3429                                                 //Get sending account settings
  3430                                                 var acctSettings = this._Settings.GetAccountSettings(sendingAccount.SmtpAddress);
  3431 
  3432                                                 if (acctSettings == null)
  3433                                                 {
  3434                                                     acctSettings = new PEPSettings.PEPAccountSettings
  3435                                                     {
  3436                                                         SmtpAddress = sendingAccount.SmtpAddress,
  3437                                                         Type = sendingAccount.AccountType.ToString()
  3438                                                     };
  3439                                                     this._Settings.AccountSettingsList.Add(acctSettings);
  3440                                                 }
  3441 
  3442                                                 /* Create the actual mailitem in the sent folder.
  3443                                                  * It's important to note the following about the rating:
  3444                                                  *   - Rating is generated from the Outgoing rating in the msg processor
  3445                                                  *   - This rating should be used in the future when showing the message in the UI
  3446                                                  *     Therefore it should be stored and the decrypt methods know to look for it.
  3447                                                  *   - However! Since a mirror does not exist at this point, the rating MUST NOT BE SAVED
  3448                                                  *     on an untrusted server
  3449                                                  *   - Set rating to undefined in CreateNewSentMail() to avoid setting the property.
  3450                                                  *   - If a rating is not stored here (it's an encrypted store, ActiveSync account, etc.), 
  3451                                                  *     decrypt will recalculate it when next shown in the UI.
  3452                                                  *     It will then simply be whatever the latest OutgoingRating is.
  3453                                                  *     It's stored at this point so will always be this in the future (unless the mailitem is moved between folders)
  3454                                                  */
  3455                                                 if (isInSecureStore)
  3456                                                 {
  3457                                                     // Do not store rating
  3458                                                     savedSentMessageStoredRating = pEpRating.pEpRatingUndefined;
  3459                                                 }
  3460 
  3461                                                 this.CreateNewSentMail(sendingStore,
  3462                                                                        acctSettings,
  3463                                                                        savedSentMessage,
  3464                                                                        savedSentMessageStoredRating);
  3465                                             }
  3466                                         }
  3467                                         catch (Exception ex)
  3468                                         {
  3469                                             Log.Error("Application_ItemSend: Failed to create sent mail item, " + ex.ToString());
  3470                                         }
  3471                                         finally
  3472                                         {
  3473                                             if (sendingAccount != null)
  3474                                             {
  3475                                                 //Marshal.ReleaseComObject(sendingAccount);
  3476                                                 sendingAccount = null;
  3477                                             }
  3478 
  3479                                             if (sendingStore != null)
  3480                                             {
  3481                                                 //Marshal.ReleaseComObject(sendingStore);
  3482                                                 sendingStore = null;
  3483                                             }
  3484                                         }
  3485 
  3486                                         // Don't send the original message
  3487                                         // At this point everything related to sending is complete so the original send can be cancelled
  3488                                         cancel = true;
  3489 
  3490                                         // Close inspector/window
  3491                                         Log.Verbose("Application_ItemSend: Closing inspector and deleting original message.");
  3492                                         insp = Application.ActiveInspector();
  3493                                         if (insp != null)
  3494                                         {
  3495                                             ((Outlook._Inspector)insp).Close(Outlook.OlInspectorClose.olDiscard);
  3496                                             // Marshal.ReleaseComObject(insp);
  3497                                             insp = null;
  3498                                         }
  3499                                         else
  3500                                         {
  3501                                             /* Close in-line response
  3502                                              * 
  3503                                              * The in-line response has a limited API and cannot be closed directly.
  3504                                              * It is also not easy to call .Close on the ActiveInlineResponse MailItem.
  3505                                              * This is because .Close can't be called during send, and after send the MailItem 
  3506                                              * is moved and the reference is lost. It may be possible to search by store/item ID then
  3507                                              * call .Close but it's complicated.
  3508                                              * 
  3509                                              * Therefore, there are two simpler work-arounds:
  3510                                              *  (1) Calling clear selection on the explorer which removes any draft being displayed.
  3511                                              *      However, this does not work in conversation view for some reason, so...
  3512                                              *  (2) Pop-out the ActiveInlineResponse item by calling .Display on it.
  3513                                              *      Then closing it as an inspector.
  3514                                              */
  3515                                             exp = Application.ActiveExplorer();
  3516                                             if (exp != null)
  3517                                             {
  3518                                                 try
  3519                                                 {
  3520                                                     // Warning: this does not work in conversation view
  3521                                                     ((Outlook._Explorer)exp).ClearSelection();
  3522                                                 }
  3523                                                 catch
  3524                                                 {
  3525                                                     try
  3526                                                     {
  3527                                                         // Pull the item out of the in-line response
  3528                                                         // Note: it will be closed automatically when delete is called on the draft
  3529                                                         inlineItem = ((Outlook.MailItem)((Outlook._Explorer)exp).ActiveInlineResponse);
  3530                                                         ((Outlook._MailItem)inlineItem).Display();
  3531                                                     }
  3532                                                     catch { }
  3533                                                     finally
  3534                                                     {
  3535                                                         if (inlineItem != null)
  3536                                                         {
  3537                                                             // Marshal.ReleaseComObject(inlineItem);
  3538                                                             inlineItem = null;
  3539                                                         }
  3540                                                     }
  3541                                                 }
  3542 
  3543                                                 // Marshal.ReleaseComObject(exp);
  3544                                                 exp = null;
  3545                                             }
  3546                                         }
  3547 
  3548                                         // Delete the originally composed mail item/draft
  3549                                         sendingAccount = omi.SendUsingAccount;
  3550 
  3551                                         // Getting delivery store can fail for new accounts when Outlook is not restarted
  3552                                         try
  3553                                         {
  3554                                             sendingStore = sendingAccount.DeliveryStore;
  3555                                         }
  3556                                         catch
  3557                                         {
  3558                                             sendingStore = null;
  3559                                             Log.Warning("Application_ItemSend: Failure getting DeliveryStore to delete composed message.");
  3560                                         }
  3561 
  3562                                         omi.PermanentlyDelete(sendingStore);
  3563 
  3564                                         if (sendingAccount != null)
  3565                                         {
  3566                                             //Marshal.ReleaseComObject(sendingAccount);
  3567                                             sendingAccount = null;
  3568                                         }
  3569 
  3570                                         if (sendingStore != null)
  3571                                         {
  3572                                             //Marshal.ReleaseComObject(sendingStore);
  3573                                             sendingStore = null;
  3574                                         }
  3575                                     }
  3576                                 }
  3577                                 else if (createSts == Globals.ReturnStatus.FailureNoConnection)
  3578                                 {
  3579                                     // Don't send the original message
  3580                                     cancel = true;
  3581 
  3582                                     if (omi?.GetIsAutoConsume() != true)
  3583                                     {
  3584                                         MessageBox.Show(Properties.Resources.Message_SendingNoConnection,
  3585                                                         Properties.Resources.Message_TitlePEPError,
  3586                                                         MessageBoxButtons.OK,
  3587                                                         MessageBoxIcon.Warning);
  3588                                     }
  3589                                 }
  3590                                 else
  3591                                 {
  3592                                     // Throw the error out to the main catch
  3593                                     throw (new Exception("Failed to create PEPMessage"));
  3594                                 }
  3595                             }
  3596                             else
  3597                             {
  3598                                 // Always log (not as only in verbose) to show when user sent unencrypted
  3599                                 Log.Info("Application_ItemSend: Sending unencrypted because message has BCCs.");
  3600 
  3601                                 // Add disclaimer if needed
  3602                                 omi.AddDisclaimer();
  3603 
  3604                                 // Remove any user properties to avoid 'winmail.dat'
  3605                                 omi.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_FORCE_UNENCRYPTED);
  3606                                 omi.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_ENABLE_PROTECTION);
  3607 
  3608                                 // Do not allow TNEF/RTF format with 'winmail.dat' attachment
  3609                                 MapiHelper.SetProperty(omi, MapiProperty.PidLidUseTnef, false);
  3610                             }
  3611                         }
  3612                         catch (Exception ex)
  3613                         {
  3614                             Log.Error("Application_ItemSend: Send failure, " + ex.ToString());
  3615 
  3616                             // Ask the user to continue (not for automatic messages)
  3617                             bool continueSending = false;
  3618                             if (omi?.GetIsAutoConsume() != true)
  3619                             {
  3620                                 result = MessageBox.Show(sendUnencryptedWarning,
  3621                                                          Properties.Resources.Message_TitlePEPError,
  3622                                                          MessageBoxButtons.YesNo,
  3623                                                          MessageBoxIcon.Error);
  3624                                 continueSending = (result == DialogResult.Yes);
  3625                             }
  3626 
  3627                             if (continueSending)
  3628                             {
  3629                                 // Send original message without processing
  3630                                 cancel = false;
  3631                                 Log.Info("Application_ItemSend: After send failure, user selected to send original unencrypted message.");
  3632                             }
  3633                             else
  3634                             {
  3635                                 // Don't send the original message
  3636                                 cancel = true;
  3637                                 Log.Info("Application_ItemSend: Mail not sent. Message was automatic message or user cancelled sending.");
  3638                             }
  3639                         }
  3640                         finally
  3641                         {
  3642                             cmi.Dispose();
  3643                         }
  3644                     }
  3645                 }
  3646                 else if (omi != null)
  3647                 {
  3648                     Log.Info("Application_ItemSend: Item skipped because pEp is disabled or message is sent forcefully unencrypted.");
  3649 
  3650                     // Add disclaimer if needed
  3651                     omi.AddDisclaimer();
  3652 
  3653                     // Remove any user properties to avoid 'winmail.dat'
  3654                     omi.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_FORCE_UNENCRYPTED);
  3655                     omi.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_ENABLE_PROTECTION);
  3656 
  3657                     // Do not allow TNEF/RTF format with 'winmail.dat' attachment
  3658                     MapiHelper.SetProperty(omi, MapiProperty.PidLidUseTnef, false);
  3659                 }
  3660                 else
  3661                 {
  3662                     Log.Error("Application_ItemSend: " + ((item == null) ? "item is null" : "error casting item to Outlook.MailItem"));
  3663                 }
  3664             }
  3665             else
  3666             {
  3667                 Log.Verbose("Application_ItemSend: Item is null, not a MailItem or item send handler is disabled.");
  3668             }
  3669 
  3670             Log.Verbose("Application_ItemSend: Completed. cancel=" + cancel.ToString());
  3671 
  3672             return;
  3673         }
  3674 
  3675         /// <summary>
  3676         /// Event handler for when a property changes within the active settings.
  3677         /// Warning: This code should be kept synchronized with the SyncWithSettings method.
  3678         /// </summary>
  3679         private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
  3680         {
  3681             bool exists;
  3682             pEpIdentity[] ownIdentities;
  3683             PEPSettings settings = (PEPSettings)sender;
  3684             List<KeyValuePair<CultureInfo, string>> languages;
  3685 
  3686             switch (e.PropertyName)
  3687             {
  3688                 case (nameof(PEPSettings.IsKeyServerUsed)):
  3689                     {
  3690                         // Sync IsKeyServerUsed with engine
  3691                         if (settings.IsKeyServerUsed)
  3692                         {
  3693                             ThisAddIn.PEPEngine.StartKeyserverLookup();
  3694                         }
  3695                         else
  3696                         {
  3697                             ThisAddIn.PEPEngine.StopKeyserverLookup();
  3698                         }
  3699                         break;
  3700                     }
  3701                 case (nameof(PEPSettings.IsPassiveModeEnabled)):
  3702                     {
  3703                         // Sync IsPassiveModeEnabled with engine
  3704                         ThisAddIn.PEPEngine.PassiveMode(settings.IsPassiveModeEnabled);
  3705                         break;
  3706                     }
  3707                 case (nameof(PEPSettings.IsPEPFolderVisible)):
  3708                     {
  3709                         // Sync IsPEPFolderVisible with Outlook
  3710                         this.SetPEPStoreRootFolderVisibility(settings.IsPEPFolderVisible);
  3711                         break;
  3712                     }
  3713                 case (nameof(PEPSettings.IsUnencryptedSubjectEnabled)):
  3714                     {
  3715                         // Sync IsUnencryptedSubjectEnabled with engine
  3716                         ThisAddIn.PEPEngine.UnencryptedSubject(settings.IsUnencryptedSubjectEnabled);
  3717                         break;
  3718                     }
  3719                 case (nameof(PEPSettings.IsVerboseLoggingEnabled)):
  3720                     {
  3721                         // Sync IsVerboseLoggingEnabled with engine
  3722                         ThisAddIn.PEPEngine.VerboseLogging(settings.IsVerboseLoggingEnabled);
  3723                         break;
  3724                     }
  3725                 case (nameof(PEPSettings.TrustwordsCulture)):
  3726                     {
  3727                         // Sync TrustwordsCulture with engine
  3728                         if (this._Settings.TrustwordsCulture != null)
  3729                         {
  3730                             // Validate it exists in the engine list
  3731                             exists = false;
  3732                             languages = this.LanguageList;
  3733                             foreach (KeyValuePair<CultureInfo, string> entry in languages)
  3734                             {
  3735                                 if (entry.Key.LCID == this._Settings.TrustwordsCulture.LCID)
  3736                                 {
  3737                                     exists = true;
  3738                                     break;
  3739                                 }
  3740                             }
  3741 
  3742                             if (exists == false)
  3743                             {
  3744                                 // Reset to default
  3745                                 this._Settings.TrustwordsCulture = this.GetActiveUICulture();
  3746                                 Log.Warning("Settings_PropertyChanged: Invalid TrustwordsCulture detected, setting to default.");
  3747                             }
  3748                         }
  3749                         else
  3750                         {
  3751                             // Reset to default
  3752                             this._Settings.TrustwordsCulture = this.GetActiveUICulture();
  3753                         }
  3754                         break;
  3755                     }
  3756                 default:
  3757                     {
  3758                         // Account sync
  3759                         if (e.PropertyName.StartsWith("AccountSettingsList") &&
  3760                             e.PropertyName.EndsWith("IsSyncEnabled"))
  3761                         {
  3762                             /* Sync the account settings with the engine's own identity flags.
  3763                              * Warning: RegisterMyself() must already have been called so the engine has all own identities.
  3764                              * The engine can commonly have more own identities than what is in the accounts list.
  3765                              * This isn't a problem though and engine-only identities will not be changed here.
  3766                              */
  3767                             ownIdentities = ThisAddIn.PEPEngine.OwnIdentitiesRetrieve();
  3768                             foreach (PEPSettings.PEPAccountSettings acctSettings in this._Settings.AccountSettingsList)
  3769                             {
  3770                                 // Find the own identity that matches with the account settings
  3771                                 foreach (pEpIdentity ident in ownIdentities)
  3772                                 {
  3773                                     pEpIdentity ownIdent = ident;
  3774 
  3775                                     if (acctSettings.EqualsByAddress(ownIdent.Address))
  3776                                     {
  3777                                         // Sync the engine's own identity flags with the account settings
  3778                                         if (ownIdent.GetIsSyncEnabled() != acctSettings.IsSyncEnabled)
  3779                                         {
  3780                                             if (acctSettings.IsSyncEnabled)
  3781                                             {
  3782                                                 ThisAddIn.PEPEngine.UnsetIdentityFlags(ref ownIdent, pEpIdentityFlags.pEpIdfNotForSync);
  3783                                             }
  3784                                             else
  3785                                             {
  3786                                                 ThisAddIn.PEPEngine.SetIdentityFlags(ref ownIdent, pEpIdentityFlags.pEpIdfNotForSync);
  3787                                             }
  3788                                         }
  3789 
  3790                                         break;
  3791                                     }
  3792                                 }
  3793                             }
  3794                         }
  3795 
  3796                         break;
  3797                     }
  3798             }
  3799 
  3800             return;
  3801         }
  3802 
  3803         /// <summary>
  3804         /// Event handler that occurs whenever a watched mail item is being disposed.
  3805         /// </summary>
  3806         //private void WatchedMailItem_Disposed(object sender, EventArgs e)
  3807         //{
  3808         //    WatchedMailItem item = sender as WatchedMailItem;
  3809 
  3810         //    this.watchedMailItems.Remove(item);
  3811         //    item.Disposed -= WatchedMailItem_Disposed;
  3812 
  3813         //    return;
  3814         //}
  3815 
  3816         /**************************************************************
  3817          * 
  3818          * Sub-classes
  3819          * 
  3820          *************************************************************/
  3821 
  3822         /// <summary>
  3823         /// Class used to store the arguments in the NewAccount event.
  3824         /// </summary>
  3825         internal class NewAccountEventArgs : EventArgs
  3826         {
  3827             /// <summary>
  3828             /// Gets or sets the Type of the account.
  3829             /// </summary>
  3830             public Outlook.OlAccountType AccountType;
  3831 
  3832             /// <summary>
  3833             /// Gets or sets the display name of the account.
  3834             /// </summary>
  3835             public string DisplayName;
  3836 
  3837             /// <summary>
  3838             /// Gets or sets the SMTP address of the account.
  3839             /// </summary>
  3840             public string SmtpAddress;
  3841 
  3842             /// <summary>
  3843             /// Gets or sets the primary user name of the account.
  3844             /// </summary>
  3845             public string UserName;
  3846 
  3847             /// <summary>
  3848             /// Default constructor.
  3849             /// </summary>
  3850             public NewAccountEventArgs()
  3851             {
  3852                 this.AccountType = Outlook.OlAccountType.olOtherAccount;
  3853                 this.DisplayName = null;
  3854                 this.SmtpAddress = null;
  3855                 this.UserName = null;
  3856             }
  3857         }
  3858 
  3859         /// <summary>
  3860         /// Stores a MailItem with connected events after it is loaded by Outlook.
  3861         /// </summary>
  3862         //private class WatchedMailItem : IDisposable
  3863         //{
  3864         //    /// <summary>
  3865         //    /// Occurs when the component is disposed by a call to the Dispose method.
  3866         //    /// </summary>
  3867         //    public event EventHandler Disposed;
  3868 
  3869         //    /// <summary>
  3870         //    /// Gets the MailItem object.
  3871         //    /// </summary>
  3872         //    private Outlook.MailItem mailItem;
  3873 
  3874         //    /**************************************************************
  3875         //     * 
  3876         //     * Constructors/Destructors
  3877         //     * 
  3878         //     *************************************************************/
  3879 
  3880         //    /// <summary>
  3881         //    /// Primary constructor.
  3882         //    /// </summary>
  3883         //    /// <param name="mailItem">The mail item to watch.</param>
  3884         //    public WatchedMailItem(Outlook.MailItem mailItem)
  3885         //    {
  3886         //        this.mailItem = mailItem;
  3887 
  3888         //        if (this.mailItem != null)
  3889         //        {
  3890         //            ((Outlook.ItemEvents_10_Event)this.mailItem).BeforeRead += MailItem_BeforeRead;
  3891         //            ((Outlook.ItemEvents_10_Event)this.mailItem).Unload += MailItem_Unload;
  3892         //            ((Outlook.ItemEvents_10_Event)this.mailItem).Write += MailItem_Write;
  3893         //        }
  3894         //    }
  3895 
  3896         //    /// <summary>
  3897         //    /// Destructor.
  3898         //    /// </summary>
  3899         //    ~WatchedMailItem()
  3900         //    {
  3901         //        this.Dispose();
  3902         //    }
  3903 
  3904         //    /**************************************************************
  3905         //     * 
  3906         //     * Methods
  3907         //     * 
  3908         //     *************************************************************/
  3909 
  3910         //    /// <summary>
  3911         //    /// Releases all resources and disconnects internal events.
  3912         //    /// </summary>
  3913         //    public void Dispose()
  3914         //    {
  3915         //        if (this.mailItem != null)
  3916         //        {
  3917         //            ((Outlook.ItemEvents_10_Event)this.mailItem).BeforeRead -= MailItem_BeforeRead;
  3918         //            ((Outlook.ItemEvents_10_Event)this.mailItem).Unload -= MailItem_Unload;
  3919         //            ((Outlook.ItemEvents_10_Event)this.mailItem).Write -= MailItem_Write;
  3920 
  3921         //            // Marshal.ReleaseComObject(this.mailItem);
  3922         //            this.mailItem = null;
  3923 
  3924         //            // Raise the disposed event
  3925         //            this.Disposed.Invoke(this, new EventArgs());
  3926         //        }
  3927 
  3928         //        return;
  3929         //    }
  3930 
  3931         //    /**************************************************************
  3932         //     * 
  3933         //     * Event Handling
  3934         //     * 
  3935         //     *************************************************************/
  3936 
  3937         //    /// <summary>
  3938         //    /// Event handler for when the mail item is unloaded from memory (programmatically or by user action).
  3939         //    /// See: https://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook.itemevents_10_event.unload.aspx
  3940         //    /// </summary>
  3941         //    private void MailItem_Unload()
  3942         //    {
  3943         //        this.Dispose();
  3944         //        return;
  3945         //    }
  3946 
  3947         //    /// <summary>
  3948         //    /// Event handler that occurs before Outlook begins to read the properties for the mail item.
  3949         //    /// See: https://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook.itemevents_10_event.beforeread.aspx
  3950         //    /// </summary>
  3951         //    private void MailItem_BeforeRead()
  3952         //    {
  3953         //        return;
  3954         //    }
  3955 
  3956         //    /// <summary>
  3957         //    /// Event handler for when the mail item is saved.
  3958         //    /// See: https://msdn.microsoft.com/en-us/library/office/ff868664.aspx
  3959         //    /// </summary>
  3960         //    /// <param name="cancel">Whether to cancel the item write.</param>
  3961         //    private void MailItem_Write(ref bool cancel)
  3962         //    {
  3963         //        return;
  3964         //    }
  3965         //}
  3966 
  3967         /// <summary>
  3968         /// Stores an Outlook Folder with connected events.
  3969         /// </summary>
  3970         private class WatchedFolder : IDisposable
  3971         {
  3972             /// <summary>
  3973             /// Occurs when the component is disposed by a call to the Dispose method.
  3974             /// </summary>
  3975             public event EventHandler Disposed;
  3976 
  3977             private Outlook.Folder            folder;
  3978             private Outlook.OlDefaultFolders? defaultFolder;
  3979             private Outlook.Items             items;
  3980 
  3981             /**************************************************************
  3982              * 
  3983              * Constructors/Destructors
  3984              * 
  3985              *************************************************************/
  3986 
  3987             /// <summary>
  3988             /// Primary constructor.
  3989             /// </summary>
  3990             /// <param name="folder">The folder to watch.</param>
  3991             /// <param name="defaultFolder">The default folder type (null if none).</param>
  3992             public WatchedFolder(Outlook.Folder folder,
  3993                                  Outlook.OlDefaultFolders? defaultFolder)
  3994             {
  3995                 this.folder = folder;
  3996                 this.defaultFolder = defaultFolder;
  3997                 this.items = this.folder?.Items;
  3998 
  3999                 if (this.folder != null)
  4000                 {
  4001                     this.folder.BeforeFolderMove += new Outlook.MAPIFolderEvents_12_BeforeFolderMoveEventHandler(Folder_BeforeFolderMove);
  4002                     this.folder.BeforeItemMove += new Outlook.MAPIFolderEvents_12_BeforeItemMoveEventHandler(Folder_BeforeItemMove);
  4003                 }
  4004 
  4005                 if (this.items != null)
  4006                 {
  4007                     this.items.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(Items_ItemAdd);
  4008                 }
  4009             }
  4010 
  4011             /// <summary>
  4012             /// Destructor.
  4013             /// </summary>
  4014             ~WatchedFolder()
  4015             {
  4016                 this.Dispose();
  4017             }
  4018 
  4019             /**************************************************************
  4020              * 
  4021              * Methods
  4022              * 
  4023              *************************************************************/
  4024 
  4025             /// <summary>
  4026             /// Releases all resources and disconnects internal events.
  4027             /// </summary>
  4028             public void Dispose()
  4029             {
  4030                 if (this.folder != null)
  4031                 {
  4032                     this.folder.BeforeFolderMove -= Folder_BeforeFolderMove;
  4033                     this.folder.BeforeItemMove -= Folder_BeforeItemMove;
  4034 
  4035                     // Marshal.ReleaseComObject(this.folder);
  4036                     this.folder = null;
  4037                 }
  4038 
  4039                 if (this.items != null)
  4040                 {
  4041                     this.items.ItemAdd -= Items_ItemAdd;
  4042 
  4043                     // Marshal.ReleaseComObject(this.items);
  4044                     this.items = null;
  4045                 }
  4046 
  4047                 // Raise the disposed event
  4048                 if (this.Disposed != null)
  4049                     this.Disposed.Invoke(this, new EventArgs());
  4050 
  4051                 return;
  4052             }
  4053 
  4054             /**************************************************************
  4055              * 
  4056              * Event Handling
  4057              * 
  4058              *************************************************************/
  4059 
  4060             /// <summary>
  4061             /// Event handler that occurs when an item is added to the folder items list.
  4062             /// Warning: This event does not run when a large number of items are added to the folder at once.
  4063             /// See: https://msdn.microsoft.com/en-us/library/office/ff869609.aspx
  4064             /// </summary>
  4065             private void Items_ItemAdd(object item)
  4066             {
  4067                 bool process = false;
  4068                 object propValue = null;
  4069                 Outlook.MailItem omi = null;
  4070 
  4071                 try
  4072                 {
  4073                     Log.Verbose("Items_ItemAdd raised. Processing mail item...");
  4074 
  4075                     // This is also the check to see if it's a mail item
  4076                     omi = item as Outlook.MailItem;
  4077 
  4078                     if (omi != null)
  4079                     {
  4080                         string entryId = omi.EntryID;
  4081 
  4082                         // Check, if message is a beacon or keysync message in Sent folder and delete it in this case
  4083                         if ((this.defaultFolder != null) &&
  4084                             (this.defaultFolder == Outlook.OlDefaultFolders.olFolderSentMail))
  4085                         {
  4086                             omi.GetPEPProperty(MailItemExtensions.PEPProperty.AutoConsume, out propValue);
  4087                             if (propValue != null)
  4088                             {
  4089                                 omi.PermanentlyDelete();
  4090                                 Log.Verbose("Items_ItemAdd: pEp internal mail item in Sent folder was deleted.");
  4091                             }
  4092                             else if (MailItemExtensions.IsInCopiedItemsList(entryId) == false)
  4093                             {
  4094                                 process = true;
  4095                             }
  4096                         }
  4097                         /* Else, process the mail item and decrypt it. This is needed especially for Sent folders as sent mails are normally
  4098                          * not read and therefore remain encrypted and not searchable in this folder.
  4099                          * 
  4100                          * NOTE: Via debugging, it could be determined that the Copy() method called during the creation of a mirror item on
  4101                          * untrusted servers most likely raises internally the Items.ItemAdd event. Therefore, we add the copied item's EntryID
  4102                          * to a list of copied items during its processing and check here if the newly added mail item is in this list. If yes,
  4103                          * do not process it. However, if for whatever reason, the ItemAdd event for one of those copied items was to be raised 
  4104                          * at a later stage, we might run into problems with a loop of copied items that will be processed, copied during creation
  4105                          * of the mirror, raise an ItemAdd event for the new copy and so on.
  4106                          */
  4107                         else if (MailItemExtensions.IsInCopiedItemsList(entryId) == false)
  4108                         {
  4109                             process = true;
  4110                             Log.Verbose("Items_ItemAdd: mail item to be processed.");
  4111                         }
  4112 
  4113                         // If this item is to be processed, add to decryption stack
  4114                         if (process)
  4115                         {
  4116                             ThisAddIn.DecryptionStack.TryPush(entryId);
  4117                         }
  4118                     }
  4119                 }
  4120                 catch (Exception ex)
  4121                 {
  4122                     Log.Error("Items_ItemAdd. Error occured. " + ex.ToString());
  4123                 }
  4124             }
  4125 
  4126             /// <summary>
  4127             /// Event handler that occurs when an item is about to be moved or deleted from a folder, 
  4128             /// either as a result of user action or through program code.
  4129             /// See: https://msdn.microsoft.com/en-us/library/office/ff869445.aspx
  4130             /// </summary>
  4131             private void Folder_BeforeItemMove(object item, Outlook.MAPIFolder moveTo, ref bool cancel)
  4132             {
  4133                 return;
  4134             }
  4135 
  4136             /// <summary>
  4137             /// Event handler that accours when a folder is about to be moved or deleted, 
  4138             /// either as a result of user action or through program code.
  4139             /// See: https://msdn.microsoft.com/en-us/library/office/ff868895.aspx
  4140             /// </summary>
  4141             private void Folder_BeforeFolderMove(Outlook.MAPIFolder moveTo, ref bool cancel)
  4142             {
  4143                 return;
  4144             }
  4145         }
  4146     }
  4147 }