ThisAddIn.cs
author Thomas
Mon, 25 Mar 2019 13:27:16 +0100
branchsync
changeset 2610 09fde2338362
parent 2594 b4351ec5c330
parent 2607 8a9eddf5347a
child 2627 47eb50143f2a
permissions -rw-r--r--
Merge with default

using pEp.Extensions;
using pEp.UI;
using pEpCOMServerAdapterLib;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
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

        /// <summary>
        /// Custom event for when the add-in is starting for the first time.
        /// </summary>
        internal event EventHandler FirstStartup;

        private List<KeyValuePair<CultureInfo, string>> _LanguageList       = null;
        private static pEpEngine                        _PEPEngine          = null;
        private static Exception                        _PEPEngineException = 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 FormControlOptions.State    lastOptionsState            = null;
        private List<WatchedFolder>         watchedFolders              = new List<WatchedFolder>();
        private System.Windows.Forms.Timer  inboxCleaner                = null;

        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;

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

                        return (null);
                    }
                }

                return (ThisAddIn._PEPEngine);
            }
        }

        /// <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 accountAdded = false;
            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
                            var accountSettings = this._Settings.GetAccountSettings(account.SmtpAddress);
                            if (accountSettings == null)
                            {
                                // Create pEp settings for the new account
                                account.CreatePEPSettings();
                                accountAdded = true;
                            }
                            else
                            {
                                // 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
                        {
                            Log.Warning("SyncAccountsList: Invalid Smtp address detected, skipping account.");
                        }

                        account = null;
                    }

                    // Add shared mail boxes
                    stores = ns?.Stores;
                    for (int i = 1; i <= stores.Count; i++)
                    {
                        store = stores[i];
                        if (store.ExchangeStoreType == Outlook.OlExchangeStoreType.olExchangeMailbox)
                        {
                            if (store.GetSmtpAddress(out string address, out string userName))
                            {
                                if (this.Settings.GetAccountSettings(address) == null)
                                {
                                    // Add to account settings if necessary
                                    Log.Verbose("GetSmtpAddress: Adding shared account " + address);
                                    this.Settings.AccountSettingsList.Add(new PEPSettings.PEPAccountSettings()
                                    {
                                        SmtpAddress = address,
                                        IsSecureStorageEnabled = false,
                                        IsSyncEnabled = false,
                                        Type = Outlook.OlAccountType.olExchange.ToString(),
                                        UserName = userName
                                    });

                                    accountAdded = true;
                                }

                                sharedAccountStores.Add(address);
                            }
                        }
                    }

                    // 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);
                        }
                    }

                    if (accountAdded)
                    {
                        // Set view filters and rules
                        this.SetRulesAndViewFilters(this._Settings?.HideInternalMessages ?? PEPSettings.HIDE_INTERNAL_MESSAGES_DEFAULT);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("SyncAccountsList: Error occured. " + ex.ToString());
            }
            finally
            {
                ns = null;
                account = null;
                accounts = null;
            }
        }

        /// <summary>
        /// Syncronizes the current settings class with the pEp for Outlook instance and the pEp engine.
        /// Warning: This code should be kept synchronized with the Settings_PropertyChanged event.
        /// <param name="syncAccountsList">Whether to also update the accounts list in pEp settings. 
        /// </summary>
        internal void SyncWithSettings(bool syncAccountsList = false)
        {
            bool exists;
            string dialogMessage = string.Empty;
            pEpIdentity[] ownIdentities;
            List<KeyValuePair<CultureInfo, string>> languages;

            try
            {
                if (this._Settings != null)
                {
                    // Update the Outlook accounts list with the one maintained in pEp settings if needed.
                    if (syncAccountsList)
                    {
                        this.SyncAccountsList();
                    }

                    /* Sync the account settings with the engine's own identity flags.
                     * Warning: RegisterMyself() must already have been called so the engine has all own identities.
                     * The engine can commonly have more own identities than what is in the accounts list.
                     * This isn't a problem though and engine-only identities will not be changed here.
                     */
                    ownIdentities = ThisAddIn.PEPEngine.OwnIdentitiesRetrieve();
                    foreach (PEPSettings.PEPAccountSettings acctSettings in this._Settings.AccountSettingsList)
                    {
                        // Find the own identity that matches with the account settings
                        foreach (pEpIdentity ident in ownIdentities)
                        {
                            pEpIdentity ownIdent = ident;

                            if (acctSettings.EqualsByAddress(ownIdent.Address))
                            {
                                // Sync the engine's own identity flags with the account or global settings
                                if (this._Settings.IsSyncEnabledForAllAccounts)
                                {
                                    if (ownIdent.GetIsSyncEnabled() == false)
                                    {
                                        ThisAddIn.PEPEngine.UnsetIdentityFlags(ref ownIdent, pEpIdentityFlags.pEpIdfNotForSync);
                                    }
                                }
                                else if (ownIdent.GetIsSyncEnabled() != acctSettings.IsSyncEnabled)
                                {
                                    if (acctSettings.IsSyncEnabled)
                                    {
                                        ThisAddIn.PEPEngine.UnsetIdentityFlags(ref ownIdent, pEpIdentityFlags.pEpIdfNotForSync);
                                    }
                                    else
                                    {
                                        ThisAddIn.PEPEngine.SetIdentityFlags(ref ownIdent, pEpIdentityFlags.pEpIdfNotForSync);
                                    }
                                }
                                break;
                            }
                        }
                    }

                    // Sync IsKeyServerUsed with engine
                    if (this._Settings.IsKeyServerUsed)
                    {
                        ThisAddIn.PEPEngine.StartKeyserverLookup();
                    }
                    else
                    {
                        ThisAddIn.PEPEngine.StopKeyserverLookup();
                    }

                    // Sync IsPassiveModeEnabled with engine
                    ThisAddIn.PEPEngine.PassiveMode(this._Settings.IsPassiveModeEnabled);

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

                    // Sync IsUnencryptedSubjectEnabled with engine
                    ThisAddIn.PEPEngine.UnencryptedSubject(this._Settings.IsUnencryptedSubjectEnabled);

                    // Sync IsVerboseLoggingEnabled with engine
                    ThisAddIn.PEPEngine.VerboseLogging(this._Settings.IsVerboseLoggingEnabled);

                    // Sync TrustwordsCulture with engine
                    if (this._Settings.TrustwordsCulture != null)
                    {
                        // Validate it exists in the engine list
                        exists = false;
                        languages = this.LanguageList;
                        foreach (KeyValuePair<CultureInfo, string> entry in languages)
                        {
                            if (entry.Key.LCID == this._Settings.TrustwordsCulture.LCID)
                            {
                                exists = true;
                                break;
                            }
                        }

                        if (exists == false)
                        {
                            // Reset to default
                            this._Settings.TrustwordsCulture = this.GetActiveUICulture();
                            Log.Warning("SyncWithSettings: Invalid TrustwordsCulture detected, setting to default.");
                        }
                    }
                    else
                    {
                        // Reset to default
                        this._Settings.TrustwordsCulture = this.GetActiveUICulture();
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("SyncWithSettings: Failure occured, " + ex.ToString());
            }
        }

        /// <summary>
        /// Compares the given settings with the current state of Outlook and detects any changes.
        /// This is currently only used to identify first startup.
        /// </summary>
        /// <param name="lastSettings">The last settings to compare with. 
        /// This should be the settings just loaded from the registry at startup (last saved settings).</param>
        private void DetectChangesFromLastSettings(PEPSettings lastSettings)
        {
            // Detect first startup
            if (lastSettings.IsFirstStartupComplete == false)
            {
                this.FirstStartup?.Invoke(this, new EventArgs());
            }
        }

        /// <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)
        {
            pEpIdentity ownIdentity;

            if (acctSettings != null)
            {
                // Create pEpIdentity
                ownIdentity = new pEpIdentity
                {
                    Address = acctSettings.SmtpAddress,
                    UserId = PEPSettings.PEP_OWN_USER_ID,
                    UserName = acctSettings.UserName
                };

                // Set not for sync flag if necessary
                if ((acctSettings.Type.Contains("Imap")) ||
                    ((Globals.ThisAddIn.Settings.IsSyncEnabledForAllAccounts == false) &&
                     (acctSettings.IsSyncEnabled == false)))
                {
                    ownIdentity.Flags = pEpIdentityFlags.pEpIdfNotForSync;
                }

                // 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
                {
                    ThisAddIn.PEPEngine.Myself(ownIdentity);
                }
                catch (COMException ex)
                {
                    Log.Warning("RegisterMyself: Engine returned exception, " + ex.ToString());
                }
            }
            else
            {
                Log.Error("RegisterMyself: account settings are null.");
            }
        }

        /// <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 = 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
                                {
                                    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 (ThisAddIn.PEPEngine.BlacklistIsListed(fpr1) == false)
                            {
                                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 = this._Settings.GetAccountSettings(entry.SmtpAddress);

                    if (acctSettings != null)
                    {
                        // Modify existing value
                        // The following are skipped: SentFolderEntryId, SmtpAddress & Type
                        acctSettings.IsDecryptAlwaysEnabled = entry.IsDecryptAlwaysEnabled;
                        acctSettings.IsPEPEnabled = entry.IsPEPEnabled;
                        acctSettings.IsSecureStorageEnabled = entry.IsSecureStorageEnabled;
                        acctSettings.IsSyncEnabled = entry.IsSyncEnabled;
                    }
                    else
                    {
                        Log.Warning("SetOptionsState: Existing account settings not found.");
                    }
                }

                // Un/set rules and view filters if necessary
                if (state.HideInternalMessages != this._Settings.HideInternalMessages)
                {
                    this.SetRules(state.HideInternalMessages);
                }
                this._Settings.HideInternalMessages = state.HideInternalMessages;

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

                // Save last state for next options opening
                this.lastOptionsState = state.Copy();

                // Update registry (in case of unforseen 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;
            }

            // Load from last state if possible
            if (this.lastOptionsState != null)
            {
                state = this.lastOptionsState.Copy();
            }
            else
            {
                state = new FormControlOptions.State();
            }

            // Get complete OpenPGP key list
            try
            {
                keylist = 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 = 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.IsAutoUpdateEnabled = this._Settings.IsAutoUpdateEnabled;
            state.IsDeveloperModeEnabled = this._Settings.IsDeveloperModeEnabled;
            state.IsEncryptAllAccountsEnabled = this._Settings.IsEncryptAllAccountsEnabled;
            state.IsKeyServerUsed = this._Settings.IsKeyServerUsed;
            state.IsNeverUnsecureOptionVisible = this._Settings.IsNeverUnsecureOptionVisible;
            state.IsPassiveModeEnabled = this._Settings.IsPassiveModeEnabled;
            state.IsPEPFolderDefaultStore = isDefaultStore;
            state.IsPEPFolderVisible = (isDefaultStore ? true : this._Settings.IsPEPFolderVisible);
            state.IsPrivacyStatusBarEnabled = this._Settings.IsPrivacyStatusBarEnabled;
            state.IsSecurityLossWarningEnabled = this._Settings.IsSecurityLossWarningEnabled;
            state.IsSyncEnabledForAllAccounts = this._Settings.IsSyncEnabledForAllAccounts;
            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 folder = 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 != null)
                    {
                        // Add default inbox folder
                        folder = null;
                        try
                        {
                            folder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
                        }
                        catch (Exception ex)
                        {
                            if (folder != null)
                            {
                                // Marshal.ReleaseComObject(folder);
                                folder = null;
                            }

                            Log.Warning("ConnectWatchedFolders: Failure getting default inbox folder. " + ex.ToString());
                        }

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

                        // Add Sent folder. This is needed so sent mails get decrypted on trusted servers to make the search function working
                        try
                        {
                            folder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail);
                        }
                        catch (Exception ex)
                        {
                            if (folder != null)
                            {
                                // Marshal.ReleaseComObject(folder);
                                folder = null;
                            }

                            Log.Warning("ConnectWatchedFolders: Failure getting default sent folder. " + ex.ToString());
                        }

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

                        //Marshal.ReleaseComObject(store);
                        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(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "pEp", (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 winStorePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Packages", "Microsoft.Office.Desktop_8wekyb3d8bbwe", "LocalCache", "Local", "pEp", (Globals.PEP_DATA_FILE_NAME + ".pst"));

                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) == true) ||
                        (store?.FilePath?.Equals(winStorePath) == 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) == true) ||
                            (store?.FilePath?.Equals(winStorePath) == 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();
                }
                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;
                    }
                    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>
        /// <param name="visible">True if the pEp folder should be visible, otherwise false.</param>
        private void SetPEPStoreRootFolderVisibility(bool visible)
        {
            Outlook.Folder folder = null;
            Outlook.NameSpace ns = null;
            Outlook.Store store = null;

            try
            {
                // If the pEp store hasn't been opened already, try to open it
                if (this._PEPStoreRootFolder == null)
                {
                    this.OpenPEPStoreRootFolder();
                }

                // Check again if the store has been found
                if (this._PEPStoreRootFolder != null)
                {
                    ns = this.Application.Session;
                    store = this._PEPStoreRootFolder.Store;

                    // Never show the default 'system' folders
                    foreach (Outlook.OlDefaultFolders defaultFolder in Enum.GetValues(typeof(Outlook.OlDefaultFolders)))
                    {
                        // Allow deleted items
                        if (defaultFolder != Outlook.OlDefaultFolders.olFolderDeletedItems)
                        {
                            try
                            {
                                folder = (Outlook.Folder)store.GetDefaultFolder(defaultFolder);
                                MapiHelper.SetProperty(folder, MapiProperty.PidTagAttributeHidden, true);
                            }
                            catch { }
                            finally
                            {
                                if (folder != null)
                                {
                                    // Marshal.ReleaseComObject(folder);
                                    folder = null;
                                }
                            }
                        }
                    }

                    // Attempt to add/remove the store from the session which sets visibility
                    if (visible)
                    {
                        try
                        {
                            // Release the temp folder before attempting an open/re-open
                            //Marshal.ReleaseComObject(this._PEPStoreRootFolder);
                            this._PEPStoreRootFolder = null;

                            // After opening it will always be visible
                            this.OpenPEPStoreRootFolder();
                        }
                        catch (Exception ex)
                        {
                            Log.Warning("SetPEPStoreRootFolderVisibility: Failed to make pEp root folder visible. " + ex.ToString());
                        }
                    }
                    else
                    {
                        /* 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 although don't log any errors.
                         */
                        try
                        {
                            ns.RemoveStore(this._PEPStoreRootFolder);
                        }
                        catch { }
                    }
                }
                else
                {
                    Log.Error("SetPEPStoreRootFolderVisibility: Couldn't set visibility. Folder is null.");
                }
            }
            catch (Exception ex)
            {
                Log.Error("SetPEPStoreRootFolderVisibility: Failed, " + ex.ToString());
            }
            finally
            {
                // Release objects
                folder = null;
                ns = null;
                store = null;
            }
        }

        /// <summary>
        /// Sets pEp rules and view filters for a given account.
        /// <param name="account">The Outlook account for which to set rules and view
        /// <param name="enable">Whether to enable or disable rules and view filters.</param>
        /// filters.</param>
        /// </summary>
        internal void SetRulesAndViewFilters(Outlook.Account account,
                                             bool enable)
        {
            // If a failure occured, do not mark first startup as completed (run again next time).
            if ((this.SetRules(account, enable) == false) ||
                 (this.SetInboxViewFilters(account, enable) == false))
            {
                this.Settings.IsFirstStartupComplete = false;
            }
        }

        /// <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(bool enable)
        {
            // If a failure occured, do not mark first startup as completed (run again next time).
            this.Settings.IsFirstStartupComplete = (this.SetRules(enable) && this.SetInboxViewFilters(enable));
        }

        /// <summary>
        /// Sets the rules to hide pEp internal category MailItems for the given account.
        /// </summary>
        /// <param name="account">The account to set the rules for.</param>
        /// <param name="enable">Whether to enable or disable the rules.</param>
        /// <returns>True, if the rules were correctly set for this account. Otherwise false.</returns>
        internal bool SetRules(Outlook.Account account,
                               bool enable)
        {
            bool success = false;
            bool categoriesSet = true;
            bool ruleExists = false;
            Outlook.AssignToCategoryRuleAction action = null;
            Outlook.Rules rules = null;
            Outlook.Rule rule = null;
            Outlook.Store deliveryStore = null;
            Outlook.TextRuleCondition condition = null;

            Log.Verbose("SetRules: Setting rules for single account.");

            try
            {
                // Getting delivery store can fail for new accounts when Outlook is not restarted
                try
                {
                    deliveryStore = account.DeliveryStore;
                }
                catch
                {
                    deliveryStore = null;
                    Log.Warning("SetRules: Failure getting DeliveryStore");
                }

                if (deliveryStore != null)
                {
                    // Get rules
                    try
                    {
                        rules = deliveryStore.GetRules();
                    }
                    catch
                    {
                        rules = null;
                        Log.Warning("SetRules: Failure getting rules");
                    }

                    // Adding rule to the rules collection
                    if (rules != null)
                    {
                        try
                        {
                            // Check if rule already was created, create new otherwise (if needed)
                            try
                            {
                                rule = rules[Globals.PEP_INTERNAL_CATEGORY_NAME];
                                ruleExists = true;
                                success = true;
                            }
                            catch
                            {
                                if (enable)
                                {
                                    rule = rules.Create(Globals.PEP_INTERNAL_CATEGORY_NAME, Outlook.OlRuleType.olRuleReceive);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            rule = null;
                            Log.Warning("SetRules: Failure creating rule. " + ex.ToString());
                        }

                        // Create or remove rule
                        if (enable)
                        {
                            // Define rule if it didn't exist previously
                            if ((rule != null) &&
                                (ruleExists == false))
                            {
                                // Condition: message header includes defined string
                                try
                                {
                                    condition = rule.Conditions?.MessageHeader;
                                    condition.Text = new string[] { PEPMessage.PR_PEP_AUTO_CONSUME_NAME.ToLower() };
                                    condition.Enabled = true;
                                }
                                catch (Exception ex)
                                {
                                    condition = null;
                                    Log.Warning("SetRules: Failure setting condition. " + ex.ToString());
                                }

                                if (condition != null)
                                {
                                    // Action: assign "pEp internal" category
                                    try
                                    {
                                        if (this.CreatePEPCategories() == false)
                                        {
                                            categoriesSet = false;
                                        }

                                        action = rule.Actions.AssignToCategory;
                                        action.Categories = new string[] { Globals.PEP_INTERNAL_CATEGORY_NAME };
                                        action.Enabled = true;
                                    }
                                    catch (Exception ex)
                                    {
                                        action = null;
                                        Log.Warning("SetRules: Failure adding category. " + ex.ToString());
                                    }

                                    if ((condition != null) &&
                                        (action != null))
                                    {
                                        rules.Save();
                                        rule.Execute();
                                        success = categoriesSet;
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (ruleExists)
                            {
                                rules.Remove(Globals.PEP_INTERNAL_CATEGORY_NAME);
                                rules.Save();
                            }

                            success = true;
                        }
                    }
                }

                Log.Verbose("SetRules: {0} processed {1}.", account?.SmtpAddress ?? "NULL", success ? "successfully" : "unsuccessfully");
            }
            catch (Exception ex)
            {
                Log.Error("SetRules: Failure occured, " + ex.ToString());
            }
            finally
            {
                action = null;
                condition = null;
                deliveryStore = null;
                rule = null;
                rules = null;
            }

            return success;
        }

        /// <summary>
        /// Sets the rules for all accounts to hide pEp internal category MailItems.
        /// </summary>
        /// <param name="enable">Whether to enable or disable the rules.</param>
        /// <returns>True, if the rules were correctly set. Otherwise false.</returns>
        internal bool SetRules(bool enable)
        {
            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 (this.SetRules(account, enable) == 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 the inbox folder to hide pEp internal category MailItems for the given account.
        /// </summary>
        /// <param name="account">The account to set the inbox view filter for.</param>
        /// <param name="enable">Whether to enable or disable the view filters.</param>
        /// <returns>True, if the filters were correctly set for this account. Otherwise false.</returns>
        internal bool SetInboxViewFilters(Outlook.Account account,
                                          bool enable)
        {
            bool success = false;
            string currentViewName = null;
            Outlook.Store deliveryStore = null;
            Outlook.Folder folder = null;
            Outlook.View view = null;
            Outlook.Views views = null;

            Log.Verbose("SetInboxViewFilters: Setting inbox view filter for single account");

            try
            {
                // Getting delivery store can fail for new accounts when Outlook is not restarted
                try
                {
                    deliveryStore = account.DeliveryStore;
                }
                catch
                {
                    deliveryStore = null;
                    Log.Warning("SetInboxViewFilters: Failure getting DeliveryStore");
                }

                if (deliveryStore != null)
                {
                    // Get the inbox
                    try
                    {
                        folder = (Outlook.Folder)deliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
                    }
                    catch
                    {
                        folder = null;
                        Log.Warning("SetInboxViewFilters: Failure getting inbox folder");
                    }

                    // Change the filter for all views in the inbox
                    if (folder != null)
                    {
                        views = folder.Views;
                        currentViewName = folder.CurrentView?.Name;

                        for (int j = 1; j <= views.Count; j++)
                        {
                            view = views[j];

                            try
                            {
                                // Only set the filter on standard views, if the user creates their own don't modify it
                                if (view.Standard)
                                {
                                    // Clear existing filter -- seems to work better
                                    view.Filter = null;
                                    view.Save();

                                    if ((currentViewName != null) &&
                                        (string.Equals(view.Name, currentViewName, StringComparison.OrdinalIgnoreCase)))
                                    {
                                        view.Apply();
                                    }

                                    if (enable)
                                    {
                                        // Set the filter
                                        if (account.AccountType == Outlook.OlAccountType.olImap)
                                        {
                                            // (1) Hide any deleted IMAP items http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/85700003 = 0
                                            // (2) Hide 'pEp internal' and "pEp processing' category items
                                            view.Filter = "(\"" + MapiProperty.PidLidImapMarkedForDeletion.DaslName + "\" = 0" +
                                                          " AND " +
                                                          "\"urn:schemas-microsoft-com:office:office#Keywords\" <> '" + Globals.PEP_INTERNAL_CATEGORY_NAME + "'" +
                                                          " AND " +
                                                          "\"urn:schemas-microsoft-com:office:office#Keywords\" <> '" + Globals.PEP_PROCESSING_CATEGORY_NAME + "')";
                                        }
                                        else
                                        {
                                            // Hide 'pEp internal' and "pEp processing' category items
                                            view.Filter = "(\"urn:schemas-microsoft-com:office:office#Keywords\" <> '" + Globals.PEP_INTERNAL_CATEGORY_NAME + "'" +
                                                          " AND " +
                                                          "\"urn:schemas-microsoft-com:office:office#Keywords\" <> '" + Globals.PEP_PROCESSING_CATEGORY_NAME + "')";
                                        }

                                        if ((currentViewName != null) &&
                                            (string.Equals(view.Name, currentViewName, StringComparison.OrdinalIgnoreCase)))
                                        {
                                            view.Save();
                                            view.Apply();
                                        }
                                        else
                                        {
                                            view.Save();
                                        }
                                    }
                                }

                                success = true;
                            }
                            catch (Exception ex)
                            {
                                Log.Warning("SetInboxViewFilters: Failed to set view. " + ex.ToString());
                            }

                            view = null;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("SetInboxViewFilters: Failure occured, " + ex.ToString());
            }
            finally
            {
                deliveryStore = null;
                folder = null;
                view = null;
                views = 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>
        /// <returns>True, if the filters were correctly set. Otherwise false.</returns>
        internal bool SetInboxViewFilters(bool enable)
        {
            bool success = true;
            Outlook.Account account = null;
            Outlook.Accounts accounts = null;
            Outlook.NameSpace ns = null;

            Log.Verbose("SetInboxViewFilters: 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 (this.SetInboxViewFilters(account, enable) == false)
                        {
                            success = false;
                        }

                        account = null;
                    }
                }
            }
            catch (Exception ex)
            {
                success = false;
                Log.Error("SetInboxViewFilters: 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>
        /// Creates the pEp internal category for the specified account's store unless it already exists.
        /// </summary>
        internal bool CreatePEPCategories(Outlook.Store deliveryStore)
        {
            bool pEpInternalExists = false;
            bool pEpProcessingExists = false;
            Outlook.Category category = null;
            Outlook.Categories categories = null;

            if (deliveryStore != null)
            {
                try
                {
                    categories = deliveryStore.Categories;

                    // Check if the categories already exists
                    for (int i = 1; i <= categories?.Count; i++)
                    {
                        category = categories[i];

                        if (category?.Name?.Equals(Globals.PEP_INTERNAL_CATEGORY_NAME, StringComparison.OrdinalIgnoreCase) == true)
                        {
                            pEpInternalExists = true;
                        }
                        else if (category?.Name?.Equals(Globals.PEP_PROCESSING_CATEGORY_NAME, StringComparison.OrdinalIgnoreCase) == true)
                        {
                            pEpProcessingExists = true;
                        }

                        category = null;

                        if (pEpInternalExists &&
                            pEpProcessingExists)
                        {
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.Error("CreatePEPCategories: Error checking if category exists. " + ex.ToString());

                    // Just try to create it anyway, this will throw an exception if it already exists
                    pEpInternalExists = false;
                    pEpProcessingExists = false;
                }
                finally
                {
                    category = null;
                    categories = null;
                }

                // If a category was not found, create and add it to account master list
                if (pEpInternalExists == false)
                {
                    try
                    {
                        // Add category
                        categories = deliveryStore.Categories;
                        categories?.Add(Globals.PEP_INTERNAL_CATEGORY_NAME, Outlook.OlCategoryColor.olCategoryColorDarkGreen);
                        pEpInternalExists = true;
                    }
                    catch (Exception ex)
                    {
                        Log.Error("CreatePEPCategories: Error setting pEp Internal category. " + ex.ToString());
                    }
                    finally
                    {
                        categories = null;
                    }
                }

                if (pEpProcessingExists == false)
                {
                    try
                    {
                        // Add category
                        categories = deliveryStore.Categories;
                        categories?.Add(Globals.PEP_PROCESSING_CATEGORY_NAME, Outlook.OlCategoryColor.olCategoryColorDarkGreen);
                        pEpProcessingExists = true;
                    }
                    catch (Exception ex)
                    {
                        Log.Error("CreatePEPCategories: Error settising pEp Processing. " + ex.ToString());
                    }
                    finally
                    {
                        categories = null;
                    }
                }
            }

            return (pEpInternalExists && pEpProcessingExists);
        }

        /// <summary>
        /// Creates the pEp internal category in Outlook for all accounts.
        /// </summary>
        internal bool CreatePEPCategories()
        {
            bool success = true;
            Outlook.Account account = null;
            Outlook.Accounts accounts = null;
            Outlook.NameSpace ns = null;
            Outlook.Store deliveryStore = null;

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

                // Set the pEp internal category for all accounts
                for (int i = 1; i <= accounts?.Count; i++)
                {
                    try
                    {
                        account = accounts[i];

                        // If the account doesn't have pEp enabled, don't create category
                        if ((account?.GetIsPEPEnabled() != true) || (account?.GetIsDecryptAlwaysEnabled() != true))
                        {
                            deliveryStore = account?.DeliveryStore;
                            if ((deliveryStore == null) ||
                                (this.CreatePEPCategories(deliveryStore) == false))
                            {
                                success = false;
                            }
                            deliveryStore = null;
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error("SetPEPInternalCategory: Error setting category for account. " + ex.ToString());
                        success = false;
                    }
                    finally
                    {
                        account = null;
                        deliveryStore = null;
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("SetPEPInternalCategory: Failure occured, " + ex.ToString());
                success = false;
            }
            finally
            {
                account = null;
                accounts = null;
                ns = null;
                deliveryStore = null;
            }

            return success;
        }

        /// <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>
        /// <returns>The active UI culture in Outlook.</returns>
        internal CultureInfo GetActiveUICulture()
        {
            CultureInfo outlookCulture = null;
            CultureInfo pEpCulture = null;
            Office.LanguageSettings lang = null;
            string languageName = null;
            const string defaultLanguageName = "en";

            // The current supported languages. Has to be synced with resources.
            List<string> pEpLanguages = new List<string>
            {
                "ca",
                "de",
                "en",
                "es",
                "fr",
                "hi",
                "it",
                "mr",
                "nl",
                "tr",
                "zh"
             };

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

                /* Get the two letter ISO language name.
                 * This is needed because we have not yet country-specific localization.
                 * If the language is supported, use it. Else, use default language.
                 */
                languageName = outlookCulture.TwoLetterISOLanguageName;
                if (pEpLanguages.Contains(languageName))
                {
                    pEpCulture = new CultureInfo(languageName);
                }
                else
                {
                    pEpCulture = new CultureInfo(defaultLanguageName);
                }
            }
            catch (Exception ex)
            {
                pEpCulture = new CultureInfo(defaultLanguageName);
                Log.Verbose("GetActiveUICulture: Failed to get UI culture, " + ex.ToString());
            }
            finally
            {
                if (lang != null)
                {
                    //Marshal.ReleaseComObject(lang);
                    lang = null;
                }
            }

            return (pEpCulture);
        }

        /// <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)
        {
            CultureInfo culture;

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

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

            // Connect custom ThisAddIn event
            Log.Info("ThisAddIn_Startup: Connect ThisAddIn events");
            this.FirstStartup += ThisAddIn_FirstStartup;

            /* Load settings from Registry and update 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();

#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

            /* 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.DetectChangesFromLastSettings(this._Settings);
            this._Settings.SaveUpdaterConfigToRegistry();
            this.SyncWithSettings();
            this._Settings.PropertyChanged += Settings_PropertyChanged;

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

            // Add reference to KeySyncWizard in order to start the dialog from the main thread
            KeySyncWizard.Wizard = new KeySyncWizard();

            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)
                {
                    // 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
                    this.ToggleInboxCleaning(true);

                    // Create the 'pEp Internal' and 'pEp Processing' categories
                    this.CreatePEPCategories();

                    // Initialization complete
                    Log.Info("ThisAddIn_Startup: Main program started.");
                    this.initialized = true;
                }

                // Startup active parts of engine
                ThisAddIn.PEPEngine.Startup();
            }
            catch (Exception ex)
            {
                string summaryMessage = Properties.Resources.Message_InitError + " " +
                                        Properties.Resources.Message_Reinstall;
                Globals.StopAndSendCrashReport(ex, summaryMessage, false);
            }
        }

        /// <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>
        /// <param name="runOnEnable">Whether to run the inbox cleaning once right away when it gets enabled.</param>
        /// </summary>
        internal void ToggleInboxCleaning(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");

                // Run it once right away
                Task.Run(() =>
                {
                    this.InboxCleaner_Tick(null, null);
                });
            }
            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)
                        {
                            this.CleanFolder(store, folder, out int foundMessages, out int deletedMessages);
                            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)
                        {
                            this.CleanFolder(store, folder, out int foundMessages, out int deletedMessages);
                            messagesCounter += foundMessages;
                            deletedMessagesCounter += deletedMessages;
                        }
                    }
                    catch (Exception ex)
                    {
                        folder = null;
                        Log.Warning("InboxCleaner_Tick: Failure getting deleted items 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>
        /// Deletes all timed-out pEp internal messages in a given folder.
        /// </summary>
        /// <param name="store">The store where this folder is located.</param>
        /// <param name="folder">The folder to be cleaned.</param>
        /// <param name="foundMessages">The pEp internal messages found in this folder.</param>
        /// <param name="deletedMessages">The pEp internal messages that have been deleted.</param>
        private void CleanFolder(Outlook.Store store, Outlook.Folder folder, out int foundMessages, out int deletedMessages)
        {
            deletedMessages = 0;
            foundMessages = 0;
            const string searchFilter = "[Categories] ='" + Globals.PEP_INTERNAL_CATEGORY_NAME + "' Or [Categories] ='" + Globals.PEP_PROCESSING_CATEGORY_NAME + "'";
            Outlook.Items inboxItems = null;
            Outlook.Items filteredInboxItems = null;
            Outlook.MailItem mailItem = null;

            // Process all internal mail items again to check if consumed flag is set
            try
            {
                Log.Verbose("CleanFolder: Cleaning folder " + folder?.FolderPath);

                inboxItems = folder?.Items as Outlook.Items;

                if (inboxItems?.Count > 0)
                {
                    // Get all mails with the "pEp Internal" category
                    filteredInboxItems = inboxItems.Restrict(searchFilter) as Outlook.Items;
                    Log.Verbose("CleanFolder: " + inboxItems.Count + " items found.");

                    if (filteredInboxItems != null)
                    {
                        foundMessages = filteredInboxItems.Count;
                        Log.Verbose("CleanFolder: " + foundMessages + " 'pEp Internal' items found.");

                        for (int j = 1; j <= foundMessages; j++)
                        {
                            try
                            {
                                mailItem = filteredInboxItems[j] as Outlook.MailItem;

                                if (mailItem != null)
                                {
                                    Log.Verbose("CleanFolder: Processing 'pEp Internal' item " + j + "...");

                                    // Check if auto consume message and delete if timed out or delete pEp Internal category
                                    if (mailItem.GetIsAutoConsume())
                                    {
                                        if (DateTime.UtcNow - (mailItem.ReceivedTime.ToUniversalTime()) > TimeSpan.FromMilliseconds(Globals.TIMEOUT_SYNC_MESSAGE))
                                        {
                                            mailItem.PermanentlyDelete(store);
                                            deletedMessages++;
                                        }
                                        else if (Globals.ThisAddIn.Settings.HideInternalMessages == false)
                                        {
                                            if (mailItem.RemoveCategories(Globals.PEP_INTERNAL_CATEGORY_NAME, Globals.PEP_PROCESSING_CATEGORY_NAME))
                                            {
                                                mailItem.Save();
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (mailItem.RemovePEPProcessingCategory())
                                        {
                                            mailItem.Save();
                                        }
                                    }
                                }

                                mailItem = null;
                            }
                            catch (Exception ex)
                            {
                                Log.Error("CleanFolder: Error processing mail item. " + ex.Message);
                            }
                        }
                    }
                }
                else
                {
                    Log.Verbose("CleanFolder: No items were found or folder was null.");
                }
            }
            catch (Exception ex)
            {
                Log.Warning("CleanFolder: Failure occured. " + ex.Message);
            }
            finally
            {
                inboxItems = null;
                filteredInboxItems = null;
                mailItem = null;
            }
        }

        /// <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 first startup of the add-in is detected.
        /// This is a custom event.
        /// </summary>
        private void ThisAddIn_FirstStartup(object sender, EventArgs e)
        {
            Log.Info("ThisAddIn_FirstStartup: First startup detected");

            // Set rules and inbox view filters for all accounts
            Task.Factory.StartNew(() =>
            {
                this.SetRulesAndViewFilters(this._Settings?.HideInternalMessages ?? PEPSettings.HIDE_INTERNAL_MESSAGES_DEFAULT);
            });
        }

        /// <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;

            // Disconnect custom ThisAddIn event
            this.FirstStartup -= ThisAddIn_FirstStartup;

            // 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)
                ThisAddIn._PEPEngine.UnregisterCallbacks();
                this.adapterCallbacks = null;
#endif
                // Release the pEp engine
                Marshal.FinalReleaseComObject(ThisAddIn._PEPEngine);
                ThisAddIn._PEPEngine = null;
            }

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

            Log.Archive();
        }

        /// <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)
        {
            string[] idList;
            Outlook.NameSpace ns = this.Application.Session;

            Log.Verbose("Application_NewMailEx: Started processing new items.");

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

            ns = null;
            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.0");
                        processMessage = false;
                    }
                    else if (omi.GetIsForcefullyProtected())
                    {
                        // If ForceProtected, create special mail
                        Log.Info("Application_ItemSend: Sending forcefully protected message.");

                        if (FPPMessage.ProcessOutgoing(omi) == Globals.ReturnStatus.Success)
                        {
                            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.");

                                    MsgProcessor processor = new MsgProcessor();
                                    if (processor.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)
        {
            bool exists;
            pEpIdentity[] ownIdentities;
            PEPSettings settings = (PEPSettings)sender;
            List<KeyValuePair<CultureInfo, string>> languages;

            switch (e.PropertyName)
            {
                case (nameof(PEPSettings.IsKeyServerUsed)):
                    {
                        // Sync IsKeyServerUsed with engine
                        if (settings.IsKeyServerUsed)
                        {
                            ThisAddIn.PEPEngine.StartKeyserverLookup();
                        }
                        else
                        {
                            ThisAddIn.PEPEngine.StopKeyserverLookup();
                        }
                        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(settings.IsPEPFolderVisible);
                        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;
                    }
                case (nameof(PEPSettings.TrustwordsCulture)):
                    {
                        // Sync TrustwordsCulture with engine
                        if (this._Settings.TrustwordsCulture != null)
                        {
                            // Validate it exists in the engine list
                            exists = false;
                            languages = this.LanguageList;
                            foreach (KeyValuePair<CultureInfo, string> entry in languages)
                            {
                                if (entry.Key.LCID == this._Settings.TrustwordsCulture.LCID)
                                {
                                    exists = true;
                                    break;
                                }
                            }

                            if (exists == false)
                            {
                                // Reset to default
                                this._Settings.TrustwordsCulture = this.GetActiveUICulture();
                                Log.Warning("Settings_PropertyChanged: Invalid TrustwordsCulture detected, setting to default.");
                            }
                        }
                        else
                        {
                            // Reset to default
                            this._Settings.TrustwordsCulture = this.GetActiveUICulture();
                        }
                        break;
                    }
                default:
                    {
                        // Account sync
                        if (e.PropertyName.StartsWith("AccountSettingsList") &&
                            e.PropertyName.EndsWith("IsSyncEnabled"))
                        {
                            /* Sync the account settings with the engine's own identity flags.
                             * Warning: RegisterMyself() must already have been called so the engine has all own identities.
                             * The engine can commonly have more own identities than what is in the accounts list.
                             * This isn't a problem though and engine-only identities will not be changed here.
                             */
                            ownIdentities = ThisAddIn.PEPEngine.OwnIdentitiesRetrieve();
                            foreach (PEPSettings.PEPAccountSettings acctSettings in this._Settings.AccountSettingsList)
                            {
                                // Find the own identity that matches with the account settings
                                foreach (pEpIdentity ident in ownIdentities)
                                {
                                    pEpIdentity ownIdent = ident;

                                    if (acctSettings.EqualsByAddress(ownIdent.Address))
                                    {
                                        // Sync the engine's own identity flags with the account settings
                                        if (ownIdent.GetIsSyncEnabled() != acctSettings.IsSyncEnabled)
                                        {
                                            if (acctSettings.IsSyncEnabled)
                                            {
                                                ThisAddIn.PEPEngine.UnsetIdentityFlags(ref ownIdent, pEpIdentityFlags.pEpIdfNotForSync);
                                            }
                                            else
                                            {
                                                ThisAddIn.PEPEngine.SetIdentityFlags(ref ownIdent, pEpIdentityFlags.pEpIdfNotForSync);
                                            }
                                        }

                                        break;
                                    }
                                }
                            }
                        }

                        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;
        //    }
        //}

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

            private Outlook.OlAccountType       accountType;
            private Outlook.OlDefaultFolders?   defaultFolder;
            private Outlook.Folder              folder;
            private Outlook.Items               items;

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

            /// <summary>
            /// Primary constructor.
            /// </summary>
            /// <param name="folder">The folder to watch.</param>
            /// <param name="defaultFolder">The default folder type (null if none).</param>
            public WatchedFolder(Outlook.Folder folder,
                                 Outlook.OlDefaultFolders? defaultFolder)
            {
                this.folder = folder;
                this.defaultFolder = defaultFolder;
                this.items = this.folder?.Items;
                this.accountType = folder.GetAccountType();

                if (this.folder != null)
                {
                    this.folder.BeforeFolderMove += new Outlook.MAPIFolderEvents_12_BeforeFolderMoveEventHandler(Folder_BeforeFolderMove);
                    this.folder.BeforeItemMove += new Outlook.MAPIFolderEvents_12_BeforeItemMoveEventHandler(Folder_BeforeItemMove);
                }

                if (this.items != null)
                {
                    this.items.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(Items_ItemAdd);
                }
            }

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

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

            /// <summary>
            /// Releases all resources and disconnects internal events.
            /// </summary>
            public void Dispose()
            {
                if (this.folder != null)
                {
                    this.folder.BeforeFolderMove -= Folder_BeforeFolderMove;
                    this.folder.BeforeItemMove -= Folder_BeforeItemMove;

                    // Marshal.ReleaseComObject(this.folder);
                    this.folder = null;
                }

                if (this.items != null)
                {
                    this.items.ItemAdd -= Items_ItemAdd;

                    // Marshal.ReleaseComObject(this.items);
                    this.items = null;
                }

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

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

            /// <summary>
            /// Event handler that occurs when an item is added to the folder items list.
            /// Warning: This event does not run when a large number of items are added to the folder at once.
            /// See: https://msdn.microsoft.com/en-us/library/office/ff869609.aspx
            /// </summary>
            private void Items_ItemAdd(object item)
            {
                bool process = false;
                object propValue = null;
                Outlook.MailItem omi = null;
                Outlook.Attachments attachments = null;

                try
                {
                    Log.Verbose("Items_ItemAdd raised. Processing mail item...");

                    // This is also the check to see if it's a mail item
                    omi = item as Outlook.MailItem;

                    if (omi != null)
                    {
                        string entryId = omi.EntryID;

                        // Check, if message is a beacon or keysync message in Sent folder and delete it in this case
                        if ((this.defaultFolder != null) &&
                            (this.defaultFolder == Outlook.OlDefaultFolders.olFolderSentMail))
                        {
                            omi.GetPEPProperty(MailItemExtensions.PEPProperty.AutoConsume, out propValue);
                            if (propValue != null)
                            {
                                omi.PermanentlyDelete();
                                Log.Verbose("Items_ItemAdd: pEp internal mail item in Sent folder was deleted.");
                            }
                            else if (MailItemExtensions.IsInCopiedItemsList(entryId) == false)
                            {
                                process = true;
                            }
                        }
                        /* Else, process the mail item and decrypt it. This is needed especially for Sent folders as sent mails are normally
                         * not read and therefore remain encrypted and not searchable in this folder.
                         * 
                         * NOTE: Via debugging, it could be determined that the Copy() method called during the creation of a mirror item on
                         * untrusted servers most likely raises internally the Items.ItemAdd event. Therefore, we add the copied item's EntryID
                         * to a list of copied items during its processing and check here if the newly added mail item is in this list. If yes,
                         * do not process it. However, if for whatever reason, the ItemAdd event for one of those copied items was to be raised 
                         * at a later stage, we might run into problems with a loop of copied items that will be processed, copied during creation
                         * of the mirror, raise an ItemAdd event for the new copy and so on.
                         */
                        else if (MailItemExtensions.IsInCopiedItemsList(entryId) == false)
                        {
                            process = true;
                            Log.Verbose("Items_ItemAdd: mail item to be processed.");
                        }

                        // If this item is to be processed, add to decryption stack
                        if (process)
                        {
                            /* If potentially encrypted message, hide during processing.
                             * Note: on IMAP, there seems to be some race condition that leads to a failure during the Save() method when removing
                             * the 'pEp Processing' category. As this means that in those environments, this category doesn't get removed correctly
                             * and the mail item remains hidden until a restart of Outlook. Therefore, don't hide messages on IMAP.
                             */
                            try
                            {
                                if ((Globals.ThisAddIn.Settings.IsUXImprovementEnabled) &&
                                    (this.accountType != Outlook.OlAccountType.olImap))
                                {
                                    attachments = omi.Attachments;
                                    if (attachments.Count == 2)
                                    {
                                        omi.AddPEPProcessingCategory();
                                        omi.UnRead = false;
                                        omi.Save();
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                Log.Error("Items_ItemAdd. Error adding pEp processing category. " + ex.ToString());
                            }
                            finally
                            {
                                attachments = null;
                            }

                            ThisAddIn.DecryptionStack.TryPush(entryId);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.Error("Items_ItemAdd. Error occured. " + ex.ToString());
                }
                finally
                {
                    omi = null;
                }
            }

            /// <summary>
            /// Event handler that occurs when an item is about to be moved or deleted from a folder, 
            /// either as a result of user action or through program code.
            /// See: https://msdn.microsoft.com/en-us/library/office/ff869445.aspx
            /// </summary>
            private void Folder_BeforeItemMove(object item, Outlook.MAPIFolder moveTo, ref bool cancel)
            {
                return;
            }

            /// <summary>
            /// Event handler that accours when a folder is about to be moved or deleted, 
            /// either as a result of user action or through program code.
            /// See: https://msdn.microsoft.com/en-us/library/office/ff868895.aspx
            /// </summary>
            private void Folder_BeforeFolderMove(Outlook.MAPIFolder moveTo, ref bool cancel)
            {
                return;
            }
        }
    }
}