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