UI/FormRegionPrivacyStatus.cs
author Dean Looyengoed
Tue, 12 Apr 2016 23:38:50 +0200
changeset 807 d35120bd4600
parent 775 1677cdf39e41
child 838 c090cd5b1d29
permissions -rw-r--r--
Add detection and caching of encrypted conversations.
Show warning when forward/reply as unencrypted to originally encrypted emails.
Dean@159
     1
´╗┐using pEpCOMServerAdapterLib;
Dean@159
     2
using System;
vb@133
     3
using System.Collections.Generic;
Dean@445
     4
using System.ComponentModel;
Dean@438
     5
using System.Drawing;
Dean@159
     6
using System.Runtime.InteropServices;
vb@133
     7
using System.Windows.Forms;
Dean@159
     8
using Color = System.Drawing.Color;
vb@133
     9
using Outlook = Microsoft.Office.Interop.Outlook;
vb@133
    10
vb@133
    11
namespace pEp
vb@133
    12
{
Dean@159
    13
    /// <summary>
Dean@312
    14
    /// Partial class for the privacy status form region that is displayed below every outlook message.
Dean@159
    15
    /// </summary>
Dean@312
    16
    partial class FormRegionPrivacyStatus
vb@133
    17
    {
vb@133
    18
        #region Form Region Factory
vb@133
    19
vb@133
    20
        [Microsoft.Office.Tools.Outlook.FormRegionMessageClass(Microsoft.Office.Tools.Outlook.FormRegionMessageClassAttribute.Note)]
Dean@312
    21
        [Microsoft.Office.Tools.Outlook.FormRegionName("pEp.FormRegionPrivacyStatus")]
Dean@312
    22
        public partial class FormRegionPrivacyStatusFactory
vb@133
    23
        {
vb@133
    24
            // Occurs before the form region is initialized.
vb@133
    25
            // To prevent the form region from appearing, set e.Cancel to true.
vb@133
    26
            // Use e.OutlookItem to get a reference to the current Outlook item.
Dean@312
    27
            private void FormRegionPrivacyStatus_FormRegionInitializing(object sender, Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs e)
vb@133
    28
            {
Dean@327
    29
                WindowFormRegionCollection formRegions;
Dean@327
    30
Dean@327
    31
                /* There is a Microsoft bug at least in Outlook 2013 and Windows 8.1
Dean@327
    32
                 * This bug causes multiple PrivacyStatus form regions to appear stacked on top of each other.
Dean@327
    33
                 * To trigger this bug, on an unencrypted server, click reply to compose an in-line response.
Dean@327
    34
                 * Then click to another tab such as People or Tasks. Then click back on the mail tab to view 
Dean@327
    35
                 * the original email again. Two form regions will be visible: 
Dean@327
    36
                 * (1) for the status during the in-line reply and 
Dean@327
    37
                 * (2) for the status of only the received message.
Dean@327
    38
                 * 
Dean@327
    39
                 * To fix this bug, any existing form regions are found and closed when initializing a new privacy status
Dean@327
    40
                 * form region.
Dean@327
    41
                 */
Dean@327
    42
Dean@327
    43
                try
Dean@327
    44
                {
Dean@327
    45
                    formRegions = Globals.FormRegions[Globals.ThisAddIn.Application.ActiveExplorer()];
Dean@327
    46
                    if (formRegions.FormRegionPrivacyStatus != null)
Dean@327
    47
                    {
Dean@327
    48
                        /* Note: there seems to be no way to actually close the form region.
Dean@327
    49
                         * Therefore, this is as close as possible to actually closing it.
Dean@327
    50
                         * The actual form regions will be cleaned up as soon as another email is selected.
Dean@327
    51
                         */
Dean@327
    52
                        formRegions.FormRegionPrivacyStatus.OutlookFormRegion.Visible = false;
Dean@327
    53
                    }
Dean@327
    54
                }
Dean@327
    55
                catch { }
Dean@327
    56
Dean@327
    57
                return;
vb@133
    58
            }
vb@133
    59
        }
vb@133
    60
vb@133
    61
        #endregion
vb@133
    62
Dean@159
    63
        /* Notes:
Dean@179
    64
         * 
Dean@159
    65
         * Use this.OutlookItem to get a reference to the current Outlook item.
Dean@159
    66
         * Use this.OutlookFormRegion to get a reference to the form region.
Dean@179
    67
         * 
Dean@179
    68
         * UI State Managment:
Dean@179
    69
         * 
Dean@179
    70
         * The UI state is almost entirely set from the associated mail item data.
Dean@179
    71
         * However, a separate state class is maintained to represent the UI as some separation is needed.
Dean@179
    72
         * This logical separation MUST be maintained throughout the code.
Dean@179
    73
         * Specific cases are noted where possible.
Dean@179
    74
         * 
Dean@356
    75
         * The separate privacy status manager form state is also managed here.
Dean@259
    76
         * 
Dean@159
    77
         */
Dean@159
    78
Dean@758
    79
        private CryptableMailItem       associatedMailItem     = null;
Dean@758
    80
        private bool                    eventsAreConnected     = false;
Dean@758
    81
        private bool                    initialized            = false;
Dean@758
    82
        private FormManagePrivacyStatus managerForm            = null;
Dean@758
    83
        private bool                    refreshOngoing         = false;
Dean@758
    84
        private bool                    displayMirrorRequested = false;
Dean@259
    85
Dean@250
    86
        // Data or controls not stored in the UI
Dean@179
    87
        private _pEp_color stateUIColorRating; // WARNING: Do NOT use this directly
vb@133
    88
Dean@159
    89
        /**************************************************************
Dean@159
    90
         * 
Dean@159
    91
         * Methods
Dean@159
    92
         * 
Dean@159
    93
         *************************************************************/
Dean@159
    94
Dean@171
    95
        /// <summary>
Dean@438
    96
        /// Determines if this form region is running in an inspector window.
Dean@438
    97
        /// If not true, it is assumed to be within an explorer window.
Dean@438
    98
        /// </summary>
Dean@438
    99
        /// <returns>True if an inspector window, false if within an explorer.</returns>
Dean@438
   100
        private bool IsWithinInspector()
Dean@438
   101
        {
Dean@438
   102
            bool isWithinInspector = false;
Dean@438
   103
            Outlook.Explorer temp = null;
Dean@438
   104
Dean@438
   105
            try
Dean@438
   106
            {
Dean@438
   107
                // Just try to cast to an explorer class
Dean@438
   108
                temp = (Outlook.Explorer)this.OutlookFormRegion.Parent;
Dean@438
   109
                isWithinInspector = false;
Dean@438
   110
            }
Dean@438
   111
            catch
Dean@438
   112
            {
Dean@438
   113
                isWithinInspector = true;
Dean@438
   114
            }
Dean@438
   115
            finally
Dean@438
   116
            {
Dean@438
   117
                if (temp != null)
Dean@438
   118
                {
Dean@438
   119
                    Marshal.ReleaseComObject(temp);
Dean@438
   120
                    temp = null;
Dean@438
   121
                }
Dean@438
   122
            }
Dean@438
   123
Dean@438
   124
            return (isWithinInspector);
Dean@438
   125
        }
Dean@438
   126
Dean@438
   127
        /// <summary>
Dean@170
   128
        /// Sets the cryptable mail item associated with this encryption status panel.
Dean@170
   129
        /// The associated mail item can then be accessed through its local variable.
Dean@170
   130
        /// </summary>
Dean@170
   131
        private void SetAssociatedMailItem()
Dean@170
   132
        {
Dean@170
   133
            bool errorOccurred = false;
Dean@170
   134
            Outlook.MailItem omi = null;
Dean@170
   135
Dean@170
   136
            // Null check
Dean@170
   137
            if (!errorOccurred)
Dean@170
   138
            {
Dean@170
   139
                try
Dean@170
   140
                {
Dean@170
   141
                    if (this.OutlookItem == null)
Dean@170
   142
                    {
Dean@170
   143
                        errorOccurred = true;
Dean@170
   144
                    }
Dean@170
   145
                }
Dean@170
   146
                catch (COMException)
Dean@170
   147
                {
Dean@170
   148
                    errorOccurred = true;
Dean@170
   149
                }
Dean@170
   150
            }
Dean@170
   151
Dean@170
   152
            // Attempt to get and cast the outlook mail item
Dean@170
   153
            if (!errorOccurred)
Dean@170
   154
            {
Dean@170
   155
                try
Dean@170
   156
                {
Dean@170
   157
                    omi = (Outlook.MailItem)this.OutlookItem;
Dean@170
   158
                }
Dean@170
   159
                catch
Dean@170
   160
                {
Dean@170
   161
                    errorOccurred = true;
Dean@170
   162
                }
Dean@170
   163
            }
Dean@170
   164
Dean@170
   165
            // Finally set the associated mail item
Dean@170
   166
            if ((errorOccurred) ||
Dean@170
   167
                (omi == null))
Dean@170
   168
            {
Dean@170
   169
                this.associatedMailItem = null;
Dean@170
   170
            }
Dean@170
   171
            else
Dean@170
   172
            {
Dean@170
   173
                // Check if the associated mail item has already been set
Dean@170
   174
                if (this.associatedMailItem != null)
Dean@170
   175
                {
Dean@170
   176
                    // Only re-set the mail item if the EntryID has changed
Dean@170
   177
                    if (this.associatedMailItem.EntryID != omi.EntryID)
Dean@170
   178
                    {
Dean@170
   179
                        this.associatedMailItem = new CryptableMailItem(omi);
Dean@170
   180
                    }
Dean@170
   181
                }
Dean@170
   182
                else
Dean@170
   183
                {
Dean@170
   184
                    this.associatedMailItem = new CryptableMailItem(omi);
Dean@170
   185
                }
Dean@170
   186
            }
Dean@170
   187
Dean@170
   188
            return;
Dean@170
   189
        }
Dean@170
   190
Dean@179
   191
        /// <summary>
Dean@672
   192
        /// Completes the handshake process when the identity of a partner was previously marked as mistrusted.
Dean@281
   193
        /// </summary>
Dean@646
   194
        /// <param name="myself">The personal identity to complete the handshake with.</param>
Dean@646
   195
        /// <param name="partner">The identity of the partner to complete the handshake with.</param>
Dean@672
   196
        private void DoHandshakeForMistrustedKey(PEPIdentity myself,
Dean@672
   197
                                                 PEPIdentity partner)
Dean@281
   198
        {
Dean@281
   199
            DialogResult result;
Dean@281
   200
Dean@281
   201
            result = MessageBox.Show(this.ParentForm,
Dean@672
   202
                                     pEp.Properties.Resources.Message_WarningMistrustedKey,
Dean@378
   203
                                     pEp.Properties.Resources.Message_TitleConfirmOperation,
Dean@423
   204
                                     MessageBoxButtons.YesNo);
Dean@281
   205
Dean@423
   206
            if (result == DialogResult.Yes)
Dean@281
   207
            {
Dean@646
   208
                this.DoHandshake(myself, partner);
Dean@281
   209
            }
Dean@281
   210
Dean@281
   211
            return;
Dean@281
   212
        }
Dean@281
   213
Dean@281
   214
        /// <summary>
Dean@281
   215
        /// Completes the handshake process where the identity of a partner is confirmed.
Dean@179
   216
        /// </summary>
Dean@646
   217
        /// <param name="myself">The personal identity to complete the handshake with.</param>
Dean@646
   218
        /// <param name="partner">The identity of the partner to complete the handshake with.</param>
Dean@646
   219
        private void DoHandshake(PEPIdentity myself,
Dean@646
   220
                                 PEPIdentity partner)
vb@133
   221
        {
Dean@155
   222
            string ownFpr;
Dean@155
   223
            string partnerFpr;
Dean@363
   224
            string trustwordsShort;
Dean@363
   225
            string trustwordsFull;
Dean@155
   226
            string[] lines;
Dean@179
   227
            DialogResult result;
Dean@155
   228
            FormHandshake handshakeDialog;
Dean@155
   229
            FormHandshake.State state = new FormHandshake.State();
markus@201
   230
Dean@759
   231
            Global.LogVerbose("DoHandshake: Handshake started.");
vb@139
   232
Dean@363
   233
            // Calculate and add trustwords
Dean@646
   234
            this.CalcTrustwords(myself, partner, out trustwordsShort, out trustwordsFull);
Dean@646
   235
            state.Myself = myself.Copy();
Dean@646
   236
            state.Partner = partner.Copy();
Dean@363
   237
            state.TrustwordsShort = trustwordsShort;
Dean@363
   238
            state.TrustwordsFull = trustwordsFull;
Dean@363
   239
Dean@363
   240
            // Calculate and add fingerprint
Dean@646
   241
            ownFpr = this.ToQuadruple(myself.Fingerprint);
Dean@646
   242
            partnerFpr = this.ToQuadruple(partner.Fingerprint);
vb@133
   243
Dean@646
   244
            if (myself.Fingerprint.CompareTo(partner.Fingerprint) > 0)
vb@133
   245
            {
Dean@155
   246
                lines = new string[3];
Dean@378
   247
                lines[0] = pEp.Properties.Resources.PrivacyStatus_TrustwordsPartnerShortPartner + partnerFpr;
Dean@378
   248
                lines[2] = pEp.Properties.Resources.PrivacyStatus_TrustwordsPartnerShortMyself + ownFpr;
Dean@155
   249
                state.Fingerprint = lines;
vb@133
   250
            }
vb@133
   251
            else
vb@133
   252
            {
Dean@155
   253
                lines = new string[3];
Dean@378
   254
                lines[0] = pEp.Properties.Resources.PrivacyStatus_TrustwordsOwnShortMyself + ownFpr;
Dean@378
   255
                lines[2] = pEp.Properties.Resources.PrivacyStatus_TrustwordsOwnShortPartner + partnerFpr;
Dean@155
   256
                state.Fingerprint = lines;
vb@133
   257
            }
vb@133
   258
Dean@155
   259
            // Create and show handshake dialog
Dean@155
   260
            handshakeDialog = new FormHandshake(state);
Dean@179
   261
            handshakeDialog.StartPosition = FormStartPosition.CenterScreen;
Dean@179
   262
            result = handshakeDialog.ShowDialog(this);
Dean@155
   263
Dean@646
   264
            this.ProcessDoHandshakeResult(result, partner);
Dean@356
   265
Dean@356
   266
            return;
Dean@356
   267
        }
Dean@356
   268
Dean@356
   269
        /// <summary>
Dean@356
   270
        /// Processes the result of the do handshake dialog after a user makes a selection.
Dean@356
   271
        /// </summary>
Dean@356
   272
        /// <param name="result">The result of the handshake dialog selection.</param>
Dean@356
   273
        /// <param name="partner">The identity of the partner to handshake with.</param>
Dean@356
   274
        private void ProcessDoHandshakeResult(DialogResult result,
Dean@384
   275
                                              PEPIdentity partner)
Dean@356
   276
        {
Dean@384
   277
            pEp_identity_s identityPartner = ThisAddIn.pEp.update_identity(partner.ToCOMType());
vb@133
   278
vb@133
   279
            switch (result)
vb@133
   280
            {
vb@133
   281
                case DialogResult.Yes:
Dean@279
   282
                    {
Dean@672
   283
                        // Check if key was previously mistrusted -- warning to user must be displayed earlier
Dean@672
   284
                        if (identityPartner.comm_type == _pEp_comm_type.pEp_ct_mistrusted)
Dean@281
   285
                        {
Dean@281
   286
                            ThisAddIn.pEp.key_reset_trust(ref identityPartner);
Dean@281
   287
                        }
Dean@281
   288
Dean@279
   289
                        identityPartner = ThisAddIn.pEp.trust_personal_key(ref identityPartner);
Dean@279
   290
Dean@279
   291
                        this.UpdateUIFromMailItem();
Dean@279
   292
Dean@279
   293
                        // Update the manager form state
Dean@279
   294
                        if (this.managerForm != null)
Dean@279
   295
                        {
Dean@279
   296
                            this.managerForm.CopyStateToUI(this.GetManagerState());
Dean@279
   297
                        }
Dean@279
   298
Dean@279
   299
                        break;
Dean@279
   300
                    }
vb@133
   301
                case DialogResult.No:
Dean@279
   302
                    {
Dean@279
   303
                        ThisAddIn.pEp.key_compromized(ref identityPartner);
Dean@279
   304
Dean@279
   305
                        this.UpdateUIFromMailItem();
Dean@279
   306
Dean@279
   307
                        // Update the manager form state
Dean@279
   308
                        if (this.managerForm != null)
Dean@279
   309
                        {
Dean@279
   310
                            this.managerForm.CopyStateToUI(this.GetManagerState());
Dean@279
   311
                        }
Dean@279
   312
Dean@279
   313
                        break;
Dean@279
   314
                    }
vb@133
   315
            }
Dean@279
   316
Dean@759
   317
            Global.LogVerbose("ProcessDoHandshakeResult: Handshake complete.");
Dean@740
   318
Dean@279
   319
            return;
vb@133
   320
        }
vb@133
   321
Dean@179
   322
        /// <summary>
Dean@179
   323
        /// Reverses any past handshake confirmation by unconfirming the given identity partner.
Dean@179
   324
        /// </summary>
Dean@384
   325
        /// <param name="partner">The identity of the partner to unconfirm.</param>
Dean@384
   326
        private void UndoHandshake(PEPIdentity partner)
vb@133
   327
        {
Dean@384
   328
            pEp_identity_s identityPartner = ThisAddIn.pEp.update_identity(partner.ToCOMType());
Dean@280
   329
            ThisAddIn.pEp.key_reset_trust(ref identityPartner);
Dean@279
   330
Dean@179
   331
            this.UpdateUIFromMailItem();
Dean@279
   332
Dean@279
   333
            // Update the manager form state
Dean@279
   334
            if (this.managerForm != null)
Dean@279
   335
            {
Dean@279
   336
                this.managerForm.CopyStateToUI(this.GetManagerState());
Dean@279
   337
            }
Dean@279
   338
Dean@279
   339
            return;
vb@133
   340
        }
vb@133
   341
Dean@161
   342
        /// <summary>
Dean@363
   343
        /// Calculates both the short and full trustwords between the given personal identity and the identity partner.
Dean@363
   344
        /// </summary>
Dean@646
   345
        /// <param name="myself">The personal identity.</param>
Dean@646
   346
        /// <param name="partner">The identity of the partner.</param>
Dean@363
   347
        /// <param name="trustwordsShort">The short version of trustwords.</param>
Dean@363
   348
        /// <param name="trustwordsFull">The full/long version of trustwords.</param>
Dean@646
   349
        private void CalcTrustwords(PEPIdentity myself,
Dean@646
   350
                                    PEPIdentity partner,
Dean@363
   351
                                    out string trustwordsShort,
Dean@363
   352
                                    out string trustwordsFull)
Dean@363
   353
        {
Dean@363
   354
            string myShort;
Dean@363
   355
            string myLong;
Dean@363
   356
            string partnerShort;
Dean@363
   357
            string partnerLong;
Dean@363
   358
Dean@674
   359
            myShort = ThisAddIn.pEp.trustwords(myself.Fingerprint, max_words: 5);
Dean@674
   360
            myLong = ThisAddIn.pEp.trustwords(myself.Fingerprint);
Dean@674
   361
            partnerShort = ThisAddIn.pEp.trustwords(partner.Fingerprint, max_words: 5);
Dean@674
   362
            partnerLong = ThisAddIn.pEp.trustwords(partner.Fingerprint);
Dean@363
   363
Dean@646
   364
            if (myself.Fingerprint.CompareTo(partner.Fingerprint) > 0)
Dean@363
   365
            {
Dean@730
   366
                trustwordsShort = partnerShort + Environment.NewLine + myShort;
Dean@730
   367
                trustwordsFull = partnerLong + Environment.NewLine + myLong;
Dean@363
   368
            }
Dean@363
   369
            else
Dean@363
   370
            {
Dean@730
   371
                trustwordsShort = myShort + Environment.NewLine + partnerShort;
Dean@730
   372
                trustwordsFull = myLong + Environment.NewLine + partnerLong;
Dean@363
   373
            }
Dean@363
   374
Dean@363
   375
            return;
Dean@363
   376
        }
Dean@363
   377
Dean@363
   378
        /// <summary>
Dean@161
   379
        /// Formats the given text string as separated 4-character groups.
Dean@161
   380
        /// Example: 49422235FC99585B891C --> 4942 2235 FC99 585B 891C
Dean@161
   381
        /// </summary>
Dean@161
   382
        /// <param name="text">The text to format in 4-character groups.</param>
Dean@161
   383
        /// <returns>The re-formatted string.</returns>
Dean@161
   384
        private string ToQuadruple(string text)
Dean@159
   385
        {
Dean@159
   386
            List<string> result = new List<string>();
Dean@159
   387
Dean@161
   388
            if (text != null)
Dean@159
   389
            {
Dean@161
   390
                for (int i = 0; i < text.Length; i += 4)
Dean@159
   391
                {
Dean@161
   392
                    try
Dean@161
   393
                    {
Dean@161
   394
                        result.Add(text.Substring(i, 4));
Dean@161
   395
                    }
Dean@161
   396
                    catch (ArgumentOutOfRangeException)
Dean@161
   397
                    {
Dean@161
   398
                        result.Add(text.Substring(i));
Dean@161
   399
                        break;
Dean@161
   400
                    }
Dean@159
   401
                }
Dean@159
   402
            }
Dean@159
   403
Dean@159
   404
            return String.Join(" ", result);
Dean@159
   405
        }
markus@201
   406
Dean@196
   407
        /// <summary>
Dean@279
   408
        /// Builds a new manager form state using this encryption state/mail item current state.
Dean@279
   409
        /// </summary>
Dean@279
   410
        /// <returns>A new manager form state.</returns>
Dean@313
   411
        private FormManagePrivacyStatus.State GetManagerState()
Dean@279
   412
        {
Dean@737
   413
            int currIndex = 0;
Dean@527
   414
            bool isMyself;
Dean@364
   415
            string trustwordsShort;
Dean@364
   416
            string trustwordsFull;
Dean@384
   417
            List<PEPIdentity> identities;
Dean@384
   418
            PEPIdentity myIdentity;
Dean@259
   419
            _pEp_color identityPartnerColor;
Dean@259
   420
            SelectionItem item;
Dean@312
   421
            PrivacyState state = this.CopyUIToState();
Dean@313
   422
            FormManagePrivacyStatus.State managerState = new FormManagePrivacyStatus.State();
Dean@259
   423
Dean@769
   424
            /* Resolve all recipients -- this ensures the identities list is correctly populated
Dean@769
   425
             *
Dean@769
   426
             * Note: The PropertyChanged changed event must be disconnected before trying to resolve.
Dean@769
   427
             * This is because the resolve process can modify the contents of the mail item which triggers an event.
Dean@769
   428
             * The PropertyChanged event would then trigger a UI refresh cycle. However, because the GetManagerState itself 
Dean@769
   429
             * is called within the UI refresh, an infinite loop could occur trying to resolve a recipient that 
Dean@769
   430
             * cannot be resolved (no address).
Dean@769
   431
             */
Dean@769
   432
            if (this.associatedMailItem != null)
Dean@769
   433
            {
Dean@769
   434
                this.associatedMailItem.PropertyChanged -= MailItem_PropertyChanged;
Dean@769
   435
                this.associatedMailItem.ResolveAllRecipients();
Dean@769
   436
                this.associatedMailItem.PropertyChanged += MailItem_PropertyChanged;
Dean@769
   437
            }
Dean@332
   438
Dean@279
   439
            managerState.VisualState = state.Copy();
Dean@279
   440
            managerState.IsIncoming = this.associatedMailItem.IsIncoming;
Dean@259
   441
Dean@653
   442
            if (managerState.IsIncoming)
Dean@259
   443
            {
Dean@384
   444
                identities = new List<PEPIdentity>();
Dean@259
   445
Dean@259
   446
                // Add only one identity
Dean@638
   447
                if (this.associatedMailItem.From != null)
Dean@638
   448
                {
Dean@764
   449
                    identities.Add(this.associatedMailItem.From);
Dean@638
   450
                }
Dean@259
   451
            }
Dean@259
   452
            else
Dean@259
   453
            {
Dean@395
   454
                identities = this.associatedMailItem.Recipients;
Dean@259
   455
            }
Dean@259
   456
Dean@366
   457
            // Get my identity
Dean@633
   458
            myIdentity = this.associatedMailItem.Myself;
Dean@366
   459
Dean@279
   460
            // Add identities
Dean@394
   461
            identities = PEPIdentity.ToFlatList(identities);
Dean@279
   462
            managerState.Identities.Clear();
Dean@384
   463
            foreach (PEPIdentity ident in identities)
Dean@259
   464
            {
Dean@737
   465
                // Needed so index can be used within anonymous methods without getting into the 'outer variable trap' of currIndex
Dean@737
   466
                int index = currIndex;
Dean@737
   467
Dean@648
   468
                if (ident.IsValid)
Dean@485
   469
                {
Dean@774
   470
                    // Update the partner identity to get a fingerprint, preserve IsForceUnencrypted
Dean@485
   471
                    pEp_identity_s partnerIdentity_s = ThisAddIn.pEp.update_identity(ident.ToCOMType());
Dean@485
   472
                    PEPIdentity partnerIdentity = new PEPIdentity(partnerIdentity_s);
Dean@774
   473
                    partnerIdentity.IsForceUnencrypted = ident.IsForceUnencrypted;
Dean@774
   474
Dean@485
   475
                    identityPartnerColor = ThisAddIn.pEp.identity_color(partnerIdentity_s);
Dean@713
   476
                    isMyself = PEPIdentity.GetIsMyIdentity(partnerIdentity.Address);
Dean@724
   477
Dean@485
   478
                    item = new SelectionItem();
Dean@485
   479
                    item.TextLine1 = partnerIdentity.Username;
Dean@485
   480
                    item.TextLine2 = partnerIdentity.Address;
Dean@259
   481
Dean@485
   482
                    // Don't show both the user name and address if they are the same
Dean@485
   483
                    if ((item.TextLine1 != null) &&
Dean@485
   484
                        (item.TextLine2 != null) &&
Dean@485
   485
                        (item.TextLine1 == item.TextLine2))
Dean@485
   486
                    {
Dean@485
   487
                        item.IsTwoTextLinesVisible = false;
Dean@485
   488
                    }
Dean@332
   489
Dean@527
   490
                    // Calculate trustwords
Dean@527
   491
                    this.CalcTrustwords(myIdentity,
Dean@527
   492
                                        partnerIdentity,
Dean@527
   493
                                        out trustwordsShort,
Dean@527
   494
                                        out trustwordsFull);
Dean@527
   495
Dean@485
   496
                    // Set image
Dean@774
   497
                    if ((partnerIdentity.IsForceUnencryptedBool) &&
Dean@653
   498
                        (managerState.IsIncoming == false))
Dean@485
   499
                    {
Dean@641
   500
                        // Show the force unencrypted image indicating this is set by the user not the engine
Dean@642
   501
                        item.ItemImage = pEp.Properties.Resources.ImageForceUnencOn;
Dean@641
   502
                    }
Dean@641
   503
                    else
Dean@641
   504
                    {
Dean@641
   505
                        switch (PrivacyState.ConvertRatingToPrivacyColor(identityPartnerColor))
Dean@641
   506
                        {
Dean@641
   507
                            case PrivacyState.PrivacyColor.Green:
Dean@641
   508
                                item.ItemImage = pEp.Properties.Resources.ImagePrivacyStatusGreen;
Dean@641
   509
                                break;
Dean@641
   510
                            case PrivacyState.PrivacyColor.Yellow:
Dean@641
   511
                                item.ItemImage = pEp.Properties.Resources.ImagePrivacyStatusYellow;
Dean@641
   512
                                break;
Dean@641
   513
                            case PrivacyState.PrivacyColor.Red:
Dean@641
   514
                                item.ItemImage = pEp.Properties.Resources.ImagePrivacyStatusRed;
Dean@641
   515
                                break;
Dean@641
   516
                            case PrivacyState.PrivacyColor.NoColor:
Dean@641
   517
                                item.ItemImage = pEp.Properties.Resources.ImagePrivacyStatusNoColor;
Dean@641
   518
                                break;
Dean@641
   519
                            default:
Dean@641
   520
                                item.ItemImage = null;
Dean@641
   521
                                break;
Dean@641
   522
                        }
Dean@485
   523
                    }
Dean@259
   524
Dean@485
   525
                    // Set button
Dean@653
   526
                    if ((isMyself) ||
Dean@774
   527
                        ((partnerIdentity.IsForceUnencryptedBool) &&
Dean@653
   528
                         (managerState.IsIncoming == false)))
Dean@653
   529
                    {
Dean@653
   530
                        item.IsButtonVisible = false;
Dean@653
   531
                        item.IsExpandable = false;
Dean@653
   532
                    }
Dean@653
   533
                    else
Dean@366
   534
                    {
Dean@485
   535
                        if (partnerIdentity.CommunicationType >= _pEp_comm_type.pEp_ct_confirmed_encryption)
Dean@485
   536
                        {
Dean@485
   537
                            // Undo handshake
Dean@485
   538
                            item.TextButton = pEp.Properties.Resources.PrivacyStatus_StopTrusting;
Dean@485
   539
                            item.IsButtonVisible = true;
Dean@646
   540
                            item.ButtonOnClick = (x, y) => { this.UndoHandshake(partnerIdentity.Copy()); };
Dean@356
   541
Dean@485
   542
                            item.IsExpandable = false;
Dean@485
   543
                        }
Dean@485
   544
                        else if (partnerIdentity.CommunicationType >= _pEp_comm_type.pEp_ct_unconfirmed_encryption &&
Dean@485
   545
                                 partnerIdentity.CommunicationType < _pEp_comm_type.pEp_ct_confirmed_encryption)
Dean@485
   546
                        {
Dean@485
   547
                            // Do handshake
Dean@737
   548
                            item.TextButton = pEp.Properties.Resources.PrivacyStatus_Handshake;
Dean@485
   549
                            item.IsButtonVisible = true;
Dean@646
   550
                            item.ButtonOnClick = (x, y) => { this.DoHandshake(myIdentity.Copy(), partnerIdentity.Copy()); };
Dean@356
   551
Dean@485
   552
                            item.IsExpandable = true;
Dean@485
   553
                            item.ExpandedText = Properties.Resources.PrivacyStatus_TrustwordDesc + "\n\n" +
Dean@485
   554
                                                trustwordsShort;
Dean@447
   555
Dean@485
   556
                            item.ExpandedButton1Text = Properties.Resources.PrivacyStatus_SelectTrust;
Dean@646
   557
                            item.ExpandedButton1OnClick = (x, y) => { this.ProcessDoHandshakeResult(DialogResult.Yes, partnerIdentity.Copy()); };
Dean@485
   558
                            item.ExpandedButton1BackColor = PrivacyState.COLOR_GREEN;
Dean@485
   559
                            item.ExpandedButton1ForeColor = Color.White;
Dean@447
   560
Dean@737
   561
                            item.ExpandedButton2Text = Properties.Resources.PrivacyStatus_SelectCancel;
Dean@737
   562
                            item.ExpandedButton2OnClick = (x, y) =>
Dean@737
   563
                                {
Dean@737
   564
                                    FormManagePrivacyStatus.State activeManagerState;
Dean@737
   565
Dean@737
   566
                                    if (this.managerForm != null)
Dean@737
   567
                                    {
Dean@737
   568
                                        activeManagerState = this.managerForm.CopyUIToState();
Dean@758
   569
Dean@737
   570
                                        if ((index >= 0) &&
Dean@737
   571
                                            (index < activeManagerState.Identities.Count))
Dean@737
   572
                                        {
Dean@737
   573
                                            item = activeManagerState.Identities[index];
Dean@758
   574
                                            item.TextButton = pEp.Properties.Resources.PrivacyStatus_Handshake;
Dean@758
   575
                                            item.IsExpanded = false;
Dean@737
   576
                                            item.IsExpandable = false;
Dean@737
   577
                                        }
Dean@737
   578
                                    }
Dean@737
   579
                                };
Dean@737
   580
Dean@737
   581
                            item.ExpandedButton3Text = Properties.Resources.PrivacyStatus_SelectMistrust;
Dean@737
   582
                            item.ExpandedButton3OnClick = (x, y) => { this.ProcessDoHandshakeResult(DialogResult.No, partnerIdentity.Copy()); };
Dean@737
   583
                            item.ExpandedButton3BackColor = PrivacyState.COLOR_RED;
Dean@737
   584
                            item.ExpandedButton3ForeColor = Color.White;
Dean@485
   585
                        }
Dean@672
   586
                        else if (partnerIdentity.CommunicationType == _pEp_comm_type.pEp_ct_mistrusted)
Dean@485
   587
                        {
Dean@485
   588
                            // Redo handshake with confirmation
Dean@485
   589
                            item.TextButton = pEp.Properties.Resources.PrivacyStatus_Handshake;
Dean@485
   590
                            item.IsButtonVisible = true;
Dean@672
   591
                            item.ButtonOnClick = (x, y) => { this.DoHandshakeForMistrustedKey(myIdentity.Copy(), partnerIdentity.Copy()); };
Dean@641
   592
Dean@641
   593
                            item.IsExpandable = false;
Dean@485
   594
                        }
Dean@485
   595
                        else
Dean@485
   596
                        {
Dean@485
   597
                            item.IsButtonVisible = false;
Dean@641
   598
                            item.IsExpandable = false;
Dean@485
   599
                        }
Dean@366
   600
                    }
Dean@281
   601
                }
Dean@485
   602
                else // Invalid identity
Dean@259
   603
                {
Dean@485
   604
                    item = new SelectionItem();
Dean@485
   605
                    item.TextLine1 = ident.Username;
Dean@485
   606
                    item.TextLine2 = ident.Address;
Dean@485
   607
                    item.ItemImage = null;
Dean@485
   608
                    item.IsTwoTextLinesVisible = true;
Dean@259
   609
                    item.IsButtonVisible = false;
Dean@259
   610
                }
Dean@259
   611
Dean@279
   612
                managerState.Identities.Add(item);
Dean@737
   613
                currIndex++;
Dean@259
   614
            }
Dean@259
   615
Dean@737
   616
            // Attempt to select and expand the first identity requiring a handshake
Dean@375
   617
            for (int i = 0; i < managerState.Identities.Count; i++)
Dean@375
   618
            {
Dean@737
   619
                // Determine if it's a handshake identity by checking if it's expandable
Dean@375
   620
                if (managerState.Identities[i].IsExpandable)
Dean@375
   621
                {
Dean@737
   622
                    managerState.Identities[i].TextButton = Properties.Resources.PrivacyStatus_HandshakeAdvanced;
Dean@737
   623
                    managerState.Identities[i].IsExpanded = true;
Dean@375
   624
                    managerState.SelectedIdentityIndex = i;
Dean@375
   625
                    break;
Dean@375
   626
                }
Dean@375
   627
            }
Dean@375
   628
Dean@279
   629
            return (managerState);
Dean@279
   630
        }
Dean@279
   631
Dean@279
   632
        /// <summary>
Dean@279
   633
        /// Builds the latest state of the encryption status manager then shows the UI.
Dean@279
   634
        /// </summary>
Dean@279
   635
        private void BuildAndShowManager()
Dean@279
   636
        {
Dean@279
   637
            DialogResult result;
Dean@313
   638
            FormManagePrivacyStatus form;
Dean@313
   639
            FormManagePrivacyStatus.State stateOut;
Dean@279
   640
Dean@483
   641
            try
Dean@483
   642
            {
Dean@483
   643
                // Show the form
Dean@483
   644
                form = new FormManagePrivacyStatus();
Dean@483
   645
                form.StartPosition = FormStartPosition.CenterScreen;
Dean@259
   646
Dean@483
   647
                this.managerForm = form;
Dean@483
   648
                result = form.ShowDialog(this.ParentForm, this.GetManagerState(), out stateOut); // Must show as dialog to block code
Dean@483
   649
            }
Dean@483
   650
            catch (Exception ex)
Dean@483
   651
            {
Dean@660
   652
                Global.StopAndSendCrashReport(ex);
Dean@483
   653
            }
Dean@259
   654
Dean@259
   655
            return;
Dean@259
   656
        }
Dean@259
   657
Dean@259
   658
        /// <summary>
Dean@179
   659
        /// Updates the status of the UI state based on the associated mail item.
Dean@179
   660
        /// </summary>
Dean@622
   661
        public void UpdateUIFromMailItem()
Dean@159
   662
        {
Dean@312
   663
            PrivacyState state;
Dean@664
   664
            bool? forceUnencryptedProperty;
Dean@159
   665
Dean@759
   666
            Global.LogVerbose("UpdateUIFromMailItem: Started.");
Dean@740
   667
Dean@173
   668
            if (this.associatedMailItem != null)
Dean@159
   669
            {
Dean@664
   670
                forceUnencryptedProperty = this.associatedMailItem.ForceUnencrypted;
Dean@342
   671
Dean@664
   672
                if ((forceUnencryptedProperty != null) &&
Dean@664
   673
                    ((bool)forceUnencryptedProperty == true))
Dean@159
   674
                {
Dean@342
   675
                    // Force unencrypted
Dean@312
   676
                    state = new PrivacyState(_pEp_color.pEp_rating_unencrypted);
Dean@680
   677
                    this.CopyStateToUI(state);
Dean@740
   678
Dean@759
   679
                    Global.LogVerbose("UpdateUIFromMailItem: Force unencrypted detected.");
Dean@159
   680
                }
Dean@159
   681
                else
Dean@159
   682
                {
Dean@759
   683
                    Global.LogVerbose("UpdateUIFromMailItem: Starting decryption.");
Dean@740
   684
Dean@680
   685
                    // Start the color rating calculation/decryption process
Dean@680
   686
                    this.associatedMailItem.StartDecryption();
Dean@159
   687
                }
Dean@159
   688
            }
Dean@159
   689
Dean@179
   690
            return;
Dean@159
   691
        }
Dean@159
   692
Dean@709
   693
        /// <summary>
Dean@709
   694
        /// Clears the associated unencrypted preview and displays the given note (if any).
Dean@709
   695
        /// </summary>
Dean@709
   696
        /// <param name="note">The note to diplsay.</param>
Dean@709
   697
        private void ClearPreview(string note = null)
Dean@709
   698
        {
Dean@709
   699
            WindowFormRegionCollection formRegions = Globals.FormRegions[Globals.ThisAddIn.Application.ActiveExplorer()];
Dean@709
   700
Dean@709
   701
            if ((formRegions != null) &&
Dean@709
   702
                (formRegions.FormRegionPreviewUnencrypted != null) &&
Dean@709
   703
                (formRegions.FormRegionPreviewUnencrypted.Visible))
Dean@709
   704
            {
Dean@709
   705
                formRegions.FormRegionPreviewUnencrypted.ClearMessage();
Dean@709
   706
                formRegions.FormRegionPreviewUnencrypted.SetNote(note);
Dean@709
   707
            }
Dean@709
   708
Dean@709
   709
            return;
Dean@709
   710
        }
Dean@709
   711
Dean@172
   712
        #region StateMethods
Dean@172
   713
Dean@172
   714
        /// <summary>
Dean@172
   715
        /// Connects or disconnects all control events from the UI.
Dean@172
   716
        /// </summary>
Dean@172
   717
        /// <param name="connect">True to connect events, false to disconnect.</param>
Dean@172
   718
        private void ConnectEvents(bool connect)
Dean@172
   719
        {
Dean@172
   720
            // Connect events only if not already connected
markus@201
   721
            if ((connect == true) &&
Dean@172
   722
                (this.eventsAreConnected == false))
Dean@172
   723
            {
Dean@312
   724
                this.ButtonPrivacyStatus.Click += this.ButtonPrivacyStatus_Click;
Dean@312
   725
                this.ButtonPrivacyStatus.MouseUp += this.ButtonPrivacyStatus_MouseUp;
Dean@179
   726
Dean@172
   727
                this.eventsAreConnected = true;
Dean@172
   728
            }
Dean@172
   729
            // Always attempt to disconnect
Dean@172
   730
            else if (connect == false)
Dean@172
   731
            {
Dean@312
   732
                this.ButtonPrivacyStatus.Click -= this.ButtonPrivacyStatus_Click;
Dean@312
   733
                this.ButtonPrivacyStatus.MouseUp -= this.ButtonPrivacyStatus_MouseUp;
Dean@179
   734
Dean@172
   735
                this.eventsAreConnected = false;
Dean@172
   736
            }
Dean@172
   737
Dean@172
   738
            return;
Dean@172
   739
        }
Dean@172
   740
Dean@172
   741
        /// <summary>
Dean@172
   742
        /// Copies the given state to the UI.
Dean@172
   743
        /// Events are turned off until the process is complete.
Dean@172
   744
        /// </summary>
Dean@172
   745
        /// <param name="state">The state to set to the UI.</param>
Dean@312
   746
        private void CopyStateToUI(PrivacyState state)
Dean@172
   747
        {
Dean@172
   748
            this.ConnectEvents(false);
markus@201
   749
Dean@172
   750
            ///////////////////////////////////////////////////////////
Dean@172
   751
            // Set UI data
Dean@172
   752
            ///////////////////////////////////////////////////////////
Dean@172
   753
Dean@179
   754
            // Save UI state maintained outside of controls
Dean@312
   755
            this.stateUIColorRating = state.PrivacyStatus;
Dean@179
   756
Dean@312
   757
            this.ButtonPrivacyStatus.BackColor = state.BackgroundColor;
Dean@312
   758
            this.ButtonPrivacyStatus.ForeColor = state.ForegroundColor;
Dean@312
   759
            this.ButtonPrivacyStatus.Text = state.ShortText;
Dean@172
   760
Dean@670
   761
            // Update the manager form state
Dean@670
   762
            if (this.managerForm != null)
Dean@670
   763
            {
Dean@670
   764
                this.managerForm.CopyStateToUI(this.GetManagerState());
Dean@670
   765
            }
Dean@670
   766
Dean@172
   767
            this.ConnectEvents(true);
Dean@172
   768
Dean@172
   769
            return;
Dean@172
   770
        }
Dean@172
   771
Dean@172
   772
        /// <summary>
Dean@172
   773
        /// Copies the UI to a new state.
Dean@172
   774
        /// </summary>
Dean@172
   775
        /// <returns>The state of the UI.</returns>
Dean@312
   776
        private PrivacyState CopyUIToState()
Dean@172
   777
        {
Dean@312
   778
            return (new PrivacyState(this.stateUIColorRating));
Dean@172
   779
        }
Dean@172
   780
Dean@172
   781
        #endregion
Dean@172
   782
Dean@159
   783
        /**************************************************************
Dean@159
   784
         * 
Dean@159
   785
         * Event Handling
Dean@159
   786
         * 
Dean@159
   787
         *************************************************************/
Dean@159
   788
Dean@159
   789
        /// <summary>
Dean@680
   790
        /// Event handler that is called when the form region is displayed.
Dean@680
   791
        /// This is called each time the form region looses then regains visiblity 
Dean@680
   792
        /// (for example an other email is selected then back to this one).
Dean@159
   793
        /// </summary>
Dean@312
   794
        private void FormRegionPrivacyStatus_FormRegionShowing(object sender, System.EventArgs e)
Dean@159
   795
        {
Dean@680
   796
            // Do not allow initialization more than once
Dean@680
   797
            if (initialized == false)
Dean@680
   798
            {
Dean@680
   799
                this.SetAssociatedMailItem();
Dean@173
   800
Dean@680
   801
                // Set background color for Office 2013
Dean@159
   802
                try
Dean@159
   803
                {
Dean@680
   804
                    if (Globals.ThisAddIn.Application.Version.Substring(0, 2) == "15")
Dean@159
   805
                    {
Dean@680
   806
                        if (this.IsWithinInspector())
Dean@680
   807
                        {
Dean@680
   808
                            this.BackColor = SystemColors.ButtonFace;
Dean@680
   809
                        }
Dean@680
   810
                        else
Dean@680
   811
                        {
Dean@680
   812
                            this.BackColor = SystemColors.Window;
Dean@680
   813
                        }
Dean@159
   814
                    }
Dean@159
   815
                }
Dean@159
   816
                catch { }
Dean@680
   817
Dean@680
   818
                // Connect cryptable mail item events
Dean@680
   819
                if (this.associatedMailItem != null)
Dean@680
   820
                {
Dean@680
   821
                    try
Dean@680
   822
                    {
Dean@680
   823
                        this.associatedMailItem.PropertyChanged += MailItem_PropertyChanged;
Dean@680
   824
                        this.associatedMailItem.DecryptionComplete += MailItem_DecryptionComplete;
Dean@709
   825
                        this.associatedMailItem.GetMirrorComplete += MailItem_GetMirrorComplete;
Dean@682
   826
                        this.associatedMailItem.Send += MailItem_Send;
Dean@680
   827
Dean@680
   828
                        if (this.associatedMailItem.IsInEncryptedStore &&
Dean@680
   829
                            this.associatedMailItem.IsPGPEncrypted)
Dean@680
   830
                        {
Dean@680
   831
                            this.associatedMailItem.Open += MailItem_Open;
Dean@680
   832
                        }
Dean@680
   833
                    }
Dean@680
   834
                    catch { }
Dean@680
   835
                }
Dean@680
   836
Dean@807
   837
                // Connect static events
Dean@807
   838
                CryptableMailItem.EncryptedConversationCacheUpdated += CryptableMailItem_EncryptedConversationCacheUpdated;
Dean@807
   839
Dean@680
   840
                this.CopyStateToUI(new PrivacyState());
Dean@683
   841
                this.TimerRefresh.Tick += TimerRefresh_Tick;
Dean@680
   842
                this.initialized = true;
Dean@179
   843
            }
Dean@159
   844
Dean@683
   845
            // Call the timer tick method manually to refresh data with no delay
Dean@199
   846
            this.TimerRefresh_Tick(null, new EventArgs());
Dean@179
   847
Dean@179
   848
            return;
Dean@159
   849
        }
Dean@159
   850
Dean@159
   851
        /// <summary>
Dean@159
   852
        /// Event handler for when the form region is closed.
Dean@159
   853
        /// </summary>
Dean@312
   854
        private void FormRegionPrivacyStatus_FormRegionClosed(object sender, System.EventArgs e)
Dean@159
   855
        {
Dean@199
   856
            // Disconnect cryptable mail item events
Dean@190
   857
            if (this.associatedMailItem != null)
Dean@179
   858
            {
Dean@179
   859
                try
Dean@179
   860
                {
Dean@445
   861
                    this.associatedMailItem.PropertyChanged -= MailItem_PropertyChanged;
Dean@680
   862
                    this.associatedMailItem.DecryptionComplete -= MailItem_DecryptionComplete;
Dean@709
   863
                    this.associatedMailItem.GetMirrorComplete -= MailItem_GetMirrorComplete;
markus@201
   864
                    this.associatedMailItem.Open -= MailItem_Open;
Dean@682
   865
                    this.associatedMailItem.Send -= MailItem_Send;
Dean@179
   866
                }
Dean@179
   867
                catch { }
Dean@179
   868
            }
Dean@179
   869
Dean@807
   870
            // Disconnect static events
Dean@807
   871
            CryptableMailItem.EncryptedConversationCacheUpdated -= CryptableMailItem_EncryptedConversationCacheUpdated;
Dean@807
   872
Dean@682
   873
            // Stop and disconnect the refresh timer
Dean@682
   874
            this.TimerRefresh.Stop();
Dean@682
   875
            this.TimerRefresh.Enabled = false;
Dean@682
   876
            this.TimerRefresh.Tick -= TimerRefresh_Tick;
Dean@682
   877
Dean@179
   878
            return;
Dean@179
   879
        }
Dean@179
   880
Dean@179
   881
        /// <summary>
Dean@179
   882
        /// Event handler called after the refresh timer has elapsed.
Dean@179
   883
        /// </summary>
Dean@179
   884
        private void TimerRefresh_Tick(object sender, EventArgs e)
Dean@179
   885
        {
Dean@199
   886
            bool tryAgain = false;
Dean@684
   887
            bool markForDownload = false;
Dean@179
   888
            this.TimerRefresh.Enabled = false; // Only once
Dean@684
   889
            Outlook.OlDownloadState dlState;
markus@201
   890
Dean@709
   891
            /* The Refresh/UI_Update process is a little more complicated here than other forms.
Dean@709
   892
             * There are the following components:
Dean@709
   893
             *   1. TimerRefresh_Tick
Dean@709
   894
             *        This is the main timer tick event handler called any time
Dean@709
   895
             *        a refresh was requested and hasn't been run yet. A refresh
Dean@709
   896
             *        is requested either at initialization or when a property changes
Dean@709
   897
             *        (such as MailItem_PropertyChanged). It is on a timer as many 
Dean@709
   898
             *        property change events could occur rapidy, but only one refresh should
Dean@709
   899
             *        occur for performance reasons.
Dean@709
   900
             * 
Dean@709
   901
             *   2. UpdateUIFromMailItem
Dean@709
   902
             *        This will re-calculate the mail item's color rating and then update the UI.
Dean@709
   903
             *        However, if a mail item is not forcefully unencrypted, it will just call StartDecryption.
Dean@709
   904
             * 
Dean@709
   905
             *   3. MailItem_DecryptionComplete
Dean@709
   906
             *        When decryption of the mail item is complete, this even handler will be called.
Dean@709
   907
             *        This will update the UI with the latest color rating then call StartGetMirror.
Dean@709
   908
             *        The CopyStateToUI method is used which means any open privacy status form will also
Dean@709
   909
             *        be updated.
Dean@709
   910
             * 
Dean@709
   911
             *   4. MailItem_GetMirrorComplete
Dean@709
   912
             *        This is the final step in updating the UI, after a mirror is located, it's contents
Dean@709
   913
             *        will be shown to the unencrypted preview.
Dean@709
   914
             * 
Dean@709
   915
             * The general calling sequence is as shown above 1->4 with each component calling the next.
Dean@709
   916
             * However, for methods that update the mail item directly, commonly only 2->4 is needed.
Dean@709
   917
             */
Dean@709
   918
Dean@179
   919
            // Ensure the tick method is not called more than once
Dean@179
   920
            if (refreshOngoing == false)
Dean@179
   921
            {
Dean@179
   922
                this.refreshOngoing = true;
Dean@179
   923
Dean@179
   924
                if (this.associatedMailItem != null)
Dean@179
   925
                {
Dean@684
   926
                    // Attempt to get the download state
Dean@684
   927
                    try
Dean@684
   928
                    {
Dean@684
   929
                        dlState = this.associatedMailItem.DownloadState;
Dean@684
   930
                    }
Dean@684
   931
                    catch (Exception ex)
Dean@684
   932
                    {
Dean@759
   933
                        Global.Log("TimerRefresh_Tick: Get DownloadState failed, " + ex.ToString());
Dean@684
   934
Dean@684
   935
                        // Assume everything is downloaded, but try to download again as well
Dean@684
   936
                        dlState = Outlook.OlDownloadState.olFullItem;
Dean@684
   937
                        markForDownload = true;
Dean@684
   938
                    }
Dean@684
   939
Dean@684
   940
                    if (dlState == Outlook.OlDownloadState.olFullItem)
Dean@179
   941
                    {
Dean@709
   942
                        // Clear any existing preview and update
Dean@709
   943
                        this.ClearPreview(Properties.Resources.Message_Opening);
Dean@179
   944
                        this.UpdateUIFromMailItem();
Dean@179
   945
                    }
Dean@179
   946
                    else
Dean@179
   947
                    {
Dean@684
   948
                        markForDownload = true;
Dean@684
   949
                    }
Dean@684
   950
Dean@684
   951
                    if (markForDownload)
Dean@684
   952
                    {
Dean@684
   953
                        // Try to mark the message for full download
Dean@684
   954
                        try
Dean@684
   955
                        {
Dean@684
   956
                            this.associatedMailItem.MarkForDownload = Outlook.OlRemoteStatus.olMarkedForDownload;
Dean@684
   957
                            tryAgain = true;
Dean@684
   958
                        }
Dean@684
   959
                        catch (Exception ex)
Dean@684
   960
                        {
Dean@759
   961
                            Global.Log("TimerRefresh_Tick: MarkForDownload failed, " + ex.ToString());
Dean@684
   962
                        }
Dean@179
   963
                    }
Dean@179
   964
                }
Dean@179
   965
Dean@199
   966
                // Set the timer to refresh again later automatically
Dean@199
   967
                if (tryAgain)
Dean@199
   968
                {
Dean@199
   969
                    this.TimerRefresh.Interval = 100;
Dean@199
   970
                    this.TimerRefresh.Enabled = true;
Dean@199
   971
                }
Dean@199
   972
Dean@179
   973
                this.refreshOngoing = false;
Dean@179
   974
            }
Dean@175
   975
Dean@159
   976
            return;
Dean@159
   977
        }
Dean@159
   978
Dean@169
   979
        /// <summary>
Dean@680
   980
        /// Event handler for when the decryption process is completed in the associated mail item.
Dean@709
   981
        /// This will then update the form region UI and the privacy status window as needed.
Dean@680
   982
        /// </summary>
Dean@704
   983
        private void MailItem_DecryptionComplete(object sender, CryptableMailItem.DecryptionEventArgs e)
Dean@680
   984
        {
Dean@759
   985
            Global.LogVerbose("MailItem_DecryptionComplete: Decryption completed.");
Dean@740
   986
Dean@680
   987
            try
Dean@680
   988
            {
Dean@680
   989
                // Marshal code back to UI thread as necessary
Dean@680
   990
                this.Invoke(new Action(() =>
Dean@680
   991
                    {
Dean@680
   992
                        this.CopyStateToUI(new PrivacyState(e.ColorRating));
Dean@709
   993
Dean@709
   994
                        /* Create the unencrypted preview if the mail item is encrypted and
Dean@709
   995
                         * it is in an encrypted (untrusted) store
Dean@709
   996
                         * 
Dean@709
   997
                         * This is done here because FormRegionPrivacyStatus has the cryptable mail item and
Dean@709
   998
                         * it also is initialized after FormRegionPreviewUnencrypted.
Dean@709
   999
                         */
Dean@747
  1000
                        try
Dean@709
  1001
                        {
Dean@747
  1002
                            if (this.associatedMailItem.IsInEncryptedStore &&
Dean@747
  1003
                                this.associatedMailItem.IsPGPEncrypted)
Dean@747
  1004
                            {
Dean@759
  1005
                                Global.LogVerbose("MailItem_DecryptionComplete: Starting mirror location.");
Dean@747
  1006
                                this.associatedMailItem.StartGetMirror();
Dean@747
  1007
                            }
Dean@747
  1008
                            else
Dean@747
  1009
                            {
Dean@747
  1010
                                this.ClearPreview();
Dean@747
  1011
                            }
Dean@709
  1012
                        }
Dean@747
  1013
                        catch (Exception ex)
Dean@719
  1014
                        {
Dean@747
  1015
                            // Error is possible in some situations where the mail item was deleted or moved while decryption was ongoing.
Dean@747
  1016
                            // While rare, just log the issue and stop the process
Dean@759
  1017
                            Global.Log("MailItem_DecryptionComplete: Failed to start mirror location, " + ex.ToString());
Dean@719
  1018
                        }
Dean@680
  1019
                    }));
Dean@680
  1020
            }
Dean@680
  1021
            catch (Exception ex)
Dean@680
  1022
            {
Dean@759
  1023
                Global.Log("MailItem_DecryptionComplete: Error setting UI state, " + ex.ToString());
Dean@680
  1024
            }
Dean@680
  1025
Dean@680
  1026
            return;
Dean@680
  1027
        }
Dean@680
  1028
Dean@680
  1029
        /// <summary>
Dean@709
  1030
        /// Event handler for when the get mirror locating process is complete for the associated mail item.
Dean@709
  1031
        /// This will then update the unencrypted preview in the UI.
Dean@709
  1032
        /// </summary>
Dean@709
  1033
        private void MailItem_GetMirrorComplete(object sender, CryptableMailItem.GetMirrorEventArgs e)
Dean@709
  1034
        {
Dean@709
  1035
            try
Dean@709
  1036
            {
Dean@709
  1037
                // Marshal code back to UI thread as necessary
Dean@709
  1038
                this.Invoke(new Action(() =>
Dean@709
  1039
                    {
Dean@709
  1040
                        WindowFormRegionCollection formRegions = Globals.FormRegions[Globals.ThisAddIn.Application.ActiveExplorer()];
Dean@709
  1041
Dean@709
  1042
                        if ((formRegions != null) &&
Dean@709
  1043
                            (formRegions.FormRegionPreviewUnencrypted != null) &&
Dean@709
  1044
                            (formRegions.FormRegionPreviewUnencrypted.Visible))
Dean@709
  1045
                        {
Dean@709
  1046
                            formRegions.FormRegionPreviewUnencrypted.SetMessage(e.Mirror);
Dean@759
  1047
                            Global.LogVerbose("MailItem_GetMirrorComplete: Mirror found and displayed.");
Dean@709
  1048
                        }
Dean@758
  1049
Dean@758
  1050
                        // Display the mirror if necessary
Dean@758
  1051
                        if ((this.associatedMailItem != null) &&
Dean@758
  1052
                            (this.displayMirrorRequested))
Dean@758
  1053
                        {
Dean@758
  1054
                            this.associatedMailItem.DisplayMirror();
Dean@758
  1055
                            this.displayMirrorRequested = false;
Dean@758
  1056
                        }
Dean@709
  1057
                    }));
Dean@709
  1058
            }
Dean@709
  1059
            catch (Exception ex)
Dean@709
  1060
            {
Dean@759
  1061
                Global.Log("MailItem_GetMirrorComplete: Error displaying preview, " + ex.ToString());
Dean@709
  1062
            }
Dean@709
  1063
Dean@709
  1064
            return;
Dean@709
  1065
        }
Dean@709
  1066
Dean@709
  1067
        /// <summary>
Dean@807
  1068
        /// Event handler for the static mail item encrypted conversation cache updated.
Dean@807
  1069
        /// This event is fired after the conversation cache has been updated with parent information following a
Dean@807
  1070
        /// forward or reply mail item event (on a different, likely parent, mail item).
Dean@807
  1071
        /// </summary>
Dean@807
  1072
        private void CryptableMailItem_EncryptedConversationCacheUpdated(object sender, EventArgs e)
Dean@807
  1073
        {
Dean@807
  1074
            // Process the mail item now that the cache is updated
Dean@807
  1075
            this.associatedMailItem.SetIsOriginallyEncryptedByCache();
Dean@807
  1076
Dean@807
  1077
            // Remove the event handler -- this can only be processed once because it's timing dependent
Dean@807
  1078
            CryptableMailItem.EncryptedConversationCacheUpdated -= CryptableMailItem_EncryptedConversationCacheUpdated;
Dean@807
  1079
Dean@807
  1080
            return;
Dean@807
  1081
        }
Dean@807
  1082
Dean@807
  1083
        /// <summary>
Dean@169
  1084
        /// Event handler for when a mail item is being opened in an inspector.
Dean@180
  1085
        /// See: https://msdn.microsoft.com/en-us/library/office/ff865989.aspx
Dean@169
  1086
        /// </summary>
Dean@169
  1087
        /// <param name="cancel">Whether to cancel the event: Value is False when the event occurs. 
Dean@169
  1088
        /// If the event procedure sets this argument to True, the open operation is not completed 
Dean@169
  1089
        /// and the inspector is not displayed.</param>
Dean@169
  1090
        private void MailItem_Open(ref bool cancel)
Dean@159
  1091
        {
Dean@758
  1092
            bool result;
Dean@758
  1093
Dean@758
  1094
            if (this.associatedMailItem != null &&
Dean@758
  1095
                CryptableMailItem.GetIsInEncryptedStore((Outlook.MailItem)this.OutlookItem) &&
Dean@758
  1096
                CryptableMailItem.GetIsPGPEncrypted((Outlook.MailItem)this.OutlookItem))
Dean@169
  1097
            {
Dean@758
  1098
                // Try to open the mirror
Dean@758
  1099
                result = this.associatedMailItem.DisplayMirror();
Dean@758
  1100
Dean@758
  1101
                if (result == false)
Dean@758
  1102
                {
Dean@758
  1103
                    // Set flag to open after decryption/mirror location
Dean@758
  1104
                    this.displayMirrorRequested = true;
Dean@758
  1105
                }
Dean@758
  1106
Dean@758
  1107
                // Always cancel opening the original
Dean@169
  1108
                cancel = true;
Dean@169
  1109
            }
Dean@169
  1110
Dean@169
  1111
            return;
Dean@159
  1112
        }
Dean@159
  1113
Dean@169
  1114
        /// <summary>
Dean@682
  1115
        /// Event handler for when a mail item is sent.
Dean@682
  1116
        /// See: https://msdn.microsoft.com/en-us/library/office/ff865379.aspx
Dean@682
  1117
        /// </summary>
Dean@682
  1118
        /// <param name="cancel">Whether to cancel the event: Value is False when the event occurs. 
Dean@682
  1119
        /// If the event procedure sets this argument to True, the send operation is not completed 
Dean@682
  1120
        /// and the inspector is left open.</param>
Dean@682
  1121
        private void MailItem_Send(ref bool cancel)
Dean@682
  1122
        {
Dean@807
  1123
            DialogResult result;
Dean@807
  1124
            PrivacyState state = this.CopyUIToState();
Dean@807
  1125
Dean@807
  1126
            if ((this.associatedMailItem.IsOriginallyEncrypted) &&
Dean@807
  1127
                (state.PrivacyStatus < _pEp_color.pEp_rating_unreliable))
Dean@807
  1128
            {
Dean@807
  1129
                result = MessageBox.Show(this.ParentForm,
Dean@807
  1130
                                         pEp.Properties.Resources.Message_WarningPreviouslyEncrypted,
Dean@807
  1131
                                         pEp.Properties.Resources.Message_TitleConfirmOperation,
Dean@807
  1132
                                         MessageBoxButtons.YesNo);
Dean@807
  1133
Dean@807
  1134
                if (result == DialogResult.No)
Dean@807
  1135
                {
Dean@807
  1136
                    // Cancel sending
Dean@807
  1137
                    cancel = true;
Dean@807
  1138
                }
Dean@807
  1139
            }
Dean@807
  1140
Dean@682
  1141
            // Stop and disconnect the refresh timer
Dean@682
  1142
            // This is necessary so an ongoing refresh doesn't try to access a mail item as it's being moved
Dean@807
  1143
            if (cancel == false)
Dean@807
  1144
            {
Dean@807
  1145
                this.TimerRefresh.Stop();
Dean@807
  1146
                this.TimerRefresh.Enabled = false;
Dean@807
  1147
                this.TimerRefresh.Tick -= TimerRefresh_Tick;
Dean@807
  1148
            }
Dean@682
  1149
Dean@682
  1150
            return;
Dean@682
  1151
        }
Dean@682
  1152
Dean@682
  1153
        /// <summary>
Dean@169
  1154
        /// Event handler for when a mail item property is changed.
Dean@169
  1155
        /// See: https://msdn.microsoft.com/en-us/library/office/ff866739.aspx
Dean@169
  1156
        /// </summary>
Dean@169
  1157
        /// <param name="propertyName">The name of the property that was changed.</param>
Dean@445
  1158
        private void MailItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
Dean@159
  1159
        {
Dean@445
  1160
            switch (e.PropertyName.ToUpper())
Dean@159
  1161
            {
Dean@179
  1162
                case "TO":
Dean@179
  1163
                    // Start the refresh timer
Dean@179
  1164
                    this.TimerRefresh.Enabled = true;
Dean@159
  1165
                    break;
Dean@159
  1166
                // Outlook bug: there are always both events, so one is enough
Dean@159
  1167
                //case "CC":
Dean@179
  1168
                //    // Start the refresh timer
Dean@179
  1169
                //    this.TimerRefresh.Enabled = true;
Dean@159
  1170
                //    break;
Dean@159
  1171
            }
Dean@169
  1172
Dean@169
  1173
            return;
Dean@159
  1174
        }
Dean@159
  1175
Dean@179
  1176
        /// <summary>
Dean@312
  1177
        /// Event handler for when the privacy status button is clicked.
Dean@179
  1178
        /// </summary>
Dean@312
  1179
        private void ButtonPrivacyStatus_Click(object sender, EventArgs e)
vb@133
  1180
        {
Dean@310
  1181
            this.BuildAndShowManager();
Dean@179
  1182
            return;
vb@133
  1183
        }
vb@133
  1184
Dean@179
  1185
        /// <summary>
Dean@312
  1186
        /// Event handler for when a mouse button is released over the privacy status button.
Dean@179
  1187
        /// </summary>
Dean@312
  1188
        private void ButtonPrivacyStatus_MouseUp(object sender, MouseEventArgs e)
vb@133
  1189
        {
Dean@179
  1190
            return;
vb@133
  1191
        }
vb@133
  1192
    }
vb@133
  1193
}