UI/FormRegionPrivacyStatus.cs
author Dean Looyengoed
Wed, 03 Feb 2016 16:48:48 +0100
changeset 375 98df9e57270b
parent 366 1bb0df0e9970
child 378 e20e56a3d3a9
permissions -rw-r--r--
Automatically select the first identity requiring a handshake in the privacy status form.
Dean@159
     1
´╗┐using pEpCOMServerAdapterLib;
Dean@159
     2
using System;
vb@133
     3
using System.Collections.Generic;
Dean@159
     4
using System.Diagnostics;
vb@133
     5
using System.Linq;
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@170
    79
        private CryptableMailItem associatedMailItem = null;
Dean@179
    80
        private bool              refreshOngoing     = false;
Dean@179
    81
        private bool              eventsAreConnected = false;
Dean@170
    82
Dean@313
    83
        private FormManagePrivacyStatus managerForm = null;
Dean@259
    84
Dean@250
    85
        // Data or controls not stored in the UI
Dean@179
    86
        private _pEp_color stateUIColorRating; // WARNING: Do NOT use this directly
vb@133
    87
Dean@159
    88
        /**************************************************************
Dean@159
    89
         * 
Dean@340
    90
         * Property Accessors
Dean@340
    91
         * 
Dean@340
    92
         *************************************************************/
Dean@340
    93
Dean@340
    94
        /// <summary>
Dean@340
    95
        /// Gets or sets whether to send the associated cryptable mail item as unencrypted.
Dean@342
    96
        /// This forwards the call to the cryptable mail item but also refreshes the UI.
Dean@340
    97
        /// </summary>
Dean@342
    98
        public bool? SendUnencrypted
Dean@340
    99
        {
Dean@340
   100
            get
Dean@340
   101
            {
Dean@340
   102
                if (this.associatedMailItem != null)
Dean@340
   103
                {
Dean@340
   104
                    return (this.associatedMailItem.SendUnencrypted);
Dean@340
   105
                }
Dean@340
   106
                else
Dean@340
   107
                {
Dean@342
   108
                    return (null);
Dean@340
   109
                }
Dean@340
   110
            }
Dean@340
   111
            set
Dean@340
   112
            {
Dean@340
   113
                if (this.associatedMailItem != null)
Dean@340
   114
                {
Dean@340
   115
                    this.associatedMailItem.SendUnencrypted = value;
Dean@340
   116
                    this.UpdateUIFromMailItem();
Dean@340
   117
                }
Dean@340
   118
            }
Dean@340
   119
        }
Dean@340
   120
Dean@340
   121
        /**************************************************************
Dean@340
   122
         * 
Dean@159
   123
         * Methods
Dean@159
   124
         * 
Dean@159
   125
         *************************************************************/
Dean@159
   126
Dean@171
   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@290
   192
        /// Completes the handshake process when the identity of a partner was previously marked as compromised.
Dean@281
   193
        /// </summary>
Dean@281
   194
        /// <param name="identityPartner">The identity of the partner to complete the handshake with.</param>
Dean@281
   195
        private void DoHandshakeForCompromisedKey(pEp_identity_s identityPartner)
Dean@281
   196
        {
Dean@281
   197
            DialogResult result;
Dean@281
   198
Dean@281
   199
            result = MessageBox.Show(this.ParentForm,
Dean@286
   200
                                     pEp.Properties.Resources.MessageWarningCompromisedKey,
Dean@286
   201
                                     pEp.Properties.Resources.TitleConfirmOperation,
Dean@281
   202
                                     MessageBoxButtons.OKCancel);
Dean@281
   203
Dean@281
   204
            if (result == DialogResult.OK)
Dean@281
   205
            {
Dean@281
   206
                this.DoHandshake(identityPartner);
Dean@281
   207
            }
Dean@281
   208
Dean@281
   209
            return;
Dean@281
   210
        }
Dean@281
   211
Dean@281
   212
        /// <summary>
Dean@281
   213
        /// Completes the handshake process where the identity of a partner is confirmed.
Dean@179
   214
        /// </summary>
Dean@179
   215
        /// <param name="identityPartner">The identity of the partner to complete the handshake with.</param>
Dean@179
   216
        private void DoHandshake(pEp_identity_s identityPartner)
vb@133
   217
        {
Dean@155
   218
            string ownFpr;
Dean@155
   219
            string partnerFpr;
Dean@363
   220
            string trustwordsShort;
Dean@363
   221
            string trustwordsFull;
Dean@155
   222
            string[] lines;
Dean@179
   223
            DialogResult result;
Dean@155
   224
            pEp_identity_s me;
Dean@155
   225
            FormHandshake handshakeDialog;
Dean@155
   226
            FormHandshake.State state = new FormHandshake.State();
markus@201
   227
Dean@190
   228
            Globals.ThisAddIn.LogVerbose("doHandshake");
Dean@190
   229
            Globals.ThisAddIn.LogVerbose(identityPartner.address);
vb@139
   230
Dean@363
   231
            // Process identities
vb@132
   232
            if (identityPartner.username == "")
Dean@363
   233
            {
vb@133
   234
                identityPartner.username = identityPartner.address;
vb@133
   235
            }
vb@133
   236
Dean@363
   237
            identityPartner.user_id = Globals.ThisAddIn.GetUserIDFromAddress(identityPartner.address, identityPartner.username);
vb@139
   238
Dean@363
   239
            me = Globals.ThisAddIn.GetMyIdentity(this.associatedMailItem);
Dean@363
   240
Dean@363
   241
            // Calculate and add trustwords
Dean@363
   242
            this.CalcTrustwords(me, identityPartner, out trustwordsShort, out trustwordsFull);
Dean@363
   243
            state.TrustwordsShort = trustwordsShort;
Dean@363
   244
            state.TrustwordsFull = trustwordsFull;
Dean@363
   245
Dean@363
   246
            // Calculate and add fingerprint
markus@201
   247
            ownFpr = this.ToQuadruple(me.fpr);
markus@201
   248
            partnerFpr = this.ToQuadruple(identityPartner.fpr);
vb@133
   249
Dean@363
   250
            if (me.fpr.CompareTo(identityPartner.fpr) > 0)
vb@133
   251
            {
Dean@155
   252
                lines = new string[3];
Dean@155
   253
                lines[0] = pEp.Properties.Resources.TrustwordsPartnershortPartner + partnerFpr;
Dean@155
   254
                lines[2] = pEp.Properties.Resources.TrustwordsPartnershortMyself + ownFpr;
Dean@155
   255
                state.Fingerprint = lines;
vb@133
   256
            }
vb@133
   257
            else
vb@133
   258
            {
Dean@155
   259
                lines = new string[3];
Dean@155
   260
                lines[0] = pEp.Properties.Resources.TrustwordsOwnshortMyself + ownFpr;
Dean@155
   261
                lines[2] = pEp.Properties.Resources.TrustwordsOwnshortPartner + partnerFpr;
Dean@155
   262
                state.Fingerprint = lines;
vb@133
   263
            }
vb@133
   264
Dean@155
   265
            // Create and show handshake dialog
Dean@155
   266
            handshakeDialog = new FormHandshake(state);
Dean@179
   267
            handshakeDialog.StartPosition = FormStartPosition.CenterScreen;
Dean@179
   268
            result = handshakeDialog.ShowDialog(this);
Dean@155
   269
Dean@356
   270
            this.ProcessDoHandshakeResult(result, identityPartner);
Dean@356
   271
Dean@356
   272
            return;
Dean@356
   273
        }
Dean@356
   274
Dean@356
   275
        /// <summary>
Dean@356
   276
        /// Processes the result of the do handshake dialog after a user makes a selection.
Dean@356
   277
        /// </summary>
Dean@356
   278
        /// <param name="result">The result of the handshake dialog selection.</param>
Dean@356
   279
        /// <param name="partner">The identity of the partner to handshake with.</param>
Dean@356
   280
        private void ProcessDoHandshakeResult(DialogResult result,
Dean@356
   281
                                              pEp_identity_s partner)
Dean@356
   282
        {
Dean@356
   283
            pEp_identity_s identityPartner = partner;
vb@133
   284
vb@133
   285
            switch (result)
vb@133
   286
            {
vb@133
   287
                case DialogResult.Yes:
Dean@279
   288
                    {
Dean@292
   289
                        identityPartner = ThisAddIn.pEp.update_identity(identityPartner);
Dean@292
   290
Dean@290
   291
                        // Check if key was previously compromised -- warning for this must be displayed earlier
Dean@281
   292
                        if (identityPartner.comm_type == _pEp_comm_type.pEp_ct_compromized)
Dean@281
   293
                        {
Dean@281
   294
                            ThisAddIn.pEp.key_reset_trust(ref identityPartner);
Dean@281
   295
                        }
Dean@281
   296
Dean@279
   297
                        identityPartner = ThisAddIn.pEp.trust_personal_key(ref identityPartner);
Dean@293
   298
                        this.associatedMailItem.ColorRating = _pEp_color.pEp_rating_trusted;
Dean@279
   299
Dean@279
   300
                        this.UpdateUIFromMailItem();
Dean@279
   301
Dean@279
   302
                        // Update the manager form state
Dean@279
   303
                        if (this.managerForm != null)
Dean@279
   304
                        {
Dean@279
   305
                            this.managerForm.CopyStateToUI(this.GetManagerState());
Dean@279
   306
                        }
Dean@279
   307
Dean@279
   308
                        break;
Dean@279
   309
                    }
vb@133
   310
                case DialogResult.No:
Dean@279
   311
                    {
Dean@292
   312
                        identityPartner = ThisAddIn.pEp.update_identity(identityPartner);
Dean@292
   313
Dean@279
   314
                        ThisAddIn.pEp.key_compromized(ref identityPartner);
Dean@279
   315
                        identityPartner.comm_type = _pEp_comm_type.pEp_ct_compromized;
Dean@279
   316
                        identityPartner = ThisAddIn.pEp.update_identity(identityPartner);
Dean@293
   317
                        this.associatedMailItem.ColorRating = _pEp_color.pEp_rating_red;
Dean@279
   318
Dean@279
   319
                        this.UpdateUIFromMailItem();
Dean@279
   320
Dean@279
   321
                        // Update the manager form state
Dean@279
   322
                        if (this.managerForm != null)
Dean@279
   323
                        {
Dean@279
   324
                            this.managerForm.CopyStateToUI(this.GetManagerState());
Dean@279
   325
                        }
Dean@279
   326
Dean@279
   327
                        break;
Dean@279
   328
                    }
vb@133
   329
            }
Dean@279
   330
Dean@279
   331
            return;
vb@133
   332
        }
vb@133
   333
Dean@179
   334
        /// <summary>
Dean@179
   335
        /// Reverses any past handshake confirmation by unconfirming the given identity partner.
Dean@179
   336
        /// </summary>
Dean@179
   337
        /// <param name="identityPartner">The identity of the partner to unconfirm.</param>
Dean@179
   338
        private void UndoHandshake(pEp_identity_s identityPartner)
vb@133
   339
        {
Dean@292
   340
            identityPartner = ThisAddIn.pEp.update_identity(identityPartner);
Dean@280
   341
            ThisAddIn.pEp.key_reset_trust(ref identityPartner);
Dean@279
   342
Dean@179
   343
            this.UpdateUIFromMailItem();
Dean@279
   344
Dean@279
   345
            // Update the manager form state
Dean@279
   346
            if (this.managerForm != null)
Dean@279
   347
            {
Dean@279
   348
                this.managerForm.CopyStateToUI(this.GetManagerState());
Dean@279
   349
            }
Dean@279
   350
Dean@279
   351
            return;
vb@133
   352
        }
vb@133
   353
Dean@161
   354
        /// <summary>
Dean@363
   355
        /// Calculates both the short and full trustwords between the given personal identity and the identity partner.
Dean@363
   356
        /// </summary>
Dean@363
   357
        /// <param name="myIdentity">The personal identity.</param>
Dean@363
   358
        /// <param name="partnerIdentity">The identity of the partner.</param>
Dean@363
   359
        /// <param name="trustwordsShort">The short version of trustwords.</param>
Dean@363
   360
        /// <param name="trustwordsFull">The full/long version of trustwords.</param>
Dean@363
   361
        private void CalcTrustwords(pEp_identity_s myIdentity,
Dean@363
   362
                                    pEp_identity_s partnerIdentity,
Dean@363
   363
                                    out string trustwordsShort,
Dean@363
   364
                                    out string trustwordsFull)
Dean@363
   365
        {
Dean@363
   366
            string myShort;
Dean@363
   367
            string myLong;
Dean@363
   368
            string partnerShort;
Dean@363
   369
            string partnerLong;
Dean@363
   370
Dean@363
   371
            myShort = ThisAddIn.pEp.trustwords(myIdentity.fpr, max_words: 5).ToLower();
Dean@363
   372
            myLong = ThisAddIn.pEp.trustwords(myIdentity.fpr).ToLower();
Dean@363
   373
            partnerShort = ThisAddIn.pEp.trustwords(partnerIdentity.fpr, max_words: 5).ToLower();
Dean@363
   374
            partnerLong = ThisAddIn.pEp.trustwords(partnerIdentity.fpr).ToLower();
Dean@363
   375
Dean@363
   376
            if (myIdentity.fpr.CompareTo(partnerIdentity.fpr) > 0)
Dean@363
   377
            {
Dean@363
   378
                trustwordsShort = partnerShort + myShort;
Dean@363
   379
                trustwordsFull = partnerLong + myLong;
Dean@363
   380
            }
Dean@363
   381
            else
Dean@363
   382
            {
Dean@363
   383
                trustwordsShort = myShort + " " + partnerShort;
Dean@363
   384
                trustwordsFull = myLong + " " + partnerLong;
Dean@363
   385
            }
Dean@363
   386
Dean@363
   387
            return;
Dean@363
   388
        }
Dean@363
   389
Dean@363
   390
        /// <summary>
Dean@161
   391
        /// Formats the given text string as separated 4-character groups.
Dean@161
   392
        /// Example: 49422235FC99585B891C --> 4942 2235 FC99 585B 891C
Dean@161
   393
        /// </summary>
Dean@161
   394
        /// <param name="text">The text to format in 4-character groups.</param>
Dean@161
   395
        /// <returns>The re-formatted string.</returns>
Dean@161
   396
        private string ToQuadruple(string text)
Dean@159
   397
        {
Dean@159
   398
            List<string> result = new List<string>();
Dean@159
   399
Dean@161
   400
            if (text != null)
Dean@159
   401
            {
Dean@161
   402
                for (int i = 0; i < text.Length; i += 4)
Dean@159
   403
                {
Dean@161
   404
                    try
Dean@161
   405
                    {
Dean@161
   406
                        result.Add(text.Substring(i, 4));
Dean@161
   407
                    }
Dean@161
   408
                    catch (ArgumentOutOfRangeException)
Dean@161
   409
                    {
Dean@161
   410
                        result.Add(text.Substring(i));
Dean@161
   411
                        break;
Dean@161
   412
                    }
Dean@159
   413
                }
Dean@159
   414
            }
Dean@159
   415
Dean@159
   416
            return String.Join(" ", result);
Dean@159
   417
        }
markus@201
   418
Dean@196
   419
        /// <summary>
Dean@196
   420
        /// Makes the unencrypted preview form.
Dean@196
   421
        /// This gets the active form region then fills it's content.
Dean@196
   422
        /// </summary>
Dean@198
   423
        /// <returns>True if successful, otherwise false.</returns>
Dean@198
   424
        private bool MakePreview()
Dean@159
   425
        {
Dean@198
   426
            bool success = true;
Dean@198
   427
            bool isSuccessful;
Dean@198
   428
            byte[] rtfBody;
Dean@198
   429
            string subject;
Dean@159
   430
            WindowFormRegionCollection formRegions;
markus@201
   431
Dean@198
   432
            formRegions = Globals.FormRegions[Globals.ThisAddIn.Application.ActiveExplorer()];
Dean@159
   433
Dean@198
   434
            if ((formRegions != null) &&
Dean@198
   435
                (formRegions.FormRegionPreviewUnencrypted != null) &&
Dean@198
   436
                (formRegions.FormRegionPreviewUnencrypted.Visible))
Dean@198
   437
            {
Dean@198
   438
                // Attempt to the the RTF body
Dean@198
   439
                isSuccessful = this.associatedMailItem.MirrorTryGetRTFBody(out rtfBody);
Dean@198
   440
                if (isSuccessful == false)
Dean@198
   441
                {
Dean@198
   442
                    return (false);
Dean@198
   443
                }
Dean@159
   444
Dean@198
   445
                // Attempt to get the subject
Dean@198
   446
                isSuccessful = this.associatedMailItem.MirrorTryGetSubject(out subject);
Dean@198
   447
                if (isSuccessful == false)
Dean@159
   448
                {
Dean@198
   449
                    return (false);
Dean@159
   450
                }
Dean@196
   451
Dean@198
   452
                // Add data to the form
markus@201
   453
                formRegions.FormRegionPreviewUnencrypted.RichTextBoxPreview.Rtf = System.Text.Encoding.ASCII.GetString(rtfBody, 0, rtfBody.Length);
markus@201
   454
                formRegions.FormRegionPreviewUnencrypted.TextBoxSubject.Text = subject;
markus@201
   455
                formRegions.FormRegionPreviewUnencrypted.TextBoxFrom.Text = this.associatedMailItem.FromUsername + " <" + this.associatedMailItem.FromAddress + ">";
Dean@198
   456
            }
Dean@198
   457
            else
Dean@198
   458
            {
Dean@198
   459
                success = false;
Dean@198
   460
            }
Dean@198
   461
Dean@198
   462
            return (success);
Dean@159
   463
        }
markus@201
   464
Dean@179
   465
        /// <summary>
Dean@279
   466
        /// Builds a new manager form state using this encryption state/mail item current state.
Dean@279
   467
        /// </summary>
Dean@279
   468
        /// <returns>A new manager form state.</returns>
Dean@313
   469
        private FormManagePrivacyStatus.State GetManagerState()
Dean@279
   470
        {
Dean@364
   471
            string trustwordsShort;
Dean@364
   472
            string trustwordsFull;
Dean@259
   473
            pEp_identity_s[] identities;
Dean@366
   474
            pEp_identity_s myIdentity;
Dean@259
   475
            pEp_identity_s incomingIdent;
Dean@259
   476
            _pEp_color identityPartnerColor;
Dean@259
   477
            SelectionItem item;
Dean@312
   478
            PrivacyState state = this.CopyUIToState();
Dean@313
   479
            FormManagePrivacyStatus.State managerState = new FormManagePrivacyStatus.State();
Dean@259
   480
Dean@332
   481
            // Resolve all recipients -- this ensures the identities list is correctly populated
Dean@332
   482
            this.associatedMailItem.ResolveAllRecipients();
Dean@332
   483
Dean@279
   484
            managerState.VisualState = state.Copy();
Dean@279
   485
            managerState.IsIncoming = this.associatedMailItem.IsIncoming;
Dean@259
   486
Dean@259
   487
            if (this.associatedMailItem.IsIncoming)
Dean@259
   488
            {
Dean@259
   489
                identities = new pEp_identity_s[1];
Dean@259
   490
Dean@259
   491
                // Add only one identity
Dean@259
   492
                incomingIdent = new pEp_identity_s();
Dean@259
   493
                incomingIdent.address = this.associatedMailItem.FromAddress;
Dean@259
   494
                incomingIdent.username = this.associatedMailItem.FromUsername;
Dean@363
   495
                incomingIdent.user_id = Globals.ThisAddIn.GetUserIDFromAddress(incomingIdent.address, incomingIdent.username);
Dean@259
   496
Dean@259
   497
                identities[0] = incomingIdent;
Dean@259
   498
            }
Dean@259
   499
            else
Dean@259
   500
            {
Dean@259
   501
                identities = this.associatedMailItem.Addresses;
Dean@259
   502
            }
Dean@259
   503
Dean@366
   504
            // Get my identity
Dean@366
   505
            myIdentity = Globals.ThisAddIn.GetMyIdentity(this.associatedMailItem);
Dean@366
   506
Dean@279
   507
            // Add identities
Dean@279
   508
            managerState.Identities.Clear();
Dean@259
   509
            foreach (pEp_identity_s ident in identities)
Dean@259
   510
            {
Dean@366
   511
                pEp_identity_s partnerIdentity = ThisAddIn.pEp.update_identity(ident);
Dean@366
   512
                identityPartnerColor = ThisAddIn.pEp.identity_color(partnerIdentity);
Dean@259
   513
Dean@259
   514
                item = new SelectionItem();
Dean@366
   515
                item.TextLine1 = partnerIdentity.username;
Dean@366
   516
                item.TextLine2 = partnerIdentity.address;
Dean@259
   517
Dean@364
   518
                // Calculate trustwords
Dean@366
   519
                this.CalcTrustwords(myIdentity,
Dean@366
   520
                                    partnerIdentity,
Dean@364
   521
                                    out trustwordsShort,
Dean@364
   522
                                    out trustwordsFull);
Dean@364
   523
Dean@332
   524
                // Don't show both the user name and address if they are the same
Dean@332
   525
                if ((item.TextLine1 != null) &&
Dean@332
   526
                    (item.TextLine2 != null) &&
Dean@332
   527
                    (item.TextLine1 == item.TextLine2))
Dean@332
   528
                {
Dean@332
   529
                    item.IsTwoTextLinesVisible = false;
Dean@332
   530
                }
Dean@332
   531
Dean@259
   532
                // Set image
Dean@359
   533
                switch (PrivacyState.ConvertRatingToPrivacyColor(identityPartnerColor))
Dean@259
   534
                {
Dean@359
   535
                    case PrivacyState.PrivacyColor.Green:
Dean@259
   536
                        item.ItemImage = pEp.Properties.Resources.green;
Dean@259
   537
                        break;
Dean@359
   538
                    case PrivacyState.PrivacyColor.Yellow:
Dean@259
   539
                        item.ItemImage = pEp.Properties.Resources.yellow;
Dean@259
   540
                        break;
Dean@359
   541
                    case PrivacyState.PrivacyColor.Red:
Dean@259
   542
                        item.ItemImage = pEp.Properties.Resources.red;
Dean@259
   543
                        break;
Dean@359
   544
                    case PrivacyState.PrivacyColor.Grey:
Dean@359
   545
                        item.ItemImage = null;
Dean@359
   546
                        break;
Dean@259
   547
                    default:
Dean@259
   548
                        item.ItemImage = null;
Dean@259
   549
                        break;
Dean@259
   550
                }
Dean@259
   551
Dean@259
   552
                // Set button
Dean@366
   553
                if (myIdentity.fpr != partnerIdentity.fpr)
Dean@259
   554
                {
Dean@366
   555
                    if (partnerIdentity.comm_type >= _pEp_comm_type.pEp_ct_confirmed_encryption)
Dean@366
   556
                    {
Dean@366
   557
                        // Undo handshake
Dean@366
   558
                        item.TextButton = pEp.Properties.Resources.PrivacyStatus_StopTrusting;
Dean@366
   559
                        item.IsButtonVisible = true;
Dean@366
   560
                        item.ButtonOnClick = (x, y) => { this.UndoHandshake(partnerIdentity); };
Dean@356
   561
Dean@366
   562
                        item.IsExpandable = false;
Dean@366
   563
                    }
Dean@366
   564
                    else if (partnerIdentity.comm_type >= _pEp_comm_type.pEp_ct_unconfirmed_encryption &&
Dean@366
   565
                             partnerIdentity.comm_type < _pEp_comm_type.pEp_ct_confirmed_encryption)
Dean@366
   566
                    {
Dean@366
   567
                        // Do handshake
Dean@366
   568
                        item.TextButton = pEp.Properties.Resources.PrivacyStatus_HandshakeAdvanced;
Dean@366
   569
                        item.IsButtonVisible = true;
Dean@366
   570
                        item.ButtonOnClick = (x, y) => { this.DoHandshake(partnerIdentity); };
Dean@356
   571
Dean@366
   572
                        item.IsExpandable = true;
Dean@366
   573
                        item.ExpandedText = Properties.Resources.PrivacyStatus_TrustwordDesc + "\n\n" +
Dean@366
   574
                                            trustwordsShort;
Dean@366
   575
                        item.ExpandedButton1Text = Properties.Resources.PrivacyStatus_SelectTrust;
Dean@366
   576
                        item.ExpandedButton2Text = Properties.Resources.PrivacyStatus_SelectMistrust;
Dean@366
   577
                        item.ExpandedButton1OnClick = (x, y) => { this.ProcessDoHandshakeResult(DialogResult.Yes, partnerIdentity); };
Dean@366
   578
                        item.ExpandedButton2OnClick = (x, y) => { this.ProcessDoHandshakeResult(DialogResult.No, partnerIdentity); };
Dean@366
   579
                    }
Dean@366
   580
                    else if (partnerIdentity.comm_type == _pEp_comm_type.pEp_ct_compromized)
Dean@366
   581
                    {
Dean@366
   582
                        /*
Dean@366
   583
                        // Redo handshake with confirmation
Dean@366
   584
                        item.TextButton = pEp.Properties.Resources.Handshake;
Dean@366
   585
                        item.IsButtonVisible = true;
Dean@366
   586
                        item.OnClick = (x, y) => { this.DoHandshakeForCompromisedKey(identityPartner); };
Dean@366
   587
                        */
Dean@366
   588
                    }
Dean@366
   589
                    else
Dean@366
   590
                    {
Dean@366
   591
                        item.IsButtonVisible = false;
Dean@366
   592
                    }
Dean@281
   593
                }
Dean@259
   594
                else
Dean@259
   595
                {
Dean@259
   596
                    item.IsButtonVisible = false;
Dean@259
   597
                }
Dean@259
   598
Dean@279
   599
                managerState.Identities.Add(item);
Dean@259
   600
            }
Dean@259
   601
Dean@375
   602
            // Attempt to select the first identity requiring a handshake
Dean@375
   603
            for (int i = 0; i < managerState.Identities.Count; i++)
Dean@375
   604
            {
Dean@375
   605
                // Determine if it's a handshake identity by simply seeing if it's expandable
Dean@375
   606
                if (managerState.Identities[i].IsExpandable)
Dean@375
   607
                {
Dean@375
   608
                    managerState.SelectedIdentityIndex = i;
Dean@375
   609
                    break;
Dean@375
   610
                }
Dean@375
   611
            }
Dean@375
   612
Dean@279
   613
            return (managerState);
Dean@279
   614
        }
Dean@279
   615
Dean@279
   616
        /// <summary>
Dean@279
   617
        /// Builds the latest state of the encryption status manager then shows the UI.
Dean@279
   618
        /// </summary>
Dean@279
   619
        private void BuildAndShowManager()
Dean@279
   620
        {
Dean@279
   621
            DialogResult result;
Dean@313
   622
            FormManagePrivacyStatus form;
Dean@313
   623
            FormManagePrivacyStatus.State stateOut;
Dean@279
   624
Dean@259
   625
            // Show the form
Dean@313
   626
            form = new FormManagePrivacyStatus();
Dean@259
   627
            form.StartPosition = FormStartPosition.CenterScreen;
Dean@259
   628
Dean@259
   629
            this.managerForm = form;
Dean@279
   630
            result = form.ShowDialog(this.ParentForm, this.GetManagerState(), out stateOut); // Must show as dialog to block code
Dean@259
   631
Dean@259
   632
            return;
Dean@259
   633
        }
Dean@259
   634
Dean@259
   635
        /// <summary>
Dean@179
   636
        /// Updates the status of the UI state based on the associated mail item.
Dean@179
   637
        /// Any previous state changes in the UI are preserved.
Dean@179
   638
        /// </summary>
Dean@179
   639
        private void UpdateUIFromMailItem()
Dean@159
   640
        {
Dean@312
   641
            PrivacyState state;
Dean@342
   642
            bool? sendUnencryptedProperty;
Dean@159
   643
Dean@173
   644
            if (this.associatedMailItem != null)
Dean@159
   645
            {
Dean@342
   646
                sendUnencryptedProperty = this.associatedMailItem.SendUnencrypted;
Dean@342
   647
Dean@342
   648
                if ((sendUnencryptedProperty != null) &&
Dean@342
   649
                    ((bool)sendUnencryptedProperty == true))
Dean@159
   650
                {
Dean@342
   651
                    // Force unencrypted
Dean@312
   652
                    state = new PrivacyState(_pEp_color.pEp_rating_unencrypted);
Dean@159
   653
                }
Dean@159
   654
                else
Dean@159
   655
                {
Dean@312
   656
                    state = new PrivacyState(this.associatedMailItem.ColorRating);
Dean@159
   657
                }
Dean@179
   658
Dean@179
   659
                this.CopyStateToUI(state);
Dean@159
   660
            }
Dean@159
   661
Dean@179
   662
            return;
Dean@159
   663
        }
Dean@159
   664
Dean@172
   665
        #region StateMethods
Dean@172
   666
Dean@172
   667
        /// <summary>
Dean@172
   668
        /// Connects or disconnects all control events from the UI.
Dean@172
   669
        /// </summary>
Dean@172
   670
        /// <param name="connect">True to connect events, false to disconnect.</param>
Dean@172
   671
        private void ConnectEvents(bool connect)
Dean@172
   672
        {
Dean@172
   673
            // Connect events only if not already connected
markus@201
   674
            if ((connect == true) &&
Dean@172
   675
                (this.eventsAreConnected == false))
Dean@172
   676
            {
Dean@312
   677
                this.ButtonPrivacyStatus.Click += this.ButtonPrivacyStatus_Click;
Dean@312
   678
                this.ButtonPrivacyStatus.MouseUp += this.ButtonPrivacyStatus_MouseUp;
Dean@179
   679
Dean@172
   680
                this.eventsAreConnected = true;
Dean@172
   681
            }
Dean@172
   682
            // Always attempt to disconnect
Dean@172
   683
            else if (connect == false)
Dean@172
   684
            {
Dean@312
   685
                this.ButtonPrivacyStatus.Click -= this.ButtonPrivacyStatus_Click;
Dean@312
   686
                this.ButtonPrivacyStatus.MouseUp -= this.ButtonPrivacyStatus_MouseUp;
Dean@179
   687
Dean@172
   688
                this.eventsAreConnected = false;
Dean@172
   689
            }
Dean@172
   690
Dean@172
   691
            return;
Dean@172
   692
        }
Dean@172
   693
Dean@172
   694
        /// <summary>
Dean@172
   695
        /// Refreshes the UI by reloading the state.
Dean@172
   696
        /// </summary>
Dean@172
   697
        private void RefreshUI()
Dean@172
   698
        {
Dean@172
   699
            this.CopyStateToUI(this.CopyUIToState());
Dean@172
   700
            return;
Dean@172
   701
        }
Dean@172
   702
Dean@172
   703
        /// <summary>
Dean@172
   704
        /// Copies the given state to the UI.
Dean@172
   705
        /// Events are turned off until the process is complete.
Dean@172
   706
        /// </summary>
Dean@172
   707
        /// <param name="state">The state to set to the UI.</param>
Dean@312
   708
        private void CopyStateToUI(PrivacyState state)
Dean@172
   709
        {
Dean@172
   710
            this.ConnectEvents(false);
markus@201
   711
Dean@172
   712
            ///////////////////////////////////////////////////////////
Dean@172
   713
            // Set UI data
Dean@172
   714
            ///////////////////////////////////////////////////////////
Dean@172
   715
Dean@179
   716
            // Save UI state maintained outside of controls
Dean@312
   717
            this.stateUIColorRating = state.PrivacyStatus;
Dean@179
   718
Dean@312
   719
            this.ButtonPrivacyStatus.BackColor = state.BackgroundColor;
Dean@312
   720
            this.ButtonPrivacyStatus.ForeColor = state.ForegroundColor;
Dean@312
   721
            this.ButtonPrivacyStatus.Text = state.ShortText;
Dean@172
   722
Dean@172
   723
            this.ConnectEvents(true);
Dean@172
   724
Dean@172
   725
            return;
Dean@172
   726
        }
Dean@172
   727
Dean@172
   728
        /// <summary>
Dean@172
   729
        /// Copies the UI to a new state.
Dean@172
   730
        /// </summary>
Dean@172
   731
        /// <returns>The state of the UI.</returns>
Dean@312
   732
        private PrivacyState CopyUIToState()
Dean@172
   733
        {
Dean@312
   734
            return (new PrivacyState(this.stateUIColorRating));
Dean@172
   735
        }
Dean@172
   736
Dean@172
   737
        #endregion
Dean@172
   738
Dean@159
   739
        /**************************************************************
Dean@159
   740
         * 
Dean@159
   741
         * Event Handling
Dean@159
   742
         * 
Dean@159
   743
         *************************************************************/
Dean@159
   744
Dean@159
   745
        /// <summary>
Dean@159
   746
        /// Event handler that is called before the form region is displayed.
Dean@159
   747
        /// </summary>
Dean@312
   748
        private void FormRegionPrivacyStatus_FormRegionShowing(object sender, System.EventArgs e)
Dean@159
   749
        {
Dean@173
   750
            this.SetAssociatedMailItem();
Dean@173
   751
Dean@199
   752
            // Connect cryptable mail item events
Dean@189
   753
            if (this.associatedMailItem != null)
Dean@159
   754
            {
Dean@159
   755
                try
Dean@159
   756
                {
Dean@189
   757
                    this.associatedMailItem.PropertyChange += MailItem_PropertyChange;
Dean@179
   758
Dean@189
   759
                    if (this.associatedMailItem.IsInEncryptedStore &&
Dean@198
   760
                        this.associatedMailItem.IsPGPEncrypted)
Dean@159
   761
                    {
Dean@189
   762
                        this.associatedMailItem.Open += MailItem_Open;
Dean@159
   763
                    }
Dean@159
   764
                }
Dean@159
   765
                catch { }
Dean@179
   766
            }
Dean@159
   767
Dean@179
   768
            // Set the default UI state
Dean@312
   769
            this.CopyStateToUI(new PrivacyState());
Dean@179
   770
Dean@199
   771
            // Call the timer tick method manually to refresh data
Dean@199
   772
            this.TimerRefresh_Tick(null, new EventArgs());
Dean@179
   773
Dean@179
   774
            return;
Dean@159
   775
        }
Dean@159
   776
Dean@159
   777
        /// <summary>
Dean@159
   778
        /// Event handler for when the form region is closed.
Dean@159
   779
        /// </summary>
Dean@312
   780
        private void FormRegionPrivacyStatus_FormRegionClosed(object sender, System.EventArgs e)
Dean@159
   781
        {
Dean@199
   782
            // Disconnect cryptable mail item events
Dean@190
   783
            if (this.associatedMailItem != null)
Dean@179
   784
            {
Dean@179
   785
                try
Dean@179
   786
                {
Dean@190
   787
                    this.associatedMailItem.PropertyChange -= MailItem_PropertyChange;
markus@201
   788
                    this.associatedMailItem.Open -= MailItem_Open;
Dean@179
   789
                }
Dean@179
   790
                catch { }
Dean@179
   791
            }
Dean@179
   792
Dean@179
   793
            return;
Dean@179
   794
        }
Dean@179
   795
Dean@179
   796
        /// <summary>
Dean@179
   797
        /// Event handler called after the refresh timer has elapsed.
Dean@179
   798
        /// </summary>
Dean@179
   799
        private void TimerRefresh_Tick(object sender, EventArgs e)
Dean@179
   800
        {
Dean@199
   801
            bool tryAgain = false;
Dean@199
   802
            bool isSuccessful;
Dean@179
   803
            this.TimerRefresh.Enabled = false; // Only once
markus@201
   804
Dean@179
   805
            // Ensure the tick method is not called more than once
Dean@179
   806
            if (refreshOngoing == false)
Dean@179
   807
            {
Dean@179
   808
                this.refreshOngoing = true;
Dean@179
   809
Dean@179
   810
                if (this.associatedMailItem != null)
Dean@179
   811
                {
Dean@179
   812
                    if (this.associatedMailItem.DownloadState == Outlook.OlDownloadState.olFullItem)
Dean@179
   813
                    {
Dean@179
   814
                        this.UpdateUIFromMailItem();
Dean@199
   815
Dean@199
   816
                        /* Create the unencrypted preview if the mail item is encrypted and
Dean@199
   817
                         * it is in an encrypted (untrusted) store
Dean@199
   818
                         * 
Dean@312
   819
                         * This is done here because FormRegionPrivacyStatus has the cryptable mail item and
Dean@199
   820
                         * it also is initialized after FormRegionPreviewUnencrypted.
Dean@199
   821
                         */
Dean@199
   822
                        if (this.associatedMailItem.IsInEncryptedStore &&
Dean@199
   823
                            this.associatedMailItem.IsPGPEncrypted)
Dean@199
   824
                        {
Dean@199
   825
                            isSuccessful = this.MakePreview();
Dean@199
   826
Dean@199
   827
                            if (isSuccessful == false)
Dean@199
   828
                            {
Dean@199
   829
                                tryAgain = true;
Dean@199
   830
                            }
Dean@199
   831
                        }
Dean@179
   832
                    }
Dean@179
   833
                    else
Dean@179
   834
                    {
Dean@179
   835
                        this.associatedMailItem.MarkForDownload = Outlook.OlRemoteStatus.olMarkedForDownload;
Dean@199
   836
                        tryAgain = true;
Dean@179
   837
                    }
Dean@179
   838
                }
Dean@179
   839
Dean@199
   840
                // Set the timer to refresh again later automatically
Dean@199
   841
                if (tryAgain)
Dean@199
   842
                {
Dean@199
   843
                    this.TimerRefresh.Interval = 100;
Dean@199
   844
                    this.TimerRefresh.Enabled = true;
Dean@199
   845
                }
Dean@199
   846
Dean@179
   847
                this.refreshOngoing = false;
Dean@179
   848
            }
Dean@175
   849
Dean@159
   850
            return;
Dean@159
   851
        }
Dean@159
   852
Dean@169
   853
        /// <summary>
Dean@169
   854
        /// Event handler for when a mail item is being opened in an inspector.
Dean@180
   855
        /// See: https://msdn.microsoft.com/en-us/library/office/ff865989.aspx
Dean@169
   856
        /// </summary>
Dean@169
   857
        /// <param name="cancel">Whether to cancel the event: Value is False when the event occurs. 
Dean@169
   858
        /// If the event procedure sets this argument to True, the open operation is not completed 
Dean@169
   859
        /// and the inspector is not displayed.</param>
Dean@169
   860
        private void MailItem_Open(ref bool cancel)
Dean@159
   861
        {
Dean@173
   862
            if (this.associatedMailItem != null && this.associatedMailItem.MirrorDisplay())
Dean@169
   863
            {
Dean@169
   864
                cancel = true;
Dean@169
   865
            }
Dean@169
   866
Dean@169
   867
            return;
Dean@159
   868
        }
Dean@159
   869
Dean@169
   870
        /// <summary>
Dean@169
   871
        /// Event handler for when a mail item property is changed.
Dean@169
   872
        /// See: https://msdn.microsoft.com/en-us/library/office/ff866739.aspx
Dean@169
   873
        /// </summary>
Dean@169
   874
        /// <param name="propertyName">The name of the property that was changed.</param>
Dean@169
   875
        private void MailItem_PropertyChange(string propertyName)
Dean@159
   876
        {
Dean@179
   877
            switch (propertyName.ToUpper())
Dean@159
   878
            {
Dean@179
   879
                case "TO":
Dean@179
   880
                    // Start the refresh timer
Dean@179
   881
                    this.TimerRefresh.Enabled = true;
Dean@159
   882
                    break;
Dean@159
   883
                // Outlook bug: there are always both events, so one is enough
Dean@159
   884
                //case "CC":
Dean@179
   885
                //    // Start the refresh timer
Dean@179
   886
                //    this.TimerRefresh.Enabled = true;
Dean@159
   887
                //    break;
Dean@159
   888
            }
Dean@169
   889
Dean@169
   890
            return;
Dean@159
   891
        }
Dean@159
   892
Dean@179
   893
        /// <summary>
Dean@312
   894
        /// Event handler for when the privacy status button is clicked.
Dean@179
   895
        /// </summary>
Dean@312
   896
        private void ButtonPrivacyStatus_Click(object sender, EventArgs e)
vb@133
   897
        {
Dean@310
   898
            this.BuildAndShowManager();
Dean@179
   899
            return;
vb@133
   900
        }
vb@133
   901
Dean@179
   902
        /// <summary>
Dean@312
   903
        /// Event handler for when a mouse button is released over the privacy status button.
Dean@179
   904
        /// </summary>
Dean@312
   905
        private void ButtonPrivacyStatus_MouseUp(object sender, MouseEventArgs e)
vb@133
   906
        {
Dean@179
   907
            return;
vb@133
   908
        }
vb@133
   909
    }
vb@133
   910
}