UI/FormRegionPrivacyStatus.cs
author Dean Looyengoed
Tue, 16 Feb 2016 03:31:29 +0100
changeset 438 ac133be97883
parent 434 6e556aac3e92
child 445 499559d35fae
permissions -rw-r--r--
Add method to determine if the privacy status form region is displayed in an inspector.
Use above method to set background color if in explorer vs. inspector.
Dean@159
     1
´╗┐using pEpCOMServerAdapterLib;
Dean@159
     2
using System;
vb@133
     3
using System.Collections.Generic;
Dean@159
     4
using System.Diagnostics;
Dean@438
     5
using System.Drawing;
vb@133
     6
using System.Linq;
Dean@159
     7
using System.Runtime.InteropServices;
vb@133
     8
using System.Windows.Forms;
Dean@159
     9
using Color = System.Drawing.Color;
vb@133
    10
using Outlook = Microsoft.Office.Interop.Outlook;
vb@133
    11
vb@133
    12
namespace pEp
vb@133
    13
{
Dean@159
    14
    /// <summary>
Dean@312
    15
    /// Partial class for the privacy status form region that is displayed below every outlook message.
Dean@159
    16
    /// </summary>
Dean@312
    17
    partial class FormRegionPrivacyStatus
vb@133
    18
    {
vb@133
    19
        #region Form Region Factory
vb@133
    20
vb@133
    21
        [Microsoft.Office.Tools.Outlook.FormRegionMessageClass(Microsoft.Office.Tools.Outlook.FormRegionMessageClassAttribute.Note)]
Dean@312
    22
        [Microsoft.Office.Tools.Outlook.FormRegionName("pEp.FormRegionPrivacyStatus")]
Dean@312
    23
        public partial class FormRegionPrivacyStatusFactory
vb@133
    24
        {
vb@133
    25
            // Occurs before the form region is initialized.
vb@133
    26
            // To prevent the form region from appearing, set e.Cancel to true.
vb@133
    27
            // Use e.OutlookItem to get a reference to the current Outlook item.
Dean@312
    28
            private void FormRegionPrivacyStatus_FormRegionInitializing(object sender, Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs e)
vb@133
    29
            {
Dean@327
    30
                WindowFormRegionCollection formRegions;
Dean@327
    31
Dean@327
    32
                /* There is a Microsoft bug at least in Outlook 2013 and Windows 8.1
Dean@327
    33
                 * This bug causes multiple PrivacyStatus form regions to appear stacked on top of each other.
Dean@327
    34
                 * To trigger this bug, on an unencrypted server, click reply to compose an in-line response.
Dean@327
    35
                 * Then click to another tab such as People or Tasks. Then click back on the mail tab to view 
Dean@327
    36
                 * the original email again. Two form regions will be visible: 
Dean@327
    37
                 * (1) for the status during the in-line reply and 
Dean@327
    38
                 * (2) for the status of only the received message.
Dean@327
    39
                 * 
Dean@327
    40
                 * To fix this bug, any existing form regions are found and closed when initializing a new privacy status
Dean@327
    41
                 * form region.
Dean@327
    42
                 */
Dean@327
    43
Dean@327
    44
                try
Dean@327
    45
                {
Dean@327
    46
                    formRegions = Globals.FormRegions[Globals.ThisAddIn.Application.ActiveExplorer()];
Dean@327
    47
                    if (formRegions.FormRegionPrivacyStatus != null)
Dean@327
    48
                    {
Dean@327
    49
                        /* Note: there seems to be no way to actually close the form region.
Dean@327
    50
                         * Therefore, this is as close as possible to actually closing it.
Dean@327
    51
                         * The actual form regions will be cleaned up as soon as another email is selected.
Dean@327
    52
                         */
Dean@327
    53
                        formRegions.FormRegionPrivacyStatus.OutlookFormRegion.Visible = false;
Dean@327
    54
                    }
Dean@327
    55
                }
Dean@327
    56
                catch { }
Dean@327
    57
Dean@327
    58
                return;
vb@133
    59
            }
vb@133
    60
        }
vb@133
    61
vb@133
    62
        #endregion
vb@133
    63
Dean@159
    64
        /* Notes:
Dean@179
    65
         * 
Dean@159
    66
         * Use this.OutlookItem to get a reference to the current Outlook item.
Dean@159
    67
         * Use this.OutlookFormRegion to get a reference to the form region.
Dean@179
    68
         * 
Dean@179
    69
         * UI State Managment:
Dean@179
    70
         * 
Dean@179
    71
         * The UI state is almost entirely set from the associated mail item data.
Dean@179
    72
         * However, a separate state class is maintained to represent the UI as some separation is needed.
Dean@179
    73
         * This logical separation MUST be maintained throughout the code.
Dean@179
    74
         * Specific cases are noted where possible.
Dean@179
    75
         * 
Dean@356
    76
         * The separate privacy status manager form state is also managed here.
Dean@259
    77
         * 
Dean@159
    78
         */
Dean@159
    79
Dean@170
    80
        private CryptableMailItem associatedMailItem = null;
Dean@179
    81
        private bool              refreshOngoing     = false;
Dean@179
    82
        private bool              eventsAreConnected = false;
Dean@170
    83
Dean@313
    84
        private FormManagePrivacyStatus managerForm = null;
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@340
    91
         * Property Accessors
Dean@340
    92
         * 
Dean@340
    93
         *************************************************************/
Dean@340
    94
Dean@340
    95
        /// <summary>
Dean@340
    96
        /// Gets or sets whether to send the associated cryptable mail item as unencrypted.
Dean@342
    97
        /// This forwards the call to the cryptable mail item but also refreshes the UI.
Dean@340
    98
        /// </summary>
Dean@342
    99
        public bool? SendUnencrypted
Dean@340
   100
        {
Dean@340
   101
            get
Dean@340
   102
            {
Dean@340
   103
                if (this.associatedMailItem != null)
Dean@340
   104
                {
Dean@340
   105
                    return (this.associatedMailItem.SendUnencrypted);
Dean@340
   106
                }
Dean@340
   107
                else
Dean@340
   108
                {
Dean@342
   109
                    return (null);
Dean@340
   110
                }
Dean@340
   111
            }
Dean@340
   112
            set
Dean@340
   113
            {
Dean@340
   114
                if (this.associatedMailItem != null)
Dean@340
   115
                {
Dean@340
   116
                    this.associatedMailItem.SendUnencrypted = value;
Dean@340
   117
                    this.UpdateUIFromMailItem();
Dean@340
   118
                }
Dean@340
   119
            }
Dean@340
   120
        }
Dean@340
   121
Dean@340
   122
        /**************************************************************
Dean@340
   123
         * 
Dean@159
   124
         * Methods
Dean@159
   125
         * 
Dean@159
   126
         *************************************************************/
Dean@159
   127
Dean@171
   128
        /// <summary>
Dean@438
   129
        /// Determines if this form region is running in an inspector window.
Dean@438
   130
        /// If not true, it is assumed to be within an explorer window.
Dean@438
   131
        /// </summary>
Dean@438
   132
        /// <returns>True if an inspector window, false if within an explorer.</returns>
Dean@438
   133
        private bool IsWithinInspector()
Dean@438
   134
        {
Dean@438
   135
            bool isWithinInspector = false;
Dean@438
   136
            Outlook.Explorer temp = null;
Dean@438
   137
Dean@438
   138
            try
Dean@438
   139
            {
Dean@438
   140
                // Just try to cast to an explorer class
Dean@438
   141
                temp = (Outlook.Explorer)this.OutlookFormRegion.Parent;
Dean@438
   142
                isWithinInspector = false;
Dean@438
   143
            }
Dean@438
   144
            catch
Dean@438
   145
            {
Dean@438
   146
                isWithinInspector = true;
Dean@438
   147
            }
Dean@438
   148
            finally
Dean@438
   149
            {
Dean@438
   150
                if (temp != null)
Dean@438
   151
                {
Dean@438
   152
                    Marshal.ReleaseComObject(temp);
Dean@438
   153
                    temp = null;
Dean@438
   154
                }
Dean@438
   155
            }
Dean@438
   156
Dean@438
   157
            return (isWithinInspector);
Dean@438
   158
        }
Dean@438
   159
Dean@438
   160
        /// <summary>
Dean@170
   161
        /// Sets the cryptable mail item associated with this encryption status panel.
Dean@170
   162
        /// The associated mail item can then be accessed through its local variable.
Dean@170
   163
        /// </summary>
Dean@170
   164
        private void SetAssociatedMailItem()
Dean@170
   165
        {
Dean@170
   166
            bool errorOccurred = false;
Dean@170
   167
            Outlook.MailItem omi = null;
Dean@170
   168
Dean@170
   169
            // Null check
Dean@170
   170
            if (!errorOccurred)
Dean@170
   171
            {
Dean@170
   172
                try
Dean@170
   173
                {
Dean@170
   174
                    if (this.OutlookItem == null)
Dean@170
   175
                    {
Dean@170
   176
                        errorOccurred = true;
Dean@170
   177
                    }
Dean@170
   178
                }
Dean@170
   179
                catch (COMException)
Dean@170
   180
                {
Dean@170
   181
                    errorOccurred = true;
Dean@170
   182
                }
Dean@170
   183
            }
Dean@170
   184
Dean@170
   185
            // Attempt to get and cast the outlook mail item
Dean@170
   186
            if (!errorOccurred)
Dean@170
   187
            {
Dean@170
   188
                try
Dean@170
   189
                {
Dean@170
   190
                    omi = (Outlook.MailItem)this.OutlookItem;
Dean@170
   191
                }
Dean@170
   192
                catch
Dean@170
   193
                {
Dean@170
   194
                    errorOccurred = true;
Dean@170
   195
                }
Dean@170
   196
            }
Dean@170
   197
Dean@170
   198
            // Finally set the associated mail item
Dean@170
   199
            if ((errorOccurred) ||
Dean@170
   200
                (omi == null))
Dean@170
   201
            {
Dean@170
   202
                this.associatedMailItem = null;
Dean@170
   203
            }
Dean@170
   204
            else
Dean@170
   205
            {
Dean@170
   206
                // Check if the associated mail item has already been set
Dean@170
   207
                if (this.associatedMailItem != null)
Dean@170
   208
                {
Dean@170
   209
                    // Only re-set the mail item if the EntryID has changed
Dean@170
   210
                    if (this.associatedMailItem.EntryID != omi.EntryID)
Dean@170
   211
                    {
Dean@170
   212
                        this.associatedMailItem = new CryptableMailItem(omi);
Dean@170
   213
                    }
Dean@170
   214
                }
Dean@170
   215
                else
Dean@170
   216
                {
Dean@170
   217
                    this.associatedMailItem = new CryptableMailItem(omi);
Dean@170
   218
                }
Dean@170
   219
            }
Dean@170
   220
Dean@170
   221
            return;
Dean@170
   222
        }
Dean@170
   223
Dean@179
   224
        /// <summary>
Dean@290
   225
        /// Completes the handshake process when the identity of a partner was previously marked as compromised.
Dean@281
   226
        /// </summary>
Dean@281
   227
        /// <param name="identityPartner">The identity of the partner to complete the handshake with.</param>
Dean@384
   228
        private void DoHandshakeForCompromisedKey(PEPIdentity identityPartner)
Dean@281
   229
        {
Dean@281
   230
            DialogResult result;
Dean@281
   231
Dean@281
   232
            result = MessageBox.Show(this.ParentForm,
Dean@378
   233
                                     pEp.Properties.Resources.Message_WarningCompromisedKey,
Dean@378
   234
                                     pEp.Properties.Resources.Message_TitleConfirmOperation,
Dean@423
   235
                                     MessageBoxButtons.YesNo);
Dean@281
   236
Dean@423
   237
            if (result == DialogResult.Yes)
Dean@281
   238
            {
Dean@281
   239
                this.DoHandshake(identityPartner);
Dean@281
   240
            }
Dean@281
   241
Dean@281
   242
            return;
Dean@281
   243
        }
Dean@281
   244
Dean@281
   245
        /// <summary>
Dean@281
   246
        /// Completes the handshake process where the identity of a partner is confirmed.
Dean@179
   247
        /// </summary>
Dean@179
   248
        /// <param name="identityPartner">The identity of the partner to complete the handshake with.</param>
Dean@384
   249
        private void DoHandshake(PEPIdentity identityPartner)
vb@133
   250
        {
Dean@155
   251
            string ownFpr;
Dean@155
   252
            string partnerFpr;
Dean@363
   253
            string trustwordsShort;
Dean@363
   254
            string trustwordsFull;
Dean@155
   255
            string[] lines;
Dean@179
   256
            DialogResult result;
Dean@384
   257
            PEPIdentity me;
Dean@155
   258
            FormHandshake handshakeDialog;
Dean@155
   259
            FormHandshake.State state = new FormHandshake.State();
markus@201
   260
Dean@190
   261
            Globals.ThisAddIn.LogVerbose("doHandshake");
Dean@384
   262
            Globals.ThisAddIn.LogVerbose(identityPartner.Address);
vb@139
   263
Dean@363
   264
            // Process identities
Dean@384
   265
            if (identityPartner.Username == "")
Dean@363
   266
            {
Dean@384
   267
                identityPartner.Username = identityPartner.Address;
vb@133
   268
            }
vb@133
   269
Dean@415
   270
            identityPartner.UserID = Globals.ThisAddIn.GetUserID(identityPartner.Address, identityPartner.Username);
vb@139
   271
Dean@384
   272
            me = PEPIdentity.GetMyIdentity(this.associatedMailItem);
Dean@363
   273
Dean@363
   274
            // Calculate and add trustwords
Dean@363
   275
            this.CalcTrustwords(me, identityPartner, out trustwordsShort, out trustwordsFull);
Dean@363
   276
            state.TrustwordsShort = trustwordsShort;
Dean@363
   277
            state.TrustwordsFull = trustwordsFull;
Dean@363
   278
Dean@363
   279
            // Calculate and add fingerprint
Dean@384
   280
            ownFpr = this.ToQuadruple(me.Fingerprint);
Dean@384
   281
            partnerFpr = this.ToQuadruple(identityPartner.Fingerprint);
vb@133
   282
Dean@384
   283
            if (me.Fingerprint.CompareTo(identityPartner.Fingerprint) > 0)
vb@133
   284
            {
Dean@155
   285
                lines = new string[3];
Dean@378
   286
                lines[0] = pEp.Properties.Resources.PrivacyStatus_TrustwordsPartnerShortPartner + partnerFpr;
Dean@378
   287
                lines[2] = pEp.Properties.Resources.PrivacyStatus_TrustwordsPartnerShortMyself + ownFpr;
Dean@155
   288
                state.Fingerprint = lines;
vb@133
   289
            }
vb@133
   290
            else
vb@133
   291
            {
Dean@155
   292
                lines = new string[3];
Dean@378
   293
                lines[0] = pEp.Properties.Resources.PrivacyStatus_TrustwordsOwnShortMyself + ownFpr;
Dean@378
   294
                lines[2] = pEp.Properties.Resources.PrivacyStatus_TrustwordsOwnShortPartner + partnerFpr;
Dean@155
   295
                state.Fingerprint = lines;
vb@133
   296
            }
vb@133
   297
Dean@155
   298
            // Create and show handshake dialog
Dean@155
   299
            handshakeDialog = new FormHandshake(state);
Dean@179
   300
            handshakeDialog.StartPosition = FormStartPosition.CenterScreen;
Dean@179
   301
            result = handshakeDialog.ShowDialog(this);
Dean@155
   302
Dean@356
   303
            this.ProcessDoHandshakeResult(result, identityPartner);
Dean@356
   304
Dean@356
   305
            return;
Dean@356
   306
        }
Dean@356
   307
Dean@356
   308
        /// <summary>
Dean@356
   309
        /// Processes the result of the do handshake dialog after a user makes a selection.
Dean@356
   310
        /// </summary>
Dean@356
   311
        /// <param name="result">The result of the handshake dialog selection.</param>
Dean@356
   312
        /// <param name="partner">The identity of the partner to handshake with.</param>
Dean@356
   313
        private void ProcessDoHandshakeResult(DialogResult result,
Dean@384
   314
                                              PEPIdentity partner)
Dean@356
   315
        {
Dean@384
   316
            pEp_identity_s identityPartner = ThisAddIn.pEp.update_identity(partner.ToCOMType());
vb@133
   317
vb@133
   318
            switch (result)
vb@133
   319
            {
vb@133
   320
                case DialogResult.Yes:
Dean@279
   321
                    {
Dean@290
   322
                        // Check if key was previously compromised -- warning for this must be displayed earlier
Dean@281
   323
                        if (identityPartner.comm_type == _pEp_comm_type.pEp_ct_compromized)
Dean@281
   324
                        {
Dean@281
   325
                            ThisAddIn.pEp.key_reset_trust(ref identityPartner);
Dean@281
   326
                        }
Dean@281
   327
Dean@279
   328
                        identityPartner = ThisAddIn.pEp.trust_personal_key(ref identityPartner);
Dean@293
   329
                        this.associatedMailItem.ColorRating = _pEp_color.pEp_rating_trusted;
Dean@279
   330
Dean@279
   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
                        break;
Dean@279
   340
                    }
vb@133
   341
                case DialogResult.No:
Dean@279
   342
                    {
Dean@279
   343
                        ThisAddIn.pEp.key_compromized(ref identityPartner);
Dean@279
   344
                        identityPartner.comm_type = _pEp_comm_type.pEp_ct_compromized;
Dean@279
   345
                        identityPartner = ThisAddIn.pEp.update_identity(identityPartner);
Dean@293
   346
                        this.associatedMailItem.ColorRating = _pEp_color.pEp_rating_red;
Dean@279
   347
Dean@279
   348
                        this.UpdateUIFromMailItem();
Dean@279
   349
Dean@279
   350
                        // Update the manager form state
Dean@279
   351
                        if (this.managerForm != null)
Dean@279
   352
                        {
Dean@279
   353
                            this.managerForm.CopyStateToUI(this.GetManagerState());
Dean@279
   354
                        }
Dean@279
   355
Dean@279
   356
                        break;
Dean@279
   357
                    }
vb@133
   358
            }
Dean@279
   359
Dean@279
   360
            return;
vb@133
   361
        }
vb@133
   362
Dean@179
   363
        /// <summary>
Dean@179
   364
        /// Reverses any past handshake confirmation by unconfirming the given identity partner.
Dean@179
   365
        /// </summary>
Dean@384
   366
        /// <param name="partner">The identity of the partner to unconfirm.</param>
Dean@384
   367
        private void UndoHandshake(PEPIdentity partner)
vb@133
   368
        {
Dean@384
   369
            pEp_identity_s identityPartner = ThisAddIn.pEp.update_identity(partner.ToCOMType());
Dean@280
   370
            ThisAddIn.pEp.key_reset_trust(ref identityPartner);
Dean@279
   371
Dean@179
   372
            this.UpdateUIFromMailItem();
Dean@279
   373
Dean@279
   374
            // Update the manager form state
Dean@279
   375
            if (this.managerForm != null)
Dean@279
   376
            {
Dean@279
   377
                this.managerForm.CopyStateToUI(this.GetManagerState());
Dean@279
   378
            }
Dean@279
   379
Dean@279
   380
            return;
vb@133
   381
        }
vb@133
   382
Dean@161
   383
        /// <summary>
Dean@363
   384
        /// Calculates both the short and full trustwords between the given personal identity and the identity partner.
Dean@363
   385
        /// </summary>
Dean@363
   386
        /// <param name="myIdentity">The personal identity.</param>
Dean@363
   387
        /// <param name="partnerIdentity">The identity of the partner.</param>
Dean@363
   388
        /// <param name="trustwordsShort">The short version of trustwords.</param>
Dean@363
   389
        /// <param name="trustwordsFull">The full/long version of trustwords.</param>
Dean@384
   390
        private void CalcTrustwords(PEPIdentity myIdentity,
Dean@384
   391
                                    PEPIdentity partnerIdentity,
Dean@363
   392
                                    out string trustwordsShort,
Dean@363
   393
                                    out string trustwordsFull)
Dean@363
   394
        {
Dean@363
   395
            string myShort;
Dean@363
   396
            string myLong;
Dean@363
   397
            string partnerShort;
Dean@363
   398
            string partnerLong;
Dean@363
   399
Dean@384
   400
            myShort = ThisAddIn.pEp.trustwords(myIdentity.Fingerprint, max_words: 5).ToLower();
Dean@384
   401
            myLong = ThisAddIn.pEp.trustwords(myIdentity.Fingerprint).ToLower();
Dean@384
   402
            partnerShort = ThisAddIn.pEp.trustwords(partnerIdentity.Fingerprint, max_words: 5).ToLower();
Dean@384
   403
            partnerLong = ThisAddIn.pEp.trustwords(partnerIdentity.Fingerprint).ToLower();
Dean@363
   404
Dean@384
   405
            if (myIdentity.Fingerprint.CompareTo(partnerIdentity.Fingerprint) > 0)
Dean@363
   406
            {
Dean@363
   407
                trustwordsShort = partnerShort + myShort;
Dean@363
   408
                trustwordsFull = partnerLong + myLong;
Dean@363
   409
            }
Dean@363
   410
            else
Dean@363
   411
            {
Dean@363
   412
                trustwordsShort = myShort + " " + partnerShort;
Dean@363
   413
                trustwordsFull = myLong + " " + partnerLong;
Dean@363
   414
            }
Dean@363
   415
Dean@363
   416
            return;
Dean@363
   417
        }
Dean@363
   418
Dean@363
   419
        /// <summary>
Dean@161
   420
        /// Formats the given text string as separated 4-character groups.
Dean@161
   421
        /// Example: 49422235FC99585B891C --> 4942 2235 FC99 585B 891C
Dean@161
   422
        /// </summary>
Dean@161
   423
        /// <param name="text">The text to format in 4-character groups.</param>
Dean@161
   424
        /// <returns>The re-formatted string.</returns>
Dean@161
   425
        private string ToQuadruple(string text)
Dean@159
   426
        {
Dean@159
   427
            List<string> result = new List<string>();
Dean@159
   428
Dean@161
   429
            if (text != null)
Dean@159
   430
            {
Dean@161
   431
                for (int i = 0; i < text.Length; i += 4)
Dean@159
   432
                {
Dean@161
   433
                    try
Dean@161
   434
                    {
Dean@161
   435
                        result.Add(text.Substring(i, 4));
Dean@161
   436
                    }
Dean@161
   437
                    catch (ArgumentOutOfRangeException)
Dean@161
   438
                    {
Dean@161
   439
                        result.Add(text.Substring(i));
Dean@161
   440
                        break;
Dean@161
   441
                    }
Dean@159
   442
                }
Dean@159
   443
            }
Dean@159
   444
Dean@159
   445
            return String.Join(" ", result);
Dean@159
   446
        }
markus@201
   447
Dean@196
   448
        /// <summary>
Dean@196
   449
        /// Makes the unencrypted preview form.
Dean@196
   450
        /// This gets the active form region then fills it's content.
Dean@196
   451
        /// </summary>
Dean@198
   452
        /// <returns>True if successful, otherwise false.</returns>
Dean@198
   453
        private bool MakePreview()
Dean@159
   454
        {
Dean@198
   455
            bool success = true;
Dean@198
   456
            bool isSuccessful;
Dean@198
   457
            byte[] rtfBody;
Dean@198
   458
            string subject;
Dean@159
   459
            WindowFormRegionCollection formRegions;
markus@201
   460
Dean@198
   461
            formRegions = Globals.FormRegions[Globals.ThisAddIn.Application.ActiveExplorer()];
Dean@159
   462
Dean@198
   463
            if ((formRegions != null) &&
Dean@198
   464
                (formRegions.FormRegionPreviewUnencrypted != null) &&
Dean@198
   465
                (formRegions.FormRegionPreviewUnencrypted.Visible))
Dean@198
   466
            {
Dean@198
   467
                // Attempt to the the RTF body
Dean@198
   468
                isSuccessful = this.associatedMailItem.MirrorTryGetRTFBody(out rtfBody);
Dean@198
   469
                if (isSuccessful == false)
Dean@198
   470
                {
Dean@198
   471
                    return (false);
Dean@198
   472
                }
Dean@159
   473
Dean@198
   474
                // Attempt to get the subject
Dean@198
   475
                isSuccessful = this.associatedMailItem.MirrorTryGetSubject(out subject);
Dean@198
   476
                if (isSuccessful == false)
Dean@159
   477
                {
Dean@198
   478
                    return (false);
Dean@159
   479
                }
Dean@196
   480
Dean@198
   481
                // Add data to the form
markus@201
   482
                formRegions.FormRegionPreviewUnencrypted.RichTextBoxPreview.Rtf = System.Text.Encoding.ASCII.GetString(rtfBody, 0, rtfBody.Length);
markus@201
   483
                formRegions.FormRegionPreviewUnencrypted.TextBoxSubject.Text = subject;
Dean@412
   484
                formRegions.FormRegionPreviewUnencrypted.TextBoxFrom.Text = this.associatedMailItem.From.Username + " <" + this.associatedMailItem.From.Address + ">";
Dean@198
   485
            }
Dean@198
   486
            else
Dean@198
   487
            {
Dean@198
   488
                success = false;
Dean@198
   489
            }
Dean@198
   490
Dean@198
   491
            return (success);
Dean@159
   492
        }
markus@201
   493
Dean@179
   494
        /// <summary>
Dean@279
   495
        /// Builds a new manager form state using this encryption state/mail item current state.
Dean@279
   496
        /// </summary>
Dean@279
   497
        /// <returns>A new manager form state.</returns>
Dean@313
   498
        private FormManagePrivacyStatus.State GetManagerState()
Dean@279
   499
        {
Dean@364
   500
            string trustwordsShort;
Dean@364
   501
            string trustwordsFull;
Dean@384
   502
            List<PEPIdentity> identities;
Dean@384
   503
            PEPIdentity myIdentity;
Dean@384
   504
            PEPIdentity incomingIdent;
Dean@259
   505
            _pEp_color identityPartnerColor;
Dean@259
   506
            SelectionItem item;
Dean@312
   507
            PrivacyState state = this.CopyUIToState();
Dean@313
   508
            FormManagePrivacyStatus.State managerState = new FormManagePrivacyStatus.State();
Dean@259
   509
Dean@332
   510
            // Resolve all recipients -- this ensures the identities list is correctly populated
Dean@332
   511
            this.associatedMailItem.ResolveAllRecipients();
Dean@332
   512
Dean@279
   513
            managerState.VisualState = state.Copy();
Dean@279
   514
            managerState.IsIncoming = this.associatedMailItem.IsIncoming;
Dean@259
   515
Dean@259
   516
            if (this.associatedMailItem.IsIncoming)
Dean@259
   517
            {
Dean@384
   518
                identities = new List<PEPIdentity>();
Dean@259
   519
Dean@259
   520
                // Add only one identity
Dean@384
   521
                incomingIdent = new PEPIdentity();
Dean@412
   522
                incomingIdent.Address = this.associatedMailItem.From.Address;
Dean@412
   523
                incomingIdent.Username = this.associatedMailItem.From.Username;
Dean@415
   524
                incomingIdent.UserID = Globals.ThisAddIn.GetUserID(incomingIdent.Address, incomingIdent.Username);
Dean@259
   525
Dean@384
   526
                identities.Add(incomingIdent);
Dean@259
   527
            }
Dean@259
   528
            else
Dean@259
   529
            {
Dean@395
   530
                identities = this.associatedMailItem.Recipients;
Dean@259
   531
            }
Dean@259
   532
Dean@366
   533
            // Get my identity
Dean@384
   534
            myIdentity = PEPIdentity.GetMyIdentity(this.associatedMailItem);
Dean@366
   535
Dean@279
   536
            // Add identities
Dean@394
   537
            identities = PEPIdentity.ToFlatList(identities);
Dean@279
   538
            managerState.Identities.Clear();
Dean@384
   539
            foreach (PEPIdentity ident in identities)
Dean@259
   540
            {
Dean@384
   541
                PEPIdentity partnerIdentity = new PEPIdentity(ThisAddIn.pEp.update_identity(ident.ToCOMType()));
Dean@384
   542
                identityPartnerColor = ThisAddIn.pEp.identity_color(partnerIdentity.ToCOMType());
Dean@259
   543
Dean@259
   544
                item = new SelectionItem();
Dean@384
   545
                item.TextLine1 = partnerIdentity.Username;
Dean@384
   546
                item.TextLine2 = partnerIdentity.Address;
Dean@259
   547
Dean@364
   548
                // Calculate trustwords
Dean@366
   549
                this.CalcTrustwords(myIdentity,
Dean@366
   550
                                    partnerIdentity,
Dean@364
   551
                                    out trustwordsShort,
Dean@364
   552
                                    out trustwordsFull);
Dean@364
   553
Dean@332
   554
                // Don't show both the user name and address if they are the same
Dean@332
   555
                if ((item.TextLine1 != null) &&
Dean@332
   556
                    (item.TextLine2 != null) &&
Dean@332
   557
                    (item.TextLine1 == item.TextLine2))
Dean@332
   558
                {
Dean@332
   559
                    item.IsTwoTextLinesVisible = false;
Dean@332
   560
                }
Dean@332
   561
Dean@259
   562
                // Set image
Dean@359
   563
                switch (PrivacyState.ConvertRatingToPrivacyColor(identityPartnerColor))
Dean@259
   564
                {
Dean@359
   565
                    case PrivacyState.PrivacyColor.Green:
Dean@433
   566
                        item.ItemImage = pEp.Properties.Resources.ImagePrivacyStatusGreen;
Dean@259
   567
                        break;
Dean@359
   568
                    case PrivacyState.PrivacyColor.Yellow:
Dean@433
   569
                        item.ItemImage = pEp.Properties.Resources.ImagePrivacyStatusYellow;
Dean@259
   570
                        break;
Dean@359
   571
                    case PrivacyState.PrivacyColor.Red:
Dean@433
   572
                        item.ItemImage = pEp.Properties.Resources.ImagePrivacyStatusRed;
Dean@259
   573
                        break;
Dean@430
   574
                    case PrivacyState.PrivacyColor.NoColor:
Dean@434
   575
                        item.ItemImage = pEp.Properties.Resources.ImagePrivacyStatusNoColor;
Dean@359
   576
                        break;
Dean@259
   577
                    default:
Dean@259
   578
                        item.ItemImage = null;
Dean@259
   579
                        break;
Dean@259
   580
                }
Dean@259
   581
Dean@259
   582
                // Set button
Dean@384
   583
                if (myIdentity.Equals(partnerIdentity) == false)
Dean@259
   584
                {
Dean@384
   585
                    if (partnerIdentity.CommunicationType >= _pEp_comm_type.pEp_ct_confirmed_encryption)
Dean@366
   586
                    {
Dean@366
   587
                        // Undo handshake
Dean@366
   588
                        item.TextButton = pEp.Properties.Resources.PrivacyStatus_StopTrusting;
Dean@366
   589
                        item.IsButtonVisible = true;
Dean@366
   590
                        item.ButtonOnClick = (x, y) => { this.UndoHandshake(partnerIdentity); };
Dean@356
   591
Dean@366
   592
                        item.IsExpandable = false;
Dean@366
   593
                    }
Dean@384
   594
                    else if (partnerIdentity.CommunicationType >= _pEp_comm_type.pEp_ct_unconfirmed_encryption &&
Dean@384
   595
                             partnerIdentity.CommunicationType < _pEp_comm_type.pEp_ct_confirmed_encryption)
Dean@366
   596
                    {
Dean@366
   597
                        // Do handshake
Dean@366
   598
                        item.TextButton = pEp.Properties.Resources.PrivacyStatus_HandshakeAdvanced;
Dean@366
   599
                        item.IsButtonVisible = true;
Dean@366
   600
                        item.ButtonOnClick = (x, y) => { this.DoHandshake(partnerIdentity); };
Dean@356
   601
Dean@366
   602
                        item.IsExpandable = true;
Dean@366
   603
                        item.ExpandedText = Properties.Resources.PrivacyStatus_TrustwordDesc + "\n\n" +
Dean@366
   604
                                            trustwordsShort;
Dean@366
   605
                        item.ExpandedButton1Text = Properties.Resources.PrivacyStatus_SelectTrust;
Dean@366
   606
                        item.ExpandedButton2Text = Properties.Resources.PrivacyStatus_SelectMistrust;
Dean@366
   607
                        item.ExpandedButton1OnClick = (x, y) => { this.ProcessDoHandshakeResult(DialogResult.Yes, partnerIdentity); };
Dean@366
   608
                        item.ExpandedButton2OnClick = (x, y) => { this.ProcessDoHandshakeResult(DialogResult.No, partnerIdentity); };
Dean@366
   609
                    }
Dean@384
   610
                    else if (partnerIdentity.CommunicationType == _pEp_comm_type.pEp_ct_compromized)
Dean@366
   611
                    {
Dean@366
   612
                        // Redo handshake with confirmation
Dean@417
   613
                        item.TextButton = pEp.Properties.Resources.PrivacyStatus_Handshake;
Dean@366
   614
                        item.IsButtonVisible = true;
Dean@417
   615
                        item.ButtonOnClick = (x, y) => { this.DoHandshakeForCompromisedKey(partnerIdentity); };
Dean@366
   616
                    }
Dean@366
   617
                    else
Dean@366
   618
                    {
Dean@366
   619
                        item.IsButtonVisible = false;
Dean@366
   620
                    }
Dean@281
   621
                }
Dean@259
   622
                else
Dean@259
   623
                {
Dean@259
   624
                    item.IsButtonVisible = false;
Dean@259
   625
                }
Dean@259
   626
Dean@279
   627
                managerState.Identities.Add(item);
Dean@259
   628
            }
Dean@259
   629
Dean@375
   630
            // Attempt to select the first identity requiring a handshake
Dean@375
   631
            for (int i = 0; i < managerState.Identities.Count; i++)
Dean@375
   632
            {
Dean@375
   633
                // Determine if it's a handshake identity by simply seeing if it's expandable
Dean@375
   634
                if (managerState.Identities[i].IsExpandable)
Dean@375
   635
                {
Dean@375
   636
                    managerState.SelectedIdentityIndex = i;
Dean@375
   637
                    break;
Dean@375
   638
                }
Dean@375
   639
            }
Dean@375
   640
Dean@279
   641
            return (managerState);
Dean@279
   642
        }
Dean@279
   643
Dean@279
   644
        /// <summary>
Dean@279
   645
        /// Builds the latest state of the encryption status manager then shows the UI.
Dean@279
   646
        /// </summary>
Dean@279
   647
        private void BuildAndShowManager()
Dean@279
   648
        {
Dean@279
   649
            DialogResult result;
Dean@313
   650
            FormManagePrivacyStatus form;
Dean@313
   651
            FormManagePrivacyStatus.State stateOut;
Dean@279
   652
Dean@259
   653
            // Show the form
Dean@313
   654
            form = new FormManagePrivacyStatus();
Dean@259
   655
            form.StartPosition = FormStartPosition.CenterScreen;
Dean@259
   656
Dean@259
   657
            this.managerForm = form;
Dean@279
   658
            result = form.ShowDialog(this.ParentForm, this.GetManagerState(), out stateOut); // Must show as dialog to block code
Dean@259
   659
Dean@259
   660
            return;
Dean@259
   661
        }
Dean@259
   662
Dean@259
   663
        /// <summary>
Dean@179
   664
        /// Updates the status of the UI state based on the associated mail item.
Dean@179
   665
        /// Any previous state changes in the UI are preserved.
Dean@179
   666
        /// </summary>
Dean@179
   667
        private void UpdateUIFromMailItem()
Dean@159
   668
        {
Dean@312
   669
            PrivacyState state;
Dean@342
   670
            bool? sendUnencryptedProperty;
Dean@159
   671
Dean@173
   672
            if (this.associatedMailItem != null)
Dean@159
   673
            {
Dean@342
   674
                sendUnencryptedProperty = this.associatedMailItem.SendUnencrypted;
Dean@342
   675
Dean@342
   676
                if ((sendUnencryptedProperty != null) &&
Dean@342
   677
                    ((bool)sendUnencryptedProperty == true))
Dean@159
   678
                {
Dean@342
   679
                    // Force unencrypted
Dean@312
   680
                    state = new PrivacyState(_pEp_color.pEp_rating_unencrypted);
Dean@159
   681
                }
Dean@159
   682
                else
Dean@159
   683
                {
Dean@312
   684
                    state = new PrivacyState(this.associatedMailItem.ColorRating);
Dean@159
   685
                }
Dean@179
   686
Dean@179
   687
                this.CopyStateToUI(state);
Dean@159
   688
            }
Dean@159
   689
Dean@179
   690
            return;
Dean@159
   691
        }
Dean@159
   692
Dean@172
   693
        #region StateMethods
Dean@172
   694
Dean@172
   695
        /// <summary>
Dean@172
   696
        /// Connects or disconnects all control events from the UI.
Dean@172
   697
        /// </summary>
Dean@172
   698
        /// <param name="connect">True to connect events, false to disconnect.</param>
Dean@172
   699
        private void ConnectEvents(bool connect)
Dean@172
   700
        {
Dean@172
   701
            // Connect events only if not already connected
markus@201
   702
            if ((connect == true) &&
Dean@172
   703
                (this.eventsAreConnected == false))
Dean@172
   704
            {
Dean@312
   705
                this.ButtonPrivacyStatus.Click += this.ButtonPrivacyStatus_Click;
Dean@312
   706
                this.ButtonPrivacyStatus.MouseUp += this.ButtonPrivacyStatus_MouseUp;
Dean@179
   707
Dean@172
   708
                this.eventsAreConnected = true;
Dean@172
   709
            }
Dean@172
   710
            // Always attempt to disconnect
Dean@172
   711
            else if (connect == false)
Dean@172
   712
            {
Dean@312
   713
                this.ButtonPrivacyStatus.Click -= this.ButtonPrivacyStatus_Click;
Dean@312
   714
                this.ButtonPrivacyStatus.MouseUp -= this.ButtonPrivacyStatus_MouseUp;
Dean@179
   715
Dean@172
   716
                this.eventsAreConnected = false;
Dean@172
   717
            }
Dean@172
   718
Dean@172
   719
            return;
Dean@172
   720
        }
Dean@172
   721
Dean@172
   722
        /// <summary>
Dean@172
   723
        /// Refreshes the UI by reloading the state.
Dean@172
   724
        /// </summary>
Dean@172
   725
        private void RefreshUI()
Dean@172
   726
        {
Dean@172
   727
            this.CopyStateToUI(this.CopyUIToState());
Dean@172
   728
            return;
Dean@172
   729
        }
Dean@172
   730
Dean@172
   731
        /// <summary>
Dean@172
   732
        /// Copies the given state to the UI.
Dean@172
   733
        /// Events are turned off until the process is complete.
Dean@172
   734
        /// </summary>
Dean@172
   735
        /// <param name="state">The state to set to the UI.</param>
Dean@312
   736
        private void CopyStateToUI(PrivacyState state)
Dean@172
   737
        {
Dean@172
   738
            this.ConnectEvents(false);
markus@201
   739
Dean@172
   740
            ///////////////////////////////////////////////////////////
Dean@172
   741
            // Set UI data
Dean@172
   742
            ///////////////////////////////////////////////////////////
Dean@172
   743
Dean@179
   744
            // Save UI state maintained outside of controls
Dean@312
   745
            this.stateUIColorRating = state.PrivacyStatus;
Dean@179
   746
Dean@312
   747
            this.ButtonPrivacyStatus.BackColor = state.BackgroundColor;
Dean@312
   748
            this.ButtonPrivacyStatus.ForeColor = state.ForegroundColor;
Dean@312
   749
            this.ButtonPrivacyStatus.Text = state.ShortText;
Dean@172
   750
Dean@172
   751
            this.ConnectEvents(true);
Dean@172
   752
Dean@172
   753
            return;
Dean@172
   754
        }
Dean@172
   755
Dean@172
   756
        /// <summary>
Dean@172
   757
        /// Copies the UI to a new state.
Dean@172
   758
        /// </summary>
Dean@172
   759
        /// <returns>The state of the UI.</returns>
Dean@312
   760
        private PrivacyState CopyUIToState()
Dean@172
   761
        {
Dean@312
   762
            return (new PrivacyState(this.stateUIColorRating));
Dean@172
   763
        }
Dean@172
   764
Dean@172
   765
        #endregion
Dean@172
   766
Dean@159
   767
        /**************************************************************
Dean@159
   768
         * 
Dean@159
   769
         * Event Handling
Dean@159
   770
         * 
Dean@159
   771
         *************************************************************/
Dean@159
   772
Dean@159
   773
        /// <summary>
Dean@159
   774
        /// Event handler that is called before the form region is displayed.
Dean@159
   775
        /// </summary>
Dean@312
   776
        private void FormRegionPrivacyStatus_FormRegionShowing(object sender, System.EventArgs e)
Dean@159
   777
        {
Dean@173
   778
            this.SetAssociatedMailItem();
Dean@173
   779
Dean@438
   780
            // Set background color
Dean@438
   781
            if (this.IsWithinInspector())
Dean@438
   782
            {
Dean@438
   783
                this.BackColor = SystemColors.ButtonFace;
Dean@438
   784
            }
Dean@438
   785
            else
Dean@438
   786
            {
Dean@438
   787
                this.BackColor = SystemColors.Window;
Dean@438
   788
            }
Dean@438
   789
Dean@199
   790
            // Connect cryptable mail item events
Dean@189
   791
            if (this.associatedMailItem != null)
Dean@159
   792
            {
Dean@159
   793
                try
Dean@159
   794
                {
Dean@189
   795
                    this.associatedMailItem.PropertyChange += MailItem_PropertyChange;
Dean@179
   796
Dean@189
   797
                    if (this.associatedMailItem.IsInEncryptedStore &&
Dean@198
   798
                        this.associatedMailItem.IsPGPEncrypted)
Dean@159
   799
                    {
Dean@189
   800
                        this.associatedMailItem.Open += MailItem_Open;
Dean@159
   801
                    }
Dean@159
   802
                }
Dean@159
   803
                catch { }
Dean@179
   804
            }
Dean@159
   805
Dean@179
   806
            // Set the default UI state
Dean@312
   807
            this.CopyStateToUI(new PrivacyState());
Dean@179
   808
Dean@199
   809
            // Call the timer tick method manually to refresh data
Dean@199
   810
            this.TimerRefresh_Tick(null, new EventArgs());
Dean@179
   811
Dean@179
   812
            return;
Dean@159
   813
        }
Dean@159
   814
Dean@159
   815
        /// <summary>
Dean@159
   816
        /// Event handler for when the form region is closed.
Dean@159
   817
        /// </summary>
Dean@312
   818
        private void FormRegionPrivacyStatus_FormRegionClosed(object sender, System.EventArgs e)
Dean@159
   819
        {
Dean@199
   820
            // Disconnect cryptable mail item events
Dean@190
   821
            if (this.associatedMailItem != null)
Dean@179
   822
            {
Dean@179
   823
                try
Dean@179
   824
                {
Dean@190
   825
                    this.associatedMailItem.PropertyChange -= MailItem_PropertyChange;
markus@201
   826
                    this.associatedMailItem.Open -= MailItem_Open;
Dean@179
   827
                }
Dean@179
   828
                catch { }
Dean@179
   829
            }
Dean@179
   830
Dean@179
   831
            return;
Dean@179
   832
        }
Dean@179
   833
Dean@179
   834
        /// <summary>
Dean@179
   835
        /// Event handler called after the refresh timer has elapsed.
Dean@179
   836
        /// </summary>
Dean@179
   837
        private void TimerRefresh_Tick(object sender, EventArgs e)
Dean@179
   838
        {
Dean@199
   839
            bool tryAgain = false;
Dean@199
   840
            bool isSuccessful;
Dean@179
   841
            this.TimerRefresh.Enabled = false; // Only once
markus@201
   842
Dean@179
   843
            // Ensure the tick method is not called more than once
Dean@179
   844
            if (refreshOngoing == false)
Dean@179
   845
            {
Dean@179
   846
                this.refreshOngoing = true;
Dean@179
   847
Dean@179
   848
                if (this.associatedMailItem != null)
Dean@179
   849
                {
Dean@179
   850
                    if (this.associatedMailItem.DownloadState == Outlook.OlDownloadState.olFullItem)
Dean@179
   851
                    {
Dean@179
   852
                        this.UpdateUIFromMailItem();
Dean@199
   853
Dean@199
   854
                        /* Create the unencrypted preview if the mail item is encrypted and
Dean@199
   855
                         * it is in an encrypted (untrusted) store
Dean@199
   856
                         * 
Dean@312
   857
                         * This is done here because FormRegionPrivacyStatus has the cryptable mail item and
Dean@199
   858
                         * it also is initialized after FormRegionPreviewUnencrypted.
Dean@199
   859
                         */
Dean@199
   860
                        if (this.associatedMailItem.IsInEncryptedStore &&
Dean@199
   861
                            this.associatedMailItem.IsPGPEncrypted)
Dean@199
   862
                        {
Dean@199
   863
                            isSuccessful = this.MakePreview();
Dean@199
   864
Dean@199
   865
                            if (isSuccessful == false)
Dean@199
   866
                            {
Dean@199
   867
                                tryAgain = true;
Dean@199
   868
                            }
Dean@199
   869
                        }
Dean@179
   870
                    }
Dean@179
   871
                    else
Dean@179
   872
                    {
Dean@179
   873
                        this.associatedMailItem.MarkForDownload = Outlook.OlRemoteStatus.olMarkedForDownload;
Dean@199
   874
                        tryAgain = true;
Dean@179
   875
                    }
Dean@179
   876
                }
Dean@179
   877
Dean@199
   878
                // Set the timer to refresh again later automatically
Dean@199
   879
                if (tryAgain)
Dean@199
   880
                {
Dean@199
   881
                    this.TimerRefresh.Interval = 100;
Dean@199
   882
                    this.TimerRefresh.Enabled = true;
Dean@199
   883
                }
Dean@199
   884
Dean@179
   885
                this.refreshOngoing = false;
Dean@179
   886
            }
Dean@175
   887
Dean@159
   888
            return;
Dean@159
   889
        }
Dean@159
   890
Dean@169
   891
        /// <summary>
Dean@169
   892
        /// Event handler for when a mail item is being opened in an inspector.
Dean@180
   893
        /// See: https://msdn.microsoft.com/en-us/library/office/ff865989.aspx
Dean@169
   894
        /// </summary>
Dean@169
   895
        /// <param name="cancel">Whether to cancel the event: Value is False when the event occurs. 
Dean@169
   896
        /// If the event procedure sets this argument to True, the open operation is not completed 
Dean@169
   897
        /// and the inspector is not displayed.</param>
Dean@169
   898
        private void MailItem_Open(ref bool cancel)
Dean@159
   899
        {
Dean@173
   900
            if (this.associatedMailItem != null && this.associatedMailItem.MirrorDisplay())
Dean@169
   901
            {
Dean@169
   902
                cancel = true;
Dean@169
   903
            }
Dean@169
   904
Dean@169
   905
            return;
Dean@159
   906
        }
Dean@159
   907
Dean@169
   908
        /// <summary>
Dean@169
   909
        /// Event handler for when a mail item property is changed.
Dean@169
   910
        /// See: https://msdn.microsoft.com/en-us/library/office/ff866739.aspx
Dean@169
   911
        /// </summary>
Dean@169
   912
        /// <param name="propertyName">The name of the property that was changed.</param>
Dean@169
   913
        private void MailItem_PropertyChange(string propertyName)
Dean@159
   914
        {
Dean@179
   915
            switch (propertyName.ToUpper())
Dean@159
   916
            {
Dean@179
   917
                case "TO":
Dean@179
   918
                    // Start the refresh timer
Dean@179
   919
                    this.TimerRefresh.Enabled = true;
Dean@159
   920
                    break;
Dean@159
   921
                // Outlook bug: there are always both events, so one is enough
Dean@159
   922
                //case "CC":
Dean@179
   923
                //    // Start the refresh timer
Dean@179
   924
                //    this.TimerRefresh.Enabled = true;
Dean@159
   925
                //    break;
Dean@159
   926
            }
Dean@169
   927
Dean@169
   928
            return;
Dean@159
   929
        }
Dean@159
   930
Dean@179
   931
        /// <summary>
Dean@312
   932
        /// Event handler for when the privacy status button is clicked.
Dean@179
   933
        /// </summary>
Dean@312
   934
        private void ButtonPrivacyStatus_Click(object sender, EventArgs e)
vb@133
   935
        {
Dean@310
   936
            this.BuildAndShowManager();
Dean@179
   937
            return;
vb@133
   938
        }
vb@133
   939
Dean@179
   940
        /// <summary>
Dean@312
   941
        /// Event handler for when a mouse button is released over the privacy status button.
Dean@179
   942
        /// </summary>
Dean@312
   943
        private void ButtonPrivacyStatus_MouseUp(object sender, MouseEventArgs e)
vb@133
   944
        {
Dean@179
   945
            return;
vb@133
   946
        }
vb@133
   947
    }
vb@133
   948
}