Wrappers/WatchedWindow.cs
author Thomas
Mon, 18 Nov 2019 20:26:40 +0100
branchsync
changeset 2867 67f0cdd1b684
parent 2857 d8bc3f810959
child 2898 85c082c01112
child 2943 24036835b358
permissions -rw-r--r--
Add missing using
Thomas@2867
     1
´╗┐using pEp.UI;
Thomas@2867
     2
using pEp.UI.Models;
Thomas@2703
     3
using pEp.UI.Views;
Thomas@2359
     4
using pEpCOMServerAdapterLib;
Thomas@2359
     5
using System;
Thomas@2780
     6
using System.Collections.Generic;
Thomas@2359
     7
using System.ComponentModel;
Thomas@2359
     8
using System.Windows.Forms;
Thomas@2359
     9
using Outlook = Microsoft.Office.Interop.Outlook;
Thomas@2359
    10
Thomas@2359
    11
namespace pEp
Thomas@2359
    12
{
Thomas@2359
    13
    /// <summary>
Thomas@2359
    14
    /// Stores an Outlook Window with connected events.
Thomas@2359
    15
    /// </summary>
Thomas@2359
    16
    public abstract class WatchedWindow : IDisposable
Thomas@2359
    17
    {
Thomas@2359
    18
        private CryptableMailItem               cryptableMailItem           = null;
Thomas@2359
    19
        private bool                            displayMirrorRequested      = false;
Thomas@2843
    20
        private PEPMessage                      handshakeMessage            = null;
Thomas@2359
    21
        private bool                            isEnabled                   = true;
Thomas@2359
    22
        private bool                            isStarted                   = false;
Thomas@2359
    23
        private bool                            processingOngoing           = false;
Thomas@2359
    24
        private bool                            refreshOngoing              = false;
Thomas@2359
    25
        private bool                            repeatProcessing            = false;
Thomas@2359
    26
        private int                             repeatCounter               = 0;
Thomas@2359
    27
        private const int                       maxRepeatCount              = 5;
Thomas@2359
    28
Thomas@2780
    29
Thomas@2844
    30
        internal static DialogWindow            HandshakeDialog             = null;
Thomas@2780
    31
Thomas@2468
    32
        public enum WindowType
Thomas@2468
    33
        {
Thomas@2468
    34
            Undefined,
Thomas@2468
    35
            Inspector,
Thomas@2468
    36
            Explorer
Thomas@2468
    37
        }
Thomas@2468
    38
Thomas@2360
    39
        #region Properties
Thomas@2359
    40
Thomas@2359
    41
        /// <summary>
Thomas@2359
    42
        /// The mail item that is connected to this Inspector/Explorer window.
Thomas@2359
    43
        /// </summary>
Thomas@2385
    44
        public Outlook.MailItem CurrentMailItem { get; set; } = null;
Thomas@2359
    45
Thomas@2359
    46
        /// <summary>
Thomas@2359
    47
        /// Gets or sets whether to disable the Force Protection option.
Thomas@2359
    48
        /// </summary>
Thomas@2359
    49
        public bool DisableForceProtection { get; set; } = false;
Thomas@2359
    50
Thomas@2359
    51
        /// <summary>
Thomas@2359
    52
        /// Gets or sets whether to send this message forcefully protected.
Thomas@2359
    53
        /// </summary>
Thomas@2359
    54
        public bool ForceProtection { get; set; } = false;
Thomas@2359
    55
Thomas@2359
    56
        /// <summary>
Thomas@2359
    57
        /// Gets or sets whether to send this message forcefully unencrypted.
Thomas@2359
    58
        /// </summary>
Thomas@2359
    59
        public bool ForceUnencrypted { get; set; } = false;
Thomas@2359
    60
Thomas@2359
    61
        /// <summary>
Thomas@2359
    62
        /// Gets or sets whether the message is a draft message.
Thomas@2359
    63
        /// </summary>
Thomas@2359
    64
        public bool IsDraft { get; set; } = false;
Thomas@2359
    65
Thomas@2359
    66
        /// <summary>
Thomas@2359
    67
        /// Gets or sets whether to always store this message as if on an untrusted server.
Thomas@2359
    68
        /// </summary>
Thomas@2359
    69
        public bool NeverUnsecure { get; set; } = false;
Thomas@2359
    70
Thomas@2359
    71
        /// <summary>
Thomas@2419
    72
        /// The privacy state of this form region.
Thomas@2419
    73
        /// </summary>
Thomas@2419
    74
        internal PrivacyState PrivacyState { get; private set; } = new PrivacyState();
Thomas@2419
    75
Thomas@2419
    76
        /// <summary>
Thomas@2359
    77
        /// Gets the rating of this message.
Thomas@2359
    78
        /// </summary>
Thomas@2419
    79
        public pEpRating Rating { get; private set; } = pEpRating.pEpRatingUndefined;
Thomas@2359
    80
Thomas@2359
    81
        /// <summary>
Thomas@2359
    82
        /// Sets the rating of this message and updates the UI.
Thomas@2359
    83
        /// </summary>
Thomas@2359
    84
        /// <param name="rating">The message rating.</param>
Thomas@2359
    85
        public void SetRating(pEpRating rating)
Thomas@2359
    86
        {
Thomas@2419
    87
            this.Rating = rating;
Thomas@2419
    88
            this.UpdatePrivacyStateAndUI();
Thomas@2359
    89
        }
Thomas@2359
    90
Thomas@2359
    91
        /// <summary>
Thomas@2359
    92
        /// The timer that schedules the updates of this window.
Thomas@2359
    93
        /// </summary>
Thomas@2359
    94
        public System.Windows.Forms.Timer TimerRefresh { get; set; } = new System.Windows.Forms.Timer();
Thomas@2360
    95
Thomas@2385
    96
        /// <summary>
Thomas@2468
    97
        /// Gets the associated Explorer/Inspector window. To be overwritten in child class.
Thomas@2385
    98
        /// </summary>
Thomas@2385
    99
        public abstract dynamic Window { get; }
Thomas@2385
   100
Thomas@2468
   101
        /// <summary>
Thomas@2468
   102
        /// Gets the window type of this window. To be overwritten in child class.
Thomas@2468
   103
        /// </summary>
Thomas@2468
   104
        public abstract WindowType Type { get; }
Thomas@2468
   105
Thomas@2359
   106
        #endregion
Thomas@2359
   107
Thomas@2359
   108
        #region Constructors / Destructors
Thomas@2359
   109
Thomas@2359
   110
        /// <summary>
Thomas@2359
   111
        /// Destructor.
Thomas@2359
   112
        /// </summary>
Thomas@2359
   113
        ~WatchedWindow()
Thomas@2359
   114
        {
Thomas@2359
   115
            this.Dispose(true);
Thomas@2359
   116
        }
Thomas@2359
   117
        #endregion
Thomas@2359
   118
Thomas@2359
   119
        #region Methods
Thomas@2359
   120
        /**************************************************************
Thomas@2359
   121
         * 
Thomas@2359
   122
         * Methods
Thomas@2359
   123
         * 
Thomas@2359
   124
         *************************************************************/
Thomas@2359
   125
Thomas@2359
   126
        /// <summary>
Thomas@2359
   127
        /// Releases all resources and disconnects internal events.
Thomas@2359
   128
        /// </summary>
Thomas@2359
   129
        public void Dispose()
Thomas@2359
   130
        {
Thomas@2359
   131
            this.Dispose(true);
Thomas@2359
   132
        }
Thomas@2359
   133
Thomas@2359
   134
        /// <summary>
Thomas@2359
   135
        /// Clean up any resources being used.
Thomas@2359
   136
        /// </summary>
Thomas@2359
   137
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
Thomas@2359
   138
        protected virtual void Dispose(bool disposing)
Thomas@2359
   139
        {
Thomas@2359
   140
            if (disposing)
Thomas@2359
   141
            {
Thomas@2360
   142
                // Disconnect cryptable mail item
Thomas@2360
   143
                if (this.cryptableMailItem != null)
Thomas@2360
   144
                {
Thomas@2360
   145
                    try
Thomas@2360
   146
                    {
Thomas@2360
   147
                        this.cryptableMailItem.PropertyChanged -= MailItem_PropertyChanged;
Thomas@2360
   148
                        this.cryptableMailItem.ProcessingCompleted -= MailItem_ProcessingCompleted;
Thomas@2360
   149
                        this.cryptableMailItem.GetMirrorCompleted -= MailItem_GetMirrorCompleted;
Thomas@2360
   150
                        this.cryptableMailItem.Open -= MailItem_Open;
Thomas@2360
   151
                        this.cryptableMailItem.Send -= MailItem_Send;
Thomas@2360
   152
                        this.cryptableMailItem.OriginallyEncryptedStatusUpdated -= CryptableMailItem_OriginallyEncryptedStatusUpdated;
Thomas@2360
   153
                    }
Thomas@2360
   154
                    catch { }
Thomas@2360
   155
Thomas@2360
   156
                    this.cryptableMailItem.Dispose();
Thomas@2360
   157
                    this.cryptableMailItem = null;
Thomas@2360
   158
                }
Thomas@2360
   159
Thomas@2360
   160
                // Disconnect other
Thomas@2360
   161
                this.SetIsEnabled(false);
Thomas@2360
   162
Thomas@2359
   163
                // Set Outlook objects to null
Thomas@2385
   164
                this.CurrentMailItem = null;
Thomas@2360
   165
Thomas@2360
   166
                // Dispose of timer
Thomas@2528
   167
                if (this.TimerRefresh != null)
Thomas@2528
   168
                {
Thomas@2528
   169
                    this.TimerRefresh.Tick -= TimerRefresh_Tick;
Thomas@2528
   170
                    this.TimerRefresh.Dispose();
Thomas@2528
   171
                    this.TimerRefresh = null;
Thomas@2528
   172
                }
Thomas@2359
   173
            }
Thomas@2359
   174
        }
Thomas@2360
   175
Thomas@2359
   176
        /// <summary>
Thomas@2359
   177
        /// Builds the latest state of the encryption status manager then shows the UI.
Thomas@2359
   178
        /// </summary>
Thomas@2854
   179
        public void BuildAndShowHandshakeDialog()
Thomas@2359
   180
        {
Thomas@2359
   181
            /* Resolve all recipients -- this ensures the identities list is correctly populated
Thomas@2359
   182
             * 
Thomas@2359
   183
             * Note: The PropertyChanged changed event must be disconnected before trying to resolve.
Thomas@2359
   184
             * This is because the resolve process can modify the contents of the mail item which triggers an event.
Thomas@2359
   185
             * The PropertyChanged event would then trigger a UI refresh cycle. However, because the GetManagerState itself 
Thomas@2359
   186
             * is called within the UI refresh, an infinite loop could occur trying to resolve a recipient that
Thomas@2359
   187
             * cannot be resolved (no address).
Thomas@2359
   188
             */
Thomas@2359
   189
            try
Thomas@2359
   190
            {
Thomas@2359
   191
                this.ResolveAllRecipients();
Thomas@2359
   192
            }
Thomas@2359
   193
            catch (Exception ex)
Thomas@2359
   194
            {
Thomas@2854
   195
                Log.Verbose("BuildAndShowHandshakeDialog: Error resolving recipients. " + ex.ToString());
Thomas@2359
   196
            }
Thomas@2359
   197
Thomas@2359
   198
            // Build the dialog
Thomas@2359
   199
            try
Thomas@2359
   200
            {
Thomas@2359
   201
                // The own identity to build the dialog with
Thomas@2359
   202
                PEPIdentity myself = this.cryptableMailItem?.Myself;
Thomas@2359
   203
Thomas@2359
   204
                // If message is a draft, create it directly from the Outlook mail item
Thomas@2359
   205
                if (this.IsDraft)
Thomas@2359
   206
                {
Thomas@2854
   207
                    Log.Verbose("BuildAndShowHandshakeDialog: Creating PEPMessage from draft.");
Thomas@2359
   208
Thomas@2843
   209
                    if (PEPMessage.Create(this.CurrentMailItem, out this.handshakeMessage, true) == Globals.ReturnStatus.Success)
Thomas@2359
   210
                    {
Thomas@2359
   211
                        // If Force Protection is set, assign a random GUID
Thomas@2359
   212
                        if ((this.ForceProtection) &&
Thomas@2359
   213
                            (this.DisableForceProtection == false))
Thomas@2359
   214
                        {
Thomas@2843
   215
                            this.handshakeMessage.ForceProtectionId = Guid.NewGuid().ToString();
Thomas@2359
   216
                        }
Thomas@2359
   217
Thomas@2359
   218
                        // If message is Force Unencrypted, assign it
Thomas@2843
   219
                        this.handshakeMessage.ForceUnencrypted = this.ForceUnencrypted;
Thomas@2359
   220
                    }
Thomas@2534
   221
                    else
Thomas@2534
   222
                    {
Thomas@2854
   223
                        Log.Error("BuildAndShowHandshakeDialog: Error creating PEPMessage from draft.");
Thomas@2843
   224
                        this.handshakeMessage = null;
Thomas@2534
   225
                    }
Thomas@2359
   226
                }
Thomas@2359
   227
                else
Thomas@2359
   228
                {
Thomas@2847
   229
                    // Create the message
Thomas@2847
   230
                    if (PEPMessage.Create(this.CurrentMailItem, out this.handshakeMessage) != Globals.ReturnStatus.Success)
Thomas@2359
   231
                    {
Thomas@2847
   232
                        this.handshakeMessage = null;
Thomas@2857
   233
                        Log.Error("BuildAndShowHandshakeDialog: Error creating PEPMessage from mirror.");
Thomas@2359
   234
                    }
Thomas@2359
   235
                }
Thomas@2359
   236
Thomas@2359
   237
                // Build dialog
Thomas@2843
   238
                if (this.handshakeMessage != null)
Thomas@2359
   239
                {
Thomas@2847
   240
                    // Get myself identiy if we don't have it yet
Thomas@2847
   241
                    if (myself == null)
Thomas@2847
   242
                    {
Thomas@2847
   243
                        myself = this.CurrentMailItem.GetMyselfIdentity();
Thomas@2847
   244
                    }
Thomas@2847
   245
Thomas@2844
   246
                    /* Add recipients to the dialog:
Thomas@2844
   247
                     *  - For incoming messages, add the From recipient as primary
Thomas@2844
   248
                     *    recipient and all others as secondary recipients.
Thomas@2844
   249
                     *  - For outgoing messages, add all To recipients as primary
Thomas@2844
   250
                     *    recipients and all others as secondary recipients.
Thomas@2844
   251
                     * Note: don't add own identities as secondary recipients.
Thomas@2847
   252
                     */
Thomas@2780
   253
                    List<PEPIdentity> primaryPartners = new List<PEPIdentity>();
Thomas@2780
   254
                    List<PEPIdentity> secondaryPartners = new List<PEPIdentity>();
Thomas@2780
   255
Thomas@2843
   256
                    if (this.handshakeMessage.Direction == pEpMsgDirection.pEpDirIncoming)
Thomas@2703
   257
                    {
Thomas@2844
   258
                        primaryPartners.Add(this.handshakeMessage.From);
Thomas@2780
   259
Thomas@2843
   260
                        this.handshakeMessage.To?.ForEach(identity =>
Thomas@2780
   261
                        {
Thomas@2780
   262
                            if (PEPIdentity.GetIsOwnIdentity(identity.Address) == false)
Thomas@2780
   263
                            {
Thomas@2780
   264
                                secondaryPartners.Add(identity);
Thomas@2780
   265
                            }
Thomas@2780
   266
                        });
Thomas@2703
   267
                    }
Thomas@2703
   268
                    else
Thomas@2703
   269
                    {
Thomas@2843
   270
                        this.handshakeMessage.To?.ForEach(identity =>
Thomas@2780
   271
                        {
Thomas@2844
   272
                            primaryPartners.Add(identity);
Thomas@2780
   273
                        });
Thomas@2703
   274
                    }
Thomas@2715
   275
Thomas@2843
   276
                    this.handshakeMessage.Cc?.ForEach(identity =>
Thomas@2780
   277
                    {
Thomas@2780
   278
                        if (PEPIdentity.GetIsOwnIdentity(identity.Address) == false)
Thomas@2780
   279
                        {
Thomas@2780
   280
                            secondaryPartners.Add(identity);
Thomas@2780
   281
                        }
Thomas@2780
   282
                    });
Thomas@2780
   283
Thomas@2843
   284
                    this.handshakeMessage.Bcc?.ForEach(identity =>
Thomas@2780
   285
                    {
Thomas@2780
   286
                        if (PEPIdentity.GetIsOwnIdentity(identity.Address) == false)
Thomas@2780
   287
                        {
Thomas@2780
   288
                            secondaryPartners.Add(identity);
Thomas@2780
   289
                        }
Thomas@2780
   290
                    });
Thomas@2780
   291
Thomas@2815
   292
                    // Do not open dialogs if no recipients are found
Thomas@2815
   293
                    if (primaryPartners?.Count > 0 || secondaryPartners?.Count > 0)
Thomas@2780
   294
                    {
Thomas@2815
   295
                        if (WatchedWindow.HandshakeDialog != null)
Thomas@2815
   296
                        {
Thomas@2815
   297
                            WatchedWindow.HandshakeDialog?.Close();
Thomas@2815
   298
                            WatchedWindow.HandshakeDialog = null;
Thomas@2815
   299
                        }
Thomas@2815
   300
Thomas@2815
   301
                        WatchedWindow.HandshakeDialog = new DialogWindow(new Dialog(Dialog.Type.Handshake, myself, primaryPartners, secondaryPartners));
Thomas@2815
   302
                        WatchedWindow.HandshakeDialog.Closed += HandshakeDialog_Closed;
Thomas@2815
   303
                        WatchedWindow.HandshakeDialog.Show();
Thomas@2780
   304
                    }
Thomas@2815
   305
                    else
Thomas@2815
   306
                    {
Thomas@2854
   307
                        Log.Error("BuildAndShowHandshakeDialog: No recipients found. No dialog opened.");
Thomas@2815
   308
                    }
Thomas@2359
   309
                }
Thomas@2359
   310
                else
Thomas@2359
   311
                {
Thomas@2359
   312
                    throw new Exception("Could not build handshake dialog. Message is null.");
Thomas@2359
   313
                }
Thomas@2359
   314
            }
Thomas@2359
   315
            catch (Exception ex)
Thomas@2359
   316
            {
Thomas@2854
   317
                Log.Error("BuildAndShowHandshakeDialog: Error creating handshake dialog. " + ex.ToString());
Thomas@2359
   318
            }
Thomas@2359
   319
        }
Thomas@2359
   320
Thomas@2359
   321
        /// <summary>
Thomas@2445
   322
        /// Initializes the watched explorer or inspector window.
Thomas@2445
   323
        /// </summary>
Thomas@2445
   324
        /// <param name="isInlineResponse">Whether this window is an inline response.</param>
Thomas@2445
   325
        protected void InitializeWindow(bool isInlineResponse = false)
Thomas@2445
   326
        {
Thomas@2445
   327
            bool enableFormRegion = false;
Thomas@2445
   328
            bool cancelOpenEvent = false;
Thomas@2445
   329
            bool isSecureAttachedMail = false;
Thomas@2445
   330
            string messageId = null;
Thomas@2445
   331
            PEPCache.CacheItem cacheItem = null;
Thomas@2445
   332
Thomas@2445
   333
            try
Thomas@2445
   334
            {
Thomas@2445
   335
                // Check if draft
Thomas@2445
   336
                this.IsDraft = this.CurrentMailItem.GetIsDraft();
Thomas@2445
   337
Thomas@2445
   338
                /* Set immediately a provisional rating in order to have a less flickery
Thomas@2445
   339
                 * UI experience.
Thomas@2445
   340
                 * The provisional rating is either a stored rating or, in case we have a
Thomas@2445
   341
                 * reply message, the rating of the original (the item we reply to).
Thomas@2445
   342
                 * This provisional rating will be replace with the real one once the full
Thomas@2445
   343
                 * processing is complete.
Thomas@2445
   344
                 */
Thomas@2445
   345
                pEpRating provisionalRating = pEpRating.pEpRatingUndefined;
Thomas@2445
   346
Thomas@2445
   347
                // Try to get an original rating (in case of reply messages)
Thomas@2552
   348
                if (this.IsDraft)
Thomas@2445
   349
                {
Thomas@2552
   350
                    if (this.CurrentMailItem?.Recipients?.Count > 0)
Thomas@2552
   351
                    {
Thomas@2552
   352
                        string originalRatingString = this.CurrentMailItem.GetUserProperty(CryptableMailItem.USER_PROPERTY_KEY_ORIGINAL_RATING) as string;
Thomas@2445
   353
Thomas@2552
   354
                        // If we have an original rating, parse it and set it.
Thomas@2552
   355
                        if (string.IsNullOrEmpty(originalRatingString) == false)
Thomas@2552
   356
                        {
Thomas@2552
   357
                            provisionalRating = AdapterExtensions.ParseRatingString(originalRatingString);
Thomas@2552
   358
                        }
Thomas@2445
   359
                    }
Thomas@2445
   360
                }
Thomas@2445
   361
                else
Thomas@2445
   362
                {
Thomas@2445
   363
                    // Try to get item from cache
Thomas@2445
   364
                    cacheItem = PEPCache.GetItemFromCache(this.CurrentMailItem.EntryID);
Thomas@2445
   365
Thomas@2445
   366
                    if (cacheItem != null)
Thomas@2445
   367
                    {
Thomas@2445
   368
                        // Set rating from cache
Thomas@2445
   369
                        provisionalRating = cacheItem?.Rating ?? pEpRating.pEpRatingUndefined;
Thomas@2445
   370
                    }
Thomas@2445
   371
                    else
Thomas@2445
   372
                    {
Thomas@2445
   373
                        // Get rating from db
Thomas@2445
   374
                        provisionalRating = PEPDatabase.GetRating(this.CurrentMailItem.EntryID);
Thomas@2445
   375
Thomas@2445
   376
                        // If there is no rating in the db, use stored or default rating
Thomas@2445
   377
                        if (provisionalRating == pEpRating.pEpRatingUndefined)
Thomas@2445
   378
                        {
Thomas@2445
   379
                            provisionalRating = this.CurrentMailItem.GetStoredRating() ?? pEpRating.pEpRatingUndefined;
Thomas@2445
   380
                        }
Thomas@2445
   381
                    }
Thomas@2445
   382
                }
Thomas@2445
   383
Thomas@2445
   384
                // Only set rating if one has been retrieved
Thomas@2445
   385
                if (provisionalRating != pEpRating.pEpRatingUndefined)
Thomas@2445
   386
                {
Thomas@2445
   387
                    this.SetRating(provisionalRating);
Thomas@2448
   388
                    Log.Verbose("InitializeWindow: Provisional rating {0} shown.", Enum.GetName(typeof(pEpRating), provisionalRating));
Thomas@2445
   389
                }
Thomas@2445
   390
Thomas@2445
   391
                // Do not process S/MIME messages
Thomas@2445
   392
                if (this.CurrentMailItem.GetIsSMIMEEnabled())
Thomas@2445
   393
                {
Thomas@2448
   394
                    Log.Verbose("InitializeWindow: S/MIME message detected. Won't be processed.");
Thomas@2445
   395
Thomas@2445
   396
                    // Set unencrypted rating
Thomas@2445
   397
                    this.SetRating(pEpRating.pEpRatingUnencrypted);
Thomas@2445
   398
Thomas@2445
   399
                    // Set icon(s) if necessary     
Thomas@2445
   400
                    if ((IsDraft == false) &&
Thomas@2445
   401
                        (this.CurrentMailItem.SetEncryptionIcons()))
Thomas@2445
   402
                    {
Thomas@2445
   403
                        try
Thomas@2445
   404
                        {
Thomas@2445
   405
                            this.CurrentMailItem.Save();
Thomas@2445
   406
                        }
Thomas@2445
   407
                        catch (Exception ex)
Thomas@2445
   408
                        {
Thomas@2448
   409
                            Log.Error("InitializeWindow: Error saving message after changing icon. " + ex.ToString());
Thomas@2445
   410
                        }
Thomas@2445
   411
                    }
Thomas@2445
   412
Thomas@2445
   413
                    return;
Thomas@2445
   414
                }
Thomas@2445
   415
Thomas@2445
   416
                /* Check if item is attached mail. For performance reasons,
Thomas@2445
   417
                 * only do the whole check if an item has been loaded to the
Thomas@2445
   418
                 * cache of attached mails and if item might be secure.
Thomas@2445
   419
                 */
Thomas@2445
   420
                if ((PEPAttachment.AttachedMailsCache.Count > 0) &&
Thomas@2445
   421
                    (this.CurrentMailItem.Attachments?.Count == 2))
Thomas@2445
   422
                {
Thomas@2445
   423
                    try
Thomas@2445
   424
                    {
Thomas@2445
   425
                        // Check if mail item is an attached mail
Thomas@2445
   426
                        if (this.CurrentMailItem.GetIsAttachedMail(out messageId))
Thomas@2445
   427
                        {
Thomas@2445
   428
                            Outlook.MailItem mirror = null;
Thomas@2445
   429
Thomas@2445
   430
                            // Try to get the mirror
Thomas@2445
   431
                            mirror = this.CurrentMailItem.GetMirror(messageId);
Thomas@2445
   432
Thomas@2445
   433
                            // If mirror was not found, decrypt and create mirror
Thomas@2445
   434
                            if (mirror != null)
Thomas@2445
   435
                            {
Thomas@2445
   436
                                isSecureAttachedMail = true;
Thomas@2445
   437
                            }
Thomas@2445
   438
                            else
Thomas@2445
   439
                            {
Thomas@2507
   440
                                if ((PEPMessage.Create(this.CurrentMailItem, out PEPMessage pEpMessage) == Globals.ReturnStatus.Success) &&
Thomas@2445
   441
                                    (pEpMessage.IsSecure))
Thomas@2445
   442
                                {
Thomas@2445
   443
                                    MsgProcessor msgProcessor = new MsgProcessor();
Thomas@2507
   444
                                    if (msgProcessor.Decrypt(pEpMessage, out PEPMessage decryptedMessage))
Thomas@2445
   445
                                    {
Thomas@2445
   446
                                        isSecureAttachedMail = true;
Thomas@2445
   447
                                        mirror = this.CurrentMailItem.CreateMirrorOMI(messageId);
Thomas@2445
   448
                                        decryptedMessage.ApplyTo(mirror, true, false);
Thomas@2445
   449
                                        mirror?.Save();
Thomas@2445
   450
                                    }
Thomas@2445
   451
                                    else
Thomas@2445
   452
                                    {
Thomas@2448
   453
                                        Log.Error("InitializeWindow: Decryption of attached mail was not successful.");
Thomas@2445
   454
                                    }
Thomas@2445
   455
                                }
Thomas@2445
   456
                            }
Thomas@2445
   457
Thomas@2445
   458
                            // Check if attachment is being opened or if only the preview is needed
Thomas@2445
   459
                            if (CryptableMailItem.PreviewAttachedMailId?.Equals(messageId) != true)
Thomas@2445
   460
                            {
Thomas@2445
   461
                                // Display mirror and cancel opening of original
Thomas@2445
   462
                                cancelOpenEvent = true;
Thomas@2445
   463
                                mirror?.Display();
Thomas@2445
   464
                            }
Thomas@2445
   465
Thomas@2445
   466
                            // Not needed anymore after this point
Thomas@2445
   467
                            CryptableMailItem.PreviewAttachedMailId = null;
Thomas@2445
   468
                        }
Thomas@2445
   469
                    }
Thomas@2445
   470
                    catch (Exception ex)
Thomas@2445
   471
                    {
Thomas@2445
   472
                        messageId = null;
Thomas@2448
   473
                        Log.Error("InitializeWindow: Error checking for attached mail. " + ex.ToString());
Thomas@2445
   474
                    }
Thomas@2445
   475
                }
Thomas@2445
   476
Thomas@2445
   477
                // Check for item from cache and show it if possible
Thomas@2445
   478
                if ((this.IsDraft == false) &&
Thomas@2445
   479
                    (cacheItem?.Mirror != null) &&
Thomas@2445
   480
                    (this.CurrentMailItem.GetIsInSecureStore()))
Thomas@2445
   481
                {
Thomas@2445
   482
                    WindowFormRegionCollection formRegions = null;
Thomas@2445
   483
                    try
Thomas@2445
   484
                    {
Thomas@2445
   485
                        formRegions = Globals.FormRegions[this.Window];
Thomas@2445
   486
                    }
Thomas@2445
   487
                    catch (Exception ex)
Thomas@2445
   488
                    {
Thomas@2448
   489
                        Log.Error("InitializeWindow: Error getting form region collection. " + ex.ToString());
Thomas@2445
   490
                    }
Thomas@2445
   491
Thomas@2445
   492
                    this.GetFormRegionPreviewUnencrypted()?.DisplayState?.SetMessage(cacheItem.Mirror);
Thomas@2445
   493
                }
Thomas@2445
   494
Thomas@2507
   495
                this.cryptableMailItem = new CryptableMailItem(this.CurrentMailItem, provisionalRating)
Thomas@2507
   496
                {
Thomas@2507
   497
                    // Set inline response property
Thomas@2507
   498
                    IsInlineResponse = isInlineResponse
Thomas@2507
   499
                };
Thomas@2445
   500
Thomas@2445
   501
                // Set properties for encrypted attached mail
Thomas@2445
   502
                if (isSecureAttachedMail)
Thomas@2445
   503
                {
Thomas@2445
   504
                    this.cryptableMailItem.MessageId = messageId;
Thomas@2445
   505
                    this.cryptableMailItem.IsSecureAttachedMail = true;
Thomas@2445
   506
                }
Thomas@2445
   507
Thomas@2445
   508
                // Connect cryptable mail item events
Thomas@2445
   509
                if (this.cryptableMailItem != null)
Thomas@2445
   510
                {
Thomas@2445
   511
                    // If we don't open the original, set property and return
Thomas@2445
   512
                    if (cancelOpenEvent)
Thomas@2445
   513
                    {
Thomas@2445
   514
                        this.cryptableMailItem.Open += MailItem_Open;
Thomas@2445
   515
                        this.cryptableMailItem.CancelOpen = true;
Thomas@2445
   516
                        return;
Thomas@2445
   517
                    }
Thomas@2445
   518
                    else
Thomas@2445
   519
                    {
Thomas@2445
   520
                        try
Thomas@2445
   521
                        {
Thomas@2445
   522
                            this.cryptableMailItem.PropertyChanged += MailItem_PropertyChanged;
Thomas@2445
   523
                            this.cryptableMailItem.ProcessingCompleted += MailItem_ProcessingCompleted;
Thomas@2445
   524
                            this.cryptableMailItem.GetMirrorCompleted += MailItem_GetMirrorCompleted;
Thomas@2445
   525
                            this.cryptableMailItem.Send += MailItem_Send;
Thomas@2445
   526
Thomas@2445
   527
                            if (this.cryptableMailItem.IsSecurelyStored)
Thomas@2445
   528
                            {
Thomas@2445
   529
                                this.cryptableMailItem.Open += MailItem_Open;
Thomas@2445
   530
                            }
Thomas@2445
   531
                        }
Thomas@2445
   532
                        catch (Exception ex)
Thomas@2445
   533
                        {
Thomas@2570
   534
                            Log.Error("InitializeWindow: Error occured. " + ex.ToString());
Thomas@2445
   535
                        }
Thomas@2445
   536
Thomas@2445
   537
                        if (this.CurrentMailItem.GetIsPEPEnabled())
Thomas@2445
   538
                        {
Thomas@2445
   539
                            // If pEp is enabled, show the form region
Thomas@2445
   540
                            enableFormRegion = true;
Thomas@2445
   541
                        }
Thomas@2445
   542
                        else
Thomas@2445
   543
                        {
Thomas@2445
   544
                            /* If pEp is disabled, process item and show form region in the following cases:
Thomas@2445
   545
                             * 1. Incoming items: if decrypt always option is set
Thomas@2445
   546
                             * 2. Outgoing items: if EnableProtection option is set
Thomas@2445
   547
                             *                    Note: if this property isn't set, subscribe to originally 
Thomas@2445
   548
                             *                          encrypted status update event handler in case the 
Thomas@2445
   549
                             *                          current code runs before CryptableMailItem.MailItem_Reply
Thomas@2445
   550
                             *                          or CryptableMailItem.MailItem_Forward.
Thomas@2445
   551
                             */
Thomas@2445
   552
                            if (this.IsDraft)
Thomas@2445
   553
                            {
Thomas@2445
   554
                                enableFormRegion = true;
Thomas@2445
   555
                                this.cryptableMailItem.OriginallyEncryptedStatusUpdated += CryptableMailItem_OriginallyEncryptedStatusUpdated;
Thomas@2445
   556
                            }
Thomas@2445
   557
                            else
Thomas@2445
   558
                            {
Thomas@2445
   559
                                enableFormRegion = this.CurrentMailItem.GetIsDecryptAlwaysEnabled();
Thomas@2445
   560
                            }
Thomas@2445
   561
                        }
Thomas@2445
   562
                    }
Thomas@2445
   563
                }
Thomas@2445
   564
Thomas@2445
   565
                // Update pEp enabled status
Thomas@2445
   566
                this.SetIsEnabled(enableFormRegion);
Thomas@2445
   567
Thomas@2445
   568
                if (this.isEnabled)
Thomas@2445
   569
                {
Thomas@2445
   570
                    // If forcefully protected, run dedicated decryption
Thomas@2445
   571
                    if (this.CurrentMailItem.GetIsForcefullyProtected())
Thomas@2445
   572
                    {
Thomas@2445
   573
                        FPPMessage fppMessage = new FPPMessage(this.CurrentMailItem);
Thomas@2445
   574
                        if ((fppMessage?.GetMessageType() != null) ||
Thomas@2445
   575
                            (fppMessage?.CurrentMessage?.IsSecure == true))
Thomas@2445
   576
                        {
Thomas@2445
   577
                            fppMessage.ProcessIncoming(true);
Thomas@2445
   578
                        }
Thomas@2445
   579
                        else
Thomas@2445
   580
                        {
Thomas@2445
   581
                            this.TimerRefresh_Tick(null, new EventArgs());
Thomas@2445
   582
                        }
Thomas@2445
   583
                    }
Thomas@2445
   584
                    else
Thomas@2445
   585
                    {
Thomas@2445
   586
                        // Call the timer tick method manually to refresh data with no delay
Thomas@2445
   587
                        this.TimerRefresh_Tick(null, new EventArgs());
Thomas@2445
   588
                    }
Thomas@2445
   589
                }
Thomas@2445
   590
            }
Thomas@2462
   591
            catch (Exception ex)
Thomas@2445
   592
            {
Thomas@2570
   593
                Log.Error("InitializeWindow: Error. " + ex.ToString());
Thomas@2445
   594
            }
Thomas@2445
   595
        }
Thomas@2445
   596
Thomas@2445
   597
        /// <summary>
Thomas@2359
   598
        /// Schedules for the pEp rating and UI (including displayed mirror) to be updated.
Thomas@2359
   599
        /// This can be called many times with no issue as the update is only run every n milliseconds.
Thomas@2359
   600
        /// </summary>
Thomas@2359
   601
        public void RequestRatingAndUIUpdate()
Thomas@2359
   602
        {
Thomas@2359
   603
            if (this.isEnabled)
Thomas@2359
   604
            {
Thomas@2359
   605
                this.TimerRefresh.Enabled = true;
Thomas@2359
   606
            }
Thomas@2359
   607
        }
Thomas@2359
   608
Thomas@2359
   609
        /// <summary>
Thomas@2359
   610
        /// Immediately starts the update of UI rating based on the associated mail item.
Thomas@2359
   611
        /// This will start decryption as necessary and also will update the displayed mirror.
Thomas@2359
   612
        /// This method by-passes the refresh timer completely.
Thomas@2359
   613
        /// WARNING: This method assumes the message is fully downloaded already.
Thomas@2359
   614
        /// </summary>
Thomas@2359
   615
        private void ImmediateRatingAndUIUpdate()
Thomas@2359
   616
        {
Thomas@2359
   617
            if ((this.isEnabled) &&
Thomas@2359
   618
                (this.cryptableMailItem != null))
Thomas@2359
   619
            {
Thomas@2359
   620
                Log.Verbose("ImmediateRatingAndUIUpdate: Starting processing.");
Thomas@2359
   621
Thomas@2359
   622
                // Start the rating calculation/decryption process
Thomas@2560
   623
                this.cryptableMailItem.StartProcessing(this.IsDraft);
Thomas@2359
   624
                this.processingOngoing = true;
Thomas@2385
   625
Thomas@2385
   626
                // If we have a draft message, fetch directly a preview outgoing rating
Thomas@2385
   627
                if (this.IsDraft)
Thomas@2385
   628
                {
Thomas@2385
   629
                    pEpRating rating = this.GetOutgoingRatingPreview();
Thomas@2385
   630
Thomas@2385
   631
                    if (rating != pEpRating.pEpRatingUndefined)
Thomas@2385
   632
                    {
Thomas@2385
   633
                        this.SetRating(rating);
Thomas@2385
   634
                    }
Thomas@2385
   635
                }
Thomas@2359
   636
            }
Thomas@2359
   637
        }
Thomas@2359
   638
Thomas@2359
   639
        /// <summary>
Thomas@2385
   640
        /// Gets a preview of the outgoing rating which is faster than the full version.
Thomas@2385
   641
        /// Caveat: might show a wrong rating in case of missing keys.
Thomas@2385
   642
        /// </summary>
Thomas@2385
   643
        /// <returns>A preview of the outgoing rating.</returns>
Thomas@2385
   644
        private pEpRating GetOutgoingRatingPreview()
Thomas@2385
   645
        {
Thomas@2385
   646
            pEpRating rating = pEpRating.pEpRatingUndefined;
Thomas@2385
   647
Thomas@2385
   648
            try
Thomas@2385
   649
            {
Thomas@2385
   650
                rating = this.CurrentMailItem?.GetOutgoingRating(false, true) ?? pEpRating.pEpRatingUndefined;
Thomas@2385
   651
            }
Thomas@2385
   652
            catch (Exception ex)
Thomas@2385
   653
            {
Thomas@2385
   654
                rating = pEpRating.pEpRatingUndefined;
Thomas@2385
   655
                Log.Error("ImmediateRatingAndUIUpdate: Error getting outgoing preview rating. " + ex.ToString());
Thomas@2385
   656
            }
Thomas@2385
   657
Thomas@2385
   658
            return rating;
Thomas@2385
   659
        }
Thomas@2385
   660
Thomas@2419
   661
        /// <summary>
Thomas@2419
   662
        /// Updates the privacy state and the UI (ribbon and form region).
Thomas@2419
   663
        /// </summary>
Thomas@2419
   664
        public void UpdatePrivacyStateAndUI()
Thomas@2419
   665
        {
Thomas@2419
   666
            try
Thomas@2419
   667
            {
Thomas@2419
   668
                this.PrivacyState = new PrivacyState(this.Rating,
Thomas@2419
   669
                                                     this.ForceProtection,
Thomas@2419
   670
                                                     this.ForceUnencrypted);
Thomas@2419
   671
                RibbonCustomizations.Invalidate();
Thomas@2445
   672
                this.GetFormRegionPrivacyStatus()?.UpdateFormRegion(this.PrivacyState);
Thomas@2419
   673
            }
Thomas@2419
   674
            catch (Exception ex)
Thomas@2419
   675
            {
Thomas@2419
   676
                Log.Error("UpdateUI: Error occured. " + ex.ToString());
Thomas@2419
   677
            }
Thomas@2419
   678
        }
Thomas@2385
   679
Thomas@2445
   680
        /// <summary>
Thomas@2445
   681
        /// Gathers all form regions for this WatchedWindow.
Thomas@2445
   682
        /// </summary>
Thomas@2445
   683
        /// <returns>The gathered form regions.</returns>
Thomas@2419
   684
        internal WindowFormRegionCollection GetFormRegions()
Thomas@2419
   685
        {
Thomas@2419
   686
            /* We have to find out here if the window is an explorer or an inspector.
Thomas@2419
   687
             * Calling Globals.FormRegions[] with an ambiguous window type leads to a crash.
Thomas@2419
   688
             */
Thomas@2419
   689
            Outlook.Explorer explorer;
Thomas@2419
   690
            Outlook.Inspector inspector;
Thomas@2419
   691
            WindowFormRegionCollection formRegions = null;
Thomas@2419
   692
            try
Thomas@2419
   693
            {
Thomas@2447
   694
                if (this.Window != null)
Thomas@2419
   695
                {
Thomas@2468
   696
                    if (this.Type == WindowType.Explorer)
Thomas@2447
   697
                    {
Thomas@2468
   698
                        explorer = this.Window as Outlook.Explorer;
Thomas@2447
   699
                        formRegions = Globals.FormRegions[explorer];
Thomas@2447
   700
                    }
Thomas@2468
   701
                    else if (this.Type == WindowType.Inspector)
Thomas@2447
   702
                    {
Thomas@2447
   703
                        inspector = this.Window as Outlook.Inspector;
Thomas@2447
   704
                        formRegions = Globals.FormRegions[inspector];
Thomas@2447
   705
                    }
Thomas@2419
   706
                }
Thomas@2419
   707
                else
Thomas@2419
   708
                {
Thomas@2447
   709
                    Log.Warning("GetFormRegions: Could not get form regions. Current window is null.");
Thomas@2419
   710
                }
Thomas@2419
   711
            }
Thomas@2419
   712
            catch (Exception ex)
Thomas@2419
   713
            {
Thomas@2419
   714
                formRegions = null;
Thomas@2446
   715
                Log.Error("GetFormRegions: Error getting form regions collection. " + ex.ToString());
Thomas@2419
   716
            }
Thomas@2419
   717
            finally
Thomas@2419
   718
            {
Thomas@2419
   719
                explorer = null;
Thomas@2419
   720
                inspector = null;
Thomas@2419
   721
            }
Thomas@2419
   722
Thomas@2419
   723
            return formRegions;
Thomas@2419
   724
        }
Thomas@2419
   725
Thomas@2445
   726
        /// <summary>
Thomas@2445
   727
        /// Gets the FormRegionPrivacyStatus of this WatchedWindow (if available).
Thomas@2445
   728
        /// </summary>
Thomas@2445
   729
        /// <returns>The FormRegionPrivacyStatus of this window or null.</returns>
Thomas@2419
   730
        internal FormRegionPrivacyStatus GetFormRegionPrivacyStatus()
Thomas@2419
   731
        {
Thomas@2419
   732
            return this.GetFormRegions()?.FormRegionPrivacyStatus;
Thomas@2419
   733
        }
Thomas@2419
   734
Thomas@2445
   735
        /// <summary>
Thomas@2445
   736
        /// Gets the FormRegionPreviewUnencrypted of this WatchedWindow (if available).
Thomas@2445
   737
        /// </summary>
Thomas@2445
   738
        /// <returns>The FormRegionPreviewUnencrypted of this window or null.</returns>
Thomas@2419
   739
        internal FormRegionPreviewUnencrypted GetFormRegionPreviewUnencrypted()
Thomas@2419
   740
        {
Thomas@2419
   741
            return this.GetFormRegions()?.FormRegionPreviewUnencrypted;
Thomas@2419
   742
        }
Thomas@2385
   743
Thomas@2385
   744
        /// <summary>
Thomas@2359
   745
        /// Resets the Enabled status of this form region and recalculates the rating if necessary.
Thomas@2359
   746
        /// </summary>
Thomas@2359
   747
        /// <param name="enable">Whether to enable the form region.</param>
Thomas@2359
   748
        public void UpdateFormRegion(bool enable)
Thomas@2359
   749
        {
Thomas@2359
   750
            this.SetIsEnabled(enable);
Thomas@2359
   751
Thomas@2359
   752
            // Refresh the UI
Thomas@2359
   753
            if (enable)
Thomas@2359
   754
            {
Thomas@2359
   755
                this.ResolveAllRecipients();
Thomas@2359
   756
                this.RequestRatingAndUIUpdate();
Thomas@2359
   757
            }
Thomas@2359
   758
        }
Thomas@2359
   759
Thomas@2359
   760
        /// <summary>
Thomas@2359
   761
        /// Workaround method to update the current inspector window. This is done by moving the mail item
Thomas@2359
   762
        /// to a temporary folder first and then back to the current folder. Both folders CANNOT be the same.
Thomas@2359
   763
        /// As a fallback, the mail item stays in the temporary folder if moving back to the current folder
Thomas@2359
   764
        /// fails.
Thomas@2359
   765
        /// </summary>
Thomas@2359
   766
        private void UpdateInspector()
Thomas@2359
   767
        {
Thomas@2359
   768
            Outlook.Application application = null;
Thomas@2359
   769
            Outlook.Folder currentFolder = null;
Thomas@2359
   770
            Outlook.Folder tempFolder = null;
Thomas@2359
   771
            Outlook.Inspector currentInspector = null;
Thomas@2359
   772
            Outlook.Inspector newInspector = null;
Thomas@2359
   773
            Outlook.MailItem tempMailItem = null;
Thomas@2359
   774
            Outlook.Store store = null;
Thomas@2359
   775
            Outlook.MailItem omi = this.CurrentMailItem;
Thomas@2359
   776
Thomas@2359
   777
            if (omi != null)
Thomas@2359
   778
            {
Thomas@2359
   779
                try
Thomas@2359
   780
                {
Thomas@2359
   781
                    application = omi.Application;
Thomas@2359
   782
                    currentInspector = omi.GetInspector;
Thomas@2359
   783
                    currentFolder = omi.Parent as Outlook.Folder;
Thomas@2359
   784
Thomas@2359
   785
                    if ((currentInspector != null) &&
Thomas@2359
   786
                        (application != null) &&
Thomas@2359
   787
                        (currentFolder != null))
Thomas@2359
   788
                    {
Thomas@2359
   789
                        var left = currentInspector.Left;
Thomas@2359
   790
                        var top = currentInspector.Top;
Thomas@2359
   791
                        var width = currentInspector.Width;
Thomas@2359
   792
                        var height = currentInspector.Height;
Thomas@2359
   793
                        var windowState = currentInspector.WindowState;
Thomas@2359
   794
Thomas@2359
   795
                        /* Check, if in trusted store. In that case, use the default drafts folder
Thomas@2359
   796
                         * as temporary folder. If the store is untrusted, use the pEp drafts folder.
Thomas@2359
   797
                         */
Thomas@2359
   798
                        store = currentFolder.Store;
Thomas@2359
   799
                        if (store?.GetIsSecureStorageEnabled() ?? false)
Thomas@2359
   800
                        {
Thomas@2359
   801
                            tempFolder = Globals.ThisAddIn.GetPEPStoreDraftsFolder();
Thomas@2359
   802
                        }
Thomas@2359
   803
                        else
Thomas@2359
   804
                        {
Thomas@2359
   805
                            tempFolder = store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDrafts) as Outlook.Folder;
Thomas@2359
   806
                        }
Thomas@2359
   807
Thomas@2359
   808
                        if (tempFolder != null)
Thomas@2359
   809
                        {
Thomas@2359
   810
                            tempMailItem = omi.Move(tempFolder) as Outlook.MailItem;
Thomas@2359
   811
Thomas@2359
   812
                            if (tempMailItem != null)
Thomas@2359
   813
                            {
Thomas@2359
   814
                                try
Thomas@2359
   815
                                {
Thomas@2359
   816
                                    omi = tempMailItem.Move(currentFolder) as Outlook.MailItem;
Thomas@2359
   817
                                }
Thomas@2359
   818
                                catch
Thomas@2359
   819
                                {
Thomas@2359
   820
                                    omi = tempMailItem.Copy();
Thomas@2359
   821
                                }
Thomas@2359
   822
Thomas@2359
   823
                                newInspector = application.Inspectors.Add(omi);
Thomas@2359
   824
Thomas@2359
   825
                                if (windowState == Outlook.OlWindowState.olNormalWindow)
Thomas@2359
   826
                                {
Thomas@2359
   827
                                    newInspector.Left = left;
Thomas@2359
   828
                                    newInspector.Top = top;
Thomas@2359
   829
                                    newInspector.Width = width;
Thomas@2359
   830
                                    newInspector.Height = height;
Thomas@2359
   831
                                }
Thomas@2359
   832
Thomas@2359
   833
                                newInspector.Display();
Thomas@2359
   834
                                newInspector.WindowState = windowState;
Thomas@2359
   835
Thomas@2359
   836
                                repeatProcessing = false;
Thomas@2359
   837
                            }
Thomas@2359
   838
                        }
Thomas@2359
   839
                        else
Thomas@2359
   840
                        {
Thomas@2359
   841
                            Log.Error("UpdateInspector: Cannot get temporary folder.");
Thomas@2359
   842
                        }
Thomas@2359
   843
                    }
Thomas@2359
   844
                    else
Thomas@2359
   845
                    {
Thomas@2359
   846
                        Log.Verbose("UpdateInspector: Error retrieving inspector window or application.");
Thomas@2359
   847
                    }
Thomas@2359
   848
                }
Thomas@2359
   849
                catch (Exception ex)
Thomas@2359
   850
                {
Thomas@2359
   851
                    Log.Verbose("UpdateInspector: Error updating inspector window. " + ex.ToString());
Thomas@2359
   852
                }
Thomas@2359
   853
                finally
Thomas@2359
   854
                {
Thomas@2359
   855
                    application = null;
Thomas@2359
   856
                    currentInspector = null;
Thomas@2359
   857
                    newInspector = null;
Thomas@2359
   858
                    omi = null;
Thomas@2359
   859
                    tempMailItem = null;
Thomas@2359
   860
                }
Thomas@2359
   861
            }
Thomas@2359
   862
        }
Thomas@2359
   863
Thomas@2359
   864
        /// <summary>
Thomas@2359
   865
        /// Clears the associated unencrypted preview and displays the given note (if any).
Thomas@2359
   866
        /// </summary>
Thomas@2359
   867
        /// <param name="note">The note to display.</param>
Thomas@2359
   868
        private void SetNote(string note = null)
Thomas@2359
   869
        {
Thomas@2359
   870
            WindowFormRegionCollection formRegions = Globals.FormRegions[Globals.ThisAddIn.Application.ActiveWindow()];
Thomas@2359
   871
Thomas@2359
   872
            if ((formRegions != null) &&
Thomas@2359
   873
                (string.IsNullOrEmpty(note) == false))
Thomas@2359
   874
            {
Thomas@2359
   875
                if ((formRegions.FormRegionPreviewUnencrypted != null) &&
Thomas@2359
   876
                    (formRegions.FormRegionPreviewUnencrypted.Visible))
Thomas@2359
   877
                {
Thomas@2359
   878
                    formRegions.FormRegionPreviewUnencrypted.DisplayState.SetNote(note);
Thomas@2359
   879
                }
Thomas@2359
   880
            }
Thomas@2359
   881
        }
Thomas@2359
   882
Thomas@2359
   883
        /// <summary>
Thomas@2359
   884
        /// Sets whether processing of the mail item is enabled.
Thomas@2359
   885
        /// This should commonly be set from the .GetIsPEPEnabled() value.
Thomas@2359
   886
        /// </summary>
Thomas@2359
   887
        private void SetIsEnabled(bool enabled)
Thomas@2359
   888
        {
Thomas@2359
   889
            Globals.ReturnStatus sts;
Thomas@2359
   890
            Outlook.Recipient currUser = null;
Thomas@2359
   891
            Outlook.Account currAccount = null;
Thomas@2359
   892
            Outlook.Account sendingAccount = null;
Thomas@2359
   893
            Outlook.NameSpace ns = null;
Thomas@2359
   894
Thomas@2359
   895
            this.isEnabled = enabled;
Thomas@2359
   896
Thomas@2359
   897
            if (enabled)
Thomas@2359
   898
            {
Thomas@2359
   899
                // Do not allow initialization more than once
Thomas@2359
   900
                if (this.isStarted == false)
Thomas@2359
   901
                {
Thomas@2359
   902
                    /* It's possible for new draft MailItems to be created outside the context of an account.
Thomas@2359
   903
                     * In this situation the SendUsingAccount will always be null which of course breaks several pEp operations.
Thomas@2359
   904
                     * The operations themselves cannot make the assumption about what account information to use.
Thomas@2359
   905
                     * Therefore, the situation is detected here and for draft mail items a null SendUsingAccount will be 
Thomas@2359
   906
                     * set with the session's default account.
Thomas@2359
   907
                     */
Thomas@2360
   908
                    if (this.IsDraft)
Thomas@2359
   909
                    {
Thomas@2360
   910
                        try
Thomas@2359
   911
                        {
Thomas@2385
   912
                            sendingAccount = this.CurrentMailItem.SendUsingAccount;
Thomas@2359
   913
                            if (sendingAccount == null)
Thomas@2359
   914
                            {
Thomas@2360
   915
                                ns = Globals.ThisAddIn.Application.Session;
Thomas@2360
   916
                                currUser = ns.CurrentUser;
Thomas@2507
   917
                                sts = PEPIdentity.Create(currUser, out PEPIdentity currIdent);
Thomas@2359
   918
Thomas@2359
   919
                                if (sts == Globals.ReturnStatus.Success)
Thomas@2359
   920
                                {
Thomas@2359
   921
                                    sendingAccount = AccountExtensions.GetDefaultAccount(Outlook.OlDefaultFolders.olFolderDrafts);
Thomas@2359
   922
                                    if (sendingAccount != null)
Thomas@2359
   923
                                    {
Thomas@2385
   924
                                        this.CurrentMailItem.SendUsingAccount = currAccount;
Thomas@2359
   925
                                    }
Thomas@2359
   926
                                }
Thomas@2359
   927
                            }
Thomas@2359
   928
Thomas@2359
   929
                            // Check if account is already registered in pEp list and add it if necessary
Thomas@2729
   930
                            PEPSettings.PEPAccountSettings acctSettings = Globals.ThisAddIn.Settings.GetAccountSettings(sendingAccount?.SmtpAddress);
Thomas@2359
   931
                            if (acctSettings == null)
Thomas@2359
   932
                            {
Thomas@2359
   933
                                Log.Verbose("SetIsEnabled: New account detected. Creating pEp settings.");
Thomas@2359
   934
Thomas@2359
   935
                                // Create pEp settings for the new account
Thomas@2359
   936
                                acctSettings = sendingAccount.CreatePEPSettings();
Thomas@2359
   937
Thomas@2359
   938
                                // Add account to list
Thomas@2359
   939
                                Globals.ThisAddIn.Settings.AccountsAddedWhileRunningList.Add(acctSettings?.SmtpAddress);
Thomas@2359
   940
                                Log.Verbose("SetIsEnabled: New account registered in pEp settings.");
Thomas@2359
   941
Thomas@2359
   942
                                // Generate key
Thomas@2359
   943
                                Globals.ThisAddIn.RegisterMyself(acctSettings);
Thomas@2359
   944
                                Log.Verbose("SetIsEnabled: Myself registered.");
Thomas@2359
   945
Thomas@2359
   946
                                // Set rules and view filters
Thomas@2438
   947
                                Globals.ThisAddIn.SetRulesAndViewFilters(sendingAccount, Globals.ThisAddIn.Settings.HideInternalMessages);
Thomas@2359
   948
                            }
Thomas@2359
   949
                        }
Thomas@2360
   950
                        catch (Exception ex)
Thomas@2360
   951
                        {
Thomas@2360
   952
                            Log.Error("SetIsEnabled: Error occured. " + ex.ToString());
Thomas@2360
   953
                        }
Thomas@2360
   954
                        finally
Thomas@2360
   955
                        {
Thomas@2360
   956
                            currAccount = null;
Thomas@2360
   957
                            currUser = null;
Thomas@2360
   958
                            ns = null;
Thomas@2360
   959
                            sendingAccount = null;
Thomas@2360
   960
                        }
Thomas@2359
   961
                    }
Thomas@2359
   962
Thomas@2359
   963
                    // Connect refresh timer
Thomas@2359
   964
                    this.TimerRefresh.Tick += TimerRefresh_Tick;
Thomas@2662
   965
                    this.TimerRefresh.Start();
Thomas@2359
   966
                    this.isStarted = true;
Thomas@2359
   967
                }
Thomas@2359
   968
            }
Thomas@2359
   969
            else
Thomas@2359
   970
            {
Thomas@2359
   971
                // Stop and disconnect the refresh timer
Thomas@2528
   972
                if (this.TimerRefresh != null)
Thomas@2528
   973
                {
Thomas@2528
   974
                    this.TimerRefresh.Stop();
Thomas@2528
   975
                    this.TimerRefresh.Enabled = false;
Thomas@2528
   976
                    this.TimerRefresh.Tick -= TimerRefresh_Tick;
Thomas@2528
   977
                }
Thomas@2359
   978
Thomas@2662
   979
                // For draft messages, just set rating to unencrypted if pEp is not enabled
Thomas@2662
   980
                if (this.IsDraft)
Thomas@2662
   981
                {
Thomas@2662
   982
                    this.SetRating(pEpRating.pEpRatingUnencrypted);
Thomas@2662
   983
                }
Thomas@2662
   984
Thomas@2359
   985
                this.isStarted = false;
Thomas@2359
   986
            }
Thomas@2359
   987
        }
Thomas@2359
   988
Thomas@2359
   989
        /// <summary>
Thomas@2359
   990
        /// Resolves all recipients of the Outlook mail item.
Thomas@2359
   991
        /// </summary>
Thomas@2359
   992
        private void ResolveAllRecipients()
Thomas@2359
   993
        {
Thomas@2359
   994
            if ((this.isEnabled) &&
Thomas@2359
   995
                (this.cryptableMailItem != null))
Thomas@2359
   996
            {
Thomas@2359
   997
                /*
Thomas@2359
   998
                 * Note: The PropertyChanged changed event must be disconnected before trying to resolve.
Thomas@2359
   999
                 * This is because the resolve process can modify the contents of the mail item which triggers an event.
Thomas@2359
  1000
                 * The PropertyChanged event would then trigger a UI refresh cycle. However, because the GetManagerState itself 
Thomas@2359
  1001
                 * is called within the UI refresh, an infinite loop could occur trying to resolve a recipient that
Thomas@2359
  1002
                 * cannot be resolved(no address).
Thomas@2359
  1003
                 */
Thomas@2359
  1004
                try
Thomas@2359
  1005
                {
Thomas@2359
  1006
                    this.cryptableMailItem.PropertyChanged -= MailItem_PropertyChanged;
Thomas@2359
  1007
                    this.cryptableMailItem.ResolveAllRecipients();
Thomas@2359
  1008
                    this.cryptableMailItem.PropertyChanged += MailItem_PropertyChanged;
Thomas@2359
  1009
                }
Thomas@2359
  1010
                catch (Exception ex)
Thomas@2359
  1011
                {
Thomas@2359
  1012
                    Log.Error("Error resolving recipients. " + ex.ToString());
Thomas@2359
  1013
                }
Thomas@2359
  1014
            }
Thomas@2359
  1015
        }
Thomas@2359
  1016
        #endregion
Thomas@2359
  1017
Thomas@2359
  1018
        #region Event Handling
Thomas@2359
  1019
        /**************************************************************
Thomas@2359
  1020
         * 
Thomas@2359
  1021
         * Event Handling
Thomas@2359
  1022
         * 
Thomas@2359
  1023
         *************************************************************/
Thomas@2359
  1024
Thomas@2359
  1025
        /// <summary>
Thomas@2359
  1026
        /// Event handler for when the processing is completed in the associated mail item.
Thomas@2359
  1027
        /// This will then update the form region UI and the privacy status window as needed.
Thomas@2359
  1028
        /// </summary>
Thomas@2359
  1029
        private void MailItem_ProcessingCompleted(object sender, MsgProcessor.ProcessingCompletedEventArgs e)
Thomas@2359
  1030
        {
Thomas@2359
  1031
            Log.Verbose("MailItem_ProcessingComplete: Decryption completed.");
Thomas@2359
  1032
Thomas@2359
  1033
            try
Thomas@2359
  1034
            {
Thomas@2661
  1035
                /* Make sure that the calculated result corresponds to the currently selected mail item
Thomas@2661
  1036
                 * If both are null, it's probably a draft message.
Thomas@2780
  1037
                 */
Thomas@2661
  1038
                if ((e.EntryId == null && this.CurrentMailItem?.EntryID == null) ||
Thomas@2661
  1039
                    (e.EntryId.Equals(this.CurrentMailItem.EntryID)))
Thomas@2612
  1040
                {
Thomas@2661
  1041
                    // Marshal code back to UI thread as necessary
Thomas@2800
  1042
                    DialogWindow.UIThreadDispatcher.Invoke(new Action(() =>
Thomas@2359
  1043
                    {
Thomas@2661
  1044
                        if (this.isEnabled)
Thomas@2359
  1045
                        {
Thomas@2661
  1046
                            try
Thomas@2661
  1047
                            {
Thomas@2661
  1048
                                this.SetRating(e.ProcessedRating);
Thomas@2359
  1049
Thomas@2780
  1050
                                // Set MAPI properties if needed
Thomas@2780
  1051
                                if (e.PropertiesToSet?.Count > 0)
Thomas@2661
  1052
                                {
Thomas@2661
  1053
                                    this.CurrentMailItem?.SetMAPIProperties(e.PropertiesToSet);
Thomas@2661
  1054
                                }
Thomas@2661
  1055
                                Log.Verbose("MailItem_ProcessingComplete: Status bar updated with rating " + Enum.GetName(typeof(pEpRating), e.ProcessedRating));
Thomas@2661
  1056
                            }
Thomas@2661
  1057
                            catch (Exception ex)
Thomas@2359
  1058
                            {
Thomas@2661
  1059
                                Log.Verbose("MailItem_ProcessingComplete: Error. " + ex.ToString());
Thomas@2359
  1060
                            }
Thomas@2359
  1061
Thomas@2661
  1062
                            if (repeatProcessing &&
Thomas@2661
  1063
                                (repeatCounter++ < maxRepeatCount))
Thomas@2661
  1064
                            {
Thomas@2661
  1065
                                repeatProcessing = false;
Thomas@2661
  1066
                                this.RequestRatingAndUIUpdate();
Thomas@2661
  1067
                            }
Thomas@2661
  1068
                            else
Thomas@2661
  1069
                            {
Thomas@2780
  1070
                                /* Check if the mail item is in Outbox and update the inspector window if possible.
Thomas@2780
  1071
                                 * This is the case when a submitted, but not yet sent email is opened again when working 
Thomas@2780
  1072
                                 * offline or without internet connection. Without this update, Outlook removes the message class 
Thomas@2780
  1073
                                 * "IPM.Note.SMIME.MultipartSigned" at the next send event and the message gets invalid and can't be
Thomas@2780
  1074
                                 * opened again (i.e. is lost).
Thomas@2780
  1075
                                 */
Thomas@2661
  1076
                                try
Thomas@2359
  1077
                                {
Thomas@2661
  1078
                                    if ((this.CurrentMailItem?.GetIsSubmitted() == true) &&
Thomas@2661
  1079
                                        (this.IsDraft))
Thomas@2661
  1080
                                    {
Thomas@2661
  1081
                                        UpdateInspector();
Thomas@2661
  1082
                                    }
Thomas@2359
  1083
                                }
Thomas@2661
  1084
                                catch (Exception ex)
Thomas@2661
  1085
                                {
Thomas@2661
  1086
                                    Log.Verbose("MailItem_ProcessingComplete: Error while checking if mail item is in outbox or updating inspector. " + ex.Message);
Thomas@2661
  1087
                                }
Thomas@2359
  1088
Thomas@2780
  1089
                                /* Create the unencrypted preview if the mail item is encrypted and
Thomas@2780
  1090
                                 * it is in an encrypted (untrusted) store
Thomas@2780
  1091
                                 * 
Thomas@2780
  1092
                                 * This is done here because FormRegionPrivacyStatus has the cryptable mail item and
Thomas@2780
  1093
                                 * it also is initialized after FormRegionPreviewUnencrypted.
Thomas@2780
  1094
                                 */
Thomas@2661
  1095
                                try
Thomas@2359
  1096
                                {
Thomas@2661
  1097
                                    if (this.cryptableMailItem.IsSecurelyStored || this.cryptableMailItem.IsSecureAttachedMail)
Thomas@2661
  1098
                                    {
Thomas@2661
  1099
                                        Log.Verbose("MailItem_ProcessingComplete: Starting mirror location.");
Thomas@2661
  1100
                                        this.cryptableMailItem.StartGetMirror(this.cryptableMailItem.MessageId);
Thomas@2661
  1101
                                    }
Thomas@2661
  1102
                                    else
Thomas@2661
  1103
                                    {
Thomas@2661
  1104
                                        this.SetNote();
Thomas@2661
  1105
                                    }
Thomas@2359
  1106
                                }
Thomas@2661
  1107
                                catch (Exception ex)
Thomas@2359
  1108
                                {
Thomas@2780
  1109
                                    // Error is possible in some situations where the mail item was deleted or moved while decryption was ongoing.
Thomas@2780
  1110
                                    // While rare, just log the issue and stop the process
Thomas@2780
  1111
                                    Log.Warning("MailItem_ProcessingComplete: Failed to start mirror location, " + ex.ToString());
Thomas@2661
  1112
                                }
Thomas@2359
  1113
Thomas@2780
  1114
                                /* OUT-470: Workaround until this is implemented in the engine:
Thomas@2780
  1115
                                 * Only enable Force Protection if all recipients are grey
Thomas@2780
  1116
                                 */
Thomas@2661
  1117
                                if (this.IsDraft)
Thomas@2661
  1118
                                {
Thomas@2661
  1119
                                    bool disableForceProtection = false;
Thomas@2661
  1120
                                    Outlook.Recipients recipients = null;
Thomas@2661
  1121
                                    Outlook.Recipient recipient = null;
Thomas@2661
  1122
                                    PEPIdentity pEpIdentity = null;
Thomas@2359
  1123
Thomas@2661
  1124
                                    try
Thomas@2359
  1125
                                    {
Thomas@2661
  1126
                                        recipients = this.CurrentMailItem?.Recipients;
Thomas@2661
  1127
                                        if (recipients?.Count > 0)
Thomas@2359
  1128
                                        {
Thomas@2661
  1129
                                            for (int i = 1; i <= recipients.Count; i++)
Thomas@2359
  1130
                                            {
Thomas@2661
  1131
                                                recipient = recipients[i];
Thomas@2661
  1132
                                                if ((PEPIdentity.Create(recipient, out pEpIdentity, false) == Globals.ReturnStatus.Success) &&
Thomas@2661
  1133
                                                    ((PEPIdentity.GetIsOwnIdentity(pEpIdentity.Address) ||
Thomas@2661
  1134
                                                     (ThisAddIn.PEPEngine.IdentityRating(pEpIdentity.ToCOMType()) >= pEpRating.pEpRatingReliable))))
Thomas@2661
  1135
                                                {
Thomas@2661
  1136
                                                    disableForceProtection = true;
Thomas@2661
  1137
                                                    Log.Verbose("MailItem_ProcessingComplete: Secure recipient found. Disabling force protection.");
Thomas@2661
  1138
                                                    break;
Thomas@2661
  1139
                                                }
Thomas@2359
  1140
                                            }
Thomas@2359
  1141
                                        }
Thomas@2661
  1142
                                        else
Thomas@2661
  1143
                                        {
Thomas@2780
  1144
                                            // If there aren't any recipients (anymore), reset values
Thomas@2780
  1145
                                            this.DisableForceProtection = false;
Thomas@2661
  1146
                                            this.ForceProtection = false;
Thomas@2661
  1147
                                            this.NeverUnsecure = false;
Thomas@2661
  1148
                                            this.ForceUnencrypted = false;
Thomas@2661
  1149
                                        }
Thomas@2359
  1150
                                    }
Thomas@2661
  1151
                                    catch (Exception ex)
Thomas@2661
  1152
                                    {
Thomas@2661
  1153
                                        Log.Error("MailItem_ProcessingComplete: Error occured. " + ex.ToString());
Thomas@2661
  1154
                                    }
Thomas@2661
  1155
                                    finally
Thomas@2661
  1156
                                    {
Thomas@2661
  1157
                                        recipient = null;
Thomas@2661
  1158
                                        recipients = null;
Thomas@2661
  1159
                                    }
Thomas@2661
  1160
Thomas@2661
  1161
                                    this.DisableForceProtection = disableForceProtection;
Thomas@2359
  1162
                                }
Thomas@2359
  1163
                            }
Thomas@2359
  1164
                        }
Thomas@2661
  1165
                    }));
Thomas@2661
  1166
                }
Thomas@2661
  1167
                else
Thomas@2661
  1168
                {
Thomas@2661
  1169
                    Log.Verbose("MailItem_ProcessingComplete: Calculated rating doesn't correspond to current mail item. Skipping...");
Thomas@2661
  1170
                }
Thomas@2359
  1171
            }
Thomas@2359
  1172
            catch (Exception ex)
Thomas@2359
  1173
            {
Thomas@2359
  1174
                Log.Error("MailItem_ProcessingComplete: Error setting UI state, " + ex.ToString());
Thomas@2359
  1175
            }
Thomas@2359
  1176
Thomas@2359
  1177
            this.processingOngoing = false;
Thomas@2359
  1178
        }
Thomas@2359
  1179
Thomas@2359
  1180
        /// <summary>
Thomas@2359
  1181
        /// Event handler for when the get mirror locating process is complete for the associated mail item.
Thomas@2359
  1182
        /// This will then update the unencrypted preview in the UI.
Thomas@2359
  1183
        /// </summary>
Thomas@2359
  1184
        private void MailItem_GetMirrorCompleted(object sender, CryptableMailItem.GetMirrorCompletedEventArgs e)
Thomas@2359
  1185
        {
Thomas@2359
  1186
            try
Thomas@2359
  1187
            {
Thomas@2359
  1188
                // Marshal code back to UI thread as necessary
Thomas@2800
  1189
                DialogWindow.UIThreadDispatcher.Invoke(new Action(() =>
Thomas@2359
  1190
                {
Thomas@2359
  1191
                    if (this.isEnabled)
Thomas@2359
  1192
                    {
Thomas@2359
  1193
                        // If the message was forcefully protected, there is no mirror and we show the actual message
Thomas@2359
  1194
                        if ((e.Mirror == null) &&
Thomas@2385
  1195
                            (this.CurrentMailItem?.GetIsForcefullyProtected() == true))
Thomas@2359
  1196
                        {
Thomas@2507
  1197
                            if (PEPMessage.Create(this.CurrentMailItem, out PEPMessage mirror) == Globals.ReturnStatus.Success)
Thomas@2359
  1198
                            {
Thomas@2359
  1199
                                e.Mirror = mirror;
Thomas@2359
  1200
                            }
Thomas@2359
  1201
                        }
Thomas@2359
  1202
Thomas@2359
  1203
                        if ((e.Mirror == null) &&
Thomas@2359
  1204
                            (this.cryptableMailItem.LastProcessedStatus == Globals.ReturnStatus.Failure))
Thomas@2359
  1205
                        {
Thomas@2359
  1206
                            this.SetNote(Properties.Resources.Message_OpenError);
Thomas@2359
  1207
                            Log.Verbose("MailItem_GetMirrorComplete: Cannot display mirror, failure during decryption.");
Thomas@2359
  1208
                        }
Thomas@2359
  1209
                        else if ((e.Mirror == null) &&
Thomas@2359
  1210
                                 (this.cryptableMailItem.LastProcessedStatus == Globals.ReturnStatus.FailureNoConnection))
Thomas@2359
  1211
                        {
Thomas@2359
  1212
                            this.SetNote(Properties.Resources.Message_DecryptionNoConnection);
Thomas@2359
  1213
                            Log.Verbose("MailItem_GetMirrorComplete: Cannot display mirror, connection failure during decryption.");
Thomas@2359
  1214
                        }
Thomas@2359
  1215
                        else
Thomas@2359
  1216
                        {
Thomas@2419
  1217
                            FormRegionPreviewUnencrypted formRegionPreviewUnencrypted = this.GetFormRegionPreviewUnencrypted();
Thomas@2360
  1218
Thomas@2360
  1219
                            // If we have a FormRegionPreviewUnencrypted and it's visible, show preview
Thomas@2419
  1220
                            if (formRegionPreviewUnencrypted?.Visible == true)
Thomas@2360
  1221
                            {
Thomas@2419
  1222
                                formRegionPreviewUnencrypted.DisplayState.OriginalEntryId = this.CurrentMailItem?.EntryID;
Thomas@2419
  1223
                                formRegionPreviewUnencrypted.DisplayState.SetMessage(e.Mirror);
Thomas@2359
  1224
Thomas@2359
  1225
                                Log.Verbose("MailItem_GetMirrorComplete: Mirror found and displayed.");
Thomas@2359
  1226
                            }
Thomas@2360
  1227
                            else
Thomas@2360
  1228
                            {
Thomas@2360
  1229
                                Log.Error("MailItem_GetMirrorComplete: FormRegionPreviewUnencrypted is null or invisible.");
Thomas@2360
  1230
                            }
Thomas@2359
  1231
                        }
Thomas@2359
  1232
Thomas@2359
  1233
                        // Display the mirror if necessary
Thomas@2359
  1234
                        if ((this.cryptableMailItem != null) &&
Thomas@2359
  1235
                            (this.displayMirrorRequested))
Thomas@2359
  1236
                        {
Thomas@2359
  1237
                            this.cryptableMailItem.DisplayMirror();
Thomas@2359
  1238
                            this.displayMirrorRequested = false;
Thomas@2359
  1239
                        }
Thomas@2359
  1240
                    }
Thomas@2359
  1241
                }));
Thomas@2359
  1242
            }
Thomas@2359
  1243
            catch (Exception ex)
Thomas@2359
  1244
            {
Thomas@2359
  1245
                Log.Error("MailItem_GetMirrorComplete: Error displaying preview, " + ex.ToString());
Thomas@2359
  1246
            }
Thomas@2359
  1247
        }
Thomas@2359
  1248
Thomas@2359
  1249
        /// <summary>
Thomas@2359
  1250
        /// Event handler for the mail item originally encrypted status updated.
Thomas@2359
  1251
        /// This event is fired after the status has been updated with parent information following a
Thomas@2359
  1252
        /// forward or reply mail item event (on a different, likely parent, mail item).
Thomas@2359
  1253
        /// </summary>
Thomas@2359
  1254
        protected void CryptableMailItem_OriginallyEncryptedStatusUpdated(object sender, EventArgs e)
Thomas@2359
  1255
        {
Thomas@2359
  1256
            // Process the mail item now that the cache is updated
Thomas@2359
  1257
            if (this.cryptableMailItem.IsOriginallyEncrypted)
Thomas@2359
  1258
            {
Thomas@2560
  1259
                this.cryptableMailItem.StartProcessing(this.IsDraft);
Thomas@2359
  1260
            }
Thomas@2359
  1261
        }
Thomas@2359
  1262
Thomas@2359
  1263
        /// <summary>
Thomas@2359
  1264
        /// Event handler for when a mail item is being opened in an inspector.
Thomas@2359
  1265
        /// See: https://msdn.microsoft.com/en-us/library/office/ff865989.aspx
Thomas@2359
  1266
        /// </summary>
Thomas@2359
  1267
        /// <param name="cancel">Whether to cancel the event: Value is False when the event occurs. 
Thomas@2359
  1268
        /// If the event procedure sets this argument to True, the open operation is not completed 
Thomas@2359
  1269
        /// and the inspector is not displayed.</param>
Thomas@2359
  1270
        protected void MailItem_Open(ref bool cancel)
Thomas@2359
  1271
        {
Thomas@2359
  1272
            if ((this.isEnabled) &&
Thomas@2359
  1273
                (this.cryptableMailItem != null))
Thomas@2359
  1274
            {
Thomas@2359
  1275
                // If cryptable mail item is not to be opened, cancel opening
Thomas@2359
  1276
                if (this.cryptableMailItem.CancelOpen)
Thomas@2359
  1277
                {
Thomas@2359
  1278
                    cancel = true;
Thomas@2359
  1279
                }
Thomas@2359
  1280
                else if ((this.cryptableMailItem.IsSecurelyStored) &&
Thomas@2359
  1281
                         (this.cryptableMailItem.IsIncoming || this.cryptableMailItem.IsOriginallyEncrypted || !this.cryptableMailItem.IsDraft))
Thomas@2359
  1282
                {
Thomas@2359
  1283
                    // Try to open the mirror
Thomas@2574
  1284
                    cancel = this.cryptableMailItem.DisplayMirror();
Thomas@2359
  1285
                }
Thomas@2359
  1286
            }
Thomas@2359
  1287
        }
Thomas@2359
  1288
Thomas@2359
  1289
        /// <summary>
Thomas@2359
  1290
        /// Event handler for when a mail item is sent.
Thomas@2359
  1291
        /// See: https://msdn.microsoft.com/en-us/library/office/ff865379.aspx
Thomas@2359
  1292
        /// </summary>
Thomas@2359
  1293
        /// <param name="cancel">Whether to cancel the event: Value is False when the event occurs. 
Thomas@2359
  1294
        /// If the event procedure sets this argument to True, the send operation is not completed 
Thomas@2359
  1295
        /// and the inspector is left open.</param>
Thomas@2359
  1296
        protected void MailItem_Send(ref bool cancel)
Thomas@2359
  1297
        {
Thomas@2359
  1298
            DialogResult result;
Thomas@2359
  1299
Thomas@2359
  1300
            if (this.isEnabled)
Thomas@2359
  1301
            {
Thomas@2359
  1302
                if ((this.cryptableMailItem.IsBeingProcessed) ||
Thomas@2359
  1303
                    (this.processingOngoing))
Thomas@2359
  1304
                {
Thomas@2385
  1305
                    /* If the mail item is still being processed, this means that the message might go out with
Thomas@2385
  1306
                     * a different rating than the one that was shown in the UI when sending was initiated.
Thomas@2385
  1307
                     * As a trade-off between security and user experience, we make at least sure that the
Thomas@2385
  1308
                     * current outgoing rating preview (which is only wrong under rare circumstances) is not
Thomas@2385
  1309
                     * smaller than the one shown in the UI. If this is the case, just abort sending.
Thomas@2385
  1310
                     */
Thomas@2385
  1311
                    pEpRating outgoingPreviewRating = this.GetOutgoingRatingPreview();
Thomas@2385
  1312
                    if (this.Rating != outgoingPreviewRating)
Thomas@2385
  1313
                    {
Thomas@2385
  1314
                        if (this.Rating > outgoingPreviewRating)
Thomas@2385
  1315
                        {
Thomas@2385
  1316
                            Log.Verbose("MailItem_Send: Mail item still being processed.");
Thomas@2385
  1317
                            cancel = true;
Thomas@2385
  1318
                            return;
Thomas@2385
  1319
                        }
Thomas@2385
  1320
Thomas@2385
  1321
                        this.SetRating(outgoingPreviewRating);
Thomas@2385
  1322
                    }
Thomas@2359
  1323
                }
Thomas@2359
  1324
Thomas@2359
  1325
                // Show warning message if needed
Thomas@2359
  1326
                if ((Globals.ThisAddIn.Settings.IsSecurityLossWarningEnabled) &&
Thomas@2359
  1327
                    (this.Rating < pEpRating.pEpRatingUnreliable) &&
Thomas@2359
  1328
                    (this.cryptableMailItem.IsOriginallyEncrypted))
Thomas@2359
  1329
                {
Thomas@2359
  1330
Thomas@2359
  1331
#if READER_RELEASE_MODE
Thomas@2359
  1332
                    FormReaderSplash warningMessage = new FormReaderSplash(true);
Thomas@2359
  1333
                    result = warningMessage.ShowDialog();
Thomas@2359
  1334
Thomas@2359
  1335
                    if (result != DialogResult.OK)
Thomas@2359
  1336
                    {
Thomas@2359
  1337
                        // Cancel sending
Thomas@2359
  1338
                        cancel = true;
Thomas@2359
  1339
                    }
Thomas@2359
  1340
#else
Thomas@2359
  1341
                    result = MessageBox.Show(Properties.Resources.Message_WarningSecurityLoss,
Thomas@2359
  1342
                                             Properties.Resources.Message_TitleConfirmOperation,
Thomas@2359
  1343
                                             MessageBoxButtons.YesNo,
Thomas@2359
  1344
                                             MessageBoxIcon.Warning);
Thomas@2359
  1345
Thomas@2359
  1346
                    if (result == DialogResult.No)
Thomas@2359
  1347
                    {
Thomas@2359
  1348
                        // Cancel sending
Thomas@2359
  1349
                        cancel = true;
Thomas@2359
  1350
                    }
Thomas@2359
  1351
#endif
Thomas@2359
  1352
                }
Thomas@2359
  1353
Thomas@2359
  1354
                if (cancel == false)
Thomas@2359
  1355
                {
Thomas@2557
  1356
                    if ((this.Type == WindowType.Inspector) &&
Thomas@2561
  1357
                        (this.cryptableMailItem.IsInlineResponse == false) &&
Thomas@2561
  1358
                        (this.CurrentMailItem.GetProcessingState() == null))
Thomas@2661
  1359
                    {
Thomas@2661
  1360
                        IntPtr hWnd = NativeMethods.GetActiveWindow();
Thomas@2557
  1361
                        NativeMethods.SetWindowPos(hWnd, NativeMethods.HWND_BOTTOM, 0, 0, 0, 0, NativeMethods.SetWindowPosFlags.DoNotActivate |
Thomas@2557
  1362
                                                                                                NativeMethods.SetWindowPosFlags.IgnoreMove |
Thomas@2557
  1363
                                                                                                NativeMethods.SetWindowPosFlags.DoNotReposition |
Thomas@2557
  1364
                                                                                                NativeMethods.SetWindowPosFlags.IgnoreResize |
Thomas@2561
  1365
                                                                                                NativeMethods.SetWindowPosFlags.HideWindow);
Thomas@2561
  1366
                        this.CurrentMailItem.SetProcessingState(MailItemExtensions.ProcessingState.ProcessInBackground);
Thomas@2562
  1367
                        Log.Verbose("MailItem_Send: Inspector window closed.");
Thomas@2557
  1368
                    }
Thomas@2557
  1369
Thomas@2359
  1370
                    // Set pEp options if needed
Thomas@2359
  1371
                    if ((this.ForceProtection) &&
Thomas@2359
  1372
                        (this.DisableForceProtection == false))
Thomas@2359
  1373
                    {
Thomas@2385
  1374
                        this.CurrentMailItem?.SetPEPProperty(MailItemExtensions.PEPProperty.ForceProtection, Guid.NewGuid().ToString());
Thomas@2359
  1375
                    }
Thomas@2359
  1376
                    else if (this.ForceUnencrypted)
Thomas@2359
  1377
                    {
Thomas@2385
  1378
                        this.CurrentMailItem?.SetPEPProperty(MailItemExtensions.PEPProperty.ForceUnencrypted, true);
Thomas@2359
  1379
                    }
Thomas@2359
  1380
Thomas@2359
  1381
                    if (this.NeverUnsecure)
Thomas@2359
  1382
                    {
Thomas@2385
  1383
                        this.CurrentMailItem?.SetPEPProperty(MailItemExtensions.PEPProperty.NeverUnsecure, true);
Thomas@2359
  1384
                    }
Thomas@2359
  1385
Thomas@2515
  1386
                    // Reset pEp options
Thomas@2515
  1387
                    this.ForceProtection = false;
Thomas@2515
  1388
                    this.DisableForceProtection = false;
Thomas@2515
  1389
                    this.ForceUnencrypted = false;
Thomas@2515
  1390
                    this.NeverUnsecure = false;
Thomas@2515
  1391
Thomas@2359
  1392
                    // Stop and disconnect the refresh timer
Thomas@2359
  1393
                    // This is necessary so an ongoing refresh doesn't try to access a mail item as it's being moved
Thomas@2359
  1394
                    this.TimerRefresh.Stop();
Thomas@2359
  1395
                    this.TimerRefresh.Enabled = false;
Thomas@2359
  1396
                    this.TimerRefresh.Tick -= TimerRefresh_Tick;
Thomas@2359
  1397
                }
Thomas@2359
  1398
            }
Thomas@2359
  1399
        }
Thomas@2359
  1400
Thomas@2359
  1401
        /// <summary>
Thomas@2359
  1402
        /// Event handler for when a mail item property is changed.
Thomas@2359
  1403
        /// See: https://msdn.microsoft.com/en-us/library/office/ff866739.aspx
Thomas@2359
  1404
        /// </summary>
Thomas@2359
  1405
        /// <param name="propertyName">The name of the property that was changed.</param>
Thomas@2359
  1406
        protected void MailItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
Thomas@2359
  1407
        {
Thomas@2359
  1408
            switch (e.PropertyName.ToUpperInvariant())
Thomas@2359
  1409
            {
Thomas@2359
  1410
                case "BCC":
Thomas@2359
  1411
                    {
Thomas@2359
  1412
                        // Outlook always fires Bcc, Cc and To together so only "TO" is used
Thomas@2359
  1413
                        break;
Thomas@2359
  1414
                    }
Thomas@2359
  1415
                case "CC":
Thomas@2359
  1416
                    {
Thomas@2359
  1417
                        // Outlook always fires Bcc, Cc and To together so only "TO" is used
Thomas@2359
  1418
                        break;
Thomas@2359
  1419
                    }
Thomas@2359
  1420
                case "SENTONBEHALFOFNAME":
Thomas@2359
  1421
                    {
Thomas@2359
  1422
                        // Always fired with "SENDUSINGACCOUNT" so is ignored
Thomas@2359
  1423
                        break;
Thomas@2359
  1424
                    }
Thomas@2359
  1425
                case "SENDUSINGACCOUNT":
Thomas@2359
  1426
                    {
Thomas@2359
  1427
                        // Update pEp enabled status
Thomas@2359
  1428
                        this.isStarted = false;
Thomas@2359
  1429
                        this.SetIsEnabled((this.CurrentMailItem)?.GetEnableFormRegion() ?? false);
Thomas@2359
  1430
Thomas@2359
  1431
                        // Refresh the UI
Thomas@2359
  1432
                        RibbonCustomizations.Invalidate();
Thomas@2359
  1433
Thomas@2359
  1434
                        break;
Thomas@2359
  1435
                    }
Thomas@2359
  1436
                case "TO":
Thomas@2359
  1437
                    {
Thomas@2359
  1438
                        if (this.isEnabled)
Thomas@2359
  1439
                        {
Thomas@2359
  1440
                            this.RequestRatingAndUIUpdate();
Thomas@2359
  1441
                        }
Thomas@2359
  1442
                        break;
Thomas@2359
  1443
                    }
Thomas@2359
  1444
            }
Thomas@2359
  1445
        }
Thomas@2359
  1446
Thomas@2359
  1447
        /// <summary>
Thomas@2359
  1448
        /// Event handler for when a handshake dialog was closed.
Thomas@2359
  1449
        /// </summary>
Thomas@2359
  1450
        protected void HandshakeDialog_Closed(object sender, EventArgs e)
Thomas@2359
  1451
        {
Thomas@2847
  1452
            pEpRating newRating = this.Rating;
Thomas@2847
  1453
Thomas@2843
  1454
            // Get the updated rating
Thomas@2847
  1455
            bool trustedServer = this.CurrentMailItem.GetIsInSecureStore();
Thomas@2847
  1456
            if (trustedServer)
Thomas@2847
  1457
            {
Thomas@2847
  1458
                string[] keyList = new string[] { };
Thomas@2847
  1459
                pEpDecryptFlags flags = pEpDecryptFlags.pEpDecryptFlagUntrustedServer;
Thomas@2847
  1460
                TextMessage msg = this.handshakeMessage.ToCOMType();
Thomas@2847
  1461
                newRating = ThisAddIn.PEPEngine.DecryptMessage(ref msg, out _, ref keyList, ref flags);
Thomas@2847
  1462
            }
Thomas@2847
  1463
            else
Thomas@2847
  1464
            {
Thomas@2847
  1465
                newRating = AdapterExtensions.ReevaluateMessageRating(this.handshakeMessage, this.Rating);
Thomas@2847
  1466
            }
Thomas@2843
  1467
Thomas@2843
  1468
            // If the new rating is different, update the message with the new rating
Thomas@2843
  1469
            if (newRating != this.Rating)
Thomas@2843
  1470
            {
Thomas@2847
  1471
                Outlook.MailItem mirror = null;
Thomas@2847
  1472
Thomas@2843
  1473
                try
Thomas@2843
  1474
                {
Thomas@2843
  1475
                    this.SetRating(newRating);
Thomas@2847
  1476
Thomas@2847
  1477
                    if (trustedServer)
Thomas@2847
  1478
                    {
Thomas@2847
  1479
                        mirror = this.CurrentMailItem.GetMirror();
Thomas@2847
  1480
                        mirror.SetPEPProperty(MailItemExtensions.PEPProperty.Rating, newRating);
Thomas@2847
  1481
                        mirror.Save();
Thomas@2847
  1482
                    }
Thomas@2847
  1483
                    else
Thomas@2847
  1484
                    {
Thomas@2847
  1485
                        this.CurrentMailItem.SetPEPProperty(MailItemExtensions.PEPProperty.Rating, newRating);
Thomas@2847
  1486
                        this.CurrentMailItem.Save();
Thomas@2847
  1487
                    }
Thomas@2843
  1488
                }
Thomas@2843
  1489
                catch (Exception ex)
Thomas@2843
  1490
                {
Thomas@2843
  1491
                    Log.Error("HandshakeDialog_Closed: Error updating rating. " + ex.ToString());
Thomas@2843
  1492
                }
Thomas@2847
  1493
                finally
Thomas@2847
  1494
                {
Thomas@2847
  1495
                    mirror = null;
Thomas@2847
  1496
                }
Thomas@2843
  1497
            }
Thomas@2843
  1498
Thomas@2847
  1499
            // Recalculate all other windows
Thomas@2715
  1500
            Globals.ThisAddIn.RecalculateAllWindows(this);
Thomas@2715
  1501
Thomas@2780
  1502
            if (WatchedWindow.HandshakeDialog != null)
Thomas@2359
  1503
            {
Thomas@2780
  1504
                WatchedWindow.HandshakeDialog.Closed -= HandshakeDialog_Closed;
Thomas@2780
  1505
                WatchedWindow.HandshakeDialog = null;
Thomas@2359
  1506
            }
Thomas@2847
  1507
Thomas@2847
  1508
            this.handshakeMessage = null;
Thomas@2359
  1509
        }
Thomas@2359
  1510
Thomas@2359
  1511
        /// <summary>
Thomas@2359
  1512
        /// Event handler called after the refresh timer has elapsed.
Thomas@2359
  1513
        /// </summary>
Thomas@2359
  1514
        private void TimerRefresh_Tick(object sender, EventArgs e)
Thomas@2359
  1515
        {
Thomas@2359
  1516
            bool tryAgain = false;
Thomas@2359
  1517
#pragma warning disable 219
Thomas@2359
  1518
            bool markForDownload = false;
Thomas@2359
  1519
#pragma warning restore 219
Thomas@2662
  1520
            this.TimerRefresh.Stop(); // Only once
Thomas@2359
  1521
            Outlook.OlDownloadState dlState;
Thomas@2359
  1522
Thomas@2359
  1523
            /* The Refresh/UI_Update process:
Thomas@2359
  1524
             * There are the following components:
Thomas@2359
  1525
             *   1. TimerRefresh_Tick
Thomas@2359
  1526
             *        This is the main timer tick event handler called any time
Thomas@2359
  1527
             *        a refresh was requested (RequestRatingAndUIUpdate) and hasn't been run yet. 
Thomas@2359
  1528
             *        A refresh is requested either at initialization or when a property changes
Thomas@2359
  1529
             *        (such as MailItem_PropertyChanged). It is on a timer as many 
Thomas@2359
  1530
             *        property change events could occur rapidy, but only one refresh should
Thomas@2359
  1531
             *        occur for performance reasons.
Thomas@2359
  1532
             * 
Thomas@2359
  1533
             *   2. ImmediateRatingAndUIUpdate
Thomas@2359
  1534
             *        This will re-calculate the mail item's rating.
Thomas@2359
  1535
             *        This internally just calls CryptableMailItem.StartProcessing.
Thomas@2359
  1536
             * 
Thomas@2359
  1537
             *   3. MailItem_ProcessingCompleted
Thomas@2359
  1538
             *        When processing of the mail item is complete, this even handler will be called.
Thomas@2359
  1539
             *        This will update the UI with the latest rating then call StartGetMirror.
Thomas@2359
  1540
             *        The CopyStateToUI method is used which means any open privacy status form will also
Thomas@2359
  1541
             *        be updated.
Thomas@2359
  1542
             * 
Thomas@2359
  1543
             *   4. MailItem_GetMirrorComplete
Thomas@2359
  1544
             *        This is the final step in updating the UI, after a mirror is located, it's contents
Thomas@2359
  1545
             *        will be shown in the unencrypted preview.
Thomas@2359
  1546
             * 
Thomas@2359
  1547
             * The general calling sequence is as shown above 1->4 with each component calling the next.
Thomas@2359
  1548
             * However, for methods that update the mail item directly, commonly only 2->4 is needed.
Thomas@2359
  1549
             */
Thomas@2359
  1550
Thomas@2359
  1551
            // Ensure the tick method is not called more than once
Thomas@2359
  1552
            if ((this.isEnabled) &&
Thomas@2359
  1553
                (refreshOngoing == false))
Thomas@2359
  1554
            {
Thomas@2359
  1555
                this.refreshOngoing = true;
Thomas@2359
  1556
Thomas@2359
  1557
                if ((this.cryptableMailItem != null) &&
Thomas@2359
  1558
                    (this.cryptableMailItem.IsBeingProcessed == false))
Thomas@2359
  1559
                {
Thomas@2359
  1560
                    // Attempt to get the download state
Thomas@2359
  1561
                    try
Thomas@2359
  1562
                    {
Thomas@2359
  1563
                        dlState = this.cryptableMailItem.DownloadState;
Thomas@2359
  1564
                    }
Thomas@2359
  1565
                    catch (Exception ex)
Thomas@2359
  1566
                    {
Thomas@2359
  1567
                        Log.Warning("TimerRefresh_Tick: Get DownloadState failed, " + ex.ToString());
Thomas@2359
  1568
Thomas@2359
  1569
                        // Assume everything is downloaded, but try to download again as well
Thomas@2359
  1570
                        dlState = Outlook.OlDownloadState.olFullItem;
Thomas@2359
  1571
                        markForDownload = true;
Thomas@2359
  1572
                    }
Thomas@2359
  1573
Thomas@2359
  1574
                    if (dlState == Outlook.OlDownloadState.olFullItem)
Thomas@2359
  1575
                    {
Thomas@2359
  1576
                        this.ImmediateRatingAndUIUpdate();
Thomas@2359
  1577
                    }
Thomas@2359
  1578
                    else
Thomas@2359
  1579
                    {
Thomas@2359
  1580
                        markForDownload = true;
Thomas@2359
  1581
                    }
Thomas@2359
  1582
Thomas@2359
  1583
                    /* 12/16/2016: It could be verified via testing that setting the MarkForDownload property
Thomas@2359
  1584
                     * can be a way to crash Outlook under certain circumstances (e.g. with IMAP on Outlook 2010 or on Windows 7). 
Thomas@2359
  1585
                     * This then is not caught by a try/catch block and therefore has to be considered an Outlook bug.
Thomas@2359
  1586
                     * It seems that in newer versions of Outlook/Windows, they fixed it, causing exceptions, if at all.
Thomas@2359
  1587
                     * For now, the following is commented out to prevent a crash. If at any point we see that header-only
Thomas@2359
  1588
                     * messages cause bigger issues, this decision has to be reevaluated.
Thomas@2359
  1589
                     */
Thomas@2359
  1590
                    //if (markForDownload)
Thomas@2359
  1591
                    //{
Thomas@2359
  1592
                    //    // Try to mark the message for full download
Thomas@2359
  1593
                    //    try
Thomas@2359
  1594
                    //    {
Thomas@2359
  1595
                    //        this.cryptableMailItem.MarkForDownload = Outlook.OlRemoteStatus.olMarkedForDownload;
Thomas@2359
  1596
                    //        tryAgain = true;
Thomas@2359
  1597
                    //    }
Thomas@2359
  1598
                    //    catch (Exception ex)
Thomas@2359
  1599
                    //    {
Thomas@2359
  1600
                    //        Log.Warning("TimerRefresh_Tick: MarkForDownload failed, " + ex.ToString());
Thomas@2359
  1601
                    //    }
Thomas@2359
  1602
                    //}
Thomas@2359
  1603
                }
Thomas@2359
  1604
                else
Thomas@2359
  1605
                {
Thomas@2359
  1606
                    repeatProcessing = true;
Thomas@2359
  1607
                }
Thomas@2359
  1608
Thomas@2359
  1609
                // Set the timer to refresh again later automatically
Thomas@2359
  1610
                if (tryAgain)
Thomas@2359
  1611
                {
Thomas@2359
  1612
                    this.TimerRefresh.Interval = 100;
Thomas@2359
  1613
                    this.TimerRefresh.Enabled = true;
Thomas@2359
  1614
                }
Thomas@2359
  1615
Thomas@2359
  1616
                this.refreshOngoing = false;
Thomas@2359
  1617
            }
Thomas@2359
  1618
        }
Thomas@2359
  1619
Thomas@2359
  1620
        #endregion
Thomas@2359
  1621
    }
Thomas@2359
  1622
}