ThisAddIn.cs
author Thomas
Thu, 17 Dec 2020 16:05:16 +0100
changeset 3389 e8cc6836f610
parent 3379 b2623f5a4593
child 3382 3022511a7f3f
permissions -rw-r--r--
OUT-78: Add method to save encrypted drafts

using Microsoft.Win32;
using pEp.Extensions;
using pEp.UI;
using pEp.UI.Models;
using pEp.UI.Views;
using pEpCOMServerAdapterLib;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Office = Microsoft.Office.Core;
using Outlook = Microsoft.Office.Interop.Outlook;

namespace pEp
{
    public partial class ThisAddIn
    {
        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }

        #endregion

        private List<KeyValuePair<CultureInfo, string>> _LanguageList       = null;
        private static pEpEngine                        _PEPEngine          = null;
        private static Exception                        _PEPEngineException = null;
        private string                                  _PEPStoreEntryId    = null;
        private Outlook.Folder                          _PEPStoreRootFolder = null;
        private PEPSettings                             _Settings           = null;
        private OutlookOptions                          _OutlookOptions     = null;
        private static DecryptionStack                  _DecryptionStack    = null;

#if !READER_RELEASE_MODE
        private AdapterCallbacks adapterCallbacks = null;
#endif

        private bool                        initialized                 = false;
        private bool                        isItemSendHandlerEnabled    = true;
        private List<WatchedFolder>         watchedFolders              = new List<WatchedFolder>();
        private System.Windows.Forms.Timer  inboxCleaner                = null;
        private int                         mainThreadId                = -1;

        private List<WatchedExplorer>       watchedExplorers            = new List<WatchedExplorer>();
        private List<WatchedInspector>      watchedInspectors           = new List<WatchedInspector>();
        private object                      mutexWatchedExplorers       = new object();
        private object                      mutexWatchedInspectors      = new object();
        private Outlook.Explorers           explorers                   = null;
        private Outlook.Inspectors          inspectors                  = null;

        /**************************************************************
         * 
         * Property Accessors
         * 
         *************************************************************/

        /// <summary>
        /// Gets the list of languages supported in the pEp engine.
        /// The list of language includes a key-value pair of the culture as well as a 
        /// display phrase in the native language for the trustwords.
        /// (phrase example: "I want to display the trustwords in English language")
        /// </summary>
        internal List<KeyValuePair<CultureInfo, string>> LanguageList
        {
            get
            {
                string code;
                string phrase;
                string engineList;
                string[] languages;
                string[] language;

                if (this._LanguageList == null)
                {
                    this._LanguageList = new List<KeyValuePair<CultureInfo, string>>();

                    try
                    {
                        engineList = ThisAddIn.PEPEngine.GetLanguageList();
                    }
                    catch
                    {
                        engineList = "";
                        Log.Error("LanguageList: Failed to get list from engine.");
                    }

                    languages = engineList.Split('\n');

                    // Go through each language group
                    for (int i = 0; i < languages.Length; i++)
                    {
                        language = languages[i].Split(',');

                        if (language.Length == 3)
                        {
                            code = language[0].Trim();
                            code = code.Replace("\"", "");

                            phrase = language[1].Trim();
                            phrase = phrase.Replace("\"", "");

                            this._LanguageList.Add(new KeyValuePair<CultureInfo, string>(new CultureInfo(code),
                                                                                         phrase));
                        }
                    }
                }

                return (this._LanguageList);
            }
        }

        /// <summary>
        /// Gets the static pEp engine reference.
        /// </summary>
        internal static pEpEngine PEPEngine
        {
            get
            {
                if (ThisAddIn._PEPEngine == null)
                {
                    try
                    {
                        ThisAddIn._PEPEngine = new pEpEngine();
                    }
                    catch (Exception ex)
                    {
                        // As Globals.StopAndSendCrashReport below internally tries to access the engine
                        // several times, we need to prevent infinite recursion in case of engine
                        // initializaition failure.
                        if (_PEPEngineException != null)
                            return null;
                        _PEPEngineException = ex;

                        // Check for management.db error
                        if (AdapterExtensions.CatchManagementDbError(ex.Message))
                        {
                            return null;
                        }

                        string summaryMessage = Properties.Resources.Message_InitError + " " +
                                                Properties.Resources.Message_Reinstall;
                        Globals.StopAndSendCrashReport(ex, summaryMessage, false, true);

                        return null;
                    }
                }

                return (ThisAddIn._PEPEngine);
            }
        }

        /// <summary>
        /// Gets the entry id of the pEp mirror store.
        /// </summary>
        internal string PEPStoreEntryId
        {
            get { return this._PEPStoreEntryId; }
        }

        /// <summary>
        /// Gets the root folder of the pEp data store.
        /// This is primarily used to save unencrypted outlook mail items (mirrors).
        /// </summary>
        internal Outlook.Folder PEPStoreRootFolder
        {
            get { return (this._PEPStoreRootFolder); }
        }

        /// <summary>
        /// Gets the current pEp settings for the add-in.
        /// </summary>
        internal PEPSettings Settings
        {
            get { return (this._Settings); }
        }

        /// <summary>
        /// Gets the Outlook options set in the program.
        /// </summary>
        internal OutlookOptions OutlookOptions
        {
            get { return (this._OutlookOptions); }
        }

        /// <summary>
        /// Gets the decryption stack.
        /// </summary>
        internal static DecryptionStack DecryptionStack
        {
            get
            {
                if (ThisAddIn._DecryptionStack == null)
                {
                    ThisAddIn._DecryptionStack = new DecryptionStack();
                }

                return ThisAddIn._DecryptionStack;
            }
        }

        /**************************************************************
         * 
         * Methods
         * 
         *************************************************************/

        /// <summary>
        /// Required override to add ribbon extensions.
        /// </summary>
        protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
        {
            return new RibbonCustomizations();
        }

        /// <summary>
        /// Adds a new WatchedExplorer to the list of watched explorers.
        /// </summary>
        /// <param name="watchedExplorer">The WatchedExplorer to add.</param>
        internal void AddToWatchedExplorers(WatchedExplorer watchedExplorer)
        {
            lock (mutexWatchedExplorers)
            {
                this.watchedExplorers.Add(watchedExplorer);
            }
        }

        /// <summary>
        /// Adds a new WatchedInspector to the list of watched inspectors.
        /// </summary>
        /// <param name="watchedInspector">The WatchedInspector to add.</param>
        internal void AddToWatchedInspectors(WatchedInspector watchedInspector)
        {
            lock (mutexWatchedInspectors)
            {
                this.watchedInspectors.Add(watchedInspector);
            }
        }

        /// <summary>
        /// Removes a WatchedExplorer from the list of watched explorers.
        /// </summary>
        /// <param name="watchedExplorer">The WatchedExplorer to remove.</param>
        internal void RemoveFromWatchedExplorers(WatchedExplorer watchedExplorer)
        {
            lock (mutexWatchedExplorers)
            {
                this.watchedExplorers.Remove(watchedExplorer);
            }
        }

        /// <summary>
        /// Removes a WatchedInspector from the list of watched inspectors.
        /// </summary>
        /// <param name="WatchedInspector">The WatchedInspector to remove.</param>
        internal void RemoveFromWatchedInspectors(WatchedInspector watchedInspector)
        {
            lock (mutexWatchedInspectors)
            {
                this.watchedInspectors.Remove(watchedInspector);
            }
        }

        /// <summary>
        /// Gets the WatchedExplorer or WatchedInspector in which the given mail item resides.
        /// </summary>
        /// <param name="omi">The Outlook mail item to process with.</param>
        /// <returns>The WatchedExplorer or WatchedInspector the mail item resides in or null if an error occured.</returns>
        internal WatchedWindow GetWatchedParentWindow(Outlook.MailItem omi)
        {
            try
            {
                // First, check all watched explorers
                foreach (var watchedExplorer in this.watchedExplorers)
                {
                    if (watchedExplorer?.CurrentMailItem?.MAPIOBJECT?.Equals(omi?.MAPIOBJECT) == true)
                    {
                        return watchedExplorer;
                    }
                }

                // If window hasn't been found yet, check inspectors
                foreach (var watchedInspector in this.watchedInspectors)
                {
                    if (watchedInspector?.CurrentMailItem?.MAPIOBJECT?.Equals(omi?.MAPIOBJECT) == true)
                    {
                        return watchedInspector;
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("GetWatchedParentWindow: Error getting window. " + ex.ToString());
            }

            // If no window has been found, return null
            return null;
        }

        /// <summary>
        /// Gets the watched window for an Inspector or Explorer object.
        /// </summary>
        /// <param name="window">The Inspector or Explorer window to get its WatchedWindow for.</param>
        /// <returns>The watched window or null if not found.</returns>
        internal WatchedWindow GetWatchedWindow(dynamic window)
        {
            Outlook.Explorer explorer = null;
            Outlook.Inspector inspector = null;
            WatchedWindow watchedWindow = null;

            try
            {
                // First, try if window is an explorer
                explorer = window as Outlook.Explorer;
                if (explorer != null)
                {
                    if (this.watchedExplorers.Count == 1)
                    {
                        watchedWindow = this.watchedExplorers[0];
                    }
                    else
                    {
                        foreach (var watchedExplorer in this.watchedExplorers)
                        {
                            if (watchedExplorer?.Explorer?.Equals(explorer) == true)
                            {
                                watchedWindow = watchedExplorer;
                                break;
                            }
                        }
                    }
                }
                else
                {
                    // If window is no explorer, try if it is an inspector
                    inspector = window as Outlook.Inspector;
                    if (inspector != null)
                    {
                        if (this.watchedInspectors.Count == 1)
                        {
                            watchedWindow = this.watchedInspectors[0];
                        }
                        else
                        {
                            foreach (var watchedInspector in this.watchedInspectors)
                            {
                                if (watchedInspector?.Inspector?.Equals(inspector) == true)
                                {
                                    watchedWindow = watchedInspector;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("GetWatchedWindow: Error occured. " + ex.ToString());
            }
            finally
            {
                explorer = null;
                inspector = null;
            }

            return watchedWindow;
        }

        /// <summary>
        /// This updates the accounts list in pEp settings with the ones found in Outlook, adding new accounts and removing
        /// all accounts that are not there anymore.
        /// </summary>
        internal void SyncAccountsList()
        {
            bool exists;
            string dialogMessage = string.Empty;
            Outlook.NameSpace ns = null;
            Outlook.Account account = null;
            Outlook.Accounts accounts = null;
            Outlook.Store store = null;
            Outlook.Stores stores = null;
            PEPSettings.PEPAccountSettings acctSettings;
            List<string> sharedAccountStores = new List<string>();

            try
            {
                ns = Application.Session;
                accounts = ns?.Accounts;

                if (this._Settings != null)
                {
                    // Add any new Outlook accounts to the account settings list
                    for (int i = 1; i <= accounts.Count; i++)
                    {
                        account = accounts[i];

                        if (string.IsNullOrWhiteSpace(account.SmtpAddress) == false)
                        {
                            // Check if it is already in the list
                            if (PEPSettings.GetAccountSettings(account.SmtpAddress) is PEPSettings.PEPAccountSettings accountSettings)
                            {
                                // Check for properties that might have not been set correctly due to errors
                                // UserName
                                if (string.IsNullOrEmpty(accountSettings.UserName))
                                {
                                    try
                                    {
                                        accountSettings.UserName = account.UserName;
                                    }
                                    catch (Exception ex)
                                    {
                                        Log.Error("SyncAccountsList: Error adding user name. " + ex.ToString());
                                    }
                                }
                            }
                            else
                            {
                                // Create pEp settings for the new account
                                account.CreatePEPSettings();
                            }
                        }
                        else
                        {
                            Log.Warning("SyncAccountsList: Invalid Smtp address detected, skipping account.");
                        }

                        account = null;
                    }

                    // Add shared mail boxes
                    try
                    {
                        stores = ns?.Stores;
                        for (int i = 1; i <= stores.Count; i++)
                        {
                            store = stores[i];
                            if ((store.ExchangeStoreType == Outlook.OlExchangeStoreType.olExchangeMailbox) &&
                                (store.GetSmtpAddress(out string address, out string userName)))
                            {
                                Log.Verbose("SyncAccountsList: Found shared account " + address ?? "<null>");

                                if (PEPSettings.GetAccountSettings(address) == null)
                                {
                                    // Create shared account settings
                                    var sharedAccountSettings = new PEPSettings.PEPAccountSettings
                                    {
                                        SmtpAddress = address,
                                        IsSecureStorageEnabled = false,
                                        IsSyncEnabled = false,
                                        Type = Outlook.OlAccountType.olExchange.ToString(),
                                        UserName = userName
                                    };

                                    // Sync its settings with the engine
                                    sharedAccountSettings.SignalSyncSettingsToEngine();

                                    // Add it to the list of accounts
                                    this.Settings.AccountSettingsList.Add(sharedAccountSettings);
                                    Log.Verbose("SyncAccountsList: Added shared account " + address);
                                }

                                sharedAccountStores.Add(address);
                            }

                            store = null;
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error("SyncAccountsList: Error adding shared mail boxes. " + ex.ToString());
                    }

                    // Remove any entries of the account settings list not in Outlook
                    // This is needed to skip any entries in the registry that may be invalid, user created, or no longer in Outlook
                    for (int i = (this._Settings.AccountSettingsList.Count - 1); i >= 0; i--)
                    {
                        acctSettings = this._Settings.AccountSettingsList[i];

                        // Check if it exists in Outlook
                        exists = false;
                        for (int j = 1; j <= accounts.Count; j++)
                        {
                            account = accounts[j];

                            if (acctSettings.EqualsByAddress(account.SmtpAddress))
                            {
                                exists = true;
                            }

                            account = null;

                            if (exists)
                            {
                                break;
                            }
                        }

                        // Delete if necessary
                        if ((exists == false) &&
                            (Globals.ThisAddIn.Settings?.AccountsAddedWhileRunningList?.Contains(acctSettings?.SmtpAddress) != true) &&
                            (sharedAccountStores.Contains(acctSettings.SmtpAddress) == false))
                        {
                            this._Settings.AccountSettingsList.RemoveAt(i);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("SyncAccountsList: Error occured. " + ex.ToString());
            }
            finally
            {
                ns = null;
                account = null;
                accounts = null;
            }
        }

        /// <summary>
        /// Detects and disables GPG for Windows GpgOL Outlook add-in.
        /// </summary>
        /// <returns>True if the GpgOL add-in was disabled.</returns>
        private bool DisableGpgOL()
        {
            bool wasDisabled = false;
            Office.COMAddIns comAddIns = null;

            try
            {
                comAddIns = Globals.ThisAddIn.Application.COMAddIns;
                foreach (Office.COMAddIn addin in comAddIns)
                {
                    if (string.Equals(addin.Description, "GpgOL - The GnuPG Outlook Plugin", StringComparison.OrdinalIgnoreCase))
                    {
                        addin.Connect = false;
                        wasDisabled = true;

                        Log.Info("DisableGpgOL: GpgOL was detected and disabled.");
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("DisableGpgOL: Error trying to disable GpgOL, " + ex.ToString());
            }
            finally
            {
                if (comAddIns != null)
                {
                    // Marshal.ReleaseComObject(comAddIns);
                    comAddIns = null;
                }
            }

            return (wasDisabled);
        }

        /// <summary>
        /// Sends a request to recalculate the message rating of all open windows (except one) again.
        /// </summary>
        /// <param name="windowToExclude">The window to not calculate its rating for</param>
        internal void RecalculateAllWindows(WatchedWindow windowToExclude)
        {
            for (int i = 0; i < this.watchedExplorers.Count; i++)
            {
                if ((windowToExclude as WatchedExplorer)?.Equals(this.watchedExplorers[i]) != true)
                {
                    this.watchedExplorers[i].RequestRatingAndUIUpdate();
                }
            }

            for (int i = 0; i < this.watchedInspectors.Count; i++)
            {
                if ((windowToExclude as WatchedInspector)?.Equals(this.watchedInspectors[i]) != true)
                {
                    this.watchedInspectors[i].RequestRatingAndUIUpdate();
                }
            }
        }

        /// <summary>
        /// Registers each own identity in the pEp engine.
        /// An own identity represents each account in Outlook.
        /// </summary>
        internal void RegisterMyself()
        {
            if (Globals.ThisAddIn.Settings.AccountSettingsList != null)
            {
                foreach (var acctSettings in Globals.ThisAddIn.Settings.AccountSettingsList)
                {
                    this.RegisterMyself(acctSettings);
                }
            }
        }

        /// <summary>
        /// Registers a pEpIdentity in the pEp engine.
        /// </summary>
        internal void RegisterMyself(PEPSettings.PEPAccountSettings acctSettings)
        {
            if (acctSettings != null)
            {
                // Create pEpIdentity
                PEPIdentity ownIdentity = new PEPIdentity
                {
                    Address = acctSettings.SmtpAddress,
                    UserId = PEPSettings.PEP_OWN_USER_ID,
                    UserName = acctSettings.UserName
                };

                // Log information if invalid
                if (string.IsNullOrWhiteSpace(ownIdentity.Address))
                {
                    Log.Warning("RegisterMyself: Myself doesn't have an address.");
                }

                if (string.IsNullOrWhiteSpace(ownIdentity.UserName))
                {
                    Log.Warning("RegisterMyself: Myself doesn't have a user name.");
                }

                /* Call engine to register myself (doesn't matter if already done in a past instance)
                 * Note that key generation can take place during the call to Myself if it's a new installation
                 * and the engine is unable to elect a key. In this situation key generation can take some time.
                 * It takes long-enough for the call to the adapter to timeout here and throw an exception.
                 * However, since the fingerprint of the own identity is not actually needed here, the assumption is 
                 * made that the engine will be OK and continue processing.
                 * The exception is simply ignored and we go to the next identity. 
                 */
                try
                {
                    AdapterExtensions.Myself(ownIdentity);
                }
                catch (COMException ex)
                {
                    Log.Warning("RegisterMyself: Engine returned exception, " + ex.ToString());
                }
            }
            else
            {
                Log.Error("RegisterMyself: account settings are null.");
            }
        }

        /// <summary>
        /// Adds a message directly to a folder.
        /// </summary>
        /// <param name="message">The message to add to the folder.</param>
        /// <param name="folder">The folder to add the message to.</param>
        /// <returns>Whether or not the operation was successful.</returns>
        internal bool AddMessageToFolder(PEPMessage message, Outlook.Folder folder)
        {
            bool success = false;
            Outlook.Application application = null;
            Outlook.MailItem mailItem = null;
            Outlook.NameSpace session = null;

            try
            {
                mailItem = Application.CreateItem(Outlook.OlItemType.olMailItem);

                if ((folder != null) &&
                    (message.ApplyTo(mailItem, true, true) == Globals.ReturnStatus.Success))
                {
                    if (message.IsAutoConsume)
                    {
                        mailItem.UnRead = false;
                        mailItem.SetMessageFlag(MapiPropertyValue.EnumPidTagMessageFlags.mfSubmitted, true);
                        mailItem.SetMessageFlag(MapiPropertyValue.EnumPidTagMessageFlags.mfUnsent, false);

                        if (folder.InAppFolderSyncObject == false)
                        {
                            Log.Verbose("AddMessageToFolder: Folder not yet registered as to be synced with the server. Enabling...");
                            folder.InAppFolderSyncObject = true;
                        }
                    }
                    mailItem = mailItem.Move(folder);
                    mailItem.Save();
                    application = Globals.ThisAddIn.Application;
                    session = application.Session;
                    session.SendAndReceive(false);
                    success = true;
                }
            }
            catch (Exception ex)
            {
                Log.Error("AddMessageToSyncFolder: Error occured. " + ex.ToString());
            }
            finally
            {
                application = null;
                mailItem = null;
                session = null;
            }

            return success;
        }

        /// <summary>
        /// Create a new Outlook MailItem from the given PEPMessage and send it.
        /// The sent message will not be processed by the 'Application_ItemSend' event.
        /// WARNING: Exchange ActiveSync accounts ignore the deleteAfterSend parameter (always false).
        /// </summary>
        /// <param name="message">The message to send.</param>
        /// <param name="deleteAfterSend">Whether the message is deleted after sending (true) or a copy is saved (false).</param>
        /// <param name="validateSendingAccount">Validates that the SendingAccount matches the From identity of the given message.
        /// This can catch situations (and throw exceptions) where the default account would be used instead.</param>
        /// <param name="processMessage">Whether or not to process this message through the pEp engine.</param>
        internal void CreateAndSendMessage(PEPMessage message,
                                           bool deleteAfterSend,
                                           bool validateSendingAccount,
                                           bool processMessage = false)
        {
            Outlook.MailItem newItem;
            Globals.ReturnStatus sts;

            this.isItemSendHandlerEnabled = processMessage;

            try
            {
                newItem = Application.CreateItem(Outlook.OlItemType.olMailItem);

                /* Notes:
                 * Do NOT include internal Header Fields (MAPI properties) on outgoing messages!
                 * The sender must be set from the from identity (setSender=true)
                 */
                sts = message.ApplyTo(newItem, false, true);

                if (sts == Globals.ReturnStatus.Success)
                {
                    // Check that the sending account is the From identity
                    if ((validateSendingAccount) &&
                        (message.From != null))
                    {
                        if (newItem.SetSendUsingAccount(message.From.Address) == false)
                        {
                            throw new Exception("Sending account does not match from identity or error setting Send account");
                        }
                    }

                    // Do not allow TNEF/RTF format with 'winmail.dat' attachment
                    MapiHelper.SetProperty(newItem, MapiProperty.PidLidUseTnef, false);

                    // If ForceUnencrypted property is set, add it to mail item (only if message is to be processed)
                    if (processMessage &&
                        message.ForceUnencrypted)
                    {
                        newItem.SetPEPProperty(MailItemExtensions.PEPProperty.ForceUnencrypted, true);
                    }

                    /* Send
                     * 
                     * Note: For ActiveSync accounts, the DeleteAfterSubmit property is ignored.
                     * This means the message will always appear in the sent folder by default.
                     * The reason for this is unknown.
                     * 
                     * It's possible this is related to the ActiveSync specification version
                     * and therefore may behave differently depending on Outlook version.
                     * More research is needed here.
                     */
                    newItem.DeleteAfterSubmit = deleteAfterSend;
                    ((Outlook._MailItem)newItem).Send();

                    newItem = null;
                }
                else
                {
                    newItem.PermanentlyDelete();
                    newItem = null;

                    throw new Exception("Failed to create MailItem to send");
                }
            }
            catch
            {
                throw new Exception("CreateMessageAndSend: Failed to send MailItem.");
            }
            finally
            {
                this.isItemSendHandlerEnabled = true;
            }
        }

        /// <summary>
        /// Create a new 'sent' Outlook MailItem and place it in the given store.
        /// WARNING: Exchange ActiveSync accounts are not supported by this method. The sent
        /// message will always appear in the drafts folder.
        /// </summary>
        /// <param name="sendingStore">The store to save the sent message to.</param>
        /// <param name="acctSettings">The sending account settings.</param>
        /// <param name="sentMessage">The message that is sent.</param>
        /// <param name="storedRating">The rating to store to the sent message mail item.
        /// Will only be set if not undefined (default). Warning: Only use this on trusted servers.</param>
        private void CreateNewSentMail(Outlook.Store sendingStore,
                                       PEPSettings.PEPAccountSettings acctSettings,
                                       PEPMessage sentMessage,
                                       pEpRating storedRating = pEpRating.pEpRatingUndefined)
        {
            Int32 messageFlags;
            Outlook.MailItem sentMailItem = null;
            Outlook.Folder sentFolder = null;
            Outlook.NameSpace ns = Application.Session;
            Outlook.Folder rootFolder = null;
            Outlook.Folders folders = null;
            Outlook.Folder folder = null;

            /* The process is:
             * 1. Use the Store.GetDefaultFolder(olFolderSentMail) to try to get the default Sent folder. 
             *    However, this can fail for a number of reasons, depending on server and folder configuration.
             * 2. As a fallback, search for the Sent folder by matching the Sent folder Entry ID
             *    However, this can also fail if no Sent folder Entry ID has been recorded.
             * 3. As another fallback, search by Folder name ("SENT") for an existing Sent folder
             *    If found, record the folder's Entry ID. If not, goto point 4.
             * 4. If everything of the above fails, create a new Sent folder and record the Entry ID.     
             */

            try
            {
                if ((sendingStore != null) &&
                    (acctSettings != null) &&
                    (sentMessage != null))
                {
                    rootFolder = (Outlook.Folder)sendingStore.GetRootFolder();
                    if (rootFolder != null)
                    {
                        folders = rootFolder.Folders;
                    }

                    // Step 1
                    try
                    {
                        sentFolder = (Outlook.Folder)sendingStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail);
                    }
                    catch
                    {
                        if (sentFolder != null)
                        {
                            // Marshal.ReleaseComObject(sentFolder);
                            sentFolder = null;
                        }
                    }

                    // Step 2
                    if (sentFolder == null)
                    {
                        try
                        {
                            /* Search for folder by entry ID
                             * Normally, the way to go would be the following line:
                             * sentFolder = (Outlook.Folder)ns.GetItemFromID(acctSettings.SentFolderEntryId, sendingStore.StoreID);
                             * However, for some reason, this can come up with the following error: "Could not open the item. Try again."
                             * Therefore, the below solution is used:
                             */
                            if (folders != null)
                            {
                                for (int i = 1; i <= folders.Count; i++)
                                {
                                    folder = (Outlook.Folder)folders[i];

                                    if (folder != null)
                                    {
                                        if ((string.IsNullOrEmpty(folder.EntryID) == false) &&
                                            (string.Equals(folder.EntryID, acctSettings.SentFolderEntryId)))
                                        {
                                            sentFolder = folder;
                                            break;
                                        }

                                        // Marshal.ReleaseComObject(folder);
                                        folder = null;
                                    }
                                }
                            }
                        }
                        catch
                        {
                            if (sentFolder != null)
                            {
                                // Marshal.ReleaseComObject(sentFolder);
                                sentFolder = null;
                            }
                        }
                    }

                    // Step 3
                    if (sentFolder == null)
                    {
                        try
                        {
                            //Search for folder by name
                            if (folders != null)
                            {
                                for (int i = 1; i <= folders.Count; i++)
                                {
                                    folder = (Outlook.Folder)folders[i];

                                    if (folder != null)
                                    {
                                        if ((string.IsNullOrEmpty(folder.Name) == false) &&
                                            (folder.Name.Trim().StartsWith(Globals.CUSTOM_SENT_FOLDER_NAME, StringComparison.OrdinalIgnoreCase)))
                                        {
                                            sentFolder = folder;
                                            break;
                                        }

                                        // Marshal.ReleaseComObject(folder);
                                        folder = null;
                                    }
                                }
                            }
                        }
                        catch
                        {
                            if (sentFolder != null)
                            {
                                // Marshal.ReleaseComObject(sentFolder);
                                sentFolder = null;
                            }
                        }
                    }

                    // Step 4
                    if (sentFolder == null)
                    {
                        try
                        {
                            // Create folder
                            if (folders != null)
                            {
                                sentFolder = (Outlook.Folder)folders.Add(Globals.CUSTOM_SENT_FOLDER_NAME);
                            }
                        }
                        catch (Exception ex)
                        {
                            if (sentFolder != null)
                            {
                                // Marshal.ReleaseComObject(sentFolder);
                                sentFolder = null;
                            }

                            Log.Error("CreateNewSentMail: No Sent folder found and no new Sent folder could be created because of the following error: " + ex.ToString());
                        }
                    }

                    if (sentFolder != null)
                    {
                        acctSettings.SentFolderEntryId = sentFolder.EntryID;

                        // Create the basic sent mail item
                        sentMailItem = (Outlook.MailItem)sentFolder.Items.Add(Outlook.OlItemType.olMailItem);
                        sentMessage.ApplyTo(sentMailItem, false, true); // Do NOT include internal Header Fields (MAPI properties)!

                        // Set the date and time the mail item was sent
                        try
                        {
                            MapiHelper.SetProperty(sentMailItem, MapiProperty.PidTagClientSubmitTime, DateTime.Now.ToUniversalTime());
                        }
                        catch { }

                        // Set flags
                        try
                        {
                            messageFlags = (System.Int32)MapiHelper.GetProperty(sentMailItem, MapiProperty.PidTagMessageFlags);
                            messageFlags &= ~((System.Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfUnsent);  // Clear UNSENT flag -- must be done before save
                            messageFlags |= ((System.Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfRead);     // Mark as read
                            MapiHelper.SetProperty(sentMailItem, MapiProperty.PidTagMessageFlags, messageFlags);
                        }
                        catch { }

                        // Save pEp rating
                        try
                        {
                            sentMailItem.SetPEPProperty(MailItemExtensions.PEPProperty.Rating, storedRating);
                        }
                        catch { }

                        // Save changes to the mail item (must be done last for some properties)
                        sentMailItem.Save();

                        /* Attempt to move to the sent folder.
                         * For ActiveSync accounts, this will fail with the error:
                         * 'Sorry, Exchange ActiveSync doesn't support what you're trying to do.'
                         * This is because the .Items.Add(folder) will ignore the specified folder and create in local Drafts.
                         * Then "Exchange ActiveSync doesn’t support the Drafts folder" so it can't be moved out.
                         * Even in the Outlook UI a draft cannot be 'dragged' out of the drafts folder.
                         */
                        try
                        {
                            sentMailItem.Move(sentFolder);
                        }
                        catch { }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("CreateNewSentMail: Error occured, " + ex.ToString());
                throw;
            }
            finally
            {
                folder = null;
                folders = null;
                ns = null;
                rootFolder = null;
                sentFolder = null;
                sentMailItem = null;
            }
        }

        /// <summary>
        /// Gets whether the given account is trusted (on the account whitelist).
        /// Character case and whitespace is ignored during comparison.
        /// </summary>
        /// <param name="address">The address of the account.</param>
        /// <returns>True if the account with the given address is trusted by default (on the whitelist), otherwise false.</returns>
        private bool GetIsAccountTrusted(string address)
        {
            bool isTrusted = false;
            string searchAddress = address.ToUpperInvariant().Trim();
            string searchDomain = searchAddress.Contains("@") ? searchAddress.Substring(searchAddress.LastIndexOf("@")) : null;
            string whitelistAccount;

            for (int i = 0; i < this._Settings.AccountWhitelist.Length; i++)
            {
                whitelistAccount = this._Settings.AccountWhitelist[i].ToUpperInvariant().Trim();

                // Check for exact match
                if (searchAddress == whitelistAccount)
                {
                    isTrusted = true;
                    break;
                }

                // Check for domain match
                if ((searchDomain != null) &&
                    whitelistAccount.StartsWith("@") &&
                    (searchDomain == whitelistAccount))
                {
                    isTrusted = true;
                    break;
                }
            }

            return (isTrusted);
        }

        /// <summary>
        /// Copies any necessary data from the given options form state back into this add-ins current state.
        /// The registry is also updated by this method.
        /// </summary>
        /// <param name="state">The state to copy data from.</param>
        internal void SetOptionsState(FormControlOptions.State state)
        {
            bool isBlacklisted;
            string fpr1;
            string fpr2;
            string[] blacklist;
            PEPSettings.PEPAccountSettings acctSettings;

            if (state != null)
            {
                // Get the blacklist
                try
                {
                    blacklist = (string[])AdapterExtensions.ExecuteWithPassphraseCheck(() => ThisAddIn.PEPEngine.BlacklistRetrieve());
                }
                catch (COMException ex)
                {
                    blacklist = new string[0];
                    Log.Error("SetOptionsState: Error getting blacklist from engine. " + ex.ToString());
                }

                // Remove any fingerprints no longer in the engine blacklist
                if (blacklist != null)
                {
                    for (int i = (blacklist.Length - 1); i >= 0; i--)
                    {
                        fpr1 = this.RemoveFprFormatting(blacklist[i]);
                        isBlacklisted = false;

                        if (string.IsNullOrEmpty(fpr1) == false)
                        {
                            // Check if fingerprint is still blacklisted
                            foreach (KVPair<PEPIdentity, bool> entry in state.Blacklist)
                            {
                                fpr2 = this.RemoveFprFormatting(entry.Key.Fingerprint);

                                // Value is true if the entry is blacklisted
                                if ((string.Equals(fpr1, fpr2, StringComparison.OrdinalIgnoreCase)) &&
                                    (entry.Value))
                                {
                                    isBlacklisted = true;
                                    break;
                                }
                            }

                            if (isBlacklisted == false)
                            {
                                try
                                {
                                    AdapterExtensions.ExecuteWithPassphraseCheck(() => ThisAddIn.PEPEngine.BlacklistDelete(fpr1));
                                }
                                catch (COMException ex)
                                {
                                    Log.Error("SetOptionsState: Failed to delete blacklist entry, " + ex.ToString());
                                }
                            }
                        }
                    }
                }

                // Add any new fingerprints to the engine blacklist
                foreach (KVPair<PEPIdentity, bool> entry in state.Blacklist)
                {
                    fpr1 = this.RemoveFprFormatting(entry.Key.Fingerprint);

                    // Value is true if the entry is blacklisted
                    if ((entry.Value) &&
                        (string.IsNullOrEmpty(fpr1) == false))
                    {
                        try
                        {
                            if ((bool)AdapterExtensions.ExecuteWithPassphraseCheck(() => ThisAddIn.PEPEngine.BlacklistIsListed(fpr1)) == false)
                            {
                                AdapterExtensions.ExecuteWithPassphraseCheck(() => ThisAddIn.PEPEngine.BlacklistAdd(fpr1));
                            }
                        }
                        catch (COMException ex)
                        {
                            Log.Error("SetOptionsState: Failed to add new blacklist entry, " + ex.ToString());
                        }
                    }
                }

                foreach (FormControlOptions.AccountState entry in state.AccountSettingsList)
                {
                    /* pEp internally uses account types as defined by microsoft.
                     * This means they have 'ol' before the type (Example: 'olImap').
                     * This isn't always user friendly so for the options UI the 'ol' is removed.
                     * However, when comparing again with the settings list, this 'ol' must be added back.
                     */
                    acctSettings = PEPSettings.GetAccountSettings(entry.SmtpAddress);

                    if (acctSettings != null)
                    {
                        // Modify existing values
                        // The following are skipped: SentFolderEntryId, SmtpAddress & Type
                        acctSettings.IsDecryptAlwaysEnabled = entry.IsDecryptAlwaysEnabled;
                        acctSettings.IsPEPEnabled = entry.IsPEPEnabled;
                        acctSettings.IsSecureStorageEnabled = entry.IsSecureStorageEnabled;

                        // If Sync is disabled for an account, signal changes to the engine
                        if (acctSettings.IsSyncEnabled != entry.IsSyncEnabled)
                        {
                            acctSettings.IsSyncEnabled = entry.IsSyncEnabled;
                            acctSettings.SignalSyncSettingsToEngine();
                        }
                    }
                    else
                    {
                        Log.Warning("SetOptionsState: Existing account settings not found.");
                    }
                }

                this._Settings.HideInternalMessages = state.HideInternalMessages;
                this._Settings.IsEncryptAllAccountsEnabled = state.IsEncryptAllAccountsEnabled;
                this._Settings.IsNeverUnsecureOptionVisible = state.IsNeverUnsecureOptionVisible;
                this._Settings.IsPassiveModeEnabled = state.IsPassiveModeEnabled;
                this._Settings.IsPassphraseForNewKeysEnabled = state.IsPassphraseForNewKeysEnabled;
                this._Settings.IsPEPFolderVisible = state.IsPEPFolderVisible;
                this._Settings.IsPEPSyncFolderEnabled = state.IsPEPSyncFolderEnabled;
                this._Settings.IsPrivacyStatusBarEnabled = state.IsPrivacyStatusBarEnabled;
                this._Settings.IsSecurityLossWarningEnabled = state.IsSecurityLossWarningEnabled;
                this._Settings.IsUnencryptedSubjectEnabled = state.IsUnencryptedSubjectEnabled;
                this._Settings.IsVerboseLoggingEnabled = state.IsVerboseLoggingEnabled;
                this._Settings.TrustwordsCulture = ((state.TrustwordsCulture != null) ? new CultureInfo(state.TrustwordsCulture.LCID) : null);

                // Set Sync settings and sync with engine if needed
                this._Settings.IsSyncEnabled = state.IsSyncEnabled;
                this._Settings.ConfigSyncSettings();

                // If Sync is disabled, the device cannot be grouped
                if (this._Settings.IsSyncEnabled == false)
                {
                    this._Settings.IsGrouped = false;
                }

                // Update registry (in case of unforeseen app shutdown)
                this._Settings.SaveToRegistry();
            }
        }

        /// <summary>
        /// Builds a new options form state using this add-ins current state.
        /// </summary>
        /// <returns>A new options form state.</returns>
        internal FormControlOptions.State GetOptionsState()
        {
            bool isDefaultStore = false;
            bool exists;
            string fpr1;
            string fpr2;
            string[] blacklist;
            StringPair[] keylist;
            PEPIdentity newIdent;
            PEPSettings.PEPAccountSettings newAcctSettings;
            FormControlOptions.State state;
            Outlook.NameSpace ns = this.Application.Session;
            Outlook.Store defaultStore = null;

            // Check if pEp is the default store
            try
            {
                defaultStore = ns.DefaultStore;
                isDefaultStore = (defaultStore.StoreID == this._PEPStoreRootFolder.StoreID);
            }
            catch
            {
                isDefaultStore = false;
            }

            state = new FormControlOptions.State();

            // Get complete OpenPGP key list
            try
            {
                keylist = (StringPair[])AdapterExtensions.ExecuteWithPassphraseCheck(() => ThisAddIn.PEPEngine.OpenPGPListKeyinfo(null));
            }
            catch (COMException ex)
            {
                keylist = new StringPair[0];
                Log.Error("GetOptionsState: Error getting OpenPGP keylist from engine. " + ex.ToString());
            }

            // Get the blacklist
            try
            {
                blacklist = (string[])AdapterExtensions.ExecuteWithPassphraseCheck(() => ThisAddIn.PEPEngine.BlacklistRetrieve());
            }
            catch (COMException ex)
            {
                blacklist = new string[0];
                Log.Error("GetOptionsState: Error getting blacklist from engine. " + ex.ToString());
            }

            // Add the OpenPGP keylist to the UI blacklist
            state.Blacklist.Clear();
            if (keylist != null)
            {
                foreach (StringPair ident in keylist)
                {
                    fpr1 = this.RemoveFprFormatting(ident.Name);
                    exists = false;

                    if (string.IsNullOrEmpty(fpr1) == false)
                    {
                        // Check if it exists in the blacklist
                        if (blacklist != null)
                        {
                            foreach (string fpr in blacklist)
                            {
                                fpr2 = this.RemoveFprFormatting(fpr);

                                if (string.Equals(fpr1, fpr2, StringComparison.OrdinalIgnoreCase))
                                {
                                    exists = true;
                                    break;
                                }
                            }
                        }

                        // Build entry to add
                        newIdent = PEPIdentity.Parse(ident.Value);
                        newIdent.Fingerprint = this.ToQuadruple(fpr1, false);

                        state.Blacklist.Add(new KVPair<PEPIdentity, bool>(newIdent, exists));
                    }
                }
            }

            // Add any fingerprints that exist only in the engine blacklist to the UI blacklist
            if (blacklist != null)
            {
                foreach (string fpr in blacklist)
                {
                    fpr1 = this.RemoveFprFormatting(fpr);
                    exists = false;

                    // Check if it already exists in the UI blacklist by fingerprint
                    foreach (KVPair<PEPIdentity, bool> entry in state.Blacklist)
                    {
                        fpr2 = this.RemoveFprFormatting(entry.Key.Fingerprint);

                        if (string.Equals(fpr1, fpr2, StringComparison.OrdinalIgnoreCase))
                        {
                            exists = true;
                            break;
                        }
                    }

                    if (exists == false)
                    {
                        newIdent = new PEPIdentity
                        {
                            Fingerprint = this.ToQuadruple(fpr1, false)
                        };

                        // Always put at the beginning so it's easier for the user to find
                        state.Blacklist.Insert(0, new KVPair<PEPIdentity, bool>(newIdent, true));
                    }
                }
            }

            state.BlacklistEnteredFingerprint = null;
            state.BlacklistSelectedIndex = -1;

            // Build AccountSettingsList
            state.AccountSettingsList.Clear();
            for (int i = 0; i < this._Settings.AccountSettingsList.Count; i++)
            {
                newAcctSettings = this._Settings.AccountSettingsList[i].Copy();

                /* pEp internally uses account types as defined by Microsoft.
                 * This means they have 'ol' before the type (Example: 'olImap').
                 * This isn't always user friendly so for the options UI the 'ol' is removed.
                 */
                if ((string.IsNullOrEmpty(newAcctSettings.Type) == false) &&
                    (newAcctSettings.Type.StartsWith("ol", StringComparison.OrdinalIgnoreCase)))
                {
                    newAcctSettings.Type = newAcctSettings.Type.Substring(2);
                }

                state.AccountSettingsList.Add(new FormControlOptions.AccountState(newAcctSettings));
            }

            state.HideInternalMessages = this._Settings.HideInternalMessages;
            state.IsDeveloperModeEnabled = this._Settings.IsDeveloperModeEnabled;
            state.IsEncryptAllAccountsEnabled = this._Settings.IsEncryptAllAccountsEnabled;
            state.IsGrouped = this._Settings.IsGrouped;
            state.IsNeverUnsecureOptionVisible = this._Settings.IsNeverUnsecureOptionVisible;
            state.IsPassiveModeEnabled = this._Settings.IsPassiveModeEnabled;
            state.IsPassphraseForNewKeysEnabled = this._Settings.IsPassphraseForNewKeysEnabled;
            state.IsPEPFolderDefaultStore = isDefaultStore;
            state.IsPEPFolderVisible = (isDefaultStore ? true : this._Settings.IsPEPFolderVisible);
            state.IsPrivacyStatusBarEnabled = this._Settings.IsPrivacyStatusBarEnabled;
            state.IsSecurityLossWarningEnabled = this._Settings.IsSecurityLossWarningEnabled;
            state.IsSyncEnabled = this._Settings.IsSyncEnabled;
            state.IsPEPSyncFolderEnabled = this._Settings.IsPEPSyncFolderEnabled;
            state.IsTNEFDisabled = this._Settings.IsTNEFDisabled;
            state.IsUnencryptedSubjectEnabled = this._Settings.IsUnencryptedSubjectEnabled;
            state.IsVerboseLoggingEnabled = this._Settings.IsVerboseLoggingEnabled;
            state.PEPCopyright = Globals.PEP_COPYRIGHT;
            state.PEPName = (Globals.RELEASE_MODE == Globals.ReleaseMode.Reader ? Globals.PEP_DISPLAY_NAME_READER : Globals.PEP_DISPLAY_NAME);
            state.SystemInfo = Globals.GetSystemInfoList();
            state.TrustwordsCulture = ((this._Settings.TrustwordsCulture != null) ? new CultureInfo(this._Settings.TrustwordsCulture.LCID) : null);

            // Release objects
            defaultStore = null;
            ns = null;

            return (state);
        }

        /// <summary>
        /// Connects events for folder/item detection in Outlook.
        /// Included folders are:
        ///  • Default Sent folders for only ActiveSync accounts
        ///  • Default Inbox folders for all accounts
        /// </summary>
        private void ConnectWatchedFolders()
        {
            Outlook.Store store = null;
            Outlook.Stores stores = null;
            Outlook.Folder inbox = null;
            Outlook.Folder sentFolder = null;
            Outlook.Folder syncFolder = null;
            Outlook.NameSpace ns = null;

            try
            {
                ns = this.Application.Session;
                stores = ns.Stores;

                for (int i = 1; i <= stores.Count; i++)
                {
                    // Note: accessing the stores can fail if the data file is missing or is in use by another program. 
                    try
                    {
                        store = stores[i];
                    }
                    catch (Exception ex)
                    {
                        Log.Warning("ConnectWatchedFolders: Failed to get store, " + ex.ToString());
                    }

                    if (store?.StoreID?.Equals(Globals.ThisAddIn.PEPStoreEntryId) != true)
                    {
                        // Add default inbox folder
                        inbox = null;
                        try
                        {
                            inbox = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
                        }
                        catch (Exception ex)
                        {
                            inbox = null;
                            Log.Warning("ConnectWatchedFolders: Failure getting default inbox folder. " + ex.ToString());
                        }

                        // Add the folder to the watched list (do not release it)
                        if (inbox != null)
                        {
                            this.watchedFolders.Add(new WatchedFolder(inbox, WatchedFolder.FolderType.Inbox));
                        }

                        // Add pEp Sync folder
                        syncFolder = null;
                        try
                        {
                            syncFolder = store.GetPEPSyncFolder(false);
                        }
                        catch (Exception ex)
                        {
                            syncFolder = null;
                            Log.Warning("ConnectWatchedFolders: Failure getting sync folder. " + ex.ToString());
                        }

                        // Add the folder to the watched list (do not release it)
                        if (syncFolder != null)
                        {
                            this.watchedFolders.Add(new WatchedFolder(syncFolder, WatchedFolder.FolderType.SyncFolder));
                        }

                        // Add Sent folder. This is needed so sent mails get decrypted on trusted servers to make the search function working
                        try
                        {
                            sentFolder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail);
                        }
                        catch (Exception ex)
                        {
                            sentFolder = null;
                            Log.Warning("ConnectWatchedFolders: Failure getting default sent folder. " + ex.ToString());
                        }

                        // Add the folder to the watched list (do not release it)
                        if (sentFolder != null)
                        {
                            this.watchedFolders.Add(new WatchedFolder(sentFolder, WatchedFolder.FolderType.SentFolder));
                        }

                        store = null;
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("ConnectWatchedFolders: Failure occured, " + ex.ToString());
            }
            finally
            {
                ns = null;
                store = null;
                stores = null;
            }
        }

        /// <summary>
        /// Opens the pEp data store root folder and saves the reference for further use.
        /// If none already exists, a new one is created.
        /// </summary>
        private void OpenPEPStoreRootFolder()
        {
            Outlook.Store pEpStore = null;
            Outlook.Store store = null;
            Outlook.Stores stores = null;
            Outlook.NameSpace ns = null;

            try
            {
                // The path to store the pEp store to
                string path = Path.Combine(Globals.ThisAddIn.Settings.PEPStoreDirectory, (Globals.PEP_DATA_FILE_NAME + ".pst"));

                /* For Outlook installations from the Windows Trusted Store the AddStoreEx function to add a store seems to
                 * convert "%LOCALAPPDATA%" to "%LOCALAPPDATA\Packages\Microsoft.Office.Desktop_8wekyb3d8bbwe\LocalCache\Local\".
                 * This is also confirmed on several sites/blogs. No official documentation has been found so far, though.
                 * In any case, we also use the above mentioned path to search for the pEp store.
                 */
                string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                string winStorePath = null;
                if (path.ToLowerInvariant().Contains(localAppData.ToLowerInvariant()))
                {
                    winStorePath = path.ToLowerInvariant().Replace(localAppData.ToLowerInvariant(), Path.Combine(localAppData, "Packages", "Microsoft.Office.Desktop_8wekyb3d8bbwe", "LocalCache", "Local"));
                }

                ns = this.Application.Session;
                stores = ns.Stores;

                // Try to find an existing store based on file name
                for (int i = 1; i <= stores.Count; i++)
                {
                    /* Note: accessing the stores can fail if the data file is missing or is in use by another program. 
                     * This is usually OK as long as it isn't  the pEp file.
                     * However, here the assumption is made that any error is not the pEp file and execution will continue.
                     * If for some reason the error prevents detecting an existing pEp.pst file, this will be caught in the next step.
                     */
                    try
                    {
                        store = stores[i];
                    }
                    catch (Exception ex)
                    {
                        Log.Warning("OpenPEPStoreRootFolder: Failed to get store, " + ex.ToString());
                    }

                    if ((store?.FilePath?.Equals(path, StringComparison.InvariantCultureIgnoreCase) == true) ||
                        (store?.FilePath?.Equals(winStorePath, StringComparison.InvariantCultureIgnoreCase) == true))
                    {
                        pEpStore = store;
                        break;
                    }

                    store = null;
                }

                // If no store was found, create new 
                if (pEpStore == null)
                {
                    /* Create the file and add it as a store, otherwise use the existing one.
                     * This can throw an error if a user either modified the list of Outlook data stores
                     * or the pEp.pst file itself.
                     */
                    try
                    {
                        ns.AddStoreEx(path, Outlook.OlStoreType.olStoreUnicode);
                    }
                    catch (Exception ex)
                    {
                        Log.Error("OpenPEPStoreRootFolder: Failed to add pEp store. " + ex.ToString());
                    }

                    for (int i = 1; i <= stores.Count; i++)
                    {
                        /* Search again for the newly created store.
                         * Note: As the creation itself might have failed, this is not guaranteed to work.
                         * We then try again in the next step or throw an error if the store cannot be accessed.
                         */
                        try
                        {
                            store = stores[i];
                        }
                        catch (Exception ex)
                        {
                            Log.Warning("OpenPEPStoreRootFolder: Failed to get store, " + ex.ToString());
                        }

                        if ((store?.FilePath?.Equals(path, StringComparison.InvariantCultureIgnoreCase) == true) ||
                            (store?.FilePath?.Equals(winStorePath, StringComparison.InvariantCultureIgnoreCase) == true))
                        {
                            pEpStore = store;
                            pEpStore.GetRootFolder().Name = Globals.PEP_DATA_FILE_NAME;
                            break; // Break before releasing
                        }

                        store = null;
                    }
                }

                if (pEpStore != null)
                {
                    this._PEPStoreRootFolder = (Outlook.Folder)pEpStore.GetRootFolder();
                    this._PEPStoreEntryId = pEpStore.StoreID;
                }
                else
                {
                    // Try again using fallback solution for file paths with prefixes
                    Log.Warning("OpenPEPStoreRootFolder: No pEp store found. Trying fallback. pEp path is " + path);

                    for (int i = 1; i <= stores.Count; i++)
                    {
                        try
                        {
                            store = stores[i];
                            Log.Info("OpenPEPStoreRootFolder: file path of store " + i + " (" + (store?.DisplayName ?? "<null>") + "): " + store?.FilePath ?? "<null>");
                        }
                        catch (Exception ex)
                        {
                            Log.Warning("OpenPEPStoreRootFolder: Failed to get store, " + ex.ToString());
                        }

                        // Custom comparison of file paths independently of their (known) prefixes
                        if (Comparisons.FilePathsAreEqual(store?.FilePath, path) ||
                            Comparisons.FilePathsAreEqual(store?.FilePath, winStorePath))
                        {
                            pEpStore = store;
                            pEpStore.GetRootFolder().Name = Globals.PEP_DATA_FILE_NAME;
                            break;
                        }

                        store = null;
                    }

                    // If pEp store was found, use it. Else throw error.
                    if (pEpStore != null)
                    {
                        this._PEPStoreRootFolder = pEpStore.GetRootFolder() as Outlook.Folder;
                        this._PEPStoreEntryId = pEpStore.StoreID;
                    }
                    else
                    {
                        throw new Exception("Cannot open required pEp.pst file.");
                    }
                }
            }
            catch (Exception ex)
            {
                Globals.StopAndSendCrashReport(ex);
            }
            finally
            {
                ns = null;
                pEpStore = null;
                store = null;
                stores = null;
            }
        }

        /// <summary>
        /// Resets the pEp data store.
        /// This will delete all files.
        /// </summary>
        internal void ResetPEPStore()
        {
            Outlook.Folder deletedFolder = null;
            Outlook.Folders folders = null;
            Outlook.Items items = null;
            Outlook.Store store = null;
            Outlook.Folder contacts = null;
            Outlook.Folders contactsFolders = null;

            try
            {
                if (this._PEPStoreRootFolder != null)
                {
                    store = this._PEPStoreRootFolder.Store;

                    // Move all folders to deleted folder
                    folders = this._PEPStoreRootFolder.Folders;
                    for (int i = folders.Count; i > 0; i--)
                    {
                        // Default folders cannot be deleted and are skipped
                        try
                        {
                            folders[i].Delete();
                        }
                        catch { }
                    }

                    if (folders != null)
                    {
                        // Marshal.ReleaseComObject(folders);
                        folders = null;
                    }

                    // Move any contact folders/contacts to the deleted folder
                    contacts = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts);
                    contactsFolders = contacts.Folders;

                    for (int i = contactsFolders.Count; i > 0; i--)
                    {
                        try
                        {
                            contactsFolders[i].Delete();
                        }
                        catch { }
                    }

                    // Delete items from deleted folder
                    deletedFolder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDeletedItems);

                    // Permanently delete items
                    items = deletedFolder.Items;
                    for (int i = items.Count; i > 0; i--)
                    {
                        try
                        {
                            items[i].Delete();
                        }
                        catch { }
                    }

                    items = null;

                    // Permanently delete folders
                    folders = deletedFolder.Folders;
                    for (int i = folders.Count; i > 0; i--)
                    {
                        try
                        {
                            folders[i].Delete();
                        }
                        catch { }
                    }

                    folders = null;
                }
            }
            catch (Exception ex)
            {
                Log.Error("ResetPEPStore: Error occured. " + ex.ToString());
            }
            finally
            {
                contacts = null;
                contactsFolders = null;
                deletedFolder = null;
                folders = null;
                items = null;
                store = null;
            }
        }

        /// <summary>
        /// Sets the visibility of the pEp data store in the navigation pane.
        /// This works by carefully releasing and maintaining references outside of Outlook.
        /// </summary>
        private void SetPEPStoreRootFolderVisibility()
        {
            // If the pEp store hasn't been opened already, try to open it
            if (this._PEPStoreRootFolder == null)
            {
                this.OpenPEPStoreRootFolder();
            }

            // If the folder exists but shouldn't be visible, remove the store
            if (this._PEPStoreRootFolder == null)
            {
                Log.Error("SetPEPStoreRootFolderVisibility: PEPStoreRootFolder is null.");
            }
            else if (this._Settings.IsPEPFolderVisible == false)
            {
                /* Remove the store from the session which will also hide it in the navigation pane.
                 * Importantly, the pEp store's root folder reference is not released.
                 * This allows the application to continue to use the store.
                 * 
                 * Also note that this call to remove the store will fail if the store is already removed.
                 * This can happen when the visibility is set to false more than once.
                 * Therefore, just try to remove it.
                 */
                Outlook.NameSpace ns = null;
                try
                {
                    ns = this.Application.Session;
                    ns.RemoveStore(this._PEPStoreRootFolder);
                }
                catch (Exception ex)
                {
                    Log.Error("SetPEPStoreRootFolderVisibility: Error removing store. " + ex.ToString());
                }
                finally
                {
                    ns = null;
                }
            }
        }

        /// <summary>
        /// Sets pEp rules and view filters for all accounts.
        /// <param name="enable">Whether to enable or disable rules and view filters.</param>
        /// filters.</param>
        /// </summary>
        internal void SetRulesAndViewFilters()
        {
            // Set view filters and save setting
            if (this.SetRules() == false)
            {
                Log.Error("SetRulesAndViewFilters: Rules could not be set. Trying again in 30 s.");
                Extensions.TaskExtensions.RunWithDelay(() => this.SetRules(), 30000);
            }
            this.SetViewFilters(this._Settings?.HideInternalMessages ?? PEPSettings.HIDE_INTERNAL_MESSAGES_DEFAULT);
        }

        /// <summary>
        /// Sets all rules.
        /// </summary>
        /// <returns>True, if the rules were correctly set. Otherwise false.</returns>
        internal bool SetRules()
        {
            bool success = true;
            Outlook.Account account = null;
            Outlook.Accounts accounts = null;
            Outlook.NameSpace ns = null;

            Log.Verbose("SetRules: Setting rules for all accounts");

            try
            {
                ns = this.Application.Session;
                accounts = ns.Accounts;

                for (int i = 1; i <= accounts.Count; i++)
                {
                    account = accounts[i];

                    if (account != null)
                    {
                        if (account.SetRules(Globals.ThisAddIn.Settings.IsPEPSyncFolderEnabled) == false)
                        {
                            success = false;
                        }

                        account = null;
                    }
                }
            }
            catch (Exception ex)
            {
                success = false;
                Log.Error("SetRules: Failure occured, " + ex.ToString());
            }
            finally
            {
                account = null;
                accounts = null;
                ns = null;
            }

            return success;
        }

        /// <summary>
        /// Sets the view filter settings of all account's inbox folders to hide pEp internal category MailItems.
        /// </summary>
        /// <param name="enable">Whether to enable or disable the view filters.</param>
        /// <param name="viewFilter">The view filter to set (only needed if <paramref name="enable"/> is true.</param>
        /// <returns>True, if the filters were correctly set. Otherwise false.</returns>
        internal bool SetViewFilters(bool enable, string viewFilter = null)
        {
            bool success = true;
            Outlook.Account account = null;
            Outlook.Accounts accounts = null;
            Outlook.NameSpace ns = null;

            Log.Verbose("SetViewFilter: Setting inbox view filter for all accounts");

            try
            {
                ns = this.Application.Session;
                accounts = ns.Accounts;

                for (int i = 1; i <= accounts.Count; i++)
                {
                    account = accounts[i];

                    if (account != null)
                    {
                        if (account.SetViewFilter(enable, viewFilter) == false)
                        {
                            success = false;
                        }

                        account = null;
                    }
                }
            }
            catch (Exception ex)
            {
                success = false;
                Log.Error("SetViewFilter: Failure occured, " + ex.ToString());
            }
            finally
            {
                account = null;
                accounts = null;
                ns = null;
            }

            return success;
        }

        /// <summary>
        /// Retrieves the pEp drafts folder and creates it if necessary.
        /// </summary>
        /// <returns>The pEp drafts folder or null if an error occurs.</returns>
        internal Outlook.Folder GetPEPStoreDraftsFolder()
        {
            Outlook.Folder pEpDraftsFolder = null;
            Outlook.Folders folders = null;

            try
            {
                folders = PEPStoreRootFolder?.Folders;

                try
                {
                    pEpDraftsFolder = folders?[Globals.PEP_DRAFTS_FOLDER_NAME] as Outlook.Folder;
                }
                catch
                {
                    pEpDraftsFolder = null;
                }

                if (pEpDraftsFolder == null)
                {
                    pEpDraftsFolder = folders?.Add(Globals.PEP_DRAFTS_FOLDER_NAME, Outlook.OlDefaultFolders.olFolderDrafts) as Outlook.Folder;
                }
            }
            catch (Exception ex)
            {
                pEpDraftsFolder = null;
                Log.Error("Cannot create pEp drafts folder. " + ex.ToString());
            }
            finally
            {
                folders = null;
            }

            return pEpDraftsFolder;
        }

        /// <summary>
        /// Method to execute during the first startup of the app.
        /// </summary>
        private void FirstStartup()
        {
            Log.Info("FirstStartup: First startup detected");

            // Show the introduction tutorial
            new DialogWindow(new Dialog(Dialog.Type.IntroTutorial))?.Show();

            this.Settings.IsFirstStartupComplete = true;
        }

        /// <summary>
        /// Detects the current UI culture in Outlook and returns it as a CultureInfo object.
        /// This will return English as the default language (never null).
        /// </summary>
        /// <param name="availableLanguages">The available languages.</param>
        /// <returns>The detected culture or English if not supported.</returns>
        private CultureInfo GetCulture(List<string> availableLanguages)
        {
            CultureInfo cultureInfo = null;
            Office.LanguageSettings languageSettings = null;

            try
            {
                // Get Outlook culture
                languageSettings = this.Application.LanguageSettings;
                CultureInfo outlookCulture = new CultureInfo(languageSettings.LanguageID[Office.MsoAppLanguageID.msoLanguageIDUI]);

                /* First, try to get the exact match of language and country code (e.g. "de-DE").
                 * Second, try to get a match only by language (e.g. "de") and use first occurrence.
                 * Third, if language is not found, use default language.
                 */
                if (availableLanguages.Find(a => a.Equals(outlookCulture.IetfLanguageTag)) is string exactMatch)
                {
                    cultureInfo = new CultureInfo(exactMatch);
                }
                else if (availableLanguages.Find(a => a.Substring(0, 2).Equals(outlookCulture.TwoLetterISOLanguageName)) is string languageMatch)
                {
                    cultureInfo = new CultureInfo(languageMatch);
                }
                else
                {
                    cultureInfo = new CultureInfo(Globals.PEP_DEFAULT_LANGUAGE);
                }
            }
            catch (Exception ex)
            {
                cultureInfo = new CultureInfo(Globals.PEP_DEFAULT_LANGUAGE);
                Log.Verbose("GetActiveUICulture: Failed to get UI culture, " + ex.ToString());
            }
            finally
            {
                languageSettings = null;
            }

            return cultureInfo;
        }

        /// <summary>
        /// Gets the UI culture synchronizing the available languages with Outlook's
        /// language settings. If the Oulook language is not available, this returns 
        /// English as default.
        /// </summary>
        /// <returns>The culture to set in the UI.</returns>
        internal CultureInfo GetActiveUICulture()
        {
            return this.GetCulture(Globals.PEP_UI_LANGUAGES);
        }

        /// <summary>
        /// Gets the Trustwords culture synchronizing the available languages with Outlook's
        /// language settings. If the Oulook language is not available, this returns 
        /// English as default.
        /// </summary>
        /// <returns>The culture to set as default Trustwords language.</returns>
        internal CultureInfo GetDefaultTrustwordsCulture()
        {
            List<string> availableLanguages = new List<string>();

            // Try to get available languages from engine
            try
            {
                var languageList = ThisAddIn.PEPEngine.GetLanguageList().Split('\n');
                foreach (var language in languageList)
                {
                    if (string.IsNullOrEmpty(language) == false)
                    {
                        availableLanguages.Add(language.Split(',')[0].Trim('\"'));
                    }
                }
            }
            catch (Exception ex)
            {
                // If an empty list is passed to the method below, the default language will be set
                availableLanguages = new List<string>();
                Log.Error("GetDefaultTrustwordsCulture: Failed to get list from engine. " + ex.ToString());
            }

            return this.GetCulture(availableLanguages);
        }

        /// <summary>
        /// Formats the given text string as separated 4-character groups.
        /// The intention is for use when showing fingerprints.
        /// Optionally, five 4-character groups are put on each line.
        /// Example: 49422235FC99585B891C --> 4942 2235 FC99 585B 891C
        /// </summary>
        /// <param name="text">The text to format in 4-character groups.</param>
        /// <param name="insertNewLine">Whether to insert a newline after each group.</param>
        /// <returns>The re-formatted string.</returns>
        internal string ToQuadruple(string text, bool insertNewLine)
        {
            List<string> groups = new List<string>();
            string result = "";

            // Separate in sections of 4 characters
            if (text != null)
            {
                for (int i = 0; i < text.Length; i += 4)
                {
                    try
                    {
                        groups.Add(text.Substring(i, 4));
                    }
                    catch (ArgumentOutOfRangeException)
                    {
                        groups.Add(text.Substring(i));
                        break;
                    }
                }
            }

            // Separate in lines of 5 groups
            if (insertNewLine)
            {
                for (int i = 0; i < groups.Count; i += 5)
                {
                    if (string.IsNullOrEmpty(result))
                    {
                        try
                        {
                            result = String.Join(" ", groups.GetRange(i, 5));
                        }
                        catch
                        {
                            result = String.Join(" ", groups.GetRange(i, (groups.Count - i)));
                        }
                    }
                    else
                    {
                        try
                        {
                            result += Environment.NewLine + String.Join(" ", groups.GetRange(i, 5));
                        }
                        catch
                        {
                            result += Environment.NewLine + String.Join(" ", groups.GetRange(i, (groups.Count - i)));
                        }
                    }
                }
            }
            else
            {
                result = string.Join(" ", groups);
            }

            return result;
        }

        /// <summary>
        /// Removes any formatting from the given fingerprint string.
        /// Example: 0x3fa1 32ca -> 3FA132CA
        /// Warning: Null can be returned if the given fingerprint is null (default return value is the input).
        /// </summary>
        /// <param name="fingerprint">The fingerprint string to remove formatting from.</param>
        /// <returns>A fingerprint string without formatting, otherwise the original input.</returns>
        internal string RemoveFprFormatting(string fingerprint)
        {
            string result = fingerprint;

            if (string.IsNullOrEmpty(fingerprint) == false)
            {
                result = fingerprint.Trim();
                result = result.ToUpperInvariant();
                result = result.Replace(" ", "");
                result = result.Replace("\r\n", "");

                if (result.StartsWith("0X"))
                {
                    result = result.Substring(2, (result.Length - 2));
                }
            }

            return (result);
        }

        /**************************************************************
         * 
         * Event Handling
         * 
         *************************************************************/

        /// <summary>
        /// Event handler for when the add-in is starting up.
        /// This is the first user code to execute in the add-in.
        /// See: https://msdn.microsoft.com/en-us/library/7xy91eax.aspx
        /// </summary>
        private void ThisAddIn_Startup(object sender, EventArgs e)
        {
#if DEBUG
            // Raise WPF binding warnings as errors
            WPFBindingExceptionListener.Attach();

            // Launch adapter if needed
            if (Process.GetProcessesByName("pEpCOMServerAdapter")?.Length < 1)
            {
                Process.Start(Path.Combine(Environment.CurrentDirectory, "..", "..", "..", "..", "pEpForWindowsAdapterSolution", "Debug", "pEpCOMServerAdapter.exe"));
            }

            // Copy the executable from the Sequoia directory to the test output directory
            File.Copy(Path.Combine(Environment.CurrentDirectory, "..", "..", "..", "..", "pEpForWindowsAdapterSolution", "sequoia", "target", "debug", "sq.exe"),
                      Path.Combine(Globals.GetAddInLocation(), "sq.exe"),
                      true);
#endif
            // Set main thread id
            this.mainThreadId = Thread.CurrentThread.ManagedThreadId;

            // Disable GPGOL
            Log.Info("ThisAddIn_Startup: Disable GPGOL");
            this.DisableGpgOL();

            // Set the UI culture for resource translation
            Log.Info("ThisAddIn_Startup: Set UI language");
            CultureInfo culture = this.GetActiveUICulture();
            CultureInfo.DefaultThreadCurrentCulture = culture;
            CultureInfo.DefaultThreadCurrentUICulture = culture;
            Log.Info("ThisAddIn_Startup: " + culture.IetfLanguageTag + " language detected.");

            // Init global error handling
            Log.Info("ThisAddIn_Startup: Connect Events");
            Globals.ConnectEvents(true);

            /* Sync accounts list. This must be done before RegisterMyself()
             * to be able to set the not for sync flag if necessary.
             */
            Log.Info("ThisAddIn_Startup: Loading settings");
            this._Settings = new PEPSettings();
            this._Settings.LoadFromRegistry();
            this.SyncAccountsList();

            // Run first startup if needed
            if (this._Settings.IsFirstStartupComplete == false)
            {
                this.FirstStartup();
            }

#if !READER_RELEASE_MODE
            // Connect callbacks, use PEPEngine property accessor in case the engine is not already initialized
            Log.Info("ThisAddIn_Startup: Register callbacks");
            this.adapterCallbacks = new AdapterCallbacks();
            ThisAddIn.PEPEngine.RegisterCallbacks(this.adapterCallbacks);
#endif
            // Import GPG keys if necessary 
            this.ImportGPGKeys();

            // Set Cipher Suite (has to be done before calling Myself)
            Log.Info("ThisAddIn_Startup: Setting cipher suite " + Enum.GetName(typeof(pEpCipherSuite), this.Settings.CipherSuite));
            AdapterExtensions.ExecuteAndLogError(() => { ThisAddIn.PEPEngine.ConfigCipherSuite(this.Settings.CipherSuite); });

            /* Register all accounts as own identities in the engine
             * Warning: This must be done BEFORE synchronizing settings because SyncWithSettings() will use 
             * the engine's OwnIdentitiesRetrieve list to synchronize own identity flags such as IsSyncEnabled.
             * The OwnIdentitiesRetrieve list will depend on what identities have been passed to Myself().
             */
            Log.Info("ThisAddIn_Startup: Register myself");
            this.RegisterMyself();

            // Process and sync settings (Warning: sequence is very important here)
            Log.Info("ThisAddIn_Startup: Sync settings");
            this._Settings.PropertyChanged += Settings_PropertyChanged;
            this._Settings.SyncWithEngine();

            // Sync IsPEPFolderVisible with Outlook
            this.SetPEPStoreRootFolderVisibility();

            // Create UI thread dispatcher to open dialog windows
            DialogWindow.UIThreadDispatcher = new DialogWindow()?.Dispatcher;

            try
            {
                /* Note: This is sensitive to time as pEp needs to start as fast as possible.
                 * If there is too much delay, Outlook will automatically disable the add-in.
                 * See: https://msdn.microsoft.com/en-us/library/office/jj228679.aspx#Anchor_7
                 * 
                 * If performance is proven to be an issue here, move to another location.
                 */
                if (this.initialized == false)
                {
                    // Set rules and view filters
                    this.SetRulesAndViewFilters();

                    // Get the Outlook options and set pEp required Outlook options
                    Log.Info("ThisAddIn_Startup: Get Outlook options and set pEp required values.");
                    this._OutlookOptions = new OutlookOptions();
                    this._OutlookOptions.ReadOptionsFromRegistry();
                    this._OutlookOptions.SetRegistryValues();

                    // Connect watched objects
                    Log.Info("ThisAddIn_Startup: Connect Watched objects");
                    this.ConnectWatchedFolders();
                    this.ConnectWatchedExplorers(true);
                    this.ConnectWatchedInspectors(true);

                    // Connect application events
                    Log.Info("ThisAddIn_Startup: Connect Application Events.");
                    this.ConnectApplicationEvents(true);

                    // Initialize inbox cleaning timer
                    Log.Info("ThisAddIn_Startup: Initializing inbox cleaner.");
                    this.ToggleInboxCleaning(true);

                    // Initialization complete
                    Log.Info("ThisAddIn_Startup: Main program started.");
                    this.initialized = true;
                }
            }
            catch (Exception ex)
            {
                string summaryMessage = Properties.Resources.Message_InitError + " " +
                                        Properties.Resources.Message_Reinstall;
                Globals.StopAndSendCrashReport(ex, summaryMessage, false);
            }
        }

        /// <summary>
        /// Imports a file with GPG keys from disk if available.
        /// </summary>
        private void ImportGPGKeys()
        {
            string gpgKeyFilePath = null;
            string importKeyRegistryEntry = "ImportGPGKeys";
            string privateKeyFilename = "gpgPrivKeys.asc";
            string publicKeyFileName = "gpgPubKeys.asc";

            try
            {
                // Check in Registry if a key import file exists
                using (RegistryKey key = Registry.LocalMachine.OpenSubKey(Path.Combine("SOFTWARE", "pEp", importKeyRegistryEntry)))
                {
                    gpgKeyFilePath = key?.GetValue("KeyFilePath") as string;
                }
            }
            catch (Exception ex)
            {
                gpgKeyFilePath = null;
                Log.Error("ImportGPGKeys: Error accessing LM Registry key. " + ex.ToString());
            }

            // If nothing was found or an error occured, check also the CU scope
            if (string.IsNullOrEmpty(gpgKeyFilePath))
            {
                try
                {
                    using (RegistryKey key = Registry.CurrentUser.OpenSubKey(Path.Combine("SOFTWARE", "pEp", importKeyRegistryEntry)))
                    {
                        gpgKeyFilePath = key?.GetValue("KeyFilePath") as string;
                    }
                }
                catch (Exception ex)
                {
                    gpgKeyFilePath = null;
                    Log.Error("ImportGPGKeys: Error accessing CU Registry key. " + ex.ToString());
                }
            }

            // If key import file exists, import it
            if (string.IsNullOrEmpty(gpgKeyFilePath) == false)
            {
                Log.Verbose("ImportGPGKeys: Registry setting ImportGPGKeys found. Trying to import keys.");
                bool error = false;

                // Import private keys
                try
                {
                    string fullPrivateKeyFilename = Path.Combine(gpgKeyFilePath, privateKeyFilename);
                    if (File.Exists(fullPrivateKeyFilename) &&
                        (File.ReadAllText(fullPrivateKeyFilename) is string privKeys))
                    {
                        pEpIdentity[] privateKeys = null;
                        AdapterExtensions.ExecuteWithPassphraseCheck(() => ThisAddIn.PEPEngine.ImportKey(privKeys, out privateKeys));

                        for (int i = 0; i < privateKeys?.Length; i++)
                        {
                            Log.Info($"ImportGPGKeys: Imported private key { privateKeys[i].fpr } for { privateKeys[i].Address }");
                        }

                        File.Delete(fullPrivateKeyFilename);
                    }
                    else
                    {
                        Log.Error("ImportGPGKeys: Private keys file does not exist or can't be read.");
                    }
                }
                catch (Exception ex)
                {
                    error = true;
                    Log.Error("ImportGPGKeys: Error importing private keys. " + ex.ToString());
                }

                // Import public keys
                try
                {
                    string fullPublicKeyFilename = Path.Combine(gpgKeyFilePath, publicKeyFileName);
                    if (File.Exists(fullPublicKeyFilename) &&
                        (File.ReadAllText(fullPublicKeyFilename) is string pubKeys))
                    {
                        AdapterExtensions.ExecuteWithPassphraseCheck(() => ThisAddIn.PEPEngine.ImportKey(pubKeys, out _));
                        File.Delete(fullPublicKeyFilename);
                    }
                    else
                    {
                        Log.Error("ImportGPGKeys: Public keys file does not exist or can't be read.");
                    }
                }
                catch (Exception ex)
                {
                    error = true;
                    Log.Error("ImportGPGKeys: Error importing public keys. " + ex.ToString());
                }

                // If keys were imported, try to delete Registry entry
                if (error == false)
                {
                    try
                    {
                        using (RegistryKey key = Registry.LocalMachine.OpenSubKey(Path.Combine("SOFTWARE", "pEp"), true))
                        {
                            if (key?.GetSubKeyNames()?.Contains(importKeyRegistryEntry) == true)
                            {
                                key.DeleteSubKey(importKeyRegistryEntry);
                                Log.Verbose("ImportGPGKeys: LM Registry entry deleted.");
                            }
                            else
                            {
                                Log.Verbose("ImportGPGKeys: LM Registry entry not found.");
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error("ImportGPGKeys: Error deleting LM Registry entry. " + ex.ToString());
                    }

                    try
                    {
                        using (RegistryKey key = Registry.CurrentUser.OpenSubKey(Path.Combine("SOFTWARE", "pEp"), true))
                        {
                            if (key?.GetSubKeyNames()?.Contains(importKeyRegistryEntry) == true)
                            {
                                key.DeleteSubKey(importKeyRegistryEntry);
                                Log.Verbose("ImportGPGKeys: CU Registry entry deleted.");
                            }
                            else
                            {
                                Log.Verbose("ImportGPGKeys: CU Registry entry not found.");
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error("ImportGPGKeys: Error deleting CU Registry entry. " + ex.ToString());
                    }
                }
            }
        }

        /// <summary>
        /// Subscribes to application events or unsubscribes from them.
        /// </summary>
        /// <param name="subscribe">Whether to subscribe or unsubscribe</param>
        private void ConnectApplicationEvents(bool subscribe)
        {
            if (this.Application != null)
            {
                try
                {
                    if (subscribe)
                    {
                        ((Outlook.ApplicationEvents_11_Event)this.Application).Startup += Application_Startup;
                        ((Outlook.ApplicationEvents_11_Event)this.Application).Quit += Application_Quit;
                        ((Outlook.ApplicationEvents_11_Event)this.Application).NewMailEx += Application_NewMailEx;
                        ((Outlook.ApplicationEvents_11_Event)this.Application).ItemSend += Application_ItemSend;
                    }
                    else
                    {
                        ((Outlook.ApplicationEvents_11_Event)this.Application).Startup -= Application_Startup;
                        ((Outlook.ApplicationEvents_11_Event)this.Application).Quit -= Application_Quit;
                        ((Outlook.ApplicationEvents_11_Event)this.Application).NewMailEx -= Application_NewMailEx;
                        ((Outlook.ApplicationEvents_11_Event)this.Application).ItemSend -= Application_ItemSend;
                    }
                }
                catch (Exception ex)
                {
                    Log.Error("ConnectApplicationEvents: Error occured. " + ex.ToString());
                }
            }
        }

        /// <summary>
        /// Connects all explorers with their respective events and the Explorers collection
        /// with the NewExplorer event.
        /// <param name="connect">Whether to connect or disconnect the explorers.</param>
        /// </summary>
        private void ConnectWatchedExplorers(bool connect)
        {
            try
            {
                if (connect)
                {
                    this.explorers = this.Application.Explorers;

                    for (int i = 1; i <= this.explorers.Count; i++)
                    {
                        this.watchedExplorers.Add(new WatchedExplorer(this.explorers[i]));
                    }

                    this.explorers.NewExplorer += Explorers_NewExplorer;
                }
                else
                {
                    this.explorers.NewExplorer -= Explorers_NewExplorer;

                    for (int i = 0; i < this.watchedExplorers.Count; i++)
                    {
                        this.RemoveFromWatchedExplorers(this.watchedExplorers[i]);
                        this.watchedExplorers[i].Dispose();
                    }

                    this.explorers = null;
                }
            }
            catch (Exception ex)
            {
                Log.Error("ConnectWatchedExplorers: Error occured. " + ex.ToString());
            }
        }

        /// <summary>
        /// Event handler for when a new explorer is being opened and 
        /// added to the Explorers collection.
        /// </summary>
        /// <param name="explorer">The newly opened explorer.</param>
        private void Explorers_NewExplorer(Outlook.Explorer explorer)
        {
            this.AddToWatchedExplorers(new WatchedExplorer(explorer));
        }

        /// <summary>
        /// Connects all inspectors with their respective events and the Inspectors collection
        /// with the Newinspector event.
        /// <param name="connect">Whether to connect or disconnect the inspectors.</param>
        /// </summary>
        private void ConnectWatchedInspectors(bool connect)
        {
            try
            {
                if (connect)
                {
                    this.inspectors = this.Application.Inspectors;

                    for (int i = 1; i <= this.inspectors.Count; i++)
                    {
                        this.watchedInspectors.Add(new WatchedInspector(this.inspectors[i]));
                    }

                    this.inspectors.NewInspector += Explorers_NewInspector;
                }
                else
                {
                    this.inspectors.NewInspector -= Explorers_NewInspector;

                    for (int i = 0; i < this.watchedInspectors.Count; i++)
                    {
                        this.RemoveFromWatchedInspectors(this.watchedInspectors[i]);
                        this.watchedInspectors[i].Dispose();
                    }

                    this.inspectors = null;
                }
            }
            catch (Exception ex)
            {
                Log.Error("ConnectWatchedInspectors: Error occured. " + ex.ToString());
            }
        }

        /// <summary>
        /// Event handler for when a new inspector is being opened and 
        /// added to the Inspectors collection.
        /// </summary>
        /// <param name="inspector">The newly opened inspector.</param>
        private void Explorers_NewInspector(Outlook.Inspector inspector)
        {
            this.AddToWatchedInspectors(new WatchedInspector(inspector));
        }

        /// <summary>
        /// Enables or disables the automatic deletion of old autoconsume messages.
        /// This gets automatically disabled again if no autoconsume messages are found in the inbox.
        /// <param name="enable">Whether to enable or disable the automatic cleaning.</param>
        /// </summary>
        internal void ToggleInboxCleaning(bool enable)
        {
            if (Thread.CurrentThread.ManagedThreadId != this.mainThreadId)
            {
                Task.Run(() =>
                {
                    DialogWindow.UIThreadDispatcher?.Invoke(() =>
                    {
                        this.ToggleCleaning(enable);
                    });
                });
            }
            else
            {
                this.ToggleCleaning(enable);
            }
        }

        /// <summary>
        /// Enables or disables the automatic deletion of old autoconsume messages.
        /// This gets automatically disabled again if no autoconsume messages are found in the inbox.
        /// <param name="enable">Whether to enable or disable the automatic cleaning.</param>
        /// </summary>
        private void ToggleCleaning(bool enable)
        {
            if ((enable) &&
                (inboxCleaner == null))
            {
                // Only enable if not yet enabled
                inboxCleaner = new System.Windows.Forms.Timer
                {
                    Interval = Globals.TIMEOUT_SYNC_MESSAGE
                };
                inboxCleaner.Tick += InboxCleaner_Tick;
                inboxCleaner.Start();
                Log.Verbose("ToggleInboxCleaning: Automatic inbox cleaning enabled");
            }
            else if ((enable == false) &&
                     (inboxCleaner != null))
            {
                inboxCleaner.Stop();
                inboxCleaner.Dispose();
                inboxCleaner = null;
                Log.Verbose("ToggleInboxCleaning: Automatic inbox cleaning disabled");
            }
        }

        /// <summary>
        /// Event handler for when the inbox cleaner timer has elapsed.
        /// This searches in all inboxes for old keysync messages and deletes them.
        /// </summary>
        private void InboxCleaner_Tick(object sender, EventArgs e)
        {
            int messagesCounter = 0;
            int deletedMessagesCounter = 0;
            Outlook.Folder folder = null;
            Outlook.NameSpace ns = null;
            Outlook.Store store = null;
            Outlook.Stores stores = null;

            Log.Verbose("InboxCleaner_Tick: Checking for messages to delete...");

            try
            {
                ns = this.Application.Session;
                stores = ns?.Stores;

                for (int i = 1; i <= stores?.Count; i++)
                {
                    // Note: accessing the stores can fail if the data file is missing or is in use by another program. 
                    try
                    {
                        store = stores[i];
                    }
                    catch (Exception ex)
                    {
                        store = null;
                        Log.Warning("InboxCleaner_Tick: Failed to get store. " + ex.ToString());
                    }

                    // Clean Inbox folder
                    try
                    {
                        folder = store?.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) as Outlook.Folder;

                        if (folder != null)
                        {
                            folder.Clean(out int foundMessages, out int deletedMessages, store);
                            messagesCounter += foundMessages;
                            deletedMessagesCounter += deletedMessages;
                        }
                    }
                    catch (Exception ex)
                    {
                        folder = null;
                        Log.Warning("InboxCleaner_Tick: Failure getting inbox folder. " + ex.Message);
                    }
                    finally
                    {
                        folder = null;
                    }

                    // Clean Deleted Items folder
                    try
                    {
                        folder = store?.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDeletedItems) as Outlook.Folder;

                        if (folder != null)
                        {
                            folder.Clean(out int foundMessages, out int deletedMessages, store);
                            messagesCounter += foundMessages;
                            deletedMessagesCounter += deletedMessages;
                        }
                    }
                    catch (Exception ex)
                    {
                        folder = null;
                        Log.Warning("InboxCleaner_Tick: Failure getting deleted items folder. " + ex.Message);
                    }
                    finally
                    {
                        folder = null;
                    }

                    // Clean Sync folder
                    try
                    {
                        folder = store?.GetPEPSyncFolder(false);

                        if (folder != null)
                        {
                            folder.Clean(out int foundMessages, out int deletedMessages, store);
                            messagesCounter += foundMessages;
                            deletedMessagesCounter += deletedMessages;
                        }
                    }
                    catch (Exception ex)
                    {
                        folder = null;
                        Log.Warning("InboxCleaner_Tick: Failure getting Sync folder. " + ex.Message);
                    }
                    finally
                    {
                        folder = null;
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("InboxCleaner_Tick: Failure occured, " + ex.ToString());
            }
            finally
            {
                folder = null;
                ns = null;
                store = null;
                stores = null;
            }

            // If no keys sync messages were found, disable the automatic cleaning.
            if (messagesCounter == 0)
            {
                Log.Verbose("InboxCleaner_Tick: No more 'pEp Internal' messages were found in the inbox. Disabling cleaning...");
                Globals.ThisAddIn.ToggleInboxCleaning(false);
            }

            Log.Verbose("InboxCleaner_Tick: Inbox cleaning finished. Deleted " + deletedMessagesCounter + " items.");
        }

        /// <summary>
        /// Event handler for when the add-in is shutdown.
        /// Importantly: In Outlook, The Shutdown event is raised only when the user 
        /// disables the VSTO Add-in by using the COM Add-ins dialog box in Outlook. 
        /// It is not raised when Outlook exits. This is why Application_Quit is used instead.
        /// See: https://msdn.microsoft.com/en-us/library/7xy91eax.aspx
        /// </summary>
        private void ThisAddIn_Shutdown(object sender, EventArgs e)
        {
            return;
        }

        /// <summary>
        /// Event handler for when the application is starting up.
        /// This occurs after all add-in programs have been loaded.
        /// See: https://msdn.microsoft.com/en-us/library/ff869298.aspx
        /// </summary>
        private void Application_Startup()
        {
            /* Open the Reader splash screen if the user has not disabled it.
             * The splash is opened with a Timer in order to keep it from slowing initialization on this thread.
             */
            if ((Globals.RELEASE_MODE == Globals.ReleaseMode.Reader) &&
                (this._Settings.IsReaderSplashEnabled == true))
            {
                System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer
                {
                    Interval = 6000 // Interval to ensure Outlook loads
                };
                timer.Tick += (s, ev) =>
                {
                    var splashScreen = new FormReaderSplash();
                    splashScreen.Show();
                    timer.Stop();
                    timer.Dispose();
                };
                timer.Start();
            }
        }

        /// <summary>
        /// Event handler for when the application is quit.
        /// This is the last user code to execute in the add-in.
        /// See: https://msdn.microsoft.com/en-us/library/ff869760.aspx
        /// </summary>
        private void Application_Quit()
        {
            // Disconnect global events
            Globals.ConnectEvents(false);

            // Disconnect application events
            this.ConnectApplicationEvents(false);

            // Disconnect watched items
            this.ConnectWatchedExplorers(false);
            this.ConnectWatchedInspectors(false);

            // Save configuration
            this._Settings.SaveToRegistry();
            this._Settings.PropertyChanged -= Settings_PropertyChanged;

#if DEBUG
            WPFBindingExceptionListener.Detach();
#endif

            // Log loaded items
            // Log.Verbose("Application_Quit: Item loaded count=" + this.watchedMailItems.Count.ToString());

            if (ThisAddIn._PEPEngine != null)
            {
#if !READER_RELEASE_MODE
                // Disconnect callbacks, DO NOT use the PEPEngine property accessor (only use existing instance)
                try
                {
                    ThisAddIn._PEPEngine.UnregisterCallbacks();
                }
                catch (Exception ex)
                {
                    Log.Error("Application_Quit: Error unregistering callbacks.. " + ex.ToString());
                }
                finally
                {
                    this.adapterCallbacks = null;
                }
#endif
                // Release the pEp engine
                try
                {
                    Marshal.FinalReleaseComObject(ThisAddIn._PEPEngine);
                }
                catch (Exception ex)
                {
                    Log.Error("Application_Quit: Error releasing engine. " + ex.ToString());
                }
                finally
                {
                    ThisAddIn._PEPEngine = null;
                }
            }

            // Stop inbox cleaning timer
            if (inboxCleaner != null)
            {
                inboxCleaner.Stop();
                inboxCleaner.Dispose();
            }

            Log.Archive();

#if DEBUG
            // Shut down the adapter so that it doesn't block new builds
            foreach (var adapterProcess in Process.GetProcessesByName("pEpCOMServerAdapter"))
            {
                adapterProcess.Kill();
            }
#endif
        }

        /// <summary>
        /// Event handler for when one or more new mail items are received.
        /// See: https://msdn.microsoft.com/en-us/library/office/ff869202.aspx
        /// </summary>
        /// <param name="entryIdCollection">A collection of EntryIDs (separated by ',')
        /// for each new mail item.</param>
        private void Application_NewMailEx(string entryIdCollection)
        {
            Log.Verbose("Application_NewMailEx: Started processing new items.");

            if ((string.IsNullOrEmpty(entryIdCollection) == false) &&
                (entryIdCollection.Split(',') is string[] idList))
            {
                foreach (string entryId in idList)
                {
                    try
                    {
                        // Try to push to decryption stack
                        ThisAddIn.DecryptionStack.TryPush(entryId);
                    }
                    catch (Exception ex)
                    {
                        Log.Error("Application_NewMailEx: Error processing item entryId. " + ex.ToString());
                    }
                }
            }

            Log.Verbose("Application_NewMailEx: Complete (decryption likely ongoing in background threads).");

        }

        // NOTE 08/30/2017: Removed this handler again. Tests showed that this event is occasionally called very often
        // and is not suitable to replace the Items_ItemAdd event. Removed due to possible performance overhead during
        // connection of new account with lots of mails.
        /// <summary>
        /// This event handler was added to test when the event is called and if it's more reliable than NewMailEx.
        /// </summary>
        /// <param name="item"></param>
        //private void Application_ItemLoad(object item)
        //{
        //    Log.Verbose("Application_ItemLoad called.");
        //}

        /// <summary>
        /// Event handler that occurs whenever an Outlook item is loaded into memory.
        /// See: https://msdn.microsoft.com/en-us/library/office/ff868544.aspx
        /// </summary>
        /// <param name="Item">A weak object reference for the loaded Outlook item.</param>
        //private void Application_ItemLoad(object item)
        //{
        //    WatchedMailItem newItem;

        //    if (item is Outlook.MailItem)
        //    {
        //        newItem = new WatchedMailItem(item as Outlook.MailItem);
        //        newItem.Disposed += WatchedMailItem_Disposed;

        //        this.watchedMailItems.Add(newItem);
        //    }

        //    return;
        //}

        /// <summary>
        /// Event handler for when the application is about to send an item.
        /// This event is called before the mail item is sent and will encrypt the data as necessary.
        /// See: https://msdn.microsoft.com/en-us/library/office/ff865076.aspx
        /// </summary>
        /// <param name="item">The outlook item being sent.</param>
        /// <param name="cancel">Whether to cancel the item send.</param>
        private void Application_ItemSend(object item, ref bool cancel)
        {
            Log.Verbose("Application_ItemSend: Send started");

            if ((this.isItemSendHandlerEnabled) &&
                (item is Outlook.MailItem omi))
            {
                // Only process if pEp is enabled
                if (omi?.GetIsSendProcessingEnabled() == true)
                {
                    // Check if a processing state has been set
                    MailItemExtensions.ProcessingState? processingState = omi.GetProcessingState();

                    // If the message has already been processed, just send it out
                    if (processingState == MailItemExtensions.ProcessingState.Processed)
                    {
                        Log.Verbose("Application_ItemSend: Message already processed. Sending directly...");
                        return;
                    }
                    else if (processingState == MailItemExtensions.ProcessingState.ProcessedAvoidWinmailDat)
                    {
                        Log.Verbose("Application_ItemSend: Message already processed. Preventing Winmail.dat and sending directly...");
                        omi.AvoidWinmailDatAttachment();
                        return;
                    }

                    // Check for special cases
                    bool processMessage = true;

                    // If S/MIME is enabled, add pEp header and do not process
                    if (omi.GetIsSMIMEEnabled())
                    {
                        omi.SetPEPProperty(MailItemExtensions.PEPProperty.PEPProtocolVersion, "2.1");
                        processMessage = false;
                    }
                    else if (omi.GetIsForcefullyProtected())
                    {
                        // If ForceProtected, create special mail
                        Log.Info("Application_ItemSend: Sending forcefully protected message.");

                        if (ForceProtectionProtocol.ProcessOutgoingMessage(omi))
                        {
                            Log.Verbose("Application_ItemSend: Successfully processed outgoing message.");
                        }
                        else
                        {
                            cancel = true;
                            processMessage = false;
                            Log.Error("Application_ItemSend: Error creating forcefully protected message.");
                        }
                    }

                    // If no special case was found, process normally
                    if (processMessage)
                    {
                        string sendUnencryptedWarning = Properties.Resources.Message_SendError + Environment.NewLine + Environment.NewLine + Properties.Resources.Message_SendUnencryptedConfirmation;
                        DialogResult result;
                        Globals.ReturnStatus status;

                        try
                        {
                            Log.Verbose("Application_ItemSend: Starting encryption and message processing.");

                            status = PEPMessage.Create(omi, out PEPMessage message);
                            if (status == Globals.ReturnStatus.Success)
                            {
                                /* If the message was marked to be processed in the background, cancel the normal
                                 * send process and process the message further in the dedicated method.
                                 */
                                if (processingState == MailItemExtensions.ProcessingState.ProcessInBackground)
                                {
                                    cancel = true;
                                    Log.Verbose("Application_ItemSend: Canceling normal sending and initiating background processing.");
                                    omi.ProcessAndSendMessageAsync(message);
                                    return;
                                }
                                else
                                {
                                    // Process outgoing message
                                    Log.Verbose("Application_ItemSend: Normal processing.");

                                    if (MsgProcessor.ProcessSentMessage(message, Globals.ThisAddIn.Settings.ExtraKeys, out PEPMessage processedMessage) != Globals.ReturnStatus.Success)
                                    {
                                        throw new Exception("Error processing message.");
                                    }

                                    // Apply processed message to outgoing item
                                    processedMessage.ApplyTo(omi, false, false, false);

                                    // Set Reply or forward icon on original message if needed
                                    string originalEntryId = omi.GetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_ORIG_ENTRY_ID) as string;
                                    if (string.IsNullOrEmpty(originalEntryId) == false)
                                    {
                                        omi.AddReplyIconsToOriginal(originalEntryId);
                                    }

                                    // Avoid creation of 'winmail.dat' attachment if needed
                                    if (processedMessage.IsSecure == false)
                                    {
                                        omi.AvoidWinmailDatAttachment();
                                    }
                                }
                            }
                            else if (status == Globals.ReturnStatus.FailureNoConnection)
                            {
                                // Don't send the original message
                                cancel = true;

                                if (omi?.GetIsAutoConsume() != true)
                                {
                                    MessageBox.Show(Properties.Resources.Message_SendingNoConnection,
                                                    Properties.Resources.Message_TitlePEPError,
                                                    MessageBoxButtons.OK,
                                                    MessageBoxIcon.Warning);
                                }
                            }
                            else
                            {
                                // Throw the error out to the main catch
                                throw (new Exception("Failed to create PEPMessage"));
                            }
                        }
                        catch (Exception ex)
                        {
                            Log.Error("Application_ItemSend: Send failure, " + ex.ToString());

                            if (processingState == MailItemExtensions.ProcessingState.ProcessInBackground)
                            {
                                omi.Display();
                            }

                            // Ask the user to continue (not for automatic messages)
                            if (omi?.GetIsAutoConsume() != true)
                            {
                                result = MessageBox.Show(sendUnencryptedWarning,
                                                         Properties.Resources.Message_TitlePEPError,
                                                         MessageBoxButtons.YesNo,
                                                         MessageBoxIcon.Error);
                                cancel = (result != DialogResult.Yes);
                            }

                            Log.Info("Application_ItemSend: Error during sending. " + (cancel ? "User aborted sending." : "user chose to send mail unencrypted."));
                        }
                    }
                }
                else if (omi != null)
                {
                    Log.Info("Application_ItemSend: Item skipped because pEp is disabled or message is sent forcefully unencrypted.");

                    // Add disclaimer if needed
                    omi.AddDisclaimer();

                    // Avoid creation of 'winmail.dat' attachment
                    omi.AvoidWinmailDatAttachment();
                }
                else
                {
                    Log.Error("Application_ItemSend: " + ((item == null) ? "item is null" : "error casting item to Outlook.MailItem"));
                }

                omi = null;
            }
            else
            {
                Log.Verbose("Application_ItemSend: Item is null, not a MailItem or item send handler is disabled.");
            }

            Log.Verbose("Application_ItemSend: Completed. cancel=" + cancel.ToString());
        }

        /// <summary>
        /// Event handler for when a property changes within the active settings.
        /// Warning: This code should be kept synchronized with the SyncWithSettings method.
        /// </summary>
        private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            PEPSettings settings = sender as PEPSettings;
            Log.Verbose("Settings_PropertyChanged: Setting changed for property " + e?.PropertyName ?? "<null>");

            /* Note: IsSyncEnabled and IsPassphraseForNewKeysEnabled aren't caught here
             * because they need a more targeted treatment in different places of the app.
             */
            switch (e.PropertyName)
            {
                case (nameof(PEPSettings.HideInternalMessages)):
                    {
                        // Un/set rules and view filters if necessary
                        this.SetViewFilters(settings.HideInternalMessages);
                        break;
                    }
                case (nameof(PEPSettings.IsPassiveModeEnabled)):
                    {
                        // Sync IsPassiveModeEnabled with engine
                        ThisAddIn.PEPEngine.PassiveMode(settings.IsPassiveModeEnabled);
                        break;
                    }
                case (nameof(PEPSettings.IsPEPFolderVisible)):
                    {
                        // Sync IsPEPFolderVisible with Outlook
                        this.SetPEPStoreRootFolderVisibility();
                        break;
                    }
                case (nameof(PEPSettings.IsPEPSyncFolderEnabled)):
                    {
                        // Un/set rules if necessary
                        this.SetRulesAndViewFilters();
                        break;
                    }
                case (nameof(PEPSettings.IsPrivacyStatusBarEnabled)):
                    {
                        // Create or remove task panes
                        this.watchedExplorers?.ForEach((explorer) =>
                        {
                            explorer.CreateOrRemoveTaskPanePrivacyStatus();
                        });
                        this.watchedInspectors?.ForEach((inspector) =>
                        {
                            inspector.CreateOrRemoveTaskPanePrivacyStatus();
                        });
                        this.RecalculateAllWindows(null);
                        break;
                    }
                case (nameof(PEPSettings.IsUnencryptedSubjectEnabled)):
                    {
                        // Sync IsUnencryptedSubjectEnabled with engine
                        ThisAddIn.PEPEngine.UnencryptedSubject(settings.IsUnencryptedSubjectEnabled);
                        break;
                    }
                case (nameof(PEPSettings.IsVerboseLoggingEnabled)):
                    {
                        // Sync IsVerboseLoggingEnabled with engine
                        ThisAddIn.PEPEngine.VerboseLogging(settings.IsVerboseLoggingEnabled);
                        break;
                    }
                default:
                    break;
            }
        }

        /// <summary>
        /// Event handler that occurs whenever a watched mail item is being disposed.
        /// </summary>
        //private void WatchedMailItem_Disposed(object sender, EventArgs e)
        //{
        //    WatchedMailItem item = sender as WatchedMailItem;

        //    this.watchedMailItems.Remove(item);
        //    item.Disposed -= WatchedMailItem_Disposed;

        //    return;
        //}

        /**************************************************************
         * 
         * Sub-classes
         * 
         *************************************************************/

        /// <summary>
        /// Stores a MailItem with connected events after it is loaded by Outlook.
        /// </summary>
        //private class WatchedMailItem : IDisposable
        //{
        //    /// <summary>
        //    /// Occurs when the component is disposed by a call to the Dispose method.
        //    /// </summary>
        //    public event EventHandler Disposed;

        //    /// <summary>
        //    /// Gets the MailItem object.
        //    /// </summary>
        //    private Outlook.MailItem mailItem;

        //    /**************************************************************
        //     * 
        //     * Constructors/Destructors
        //     * 
        //     *************************************************************/

        //    /// <summary>
        //    /// Primary constructor.
        //    /// </summary>
        //    /// <param name="mailItem">The mail item to watch.</param>
        //    public WatchedMailItem(Outlook.MailItem mailItem)
        //    {
        //        this.mailItem = mailItem;

        //        if (this.mailItem != null)
        //        {
        //            ((Outlook.ItemEvents_10_Event)this.mailItem).BeforeRead += MailItem_BeforeRead;
        //            ((Outlook.ItemEvents_10_Event)this.mailItem).Unload += MailItem_Unload;
        //            ((Outlook.ItemEvents_10_Event)this.mailItem).Write += MailItem_Write;
        //        }
        //    }

        //    /// <summary>
        //    /// Destructor.
        //    /// </summary>
        //    ~WatchedMailItem()
        //    {
        //        this.Dispose();
        //    }

        //    /**************************************************************
        //     * 
        //     * Methods
        //     * 
        //     *************************************************************/

        //    /// <summary>
        //    /// Releases all resources and disconnects internal events.
        //    /// </summary>
        //    public void Dispose()
        //    {
        //        if (this.mailItem != null)
        //        {
        //            ((Outlook.ItemEvents_10_Event)this.mailItem).BeforeRead -= MailItem_BeforeRead;
        //            ((Outlook.ItemEvents_10_Event)this.mailItem).Unload -= MailItem_Unload;
        //            ((Outlook.ItemEvents_10_Event)this.mailItem).Write -= MailItem_Write;

        //            // Marshal.ReleaseComObject(this.mailItem);
        //            this.mailItem = null;

        //            // Raise the disposed event
        //            this.Disposed.Invoke(this, new EventArgs());
        //        }

        //        return;
        //    }

        //    /**************************************************************
        //     * 
        //     * Event Handling
        //     * 
        //     *************************************************************/

        //    /// <summary>
        //    /// Event handler for when the mail item is unloaded from memory (programmatically or by user action).
        //    /// See: https://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook.itemevents_10_event.unload.aspx
        //    /// </summary>
        //    private void MailItem_Unload()
        //    {
        //        this.Dispose();
        //        return;
        //    }

        //    /// <summary>
        //    /// Event handler that occurs before Outlook begins to read the properties for the mail item.
        //    /// See: https://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook.itemevents_10_event.beforeread.aspx
        //    /// </summary>
        //    private void MailItem_BeforeRead()
        //    {
        //        return;
        //    }

        //    /// <summary>
        //    /// Event handler for when the mail item is saved.
        //    /// See: https://msdn.microsoft.com/en-us/library/office/ff868664.aspx
        //    /// </summary>
        //    /// <param name="cancel">Whether to cancel the item write.</param>
        //    private void MailItem_Write(ref bool cancel)
        //    {
        //        return;
        //    }
        //}
    }
}