PEPMessage.cs
author Markus Schaber <markus@pep-security.net>
Fri, 07 Oct 2016 22:14:52 +0200
changeset 1356 05735f41b1a2
parent 1354 d15f55758813
child 1359 ead02dab7392
permissions -rw-r--r--
OUT-117: Catching up rename-refactorings from COM-24, COM-29, COM-30, COM-31
Dean@384
     1
using Microsoft.Win32;
Dean@384
     2
using pEpCOMServerAdapterLib;
Dean@384
     3
using System;
Dean@384
     4
using System.Collections.Generic;
Dean@1094
     5
using System.ComponentModel;
Dean@384
     6
using System.Runtime.InteropServices;
Dean@1238
     7
using System.Text;
Dean@384
     8
using Outlook = Microsoft.Office.Interop.Outlook;
Dean@384
     9
Dean@384
    10
namespace pEp
Dean@384
    11
{
Dean@384
    12
    /// <summary>
markus@1337
    13
    /// Class for a completely in-memory message based on the pEp engine TextMessage.
Dean@384
    14
    /// </summary>
Dean@1094
    15
    internal class PEPMessage : INotifyPropertyChanged,
Dean@1238
    16
                                IEquatable<PEPMessage>,
Dean@1094
    17
                                Interfaces.ICopy<PEPMessage>,
Dean@1094
    18
                                Interfaces.IReset
Dean@384
    19
    {
Dean@1094
    20
        /// <summary>
Dean@1094
    21
        /// Event raised when a property is changed on a component.
Dean@1094
    22
        /// </summary>
Dean@1094
    23
        public event PropertyChangedEventHandler PropertyChanged;
Dean@1094
    24
Dean@1223
    25
        // pEp named MAPI properties
Dean@1212
    26
        public const string PR_X_ENC_STATUS_NAME           = "X-EncStatus";
Dean@1212
    27
        public const string PR_X_KEY_LIST_NAME             = "X-KeyList";
Dean@1212
    28
        public const string PR_X_PEP_NEVER_UNSECURE_NAME   = "X-pEp-Never-Unsecure";
Dean@1212
    29
        public const string PR_X_PEP_PROTOCOL_VERSION_NAME = "X-pEp-Version";
Dean@780
    30
Dean@1350
    31
        public static readonly MapiProperty.MapiProp XPidNameEncStatus          = new MapiProperty.MapiProp(PR_X_ENC_STATUS_NAME,           MapiProperty.PS_INTERNET_HEADERS);
Dean@1350
    32
        public static readonly MapiProperty.MapiProp XPidNameKeyList            = new MapiProperty.MapiProp(PR_X_KEY_LIST_NAME,             MapiProperty.PS_INTERNET_HEADERS);
Dean@1350
    33
        public static readonly MapiProperty.MapiProp XPidNamePEPNeverUnsecure   = new MapiProperty.MapiProp(PR_X_PEP_NEVER_UNSECURE_NAME,   MapiProperty.PS_INTERNET_HEADERS);
Dean@1350
    34
        public static readonly MapiProperty.MapiProp XPidNamePEPProtocolVersion = new MapiProperty.MapiProp(PR_X_PEP_PROTOCOL_VERSION_NAME, MapiProperty.PS_INTERNET_HEADERS);
Dean@1224
    35
Dean@384
    36
        private List<PEPAttachment> _Attachments;
Dean@1348
    37
        private List<PEPIdentity>   _Bcc;
Dean@1348
    38
        private List<PEPIdentity>   _Cc;
Dean@1348
    39
        private string              _ConversationId;
Dean@1113
    40
        private string              _ConversationIndex;
Dean@1113
    41
        private string              _ConversationTopic;
Dean@1340
    42
        private pEpMsgDirection     _Direction;
Dean@1094
    43
        private bool                _ForceUnencrypted;
Dean@384
    44
        private PEPIdentity         _From;
Dean@1348
    45
        private string              _Id;
Dean@1094
    46
        private string              _KeyList;
Dean@384
    47
        private List<string>        _Keywords;
Dean@384
    48
        private string              _LongMsg;
Dean@1354
    49
        private string              _LongMsgFormattedHtml;
Dean@1354
    50
        private string              _LongMsgFormattedRtf;
Dean@1104
    51
        private bool                _NeverUnsecure;
Dean@1212
    52
        private string              _PEPProtocolVersion;
Dean@1340
    53
        private pEpRating           _Rating;
Dean@721
    54
        private DateTime?           _ReceivedOn;
Dean@721
    55
        private DateTime?           _SentOn;
Dean@384
    56
        private string              _ShortMsg;
Dean@384
    57
        private List<PEPIdentity>   _To;
Dean@384
    58
Dean@384
    59
        /**************************************************************
Dean@384
    60
         * 
Dean@384
    61
         * Constructors
Dean@384
    62
         * 
Dean@384
    63
         *************************************************************/
Dean@384
    64
Dean@384
    65
        /// <summary>
Dean@384
    66
        /// Default constructor.
Dean@384
    67
        /// </summary>
Dean@384
    68
        public PEPMessage()
Dean@384
    69
        {
Dean@1094
    70
            this.Reset();
Dean@384
    71
        }
Dean@384
    72
Dean@384
    73
        /**************************************************************
Dean@384
    74
         * 
Dean@384
    75
         * Property Accessors
Dean@384
    76
         * 
Dean@384
    77
         *************************************************************/
Dean@384
    78
Dean@910
    79
        #region Property Accessors
Dean@910
    80
Dean@384
    81
        /// <summary>
Dean@384
    82
        /// Gets the list of attachements for this message.
Dean@1094
    83
        ///          MailItem : Corresponds to property 'Attachments' which contains 'Attachment'
Dean@1094
    84
        /// CryptableMailItem : not exposed
Dean@1348
    85
        ///       TextMessage : Corresponds to property 'Attachments' which contains 'Blob'
Dean@384
    86
        /// </summary>
Dean@384
    87
        public List<PEPAttachment> Attachments
Dean@384
    88
        {
Dean@384
    89
            get { return (this._Attachments); }
Dean@384
    90
        }
Dean@384
    91
Dean@384
    92
        /// <summary>
Dean@387
    93
        /// Gets the list of identities to be blind carbon copied on the message.
Dean@1094
    94
        ///          MailItem : Component of property 'Recipients' which contains 'Recipient'
Dean@1094
    95
        /// CryptableMailItem : not exposed directly (part of Recipients)
Dean@1348
    96
        ///       TextMessage : Corresponds to property 'Bcc'
Dean@384
    97
        /// </summary>
Dean@1348
    98
        public List<PEPIdentity> Bcc
Dean@384
    99
        {
Dean@1348
   100
            get { return (this._Bcc); }
Dean@384
   101
        }
Dean@384
   102
Dean@384
   103
        /// <summary>
Dean@384
   104
        /// Gets the list of identities to be carbon copied on the message.
Dean@1094
   105
        ///          MailItem : Component of property 'Recipients' which contains 'Recipient'
Dean@1094
   106
        /// CryptableMailItem : not exposed directly (part of Recipients)
Dean@1348
   107
        ///       TextMessage : Corresponds to property 'Cc'
Dean@384
   108
        /// </summary>
Dean@1348
   109
        public List<PEPIdentity> Cc
Dean@384
   110
        {
Dean@1348
   111
            get { return (this._Cc); }
Dean@384
   112
        }
Dean@384
   113
Dean@384
   114
        /// <summary>
Dean@1113
   115
        /// Gets or sets the conversation ID of the message.
Dean@1113
   116
        /// Outlook should normally manage this itself.
Dean@1113
   117
        ///          MailItem : Corresponds to property 'ConversationID' (Underlying MAPI property PidTagConversationId)
Dean@1113
   118
        /// CryptableMailItem : Corresponds to property 'ConversationID'
Dean@1340
   119
        ///       TextMessage : not supported
Dean@1113
   120
        /// </summary>
Dean@1348
   121
        public string ConversationId
Dean@1113
   122
        {
Dean@1348
   123
            get { return (this._ConversationId); }
Dean@1113
   124
            set
Dean@1113
   125
            {
Dean@1348
   126
                this._ConversationId = value;
Dean@1348
   127
                this.RaisePropertyChangedEvent(nameof(this.ConversationId));
Dean@1113
   128
            }
Dean@1113
   129
        }
Dean@1113
   130
Dean@1113
   131
        /// <summary>
Dean@1113
   132
        /// Gets or sets the conversation index of the message.
Dean@1113
   133
        /// Outlook should normally manage this itself.
Dean@1113
   134
        ///          MailItem : Corresponds to property 'ConversationIndex' (Underlying MAPI property PidTagConversationIndex)
Dean@1113
   135
        /// CryptableMailItem : Corresponds to property 'ConversationIndex'
Dean@1340
   136
        ///       TextMessage : not supported
Dean@1113
   137
        /// </summary>
Dean@1113
   138
        public string ConversationIndex
Dean@1113
   139
        {
Dean@1113
   140
            get { return (this._ConversationIndex); }
Dean@1113
   141
            set
Dean@1113
   142
            {
Dean@1113
   143
                this._ConversationIndex = value;
Dean@1242
   144
                this.RaisePropertyChangedEvent(nameof(this.ConversationIndex));
Dean@1113
   145
            }
Dean@1113
   146
        }
Dean@1113
   147
Dean@1113
   148
        /// <summary>
Dean@1113
   149
        /// Gets or sets the conversation index of the message.
Dean@1113
   150
        /// Outlook should normally manage this itself.
Dean@1113
   151
        ///          MailItem : Corresponds to property 'ConversationTopic' (Underlying MAPI property PidTagConversationTopic)
Dean@1113
   152
        /// CryptableMailItem : Corresponds to property 'ConversationTopic'
Dean@1340
   153
        ///       TextMessage : not supported
Dean@1113
   154
        /// </summary>
Dean@1113
   155
        public string ConversationTopic
Dean@1113
   156
        {
Dean@1113
   157
            get { return (this._ConversationTopic); }
Dean@1113
   158
            set
Dean@1113
   159
            {
Dean@1113
   160
                this._ConversationTopic = value;
Dean@1242
   161
                this.RaisePropertyChangedEvent(nameof(this.ConversationTopic));
Dean@1113
   162
            }
Dean@1113
   163
        }
Dean@1113
   164
Dean@1113
   165
        /// <summary>
Dean@721
   166
        /// Gets the date and time when the message was either sent or received.
Dean@1094
   167
        /// This corresponds with the SentOn and ReceivedOn properties defined separately.
Dean@691
   168
        /// </summary>
Dean@691
   169
        public DateTime? DateTimeSentOrReceived
Dean@691
   170
        {
Dean@721
   171
            get
Dean@721
   172
            {
markus@1337
   173
                if (this._Direction == pEpMsgDirection.pEpDirIncoming)
Dean@721
   174
                {
Dean@721
   175
                    return (this._ReceivedOn);
Dean@721
   176
                }
Dean@721
   177
                else
Dean@721
   178
                {
Dean@721
   179
                    return (this._SentOn);
Dean@721
   180
                }
Dean@721
   181
            }
Dean@691
   182
        }
Dean@691
   183
Dean@691
   184
        /// <summary>
Dean@384
   185
        /// Gets or sets the direction (incoming or outgoing) of the message.
Dean@1094
   186
        ///          MailItem : not supported (calculated from various properties)
Dean@1094
   187
        /// CryptableMailItem : Corresponds 1-to-1 with property 'IsIncoming'
Dean@1348
   188
        ///       TextMessage : Corresponds to property 'Dir'
Dean@384
   189
        /// </summary>
markus@1337
   190
        public pEpMsgDirection Direction
Dean@384
   191
        {
Dean@384
   192
            get { return (this._Direction); }
Dean@1094
   193
            set
Dean@1094
   194
            {
Dean@1094
   195
                this._Direction = value;
Dean@1242
   196
                this.RaisePropertyChangedEvent(nameof(this.Direction));
Dean@1094
   197
            }
Dean@1094
   198
        }
Dean@1094
   199
Dean@1094
   200
        /// <summary>
Dean@1094
   201
        /// Gets or sets the force unencrypted status.
Dean@1094
   202
        ///          MailItem : Corresponds to UserProperty USER_PROPERTY_KEY_FORCE_UNENCRYPTED
Dean@1094
   203
        /// CryptableMailItem : Corresponds to property 'ForceUnencryptedBool'
Dean@1340
   204
        ///       TextMessage : not supported
Dean@1094
   205
        /// </summary>
Dean@1094
   206
        public bool ForceUnencrypted
Dean@1094
   207
        {
Dean@1094
   208
            get { return (this._ForceUnencrypted); }
Dean@1094
   209
            set
Dean@1094
   210
            {
Dean@1094
   211
                this._ForceUnencrypted = value;
Dean@1242
   212
                this.RaisePropertyChangedEvent(nameof(this.ForceUnencrypted));
Dean@1094
   213
            }
Dean@384
   214
        }
Dean@384
   215
Dean@384
   216
        /// <summary>
Dean@384
   217
        /// Gets or sets the from identity of the message.
Dean@384
   218
        /// Warning: this value can be null.
Dean@1094
   219
        ///          MailItem : not supported (calculated from various properties)
Dean@1094
   220
        /// CryptableMailItem : Corresponds to property 'From'
Dean@1348
   221
        ///       TextMessage : Corresponds to property 'From'
Dean@384
   222
        /// </summary>
Dean@384
   223
        public PEPIdentity From
Dean@384
   224
        {
Dean@384
   225
            get { return (this._From); }
Dean@1094
   226
            set
Dean@1094
   227
            {
Dean@1094
   228
                this._From = value;
Dean@1242
   229
                this.RaisePropertyChangedEvent(nameof(this.From));
Dean@1094
   230
            }
Dean@384
   231
        }
Dean@384
   232
Dean@384
   233
        /// <summary>
Dean@384
   234
        /// Gets or sets the ID of the message.
Dean@384
   235
        /// Warning: this value can be null.
Dean@1095
   236
        ///          MailItem : Corresponds to MAPI property PidTagInternetMessageId
Dean@1095
   237
        /// CryptableMailItem : not supported
Dean@1348
   238
        ///       TextMessage : Corresponds to property 'Id'
Dean@384
   239
        /// </summary>
Dean@1348
   240
        public string Id
Dean@384
   241
        {
Dean@1348
   242
            get { return (this._Id); }
Dean@1094
   243
            set
Dean@1094
   244
            {
Dean@1348
   245
                this._Id = value;
Dean@1348
   246
                this.RaisePropertyChangedEvent(nameof(this.Id));
Dean@1094
   247
            }
Dean@384
   248
        }
Dean@384
   249
Dean@384
   250
        /// <summary>
Dean@1330
   251
        /// Gets whether this message is considered secure.
Dean@927
   252
        /// This will forward the call to the static method of the same purpose.
Dean@927
   253
        /// </summary>
Dean@1330
   254
        public bool IsSecure
Dean@927
   255
        {
Dean@1330
   256
            get { return (PEPMessage.GetIsSecure(this)); }
Dean@927
   257
        }
Dean@927
   258
Dean@927
   259
        /// <summary>
Dean@1224
   260
        /// Gets whether this message is secured using PGP/MIME format.
Dean@1224
   261
        /// This will forward the call to the static method of the same purpose.
Dean@1224
   262
        /// </summary>
Dean@1224
   263
        public bool IsPGPMIMEEncrypted
Dean@1224
   264
        {
Dean@1224
   265
            get { return (PEPMessage.GetIsPGPMIMEEncrypted(this)); }
Dean@1224
   266
        }
Dean@1224
   267
Dean@1224
   268
        /// <summary>
Dean@1094
   269
        /// Gets or sets the list of keys associated with this message.
Dean@1094
   270
        /// Commonly this contains the list of decryption keys.
Dean@1094
   271
        /// Warning: Since this is stored as a header field, care must be taken not to apply this to a MailItem on an 
Dean@1094
   272
        /// untrusted server or on an outgoing message.
Dean@1094
   273
        ///          MailItem : Corresponds to MAPI Property PR_X_KEY_LIST (Header field)
Dean@1118
   274
        /// CryptableMailItem : not supported
Dean@1340
   275
        ///       TextMessage : Corresponds with the key PR_X_KEY_LIST_NAME within 'opt_fields' array. 
Dean@1094
   276
        ///                     This is also an out parameter of the decrypt function.
Dean@1094
   277
        /// </summary>
Dean@1094
   278
        public string KeyList
Dean@1094
   279
        {
Dean@1094
   280
            get { return (this._KeyList); }
Dean@1094
   281
            set
Dean@1094
   282
            {
Dean@1094
   283
                this._KeyList = value;
Dean@1242
   284
                this.RaisePropertyChangedEvent(nameof(this.KeyList));
Dean@1094
   285
            }
Dean@1094
   286
        }
Dean@1094
   287
Dean@1094
   288
        /// <summary>
Dean@384
   289
        /// Gets the list of keywords associated with the message.
Dean@1094
   290
        ///          MailItem : Corresponds to property 'Categories'
Dean@1094
   291
        /// CryptableMailItem : not supported
Dean@1348
   292
        ///       TextMessage : Corresponds to property 'Keywords'
Dean@384
   293
        /// </summary>
Dean@384
   294
        public List<string> Keywords
Dean@384
   295
        {
Dean@384
   296
            get { return (this._Keywords); }
Dean@384
   297
        }
Dean@384
   298
Dean@384
   299
        /// <summary>
Dean@1094
   300
        /// Gets or sets the plain text long-form (body) of the message.
Dean@384
   301
        /// Warning: this value can be null.
Dean@1094
   302
        ///          MailItem : Corresponds to property 'Body' (also BodyFormat)
Dean@1094
   303
        /// CryptableMailItem : not exposed (processed internally)
Dean@1348
   304
        ///       TextMessage : Corresponds to property 'LongMsg'
Dean@384
   305
        /// </summary>
Dean@384
   306
        public string LongMsg
Dean@384
   307
        {
Dean@384
   308
            get { return (this._LongMsg); }
Dean@1094
   309
            set
Dean@1094
   310
            {
Dean@1094
   311
                this._LongMsg = value;
Dean@1242
   312
                this.RaisePropertyChangedEvent(nameof(this.LongMsg));
Dean@1094
   313
            }
Dean@384
   314
        }
Dean@384
   315
Dean@384
   316
        /// <summary>
Dean@697
   317
        /// Gets or sets the HTML formatted long-form (body) of the message.
Dean@384
   318
        /// Warning: this value can be null.
Dean@1094
   319
        ///          MailItem : Corresponds to property 'HTMLBody' (also BodyFormat)
Dean@1094
   320
        /// CryptableMailItem : not exposed (processed internally)
Dean@1348
   321
        ///       TextMessage : Corresponds to property 'LongMsgFormatted'
Dean@384
   322
        /// </summary>
Dean@1354
   323
        public string LongMsgFormattedHtml
Dean@384
   324
        {
Dean@1354
   325
            get { return (this._LongMsgFormattedHtml); }
Dean@1094
   326
            set
Dean@1094
   327
            {
Dean@1354
   328
                this._LongMsgFormattedHtml = value;
Dean@1354
   329
                this.RaisePropertyChangedEvent(nameof(this.LongMsgFormattedHtml));
Dean@1094
   330
            }
Dean@697
   331
        }
Dean@697
   332
Dean@697
   333
        /// <summary>
Dean@697
   334
        /// Gets or sets the RTF formatted long-form (body) of the message.
Dean@1094
   335
        /// Warning: this value can be null, it is only supported when creating from a MailItem. It is
Dean@1094
   336
        /// not applied to a MailItem during .ApplyTo as it's unsupported by the engine.
Dean@1094
   337
        ///          MailItem : Corresponds to property 'RTFBody' (also BodyFormat)
Dean@1094
   338
        /// CryptableMailItem : not exposed (processed internally)
Dean@1340
   339
        ///       TextMessage : not supported
Dean@697
   340
        /// </summary>
Dean@1354
   341
        public string LongMsgFormattedRtf
Dean@697
   342
        {
Dean@1354
   343
            get { return (this._LongMsgFormattedRtf); }
Dean@1094
   344
            set
Dean@1094
   345
            {
Dean@1354
   346
                this._LongMsgFormattedRtf = value;
Dean@1354
   347
                this.RaisePropertyChangedEvent(nameof(this.LongMsgFormattedRtf));
Dean@1094
   348
            }
Dean@384
   349
        }
Dean@384
   350
Dean@384
   351
        /// <summary>
Dean@1104
   352
        /// Gets or sets whether this message is marked to never be unsecure.
Dean@1104
   353
        /// Note: While this is stored as a header field, it is OK to apply to any MailItem and outgoing messages.
Dean@1104
   354
        /// It's intended function is to flag a message to keep it encrypted.
Dean@1104
   355
        ///          MailItem : Corresponds to MAPI Property PR_X_PEP_NEVER_UNSECURE (Header field)
Dean@1104
   356
        /// CryptableMailItem : Corresponds to property 'NeverUnsecure'
Dean@1340
   357
        ///       TextMessage : not supported
Dean@1104
   358
        /// </summary>
Dean@1104
   359
        public bool NeverUnsecure
Dean@1104
   360
        {
Dean@1104
   361
            get { return (this._NeverUnsecure); }
Dean@1104
   362
            set
Dean@1104
   363
            {
Dean@1104
   364
                this._NeverUnsecure = value;
Dean@1242
   365
                this.RaisePropertyChangedEvent(nameof(this.NeverUnsecure));
Dean@1104
   366
            }
Dean@1104
   367
        }
Dean@1104
   368
Dean@1104
   369
        /// <summary>
Dean@1212
   370
        /// Gets or sets the pEp protocol (or 'format') version of this message.
Dean@1212
   371
        /// This is not to be confused with pEp engine version which can be different.
Dean@1094
   372
        /// This is commonly set after decryption.
Dean@1094
   373
        /// Note: While this is stored as a header field, it is OK to apply to any MailItem and outgoing messages.
Dean@1094
   374
        /// It's intended function is just to show the version of pEp last used to process the message.
Dean@1212
   375
        ///          MailItem : Corresponds to MAPI Property PR_X_PEP_PROTOCOL_VERSION (Header field)
Dean@1094
   376
        /// CryptableMailItem : not supported
Dean@1340
   377
        ///       TextMessage : Corresponds with the key PR_X_PEP_PROTOCOL_VERSION_NAME within 'opt_fields' array. 
Dean@384
   378
        /// </summary>
Dean@1212
   379
        public string PEPProtocolVersion
Dean@384
   380
        {
Dean@1212
   381
            get { return (this._PEPProtocolVersion); }
Dean@1094
   382
            set
Dean@1094
   383
            {
Dean@1212
   384
                this._PEPProtocolVersion = value;
Dean@1242
   385
                this.RaisePropertyChangedEvent(nameof(this.PEPProtocolVersion));
Dean@1094
   386
            }
Dean@384
   387
        }
Dean@384
   388
Dean@384
   389
        /// <summary>
Dean@1303
   390
        /// Gets or sets the pEp rating of the message.
Dean@1303
   391
        /// This should corresponds primarily with the decryption rating.
Dean@1303
   392
        /// Warning: Since this is stored as a header field, care must be taken not to apply this to a MailItem on an 
Dean@1303
   393
        /// untrusted server or on an outgoing message.
Dean@1303
   394
        ///          MailItem : Corresponds to MAPI Property PR_X_ENC_STATUS (Header field)
Dean@1303
   395
        /// CryptableMailItem : not exposed (processed internally)
Dean@1340
   396
        ///       TextMessage : Corresponds with the key PR_X_ENC_STATUS_NAME within 'opt_fields' array
Dean@1303
   397
        /// </summary>
markus@1337
   398
        public pEpRating Rating
Dean@1303
   399
        {
Dean@1303
   400
            get { return (this._Rating); }
Dean@1303
   401
            set
Dean@1303
   402
            {
Dean@1303
   403
                this._Rating = value;
Dean@1303
   404
                this.RaisePropertyChangedEvent(nameof(this.Rating));
Dean@1303
   405
            }
Dean@1303
   406
        }
Dean@1303
   407
Dean@1303
   408
        /// <summary>
Dean@721
   409
        /// Gets or sets the date and time when the message was received.
Dean@1094
   410
        ///          MailItem : Corresponds to property 'ReceivedTime'
Dean@1094
   411
        /// CryptableMailItem : not supported
Dean@1340
   412
        ///       TextMessage : Corresponds to property 'recv'
Dean@721
   413
        /// </summary>
Dean@721
   414
        public DateTime? ReceivedOn
Dean@721
   415
        {
Dean@721
   416
            get { return (this._ReceivedOn); }
Dean@1094
   417
            set
Dean@1094
   418
            {
Dean@1094
   419
                this._ReceivedOn = value;
Dean@1242
   420
                this.RaisePropertyChangedEvent(nameof(this.ReceivedOn));
Dean@1094
   421
            }
Dean@721
   422
        }
Dean@721
   423
Dean@721
   424
        /// <summary>
Dean@721
   425
        /// Gets or sets the date and time when the message was sent.
Dean@1094
   426
        ///          MailItem : Corresponds to property 'SentOn'
Dean@1094
   427
        /// CryptableMailItem : not supported
Dean@1340
   428
        ///       TextMessage : Corresponds to property 'sent'
Dean@721
   429
        /// </summary>
Dean@721
   430
        public DateTime? SentOn
Dean@721
   431
        {
Dean@721
   432
            get { return (this._SentOn); }
Dean@1094
   433
            set
Dean@1094
   434
            {
Dean@1094
   435
                this._SentOn = value;
Dean@1242
   436
                this.RaisePropertyChangedEvent(nameof(this.SentOn));
Dean@1094
   437
            }
Dean@721
   438
        }
Dean@721
   439
Dean@721
   440
        /// <summary>
Dean@384
   441
        /// Gets or sets the short-form (subject) of the message.
Dean@384
   442
        /// Warning: this value can be null.
Dean@1094
   443
        ///          MailItem : Corresponds to property 'Subject'
Dean@1094
   444
        /// CryptableMailItem : not supported
Dean@1348
   445
        ///       TextMessage : Corresponds to property 'Shortmsg'
Dean@384
   446
        /// </summary>
Dean@384
   447
        public string ShortMsg
Dean@384
   448
        {
Dean@384
   449
            get { return (this._ShortMsg); }
Dean@1094
   450
            set
Dean@1094
   451
            {
Dean@1094
   452
                this._ShortMsg = value;
Dean@1242
   453
                this.RaisePropertyChangedEvent(nameof(this.ShortMsg));
Dean@1094
   454
            }
Dean@384
   455
        }
Dean@384
   456
Dean@384
   457
        /// <summary>
Dean@384
   458
        /// Gets the list of identities to receive the message.
Dean@1094
   459
        ///          MailItem : Component of property 'Recipients' which contains 'Recipient'
Dean@1094
   460
        /// CryptableMailItem : not exposed directly (part of Recipients)
Dean@1348
   461
        ///       TextMessage : Corresponds to property 'To'
Dean@384
   462
        /// </summary>
Dean@384
   463
        public List<PEPIdentity> To
Dean@384
   464
        {
Dean@384
   465
            get { return (this._To); }
Dean@384
   466
        }
Dean@384
   467
Dean@625
   468
        /// <summary>
Dean@1348
   469
        /// Gets the total number of all recipients in the message (Bcc, Cc &amp; To).
Dean@625
   470
        /// </summary>
Dean@625
   471
        public int RecipientCount
Dean@625
   472
        {
Dean@1348
   473
            get { return (this._Bcc.Count + this._Cc.Count + this._To.Count); }
Dean@625
   474
        }
Dean@625
   475
Dean@625
   476
        /// <summary>
Dean@1348
   477
        /// Gets a list of all recipients in the message (Bcc, Cc &amp; To).
Dean@625
   478
        /// Each recipient in the returned list is a copy.
Dean@625
   479
        /// </summary>
Dean@625
   480
        public PEPIdentity[] Recipients
Dean@625
   481
        {
Dean@625
   482
            get
Dean@625
   483
            {
Dean@625
   484
                List<PEPIdentity> recipients = new List<PEPIdentity>();
Dean@625
   485
Dean@1348
   486
                // Bcc
Dean@1348
   487
                for (int i = 0; i < this._Bcc.Count; i++)
Dean@625
   488
                {
Dean@1348
   489
                    recipients.Add(this._Bcc[i].Copy());
Dean@625
   490
                }
Dean@625
   491
Dean@1348
   492
                // Cc
Dean@1348
   493
                for (int i = 0; i < this._Cc.Count; i++)
Dean@625
   494
                {
Dean@1348
   495
                    recipients.Add(this._Cc[i].Copy());
Dean@625
   496
                }
Dean@625
   497
Dean@625
   498
                // To
Dean@625
   499
                for (int i = 0; i < this._To.Count; i++)
Dean@625
   500
                {
Dean@625
   501
                    recipients.Add(this._To[i].Copy());
Dean@625
   502
                }
Dean@625
   503
Dean@625
   504
                return (recipients.ToArray());
Dean@625
   505
            }
Dean@625
   506
        }
Dean@625
   507
Dean@910
   508
        #endregion
Dean@910
   509
Dean@384
   510
        /**************************************************************
Dean@384
   511
         * 
Dean@384
   512
         * Methods
Dean@384
   513
         * 
Dean@384
   514
         *************************************************************/
Dean@384
   515
Dean@384
   516
        /// <summary>
Dean@1094
   517
        /// Raises the property changed event, if possible, with the given arguments.
Dean@1094
   518
        /// </summary>
Dean@1094
   519
        /// <param name="propertyName">The name of the property that changed.</param>
Dean@1094
   520
        private void RaisePropertyChangedEvent(string propertyName)
Dean@1094
   521
        {
Dean@1244
   522
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
Dean@1094
   523
            return;
Dean@1094
   524
        }
Dean@1094
   525
Dean@1094
   526
        /// <summary>
markus@1337
   527
        /// Returns this pEp message as a new pEp engine TextMessage struct.
Dean@910
   528
        /// Warning: Any identity members (groups) are lost, call FlattenAllRecipientIdentities() before this method.
Dean@384
   529
        /// </summary>
Dean@1094
   530
        /// <param name="alwaysAddOptFields">Whether to always add optional fields (default is false). When false, 
Dean@1094
   531
        /// optional fields will not be added if they are undefined/null/empty in the PEPMessage. This is to prevent 
Dean@1094
   532
        /// against adding optional fields when they have never been set.
Dean@1094
   533
        /// </param>
Dean@384
   534
        /// <returns>A pEp engine identity.</returns>
markus@1337
   535
        public TextMessage ToCOMType(bool alwaysAddOptFields = false)
Dean@384
   536
        {
Dean@1095
   537
            long recvSec;
Dean@1095
   538
            long sentSec;
Dean@1095
   539
            TimeSpan span;
markus@1337
   540
            List<Blob> attachments = new List<Blob>();
markus@1337
   541
            List<pEpIdentity> bcc = new List<pEpIdentity>();
markus@1337
   542
            List<pEpIdentity> cc = new List<pEpIdentity>();
markus@1337
   543
            List<pEpIdentity> to = new List<pEpIdentity>();
markus@1337
   544
            List<StringPair> optionalFields = new List<StringPair>();
markus@1337
   545
            StringPair field;
markus@1337
   546
            TextMessage result = new TextMessage();
Dean@384
   547
Dean@388
   548
            // Convert attachments
Dean@384
   549
            for (int i = 0; i < this._Attachments.Count; i++)
Dean@384
   550
            {
Dean@384
   551
                attachments.Add(this._Attachments[i].ToCOMType());
Dean@384
   552
            }
Dean@398
   553
Dean@1348
   554
            // Convert Bcc
Dean@1348
   555
            for (int i = 0; i < this._Bcc.Count; i++)
Dean@384
   556
            {
Dean@1348
   557
                bcc.Add(this._Bcc[i].ToCOMType());
Dean@384
   558
            }
Dean@398
   559
Dean@1348
   560
            // Convert Cc
Dean@1348
   561
            for (int i = 0; i < this._Cc.Count; i++)
Dean@384
   562
            {
Dean@1348
   563
                cc.Add(this._Cc[i].ToCOMType());
Dean@384
   564
            }
Dean@398
   565
Dean@388
   566
            // Convert To
Dean@388
   567
            for (int i = 0; i < this._To.Count; i++)
Dean@388
   568
            {
Dean@388
   569
                to.Add(this._To[i].ToCOMType());
Dean@388
   570
            }
Dean@388
   571
Dean@1094
   572
            // Create optional fields
Dean@1094
   573
            if ((alwaysAddOptFields) ||
markus@1337
   574
                (this._Rating != pEpRating.pEpRatingUndefined))
Dean@1094
   575
            {
markus@1337
   576
                field = new StringPair();
markus@1347
   577
                field.Name = PEPMessage.PR_X_ENC_STATUS_NAME;
markus@1347
   578
                field.Value = this._Rating.ToEngineString();
Dean@1094
   579
                optionalFields.Add(field);
Dean@1094
   580
            }
Dean@1094
   581
Dean@1094
   582
            if ((alwaysAddOptFields) ||
Dean@1094
   583
                (string.IsNullOrEmpty(this._KeyList) == false))
Dean@1094
   584
            {
markus@1337
   585
                field = new StringPair();
markus@1347
   586
                field.Name = PEPMessage.PR_X_KEY_LIST_NAME;
markus@1347
   587
                field.Value = this._KeyList;
Dean@1094
   588
                optionalFields.Add(field);
Dean@1094
   589
            }
Dean@1094
   590
Dean@1094
   591
            if ((alwaysAddOptFields) ||
Dean@1104
   592
                (this._NeverUnsecure == true))
Dean@1095
   593
            {
markus@1337
   594
                field = new StringPair();
markus@1347
   595
                field.Name = PEPMessage.PR_X_PEP_NEVER_UNSECURE_NAME;
markus@1347
   596
                field.Value = "1";
Dean@1095
   597
                optionalFields.Add(field);
Dean@1095
   598
            }
Dean@1095
   599
Dean@1095
   600
            if ((alwaysAddOptFields) ||
Dean@1212
   601
                (string.IsNullOrEmpty(this._PEPProtocolVersion) == false))
Dean@1094
   602
            {
markus@1337
   603
                field = new StringPair();
markus@1347
   604
                field.Name = PEPMessage.PR_X_PEP_PROTOCOL_VERSION_NAME;
markus@1347
   605
                field.Value = this._PEPProtocolVersion;
Dean@1094
   606
                optionalFields.Add(field);
Dean@1094
   607
            }
Dean@1094
   608
Dean@1095
   609
            // ReceivedOn
Dean@1095
   610
            recvSec = -1;
Dean@1095
   611
            if (this._ReceivedOn != null)
Dean@1095
   612
            {
Dean@1095
   613
                span = (DateTime)this._ReceivedOn - new DateTime(1970, 1, 1);
Dean@1095
   614
                recvSec = (long)span.TotalSeconds;
Dean@1095
   615
            }
Dean@1095
   616
Dean@1095
   617
            // SentOn
Dean@1095
   618
            sentSec = -1;
Dean@1095
   619
            if (this._SentOn != null)
Dean@1095
   620
            {
Dean@1095
   621
                span = (DateTime)this._SentOn - new DateTime(1970, 1, 1);
Dean@1095
   622
                sentSec = (long)span.TotalSeconds;
Dean@1095
   623
            }
Dean@1095
   624
markus@1337
   625
            /* Note: Skip the following properties which are not supported in TextMessage
Dean@1354
   626
             *   • ConversationId
Dean@1113
   627
             *   • ConversationIndex
Dean@1113
   628
             *   • ConversationTopic
Dean@1094
   629
             *   • ForceUnencrypted
Dean@1354
   630
             *   • LongMsgFormattedRtf
Dean@1095
   631
             * 
Dean@1095
   632
             * Also not the following are handled as optional fields
Dean@1130
   633
             *   • Rating
Dean@1104
   634
             *   • NeverUnsecure
Dean@1095
   635
             *   • KeyList
Dean@1212
   636
             *   • PEPProtocolVersion
Dean@1095
   637
             * 
markus@1337
   638
             * This also skips a number of TextMessage properties currently unsupported in PEPMessage.
Dean@697
   639
             */
markus@1347
   640
            result.Attachments = attachments.ToArray();
markus@1347
   641
            result.Bcc = bcc.ToArray();
markus@1347
   642
            result.Cc = cc.ToArray();
markus@1347
   643
            result.Dir = this._Direction;
markus@1347
   644
            result.From = (this._From != null ? this._From.ToCOMType() : result.From);
Dean@1348
   645
            result.Id = this._Id;
markus@1347
   646
            result.Keywords = this._Keywords.ToArray();
markus@1356
   647
            result.LongMsg = this._LongMsg;
markus@1356
   648
            result.LongMsgFormatted = this._LongMsgFormattedHtml;
markus@1347
   649
            result.OptFields = optionalFields.ToArray();
markus@1347
   650
            result.Recv = ((recvSec >= 0) ? recvSec : result.Recv);
markus@1347
   651
            result.Sent = ((sentSec >= 0) ? sentSec : result.Sent);
markus@1356
   652
            result.ShortMsg = this._ShortMsg;
markus@1347
   653
            result.To = to.ToArray();
Dean@384
   654
Dean@384
   655
            return (result);
Dean@384
   656
        }
Dean@384
   657
Dean@384
   658
        /// <summary>
Dean@1238
   659
        /// Serves as a hash function for a particular type.
Dean@1238
   660
        /// </summary>
Dean@1238
   661
        /// <returns>A hash code for the current object.</returns>
Dean@1238
   662
        public override int GetHashCode()
Dean@1238
   663
        {
Dean@1238
   664
            return base.GetHashCode();
Dean@1238
   665
        }
Dean@1238
   666
Dean@1238
   667
        /// <summary>
Dean@1238
   668
        /// Indicates whether the current object is equal to another object of the same type.
Dean@1238
   669
        /// </summary>
Dean@1238
   670
        /// <param name="obj">The object to check equality with.</param>
Dean@1238
   671
        /// <returns>True if both objects are considered equal, otherwise false.</returns>
Dean@1238
   672
        public override bool Equals(object obj)
Dean@1238
   673
        {
Dean@1238
   674
            if ((obj == null) ||
Dean@1238
   675
                !(obj is PEPMessage))
Dean@1238
   676
            {
Dean@1238
   677
                return (false);
Dean@1238
   678
            }
Dean@1238
   679
Dean@1238
   680
            return (this.Equals((PEPMessage)obj));
Dean@1238
   681
        }
Dean@1238
   682
Dean@1238
   683
        /// <summary>
Dean@1238
   684
        /// Indicates whether the current object is equal to another object of the same type.
Dean@1238
   685
        /// </summary>
Dean@1238
   686
        /// <param name="obj">The object to check equality with.</param>
Dean@1238
   687
        /// <returns>True if both objects are considered equal, otherwise false.</returns>
Dean@1238
   688
        public bool Equals(PEPMessage obj)
Dean@1238
   689
        {
Dean@1238
   690
            if (obj == null)
Dean@1238
   691
            {
Dean@1238
   692
                return (false);
Dean@1238
   693
            }
Dean@1238
   694
Dean@1238
   695
            if (Comparisons.Equals(this.Attachments, obj.Attachments) &&
Dean@1348
   696
                Comparisons.Equals(this.Bcc, obj.Bcc) &&
Dean@1348
   697
                Comparisons.Equals(this.Cc, obj.Cc) &&
Dean@1348
   698
                Comparisons.Equals(this.ConversationId, obj.ConversationId) &&
Dean@1238
   699
                Comparisons.Equals(this.ConversationIndex, obj.ConversationIndex) &&
Dean@1238
   700
                Comparisons.Equals(this.ConversationTopic, obj.ConversationTopic) &&
Dean@1238
   701
                Comparisons.Equals(this.Direction, obj.Direction) &&
Dean@1238
   702
                Comparisons.Equals(this.ForceUnencrypted, obj.ForceUnencrypted) &&
Dean@1238
   703
                Comparisons.Equals(this.From, obj.From) &&
Dean@1348
   704
                Comparisons.Equals(this.Id, obj.Id) &&
Dean@1238
   705
                Comparisons.Equals(this.KeyList, obj.KeyList) &&
Dean@1238
   706
                Comparisons.Equals(this.Keywords, obj.Keywords) &&
Dean@1238
   707
                Comparisons.Equals(this.LongMsg, obj.LongMsg) &&
Dean@1354
   708
                Comparisons.Equals(this.LongMsgFormattedHtml, obj.LongMsgFormattedHtml) &&
Dean@1354
   709
                Comparisons.Equals(this.LongMsgFormattedRtf, obj.LongMsgFormattedRtf) &&
Dean@1238
   710
                Comparisons.Equals(this.NeverUnsecure, obj.NeverUnsecure) &&
Dean@1238
   711
                Comparisons.Equals(this.PEPProtocolVersion, obj.PEPProtocolVersion) &&
Dean@1303
   712
                Comparisons.Equals(this.Rating, obj.Rating) &&
Dean@1238
   713
                Comparisons.Equals(this.ReceivedOn, obj.ReceivedOn) &&
Dean@1238
   714
                Comparisons.Equals(this.SentOn, obj.SentOn) &&
Dean@1238
   715
                Comparisons.Equals(this.ShortMsg, obj.ShortMsg) &&
Dean@1238
   716
                Comparisons.Equals(this.To, obj.To))
Dean@1238
   717
            {
Dean@1238
   718
                return (true);
Dean@1238
   719
            }
Dean@1238
   720
Dean@1238
   721
            return (false);
Dean@1238
   722
        }
Dean@1238
   723
Dean@1238
   724
        /// <summary>
Thomas@1326
   725
        /// Indicates whether the content of the current PEPMessage is equal to another one's.
Thomas@1326
   726
        /// </summary>
Thomas@1326
   727
        /// <param name="obj">The object to check equality with.</param>
Thomas@1326
   728
        /// <returns>True if both objects' content is considered equal, otherwise false.</returns>
Thomas@1326
   729
        public bool EqualsByContent(PEPMessage obj)
Thomas@1326
   730
        {
Thomas@1326
   731
            if (obj == null)
Thomas@1326
   732
            {
Thomas@1326
   733
                return false;
Thomas@1326
   734
            }
Thomas@1326
   735
Thomas@1326
   736
            if (Comparisons.Equals(this.ConversationTopic, obj.ConversationTopic) &&
Thomas@1326
   737
                Comparisons.Equals(this.Keywords, obj.Keywords) &&
Thomas@1326
   738
                Comparisons.Equals(this.LongMsg, obj.LongMsg) &&
Dean@1354
   739
                Comparisons.Equals(this.LongMsgFormattedHtml, obj.LongMsgFormattedHtml) &&
Dean@1354
   740
                Comparisons.Equals(this.LongMsgFormattedRtf, obj.LongMsgFormattedRtf) &&
Thomas@1326
   741
                Comparisons.Equals(this.ShortMsg, obj.ShortMsg))
Thomas@1326
   742
            {
Thomas@1326
   743
                return true;
Thomas@1326
   744
            }
Thomas@1326
   745
            else
Thomas@1326
   746
            {
Thomas@1326
   747
                return false;
Thomas@1326
   748
            }
Thomas@1326
   749
        }
Thomas@1326
   750
Thomas@1326
   751
        /// <summary>
Thomas@1294
   752
        /// Gets a deep copy of the object and all its data.
Dean@389
   753
        /// </summary>
Dean@389
   754
        /// <returns>The deep copy of the object.</returns>
Dean@389
   755
        public PEPMessage Copy()
Dean@389
   756
        {
Dean@390
   757
            return (this.Copy(false));
Dean@390
   758
        }
Dean@390
   759
Dean@390
   760
        /// <summary>
Dean@390
   761
        /// Gets a copy of the PEPMessage with or without data.
Dean@390
   762
        /// </summary>
Dean@586
   763
        /// <param name="createWithoutContent">Whether to include content such as text body, attachments 
Dean@586
   764
        /// and optional properties.</param>
Dean@390
   765
        /// <returns>The copy of the PEPMessage.</returns>
Dean@390
   766
        public PEPMessage Copy(bool createWithoutContent = false)
Dean@390
   767
        {
Dean@389
   768
            PEPMessage copy = new PEPMessage();
Dean@389
   769
Dean@389
   770
            // Attachments
Dean@389
   771
            copy.Attachments.Clear();
Dean@390
   772
            if (createWithoutContent == false)
Dean@389
   773
            {
Dean@390
   774
                for (int i = 0; i < this._Attachments.Count; i++)
Dean@390
   775
                {
Dean@390
   776
                    copy.Attachments.Add(this._Attachments[i].Copy());
Dean@390
   777
                }
Dean@389
   778
            }
Dean@389
   779
Dean@1348
   780
            // Bcc
Dean@1348
   781
            copy.Bcc.Clear();
Dean@1348
   782
            for (int i = 0; i < this._Bcc.Count; i++)
Dean@389
   783
            {
Dean@1348
   784
                copy.Bcc.Add(this._Bcc[i].Copy());
Dean@389
   785
            }
Dean@389
   786
Dean@1348
   787
            // Cc
Dean@1348
   788
            copy.Cc.Clear();
Dean@1348
   789
            for (int i = 0; i < this._Cc.Count; i++)
Dean@389
   790
            {
Dean@1348
   791
                copy.Cc.Add(this._Cc[i].Copy());
Dean@389
   792
            }
Dean@389
   793
Dean@1348
   794
            copy.ConversationId = (this._ConversationId == null ? null : string.Copy(this._ConversationId));
Dean@1291
   795
            copy.ConversationIndex = (this._ConversationIndex == null ? null : string.Copy(this._ConversationIndex));
Dean@1291
   796
            copy.ConversationTopic = (this._ConversationTopic == null ? null : string.Copy(this._ConversationTopic));
Dean@389
   797
            copy.Direction = this._Direction;
Dean@1094
   798
            copy.ForceUnencrypted = this._ForceUnencrypted;
Dean@389
   799
            copy.From = (this._From == null ? null : this._From.Copy());
Dean@1348
   800
            copy.Id = (this._Id == null ? null : string.Copy(this._Id));
Dean@1291
   801
            copy.KeyList = (this._KeyList == null ? null : string.Copy(this._KeyList));
Dean@389
   802
Dean@389
   803
            // Keywords
Dean@389
   804
            copy.Keywords.Clear();
Dean@389
   805
            for (int i = 0; i < this._Keywords.Count; i++)
Dean@389
   806
            {
Dean@389
   807
                copy.Keywords.Add(this._Keywords[i]);
Dean@389
   808
            }
Dean@398
   809
Dean@390
   810
            // Body
Dean@390
   811
            if (createWithoutContent == false)
Dean@390
   812
            {
Thomas@1294
   813
                copy.LongMsg = (this._LongMsg == null ? null : string.Copy(this._LongMsg));
Dean@1354
   814
                copy.LongMsgFormattedHtml = (this._LongMsgFormattedHtml == null ? null : string.Copy(this._LongMsgFormattedHtml));
Dean@1354
   815
                copy.LongMsgFormattedRtf = (this._LongMsgFormattedRtf == null ? null : string.Copy(this._LongMsgFormattedRtf));
Dean@390
   816
            }
Dean@390
   817
            else
Dean@390
   818
            {
Dean@390
   819
                copy.LongMsg = null;
Dean@1354
   820
                copy.LongMsgFormattedHtml = null;
Dean@1354
   821
                copy.LongMsgFormattedRtf = null;
Dean@390
   822
            }
Dean@389
   823
Dean@1104
   824
            copy.NeverUnsecure = this._NeverUnsecure;
Dean@1291
   825
            copy.PEPProtocolVersion = (this._PEPProtocolVersion == null ? null : string.Copy(this._PEPProtocolVersion));
Dean@1303
   826
            copy.Rating = this._Rating;
Dean@604
   827
Dean@721
   828
            // ReceivedOn
Dean@721
   829
            if (this._ReceivedOn != null)
Dean@721
   830
            {
Dean@721
   831
                copy.ReceivedOn = new DateTime(((DateTime)this._ReceivedOn).Ticks);
Dean@721
   832
            }
Dean@721
   833
            else
Dean@721
   834
            {
Dean@721
   835
                copy.ReceivedOn = null;
Dean@721
   836
            }
Dean@721
   837
Dean@721
   838
            // SentOn
Dean@721
   839
            if (this._SentOn != null)
Dean@721
   840
            {
Dean@721
   841
                copy.SentOn = new DateTime(((DateTime)this._SentOn).Ticks);
Dean@721
   842
            }
Dean@721
   843
            else
Dean@721
   844
            {
Dean@721
   845
                copy.SentOn = null;
Dean@721
   846
            }
Dean@721
   847
Dean@1291
   848
            copy.ShortMsg = (this._ShortMsg == null ? null : string.Copy(this._ShortMsg));
Dean@389
   849
Dean@389
   850
            // To
Dean@389
   851
            copy.To.Clear();
Dean@389
   852
            for (int i = 0; i < this._To.Count; i++)
Dean@389
   853
            {
Dean@389
   854
                copy.To.Add(this._To[i].Copy());
Dean@389
   855
            }
Dean@389
   856
Dean@389
   857
            return (copy);
Dean@389
   858
        }
Dean@389
   859
Dean@389
   860
        /// <summary>
Thomas@1294
   861
        /// Resets the object to its default state/values.
Dean@1094
   862
        /// </summary>
Dean@1094
   863
        public void Reset()
Dean@1094
   864
        {
Dean@1094
   865
            this._Attachments = new List<PEPAttachment>();
Dean@1348
   866
            this._Bcc = new List<PEPIdentity>();
Dean@1348
   867
            this._Cc = new List<PEPIdentity>();
Dean@1348
   868
            this._ConversationId = null;
Dean@1113
   869
            this._ConversationIndex = null;
Dean@1113
   870
            this._ConversationTopic = null;
markus@1337
   871
            this._Direction = pEpMsgDirection.pEpDirIncoming;
Dean@1094
   872
            this._ForceUnencrypted = false;
Dean@1094
   873
            this._From = null;
Dean@1348
   874
            this._Id = null;
Dean@1094
   875
            this._KeyList = null;
Dean@1094
   876
            this._Keywords = new List<string>();
Dean@1094
   877
            this._LongMsg = null;
Dean@1354
   878
            this._LongMsgFormattedHtml = null;
Dean@1354
   879
            this._LongMsgFormattedRtf = null;
Dean@1104
   880
            this._NeverUnsecure = false;
Dean@1212
   881
            this._PEPProtocolVersion = null;
markus@1337
   882
            this._Rating = pEpRating.pEpRatingUndefined;
Dean@1094
   883
            this._ReceivedOn = null;
Dean@1094
   884
            this._SentOn = null;
Dean@1094
   885
            this._ShortMsg = null;
Dean@1094
   886
            this._To = new List<PEPIdentity>();
Dean@1094
   887
Dean@1242
   888
            this.RaisePropertyChangedEvent(nameof(this.Attachments));
Dean@1348
   889
            this.RaisePropertyChangedEvent(nameof(this.Bcc));
Dean@1348
   890
            this.RaisePropertyChangedEvent(nameof(this.Cc));
Dean@1348
   891
            this.RaisePropertyChangedEvent(nameof(this.ConversationId));
Dean@1242
   892
            this.RaisePropertyChangedEvent(nameof(this.ConversationIndex));
Dean@1242
   893
            this.RaisePropertyChangedEvent(nameof(this.ConversationTopic));
Dean@1242
   894
            this.RaisePropertyChangedEvent(nameof(this.Direction));
Dean@1242
   895
            this.RaisePropertyChangedEvent(nameof(this.ForceUnencrypted));
Dean@1242
   896
            this.RaisePropertyChangedEvent(nameof(this.From));
Dean@1348
   897
            this.RaisePropertyChangedEvent(nameof(this.Id));
Dean@1242
   898
            this.RaisePropertyChangedEvent(nameof(this.KeyList));
Dean@1242
   899
            this.RaisePropertyChangedEvent(nameof(this.Keywords));
Dean@1242
   900
            this.RaisePropertyChangedEvent(nameof(this.LongMsg));
Dean@1354
   901
            this.RaisePropertyChangedEvent(nameof(this.LongMsgFormattedHtml));
Dean@1354
   902
            this.RaisePropertyChangedEvent(nameof(this.LongMsgFormattedRtf));
Dean@1242
   903
            this.RaisePropertyChangedEvent(nameof(this.NeverUnsecure));
Dean@1242
   904
            this.RaisePropertyChangedEvent(nameof(this.PEPProtocolVersion));
Dean@1303
   905
            this.RaisePropertyChangedEvent(nameof(this.Rating));
Dean@1242
   906
            this.RaisePropertyChangedEvent(nameof(this.ReceivedOn));
Dean@1242
   907
            this.RaisePropertyChangedEvent(nameof(this.SentOn));
Dean@1242
   908
            this.RaisePropertyChangedEvent(nameof(this.ShortMsg));
Dean@1242
   909
            this.RaisePropertyChangedEvent(nameof(this.To));
Dean@1094
   910
Dean@1094
   911
            return;
Dean@1094
   912
        }
Dean@1094
   913
Dean@1094
   914
        /// <summary>
Dean@1095
   915
        /// Copies main properties that are unsupported by the engine from the given message into this message.
Dean@1113
   916
        /// This is commonly used to restore information that would otherwise be lost in conversions to/from 
markus@1337
   917
        /// TextMessage during engine processing.
Dean@1354
   918
        /// Key properties that should be set separately for encryption concerns (such as LongMsgFormattedRtf) are ignored.
Dean@1095
   919
        /// </summary>
Dean@1095
   920
        /// <param name="msg">The message to copy over properties from.</param>
Dean@1095
   921
        public void SetNonEnginePropertiesFrom(PEPMessage msg)
Dean@1095
   922
        {
Dean@1095
   923
            /* Include the following properties:
Dean@1354
   924
             *   • ConversationId
Dean@1113
   925
             *   • ConversationIndex
Dean@1113
   926
             *   • ConversationTopic
Dean@1095
   927
             *   • ForceUnencrypted
Dean@1104
   928
             *   • NeverUnsecure
Dean@1095
   929
             * 
Dean@1095
   930
             * Also note:
Dean@1130
   931
             *              Rating: This is handled by the decrypt function of the engine only. It should not be
Dean@1095
   932
             *                      added back separately so is skipped.
Dean@1095
   933
             *             KeyList: This is handled by the decrypt function of the engine only. It should not be
Dean@1095
   934
             *                      added back separately so is skipped.
Dean@1354
   935
             * LongMsgFormattedRtf: This is completely unsupported by the engine but otherwise should be encrypted.
Dean@1095
   936
             *                      Due to this, it's completely skipped.
Dean@1212
   937
             *  PEPProtocolVersion: This is handled in both the decrypt and encrypt functions of the engine.
markus@1337
   938
             *                      It should almost always be set within the TextMessage there isn't needed to add back.
Dean@1095
   939
             */
Dean@1095
   940
Dean@1095
   941
            if (msg != null)
Dean@1095
   942
            {
Dean@1348
   943
                this._ConversationId = msg.ConversationId;
Dean@1113
   944
                this._ConversationIndex = msg.ConversationIndex;
Dean@1113
   945
                this._ConversationTopic = msg.ConversationTopic;
Dean@1095
   946
                this._ForceUnencrypted = msg.ForceUnencrypted;
Dean@1104
   947
                this._NeverUnsecure = msg.NeverUnsecure;
Dean@1095
   948
Dean@1348
   949
                this.RaisePropertyChangedEvent(nameof(this.ConversationId));
Dean@1242
   950
                this.RaisePropertyChangedEvent(nameof(this.ConversationIndex));
Dean@1242
   951
                this.RaisePropertyChangedEvent(nameof(this.ConversationTopic));
Dean@1242
   952
                this.RaisePropertyChangedEvent(nameof(this.ForceUnencrypted));
Dean@1242
   953
                this.RaisePropertyChangedEvent(nameof(this.NeverUnsecure));
Dean@1095
   954
            }
Dean@1095
   955
Dean@1095
   956
            return;
Dean@1095
   957
        }
Dean@1095
   958
Dean@1095
   959
        /// <summary>
Dean@384
   960
        /// Applies this pEp message's data to the given Outlook item.
Dean@384
   961
        /// </summary>
Dean@384
   962
        /// <param name="omi">The Outlook mail item to apply this pEp message's data to.</param>
Thomas@1299
   963
        /// <param name="setInternalHeaderFields">Whether to set internal header fields (Stored as MAPI properites) such as 
Dean@1130
   964
        /// Rating and KeyList. Warning: Never apply these to outgoing messages -- used only for mirrors or internal MailItems.
Dean@1212
   965
        /// Note that some header fields will be applied regardless such as NeverUnsecure or PEPProtocolVersion. 
Dean@1094
   966
        /// That is because these are not considered internal only and don't contain sensitive information.</param>
Thomas@1299
   967
        /// /// <param name="setSender">Whether to set the sender manually. This is only needed when creating a sent message. 
Thomas@1299
   968
        /// Important: Setting this to true for outgoing messages can cause problems with Exchange accounts.</param>
Dean@910
   969
        /// <returns>The status of the method.</returns>
Dean@910
   970
        public Globals.ReturnStatus ApplyTo(Outlook.MailItem omi,
Thomas@1299
   971
                                            bool setInternalHeaderFields = false,
Thomas@1299
   972
                                            bool setSender = false)
Dean@384
   973
        {
Dean@1113
   974
            byte[] bytes;
Dean@543
   975
            Outlook.Attachments attachments = null;
Dean@543
   976
            Outlook.Recipient newRecipient = null;
Dean@543
   977
            Outlook.Recipients recipients = null;
Dean@543
   978
            Outlook.Account currAccount = null;
Dean@543
   979
            Outlook.Account sendUsingAccount = null;
Dean@543
   980
            Outlook.Accounts accounts = null;
Thomas@1119
   981
            Outlook.Recipient fromRecipient = null;
Dean@910
   982
            Globals.ReturnStatus status = Globals.ReturnStatus.Success;
Thomas@1119
   983
            bool fromRecipientRemoved = false;
Dean@1231
   984
            bool isPGPMIMEMsg = this.IsPGPMIMEEncrypted;
Dean@408
   985
Dean@543
   986
            try
Dean@408
   987
            {
Dean@1094
   988
                // Remove all existing recipients
Dean@543
   989
                recipients = omi.Recipients;
Dean@543
   990
                while (recipients.Count > 0)
Dean@543
   991
                {
Dean@543
   992
                    recipients.Remove(1);
Dean@543
   993
                }
Dean@543
   994
Dean@543
   995
                // Set recipients
Dean@1348
   996
                for (int i = 0; i < this._Bcc.Count; i++)
Dean@543
   997
                {
Dean@1348
   998
                    if (string.IsNullOrWhiteSpace(this._Bcc[i].Address) == false)
Dean@543
   999
                    {
Dean@795
  1000
                        // Add by address
Dean@1348
  1001
                        newRecipient = recipients.Add(this._Bcc[i].Address);
Dean@543
  1002
                        newRecipient.Type = (int)Outlook.OlMailRecipientType.olBCC;
Dean@543
  1003
Dean@543
  1004
                        Marshal.ReleaseComObject(newRecipient);
Dean@543
  1005
                        newRecipient = null;
Dean@543
  1006
                    }
Dean@1348
  1007
                    else if (string.IsNullOrWhiteSpace(this._Bcc[i].UserName) == false)
Dean@795
  1008
                    {
Dean@1348
  1009
                        // Add by user name (required for distribution lists)
Dean@1348
  1010
                        newRecipient = recipients.Add(this._Bcc[i].UserName);
Dean@795
  1011
                        newRecipient.Type = (int)Outlook.OlMailRecipientType.olBCC;
Dean@795
  1012
Dean@795
  1013
                        Marshal.ReleaseComObject(newRecipient);
Dean@795
  1014
                        newRecipient = null;
Dean@795
  1015
                    }
Dean@543
  1016
                }
Dean@543
  1017
Dean@1348
  1018
                for (int i = 0; i < this._Cc.Count; i++)
Dean@543
  1019
                {
Dean@1348
  1020
                    if (string.IsNullOrWhiteSpace(this._Cc[i].Address) == false)
Dean@543
  1021
                    {
Dean@795
  1022
                        // Add by address
Dean@1348
  1023
                        newRecipient = recipients.Add(this._Cc[i].Address);
Dean@543
  1024
                        newRecipient.Type = (int)Outlook.OlMailRecipientType.olCC;
Dean@543
  1025
Dean@543
  1026
                        Marshal.ReleaseComObject(newRecipient);
Dean@543
  1027
                        newRecipient = null;
Dean@543
  1028
                    }
Dean@1348
  1029
                    else if (string.IsNullOrWhiteSpace(this._Cc[i].UserName) == false)
Dean@795
  1030
                    {
Dean@1348
  1031
                        // Add by user name (required for distribution lists)
Dean@1348
  1032
                        newRecipient = recipients.Add(this._Cc[i].UserName);
Dean@795
  1033
                        newRecipient.Type = (int)Outlook.OlMailRecipientType.olCC;
Dean@795
  1034
Dean@795
  1035
                        Marshal.ReleaseComObject(newRecipient);
Dean@795
  1036
                        newRecipient = null;
Dean@795
  1037
                    }
Dean@543
  1038
                }
Dean@543
  1039
Dean@543
  1040
                for (int i = 0; i < this._To.Count; i++)
Dean@543
  1041
                {
Dean@795
  1042
                    if (string.IsNullOrWhiteSpace(this._To[i].Address) == false)
Dean@543
  1043
                    {
Dean@795
  1044
                        // Add by address
Dean@543
  1045
                        newRecipient = recipients.Add(this._To[i].Address);
Dean@543
  1046
                        newRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
Dean@543
  1047
Dean@543
  1048
                        Marshal.ReleaseComObject(newRecipient);
Dean@543
  1049
                        newRecipient = null;
Dean@543
  1050
                    }
Dean@1348
  1051
                    else if (string.IsNullOrWhiteSpace(this._To[i].UserName) == false)
Dean@795
  1052
                    {
Dean@1348
  1053
                        // Add by user name (required for distribution lists)
Dean@1348
  1054
                        newRecipient = recipients.Add(this._To[i].UserName);
Dean@795
  1055
                        newRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
Dean@795
  1056
Dean@795
  1057
                        Marshal.ReleaseComObject(newRecipient);
Dean@795
  1058
                        newRecipient = null;
Dean@795
  1059
                    }
Dean@543
  1060
                }
Dean@543
  1061
Thomas@1119
  1062
                /* Add the pEp From identity as its own recipient. This will be removed later.
Dean@1135
  1063
                 * However, here it simplifies getting the Sender/From AddressEntry, which will be set later.
Dean@1135
  1064
                 * 
Dean@1135
  1065
                 * Note: Outlook will not allow the Sender to be set for received mail items.
Dean@1135
  1066
                 * If you try to set the sender in this situation, it will always throw an error.
Dean@1135
  1067
                 * To mitigate this, the Sender should only be set to the MailItem if one is not already existing that already
Dean@1135
  1068
                 * matches the Sender we would otherwise try to set. This is considered good enough as determining
Dean@1135
  1069
                 * if a mail item is received is not completely reliable.
Thomas@1119
  1070
                 */
Thomas@1299
  1071
                if (setSender)
Thomas@1119
  1072
                {
Thomas@1299
  1073
                    if ((this._From != null) &&
Thomas@1299
  1074
                        ((omi.Sender == null) ||
Thomas@1299
  1075
                         ((omi.Sender != null) &&
Thomas@1299
  1076
                          (this._From != null) &&
Thomas@1299
  1077
                          (this._From.EqualsByAddress(omi.Sender.Address) == false))))
Thomas@1131
  1078
                    {
Thomas@1299
  1079
                        if (string.IsNullOrWhiteSpace(this._From.Address) == false)
Thomas@1299
  1080
                        {
Thomas@1299
  1081
                            // Add by address
Thomas@1299
  1082
                            fromRecipient = recipients.Add(this._From.Address);
Thomas@1299
  1083
                            fromRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
Thomas@1299
  1084
                        }
Dean@1348
  1085
                        else if (string.IsNullOrWhiteSpace(this._From.UserName) == false)
Thomas@1299
  1086
                        {
Dean@1348
  1087
                            // Add by user name (required for distribution lists)
Dean@1348
  1088
                            fromRecipient = recipients.Add(this._From.UserName);
Thomas@1299
  1089
                            fromRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
Thomas@1299
  1090
                        }
Thomas@1131
  1091
                    }
Thomas@1299
  1092
Thomas@1299
  1093
                    try
Thomas@1131
  1094
                    {
Thomas@1299
  1095
                        recipients.ResolveAll();
Thomas@1131
  1096
                    }
Thomas@1299
  1097
                    catch { }
Thomas@1119
  1098
Thomas@1299
  1099
                    /* Set sender account
Thomas@1299
  1100
                     * Note that if fails, will be empty which eventually will use the default send account
Thomas@1299
  1101
                     * If the send using account is already populated, this cannot be re-set.
Thomas@1299
  1102
                     * So far this doesn't appear to be an issue as it occurs when applying unencrypted data to a mirror.
Thomas@1299
  1103
                     * However, the mirror SendUsingAccount is already correct at this point and doesn't need to be set.
Thomas@1299
  1104
                     */
Thomas@1299
  1105
                    sendUsingAccount = omi.SendUsingAccount;
Dean@543
  1106
Thomas@1299
  1107
                    if ((this._From != null) &&
Thomas@1299
  1108
                        (this._From.Address != null) &&
Thomas@1299
  1109
                        (sendUsingAccount == null))
Thomas@1299
  1110
                    {
Thomas@1299
  1111
                        accounts = Globals.ThisAddIn.Application.Session.Accounts;
Dean@543
  1112
Thomas@1299
  1113
                        // Note: Index starts at 1
Thomas@1299
  1114
                        for (int i = 1; i <= accounts.Count; i++)
Thomas@1299
  1115
                        {
Thomas@1299
  1116
                            currAccount = accounts[i];
Dean@543
  1117
Thomas@1299
  1118
                            if ((currAccount.SmtpAddress != null) &&
Dean@1349
  1119
                                (currAccount.SmtpAddress.ToUpperInvariant() == this._From.Address.ToUpperInvariant()))
Thomas@1299
  1120
                            {
Thomas@1299
  1121
                                /* Try to set the SendUsingAccount
Thomas@1299
  1122
                                 * This will fail if the mail item is already marked as sent or the SendUsingAccount is not null, etc...
Thomas@1299
  1123
                                 * If it fails, Outlook will in the end just use the default account.
Thomas@1299
  1124
                                 * This property should ideally be set before a mail item is saved.
Thomas@1299
  1125
                                 */
Thomas@1299
  1126
                                try
Thomas@1299
  1127
                                {
Thomas@1299
  1128
                                    omi.SendUsingAccount = currAccount;
Thomas@1299
  1129
                                }
Thomas@1299
  1130
                                catch { }
Dean@543
  1131
Thomas@1299
  1132
                                Marshal.ReleaseComObject(currAccount);
Thomas@1299
  1133
                                currAccount = null;
Thomas@1299
  1134
Thomas@1299
  1135
                                break;
Dean@543
  1136
                            }
Dean@543
  1137
Dean@543
  1138
                            Marshal.ReleaseComObject(currAccount);
Dean@543
  1139
                            currAccount = null;
Thomas@1299
  1140
                        }
Thomas@1299
  1141
                    }
Dean@543
  1142
Thomas@1299
  1143
                    /* Set Sender
Thomas@1299
  1144
                     * Outlook takes the Sender property to display the respective name in the Sent folder.
Thomas@1299
  1145
                     * This Sender property is based on the name, the email address and the EntryID MAPI properties. As the EntryID can't be accessed
Thomas@1299
  1146
                     * directly with MAPI methods, we have to set these properties indirectly through the Sender.
Thomas@1299
  1147
                     */
Thomas@1299
  1148
                    if (fromRecipient != null)
Thomas@1299
  1149
                    {
Thomas@1299
  1150
                        try
Thomas@1299
  1151
                        {
Thomas@1299
  1152
                            omi.Sender = fromRecipient.AddressEntry;
Thomas@1299
  1153
                        }
Thomas@1299
  1154
                        catch
Thomas@1299
  1155
                        {
Thomas@1299
  1156
                            Log.Warning("PEPMessage.ApplyTo: Failed to set the Sender.");
Dean@543
  1157
                        }
Dean@543
  1158
Thomas@1299
  1159
                        // Remove the From recipient from the Recipients collection by searching for a match by EntryID
Thomas@1299
  1160
                        for (int i = 1; i <= recipients.Count; i++)
Thomas@1299
  1161
                        {
Thomas@1299
  1162
                            newRecipient = recipients[i];
Thomas@1299
  1163
                            if (newRecipient != null)
Thomas@1299
  1164
                            {
Thomas@1299
  1165
                                if ((string.IsNullOrEmpty(newRecipient.EntryID) == false) &&
Thomas@1299
  1166
                                    (string.IsNullOrEmpty(fromRecipient.EntryID) == false) &&
Thomas@1299
  1167
                                    (newRecipient.EntryID == fromRecipient.EntryID))
Thomas@1299
  1168
                                {
Thomas@1299
  1169
                                    try
Thomas@1299
  1170
                                    {
Thomas@1299
  1171
                                        recipients.Remove(i);
Thomas@1299
  1172
                                        fromRecipientRemoved = true;
Thomas@1299
  1173
                                    }
Thomas@1299
  1174
                                    catch
Thomas@1299
  1175
                                    {
Thomas@1299
  1176
                                        Log.Warning("PEPMessage.ApplyTo: Error removing 'From' recipient from Recipients collection.");
Thomas@1299
  1177
                                    }
Thomas@1299
  1178
                                    break;
Thomas@1299
  1179
                                }
Thomas@1299
  1180
Thomas@1299
  1181
                                Marshal.ReleaseComObject(newRecipient);
Thomas@1299
  1182
                                newRecipient = null;
Thomas@1299
  1183
                            }
Thomas@1299
  1184
                        }
Thomas@1299
  1185
Thomas@1299
  1186
                        // Fallback solution in case there is no EntryID match
Thomas@1299
  1187
                        if (fromRecipientRemoved == false)
Thomas@1299
  1188
                        {
Thomas@1299
  1189
                            try
Thomas@1299
  1190
                            {
Thomas@1299
  1191
                                recipients.Remove(recipients.Count);
Thomas@1299
  1192
                                Log.Warning("PEPMessage.ApplyTo: No EntryID match for 'From' recipient in Recipients collection. Removed using fallback solution.");
Thomas@1299
  1193
                            }
Thomas@1299
  1194
                            catch
Thomas@1299
  1195
                            {
Thomas@1299
  1196
                                Log.Error("PEPMessage.ApplyTo: No EntryID match for 'From' recipient in Recipients collection. Fallback solution also failed.");
Thomas@1299
  1197
                            }
Thomas@1299
  1198
                        }
Thomas@1299
  1199
Thomas@1299
  1200
                        // Set Sender's name and email properties via MAPIProperty (won't be created automatically when assigníng Sender)
Thomas@1299
  1201
                        try
Thomas@1299
  1202
                        {
Dean@1350
  1203
                            MapiHelper.SetProperty(omi, MapiProperty.PidTagSenderName, this._From.UserName);
Dean@1350
  1204
                            MapiHelper.SetProperty(omi, MapiProperty.PidTagSenderEmailAddress, this._From.Address);
Thomas@1299
  1205
                        }
Thomas@1299
  1206
                        catch (Exception ex)
Thomas@1299
  1207
                        {
Thomas@1299
  1208
                            Log.Warning("PEPMessage.ApplyTo: Unable to set sender's name or email address via MAPIProperty. " + ex.Message);
Thomas@1299
  1209
                        }
Thomas@1299
  1210
Thomas@1299
  1211
                        Marshal.ReleaseComObject(fromRecipient);
Thomas@1299
  1212
                        fromRecipient = null;
Dean@543
  1213
                    }
Dean@543
  1214
                }
Thomas@1299
  1215
                else
Thomas@1119
  1216
                {
Thomas@1119
  1217
                    try
Thomas@1119
  1218
                    {
Thomas@1299
  1219
                        recipients.ResolveAll();
Thomas@1119
  1220
                    }
Thomas@1299
  1221
                    catch { }
Thomas@1119
  1222
                }
Thomas@1119
  1223
Dean@1056
  1224
                /* Set the encoding to UTF-8
Dean@1056
  1225
                 * See: https://msdn.microsoft.com/en-us/library/office/ff860730.aspx
Dean@1058
  1226
                 * All PEPMessages should be UTF especially those coming from the engine.
Dean@1058
  1227
                 * 
Dean@1058
  1228
                 * It is important that the encoding is set BEFORE the body and other text of the 
Dean@1058
  1229
                 * mail item. This is necessary to avoid character encoding issues where
Dean@1058
  1230
                 * Outlook will not respect the UTF encoding of strings set to the body/subject/etc.
Dean@1058
  1231
                 * It will instead try to use the strings with the encoding of the codepage.
Dean@1056
  1232
                 */
Dean@1056
  1233
                try
Dean@1056
  1234
                {
Dean@1350
  1235
                    MapiHelper.SetProperty(omi, MapiProperty.PidTagInternetCodepage, 65001);
Dean@1350
  1236
                    MapiHelper.SetProperty(omi, MapiProperty.PidTagMessageCodepage, 65001);
Dean@1056
  1237
                }
Dean@1056
  1238
                catch
Dean@1056
  1239
                {
Dean@1249
  1240
                    Log.Warning("PEPMessage.ApplyTo: Failed to set UTF-8 encoding.");
Dean@1056
  1241
                }
Dean@1056
  1242
Dean@543
  1243
                // Set the subject
Dean@543
  1244
                omi.Subject = this._ShortMsg;
Dean@543
  1245
Dean@1224
  1246
                /* Set the body
Dean@1224
  1247
                 * PGP/MIME format must be handled specially.
Dean@1224
  1248
                 * In addition, RTF format is ignored. Only HTML is used the same as the pEp engine.
Dean@1224
  1249
                 */
Dean@1231
  1250
                if (isPGPMIMEMsg)
Dean@1224
  1251
                {
Dean@1236
  1252
                    /* Clear any old body content, these cannot exist for PGP/MIME encrypted messages.
Dean@1236
  1253
                     * Note that setting the RTFBody to null or an empty byte array throws an exception.
Dean@1236
  1254
                     * Therefore, it is sent to a single zero byte to be constant.
Dean@1236
  1255
                     * The RTFBody is ignored when sending the actual message.
Dean@1236
  1256
                     */
markus@1228
  1257
                    omi.BodyFormat = Outlook.OlBodyFormat.olFormatPlain;
Dean@1236
  1258
                    omi.Body = null;
markus@1228
  1259
                    omi.HTMLBody = null;
Dean@1236
  1260
                    omi.RTFBody = new byte[] { 0x00 };
markus@1228
  1261
Dean@1236
  1262
                    // Now, set the mail mime type to S/MIME multipart/signed, this is used later to add a special attachment
Dean@1275
  1263
                    try
Dean@1275
  1264
                    {
Dean@1350
  1265
                        MapiHelper.SetProperty(omi, MapiProperty.PidTagMessageClass, MapiPropertyValue.PidTagMessageClassSMIMEMultipartSigned);
Dean@1275
  1266
                    }
Dean@1275
  1267
                    catch
Dean@1275
  1268
                    {
Dean@1275
  1269
                        status = Globals.ReturnStatus.Failure;
Dean@1275
  1270
                        throw new Exception("Failure to set message class for PGP/MIME message.");
Dean@1275
  1271
                    }
Dean@1224
  1272
                }
Dean@1354
  1273
                else if (string.IsNullOrWhiteSpace(this._LongMsgFormattedHtml))
Dean@543
  1274
                {
Dean@543
  1275
                    omi.BodyFormat = Outlook.OlBodyFormat.olFormatPlain;
Dean@543
  1276
                    omi.Body = this._LongMsg;
Dean@543
  1277
                }
Dean@543
  1278
                else
Dean@543
  1279
                {
Dean@543
  1280
                    omi.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
Dean@1354
  1281
                    omi.HTMLBody = this._LongMsgFormattedHtml;
Dean@543
  1282
                }
Dean@543
  1283
Dean@543
  1284
                // Remove any previous attachments
Dean@543
  1285
                attachments = omi.Attachments;
Dean@543
  1286
                while (attachments.Count > 0)
Dean@543
  1287
                {
Dean@543
  1288
                    attachments.Remove(1);
Dean@543
  1289
                }
Dean@543
  1290
Dean@1231
  1291
                if (isPGPMIMEMsg)
Dean@543
  1292
                {
Dean@1236
  1293
                    PEPAttachment pgpMIME = this.ConvertToPGPMIMEAttachment();
Dean@1236
  1294
Dean@1236
  1295
                    if (pgpMIME != null)
Dean@1236
  1296
                    {
Dean@1236
  1297
                        pgpMIME.AddTo(attachments);
Dean@1236
  1298
                    }
Dean@1236
  1299
                    else
Dean@1236
  1300
                    {
Dean@1236
  1301
                        throw new Exception("Failed to create and add PGP/MIME attachment.");
Dean@1236
  1302
                    }
Dean@1224
  1303
                }
Dean@1224
  1304
                else
Dean@1224
  1305
                {
Dean@1224
  1306
                    for (int i = 0; i < this._Attachments.Count; i++)
Dean@1224
  1307
                    {
Dean@1236
  1308
                        this._Attachments[i].AddTo(attachments, ("attachment" + i.ToString()));
Dean@1224
  1309
                    }
Dean@543
  1310
                }
Dean@543
  1311
Dean@1095
  1312
                // ID
Dean@1224
  1313
                try
Dean@1224
  1314
                {
Dean@1350
  1315
                    MapiHelper.SetProperty(omi, MapiProperty.PidTagInternetMessageId, this._Id);
Dean@1224
  1316
                }
Dean@1224
  1317
                catch { }
Dean@1095
  1318
Dean@1113
  1319
                // Conversation information
Dean@1113
  1320
                try
Dean@1113
  1321
                {
Dean@1113
  1322
                    /* Note: PidTagConversationId cannot be set even through the MAPI accessor.
Dean@1113
  1323
                     * This is by design since this property is computed automatically from other properties.
Dean@1113
  1324
                     * See: https://msdn.microsoft.com/en-us/library/ee204279.aspx
Dean@1113
  1325
                     */
Dean@1350
  1326
                    bytes = MapiHelper.StringToPtypBinary(this._ConversationIndex);
Dean@1217
  1327
                    if ((bytes != null) &&
Dean@1217
  1328
                        (bytes.Length > 0))
Dean@1113
  1329
                    {
Dean@1350
  1330
                        MapiHelper.SetProperty(omi, MapiProperty.PidTagConversationIndex, bytes);
Dean@1113
  1331
                    }
Dean@1113
  1332
Dean@1350
  1333
                    MapiHelper.SetProperty(omi, MapiProperty.PidTagConversationIndexTracking, false);
Dean@1350
  1334
                    MapiHelper.SetProperty(omi, MapiProperty.PidTagConversationTopic, this._ConversationTopic);
Dean@1113
  1335
                }
Dean@1113
  1336
                catch (Exception ex1)
Dean@1113
  1337
                {
Dean@1249
  1338
                    Log.Warning("PEPMessage.ApplyTo: Failure setting conversation information. " + ex1.ToString());
Dean@1113
  1339
                }
Dean@1113
  1340
Dean@1094
  1341
                /* Add internal properties
Dean@1154
  1342
                 * Note: there is no need to check for defaults as it's all handled within the SetInterpretedProperty method.
Dean@1154
  1343
                 * Also, .Save() must never be called here as the .ApplyTo() method can be used on MailItems that need to change properties before save.
Dean@1094
  1344
                 */
Thomas@1299
  1345
                if (setInternalHeaderFields)
Dean@543
  1346
                {
Dean@1303
  1347
                    // Note: ignore return status
Dean@1303
  1348
                    omi.SetPEPProperty(MailItemExtensions.PEPProperty.KeyList, this._KeyList);
Dean@1303
  1349
                    omi.SetPEPProperty(MailItemExtensions.PEPProperty.Rating, this._Rating);
Dean@543
  1350
                }
Dean@1094
  1351
Dean@1303
  1352
                // Note: ignore return status
Dean@1303
  1353
                omi.SetPEPProperty(MailItemExtensions.PEPProperty.NeverUnsecure, this._NeverUnsecure);
Dean@1303
  1354
                omi.SetPEPProperty(MailItemExtensions.PEPProperty.PEPProtocolVersion, this._PEPProtocolVersion);
Dean@408
  1355
            }
Dean@545
  1356
            catch (Exception ex)
Dean@545
  1357
            {
Dean@910
  1358
                status = Globals.ReturnStatus.Failure;
Dean@1249
  1359
                Log.Error("PEPMessage.ApplyTo: Failure occured, " + ex.ToString());
Dean@545
  1360
            }
Dean@543
  1361
            finally
Dean@543
  1362
            {
Dean@543
  1363
                if (attachments != null)
Dean@466
  1364
                {
Dean@543
  1365
                    Marshal.ReleaseComObject(attachments);
Dean@543
  1366
                    attachments = null;
Dean@543
  1367
                }
Dean@408
  1368
Dean@543
  1369
                if (newRecipient != null)
Dean@543
  1370
                {
Dean@466
  1371
                    Marshal.ReleaseComObject(newRecipient);
Dean@543
  1372
                    newRecipient = null;
Dean@543
  1373
                }
Dean@543
  1374
Dean@543
  1375
                if (recipients != null)
Dean@543
  1376
                {
Dean@543
  1377
                    Marshal.ReleaseComObject(recipients);
Dean@543
  1378
                    recipients = null;
Dean@543
  1379
                }
Dean@543
  1380
Dean@543
  1381
                if (currAccount != null)
Dean@543
  1382
                {
Dean@543
  1383
                    Marshal.ReleaseComObject(currAccount);
Dean@543
  1384
                    currAccount = null;
Dean@543
  1385
                }
Dean@543
  1386
Dean@543
  1387
                if (sendUsingAccount != null)
Dean@543
  1388
                {
Dean@543
  1389
                    Marshal.ReleaseComObject(sendUsingAccount);
Dean@543
  1390
                    sendUsingAccount = null;
Dean@543
  1391
                }
Dean@543
  1392
Dean@543
  1393
                if (accounts != null)
Dean@543
  1394
                {
Dean@543
  1395
                    Marshal.ReleaseComObject(accounts);
Dean@543
  1396
                    accounts = null;
Dean@543
  1397
                }
Thomas@1119
  1398
Thomas@1119
  1399
                if (fromRecipient != null)
Thomas@1119
  1400
                {
Thomas@1119
  1401
                    Marshal.ReleaseComObject(fromRecipient);
Thomas@1119
  1402
                    fromRecipient = null;
Thomas@1119
  1403
                }
Dean@408
  1404
            }
Dean@408
  1405
Dean@910
  1406
            return (status);
Dean@384
  1407
        }
Dean@384
  1408
Dean@1236
  1409
        /// <summary>
Dean@1236
  1410
        /// Converts this message into a single PGP/MIME attachment.
Dean@1236
  1411
        /// This is necessary in order for Outlook and MAPI to correctly transport PGP/MIME messages.
Dean@1236
  1412
        /// The attachment will report itself as S/MIME to MAPI, S/MIME attachments are passed-through as unmodified text.
Dean@1236
  1413
        /// Since we can insert unmodified text, this allows us to build a correct PGP/MIME message from existing attachments.
Dean@1236
  1414
        /// Warning: This can return null if a failure occured.
Dean@1236
  1415
        /// </summary>
Dean@1236
  1416
        /// <returns>This message converted to a single PGP/MIME attachment, otherwise null.</returns>
Dean@1236
  1417
        private PEPAttachment ConvertToPGPMIMEAttachment()
markus@1221
  1418
        {
Dean@1236
  1419
            string boundary;
Dean@1236
  1420
            byte[] bytes;
Dean@1236
  1421
            bool versionInfoFound = false;
Dean@1236
  1422
            bool contentFound = false;
Dean@1236
  1423
            PEPAttachment newAttachment = null;
Dean@1236
  1424
            List<byte> data = new List<byte>();
markus@1221
  1425
Dean@1236
  1426
            if (this._Attachments.Count == 2)
markus@1221
  1427
            {
Dean@1236
  1428
                boundary = "----=_NextPart_" + Guid.NewGuid().ToString().Replace('-', '_');
markus@1221
  1429
Dean@1236
  1430
                /* See below for an example PGP/MIME formatted message.
Dean@1236
  1431
                 * this is from RFC 3156.  
Dean@1236
  1432
                 *    
Dean@1236
  1433
                 *  Content-Type: multipart/encrypted; boundary=foo;
Dean@1236
  1434
                 *     protocol="application/pgp-encrypted"
Dean@1236
  1435
                 * 
Dean@1236
  1436
                 *  --foo
Dean@1236
  1437
                 *  Content-Type: application/pgp-encrypted
Dean@1236
  1438
                 * 
Dean@1236
  1439
                 *  Version: 1
Dean@1236
  1440
                 * 
Dean@1236
  1441
                 *  --foo
Dean@1236
  1442
                 *  Content-Type: application/octet-stream
Dean@1236
  1443
                 * 
Dean@1236
  1444
                 *  -----BEGIN PGP MESSAGE-----
Dean@1236
  1445
                 *  Version: 2.6.2
Dean@1236
  1446
                 * 
Dean@1236
  1447
                 *  hIwDY32hYGCE8MkBA/wOu7d45aUxF4Q0RKJprD3v5Z9K1YcRJ2fve87lMlDlx4Oj
Dean@1236
  1448
                 *  eW4GDdBfLbJE7VUpp13N19GL8e/AqbyyjHH4aS0YoTk10QQ9nnRvjY8nZL3MPXSZ
Dean@1236
  1449
                 *  g9VGQxFeGqzykzmykU6A26MSMexR4ApeeON6xzZWfo+0yOqAq6lb46wsvldZ96YA
Dean@1236
  1450
                 *  AABH78hyX7YX4uT1tNCWEIIBoqqvCeIMpp7UQ2IzBrXg6GtukS8NxbukLeamqVW3
Dean@1236
  1451
                 *  1yt21DYOjuLzcMNe/JNsD9vDVCvOOG3OCi8=
Dean@1236
  1452
                 *  =zzaA
Dean@1236
  1453
                 *  -----END PGP MESSAGE-----
Dean@1236
  1454
                 * 
Dean@1236
  1455
                 *  --foo--
Dean@1236
  1456
                 */
Dean@1236
  1457
Dean@1236
  1458
                // Add internet headers (these are not added by default for S/MIME messages which allows them to be customized)
Dean@1240
  1459
                bytes = Encoding.UTF8.GetBytes("MIME-Version: 1.0" + Environment.NewLine +
Dean@1240
  1460
                                               "Content-Type: multipart/encrypted;" + Environment.NewLine +
Dean@1240
  1461
                                               "\tprotocol=\"application/pgp-encrypted\";" + Environment.NewLine +
Dean@1240
  1462
                                               "\tboundary=\"" + boundary + "\"" + Environment.NewLine);
Dean@1236
  1463
                data.AddRange(bytes);
Dean@1236
  1464
Dean@1236
  1465
                // Add the version identification attachment
Dean@1240
  1466
                bytes = Encoding.UTF8.GetBytes(Environment.NewLine +
Dean@1240
  1467
                                               "--" + boundary + Environment.NewLine +
Dean@1240
  1468
                                               "Content-Type: application/pgp-encrypted" + Environment.NewLine +
Dean@1285
  1469
                                               "Content-Description: PGP/MIME version identification" + Environment.NewLine +
Dean@1240
  1470
                                               Environment.NewLine);
Dean@1236
  1471
                data.AddRange(bytes);
Dean@1236
  1472
Dean@1236
  1473
                foreach (PEPAttachment attach in this._Attachments)
markus@1221
  1474
                {
Dean@1236
  1475
                    if (PEPMessage.IsPGPMIMEVersionInfoFormat(attach))
Dean@1236
  1476
                    {
Dean@1236
  1477
                        data.AddRange(attach.Data);
Dean@1236
  1478
                        versionInfoFound = true;
Dean@1236
  1479
                        break;
Dean@1236
  1480
                    }
markus@1221
  1481
                }
markus@1221
  1482
Dean@1236
  1483
                // Add the content attachment
Dean@1240
  1484
                bytes = Encoding.UTF8.GetBytes(Environment.NewLine +
Dean@1240
  1485
                                               Environment.NewLine +
Dean@1240
  1486
                                               "--" + boundary + Environment.NewLine +
Dean@1285
  1487
                                               "Content-Type: application/octet-stream;" + Environment.NewLine +
Dean@1285
  1488
                                               "\tname=\"msg.asc\"" + Environment.NewLine +
Dean@1240
  1489
                                               Environment.NewLine);
Dean@1236
  1490
                data.AddRange(bytes);
Dean@1236
  1491
Dean@1236
  1492
                foreach (PEPAttachment attach in this._Attachments)
markus@1221
  1493
                {
Dean@1236
  1494
                    if (PEPMessage.IsPGPMIMEContentFormat(attach))
Dean@1236
  1495
                    {
Dean@1236
  1496
                        data.AddRange(attach.Data);
Dean@1236
  1497
                        contentFound = true;
Dean@1236
  1498
                        break;
Dean@1236
  1499
                    }
markus@1221
  1500
                }
Dean@1236
  1501
Dean@1240
  1502
                bytes = Encoding.UTF8.GetBytes(Environment.NewLine +
Dean@1240
  1503
                                               "--" + boundary + "--" + Environment.NewLine);
Dean@1236
  1504
                data.AddRange(bytes);
Dean@1236
  1505
Dean@1236
  1506
                // Create the new attachment
Dean@1236
  1507
                if (versionInfoFound && contentFound)
markus@1221
  1508
                {
Dean@1236
  1509
                    newAttachment = new PEPAttachment()
Dean@1236
  1510
                    {
Dean@1236
  1511
                        Data = data.ToArray(),
Dean@1236
  1512
                        MIMEType = "multipart/signed",
Dean@1350
  1513
                        Tag = MapiPropertyValue.PidTagAttachTagMIME
Dean@1236
  1514
                    };
markus@1221
  1515
                }
markus@1221
  1516
            }
markus@1221
  1517
Dean@1236
  1518
            return (newAttachment);
markus@1221
  1519
        }
markus@1221
  1520
Dean@398
  1521
        /// <summary>
Dean@1348
  1522
        /// Recursivley converts all "Bcc", "Cc", and "To" identities into a 'flat' list of any members.
Dean@398
  1523
        /// This will remove groups (hierarchy) and convert a group into it's members.
Dean@398
  1524
        /// </summary>
Dean@398
  1525
        public void FlattenAllRecipientIdentities()
Dean@398
  1526
        {
Dean@1348
  1527
            this._Bcc = PEPIdentity.ToFlatList(this._Bcc);
Dean@1348
  1528
            this._Cc = PEPIdentity.ToFlatList(this._Cc);
Dean@398
  1529
            this._To = PEPIdentity.ToFlatList(this._To);
Dean@398
  1530
Dean@398
  1531
            return;
Dean@398
  1532
        }
Dean@413
  1533
Dean@808
  1534
        /// <summary>
Dean@808
  1535
        /// Processes all content IDs in the image attachments as well as HTML body.
Dean@808
  1536
        /// This will convert the CID's to the image filename only.
Dean@808
  1537
        /// </summary>
Dean@808
  1538
        public void NormalizeContentIDs()
Dean@808
  1539
        {
Dean@808
  1540
            int currImgTagIndex;
Dean@808
  1541
            int currImgTagStopIndex;
Dean@808
  1542
            int currImgTagCIDIndex;
Dean@808
  1543
            int currImgTagCIDStopIndex;
Dean@920
  1544
            int tempIndex;
Dean@808
  1545
            string currCID;
Dean@808
  1546
            string newCID;
Dean@808
  1547
            string beginning;
Dean@808
  1548
            string ending;
Dean@808
  1549
            string currFileName;
Dean@1354
  1550
            string workingHtml;
Dean@808
  1551
            Dictionary<string, string> modifiedCIDs = new Dictionary<string, string>();
Dean@808
  1552
Dean@808
  1553
            // Modify CIDs for all attachments and store modified CIDs in the dictionary
Dean@808
  1554
            for (int i = 0; i < this._Attachments.Count; i++)
Dean@808
  1555
            {
Dean@808
  1556
                currCID = this._Attachments[i].ContentID;
Dean@808
  1557
                currFileName = this._Attachments[i].FileName;
Dean@808
  1558
Dean@808
  1559
                if (string.IsNullOrEmpty(currCID))
Dean@808
  1560
                {
Dean@920
  1561
                    /* Just use the file name (even if file name is null or empty)
Dean@920
  1562
                     * It's doesn't matter as it shouldn't be replaced in HTML.
Dean@920
  1563
                     * This situation should mean either the attachment isn't an embedded image, or
Dean@920
  1564
                     * the filename is already used as the CID in the HTML.
Dean@920
  1565
                     */
Dean@808
  1566
                    newCID = currFileName;
Dean@808
  1567
                }
Dean@808
  1568
                else
Dean@808
  1569
                {
Dean@808
  1570
                    if (string.IsNullOrEmpty(currFileName))
Dean@808
  1571
                    {
Dean@808
  1572
                        // A name must exist for HTML so create one
Dean@808
  1573
                        newCID = "attachment" + i.ToString();
Dean@808
  1574
                    }
Dean@808
  1575
                    else
Dean@808
  1576
                    {
Dean@843
  1577
                        newCID = currFileName;
Dean@808
  1578
                    }
Dean@808
  1579
Dean@808
  1580
                    modifiedCIDs.Add(currCID, newCID);
Dean@808
  1581
                }
Dean@808
  1582
Dean@808
  1583
                this._Attachments[i].ContentID = newCID;
Dean@808
  1584
            }
Dean@808
  1585
Dean@808
  1586
            /* Process the HTML body replacing all modified CIDs
Dean@808
  1587
             * Since no HTML document library is provided in standard C#, text operations are done.
Dean@808
  1588
             * This should work ok, but there are some potential issue with malformed HTML that might render correctly.
Dean@808
  1589
             * Only " is supported instead of ' as well.
Dean@808
  1590
             * This shouldn't be an issue since the method is primarily run on mail items generated internally.
Dean@808
  1591
             */
Dean@1354
  1592
            workingHtml = this._LongMsgFormattedHtml;
Dean@1354
  1593
            if (string.IsNullOrEmpty(workingHtml) == false)
Dean@808
  1594
            {
Dean@1354
  1595
                tempIndex = workingHtml.IndexOf("<img", 0);
Dean@920
  1596
                currImgTagIndex = (tempIndex > -1 ? (tempIndex + 4) : -1); // MUST start index after "<img"
Dean@808
  1597
Dean@808
  1598
                while (currImgTagIndex > -1)
Dean@808
  1599
                {
Dean@920
  1600
                    try
Dean@920
  1601
                    {
Dean@1354
  1602
                        tempIndex = workingHtml.IndexOf("src=\"cid:", currImgTagIndex);
Dean@920
  1603
                        currImgTagCIDIndex = (tempIndex > -1 ? (tempIndex + 9) : -1);
Dean@1354
  1604
                        currImgTagCIDStopIndex = workingHtml.IndexOf("\"", currImgTagCIDIndex);
Dean@1354
  1605
                        currImgTagStopIndex = workingHtml.IndexOf(">", currImgTagIndex);
Dean@920
  1606
                    }
Dean@920
  1607
                    catch
Dean@920
  1608
                    {
Dean@1249
  1609
                        Log.Warning("NormalizeContentIDs: Incorrect index detected when calculating CID position, skipping current img.");
Dean@920
  1610
Dean@920
  1611
                        // Likely System.ArgumentOutOfRangeException from incorrect index
Dean@920
  1612
                        // Just invalidate all locations and will not change this <img>
Dean@920
  1613
                        currImgTagCIDIndex = -1;
Dean@920
  1614
                        currImgTagCIDStopIndex = -1;
Dean@920
  1615
                        currImgTagStopIndex = -1;
Dean@920
  1616
                    }
Dean@843
  1617
Dean@808
  1618
                    // Validate relative index positions
Dean@808
  1619
                    if ((currImgTagCIDIndex < currImgTagStopIndex) &&
Dean@808
  1620
                        (currImgTagCIDStopIndex < currImgTagStopIndex) &&
Dean@808
  1621
                        ((currImgTagCIDStopIndex - currImgTagCIDIndex) >= 1))
Dean@808
  1622
                    {
Dean@808
  1623
                        // Split the HTML at the CID and modify if necessary
Dean@920
  1624
                        try
Dean@920
  1625
                        {
Dean@1354
  1626
                            beginning = workingHtml.Substring(0, currImgTagCIDIndex);
Dean@1354
  1627
                            ending = workingHtml.Substring(currImgTagCIDStopIndex);
Dean@1354
  1628
                            currCID = workingHtml.Substring(currImgTagCIDIndex, (currImgTagCIDStopIndex - currImgTagCIDIndex));
Dean@920
  1629
                        }
Dean@920
  1630
                        catch
Dean@920
  1631
                        {
Dean@1249
  1632
                            Log.Warning("NormalizeContentIDs: Error splitting HTML at CID, skipping current img.");
Dean@920
  1633
Dean@920
  1634
                            beginning = null;
Dean@920
  1635
                            ending = null;
Dean@920
  1636
                            currCID = null;
Dean@920
  1637
                        }
Dean@808
  1638
Dean@808
  1639
                        // Lookup the new CID
Dean@808
  1640
                        newCID = null;
Dean@808
  1641
                        try
Dean@808
  1642
                        {
Dean@920
  1643
                            if (currCID != null)
Dean@920
  1644
                            {
Dean@920
  1645
                                newCID = modifiedCIDs[currCID];
Dean@920
  1646
                            }
Dean@808
  1647
                        }
Dean@808
  1648
                        catch
Dean@808
  1649
                        {
Dean@808
  1650
                            newCID = null;
Dean@808
  1651
                        }
Dean@808
  1652
Dean@808
  1653
                        // Replace
Dean@920
  1654
                        if ((beginning != null) &&
Dean@920
  1655
                            (string.IsNullOrEmpty(newCID) == false) &&
Dean@920
  1656
                            (ending != null))
Dean@808
  1657
                        {
Dean@1354
  1658
                            workingHtml = beginning + newCID + ending;
Dean@808
  1659
                        }
Dean@808
  1660
                    }
Dean@808
  1661
Dean@920
  1662
                    try
Dean@920
  1663
                    {
Dean@1354
  1664
                        tempIndex = workingHtml.IndexOf("<img", currImgTagIndex);
Dean@920
  1665
                        currImgTagIndex = (tempIndex > -1 ? (tempIndex + 4) : -1); // MUST start index after "<img"
Dean@920
  1666
                    }
Dean@920
  1667
                    catch
Dean@920
  1668
                    {
Dean@1249
  1669
                        Log.Warning("NormalizeContentIDs: Incorrect index detected, stopping calculation.");
Dean@920
  1670
Dean@920
  1671
                        // Likely System.ArgumentOutOfRangeException from incorrect index
Dean@920
  1672
                        // Stop processing the HTML and use whatever is processed up to this point
Dean@920
  1673
                        currImgTagIndex = -1;
Dean@920
  1674
                    }
Dean@808
  1675
                }
Dean@808
  1676
            }
Dean@1354
  1677
            this._LongMsgFormattedHtml = workingHtml;
Dean@808
  1678
Dean@808
  1679
            return;
Dean@808
  1680
        }
Dean@927
  1681
Dean@927
  1682
        /**************************************************************
Dean@927
  1683
         * 
Dean@927
  1684
         * Static Methods
Dean@927
  1685
         * 
Dean@927
  1686
         *************************************************************/
Dean@927
  1687
Dean@927
  1688
        /// <summary>
markus@1337
  1689
        /// Constructs a new message from the given pEp engine TextMessage.
Dean@927
  1690
        /// The output will never be null.
Dean@927
  1691
        /// </summary>
markus@1337
  1692
        /// <param name="msg">The TextMessage to construct from.</param>
Dean@927
  1693
        /// <param name="createdMessage">The output newly created message (will never be null).</param>
Dean@927
  1694
        /// <returns>The status of the method.</returns>
markus@954
  1695
        /// <remarks>In some contexts which call this message, a partial conversion of the
markus@1337
  1696
        /// <see cref="TextMessage"/> is acceptable, non-critical, and better than failure (e. G. displaying
markus@954
  1697
        /// a preview to the user). Thus, this method will always create a <see cref="PEPMessage"/>
markus@954
  1698
        /// which is "best effort", and return the <see cref="Globals.ReturnStatus"/> which can be evaluated
markus@954
  1699
        /// in the more critical contexts. Callers of that message need to be aware of this
markus@954
  1700
        /// and check the result if appropriate.</remarks>
markus@1337
  1701
        public static Globals.ReturnStatus Create(TextMessage msg,
Dean@927
  1702
                                                  out PEPMessage createdMessage)
Dean@927
  1703
        {
Dean@927
  1704
            Globals.ReturnStatus status = Globals.ReturnStatus.Success;
Dean@927
  1705
            PEPMessage newMessage = new PEPMessage();
Dean@927
  1706
Dean@927
  1707
            try
Dean@927
  1708
            {
markus@1337
  1709
                /* Note: Skip the following properties which are not supported in the TextMessage
Dean@1113
  1710
                 *   • ConversationID
Dean@1113
  1711
                 *   • ConversationIndex
Dean@1113
  1712
                 *   • ConversationTopic
Dean@1095
  1713
                 *   • ForceUnencrypted
Dean@1354
  1714
                 *   • LongMsgFormattedRtf
Dean@1095
  1715
                 * 
Dean@1095
  1716
                 * Also not the following are handled as optional fields
Dean@1130
  1717
                 *   • Rating
Dean@1095
  1718
                 *   • KeyList
Dean@1104
  1719
                 *   • NeverUnsecure
Dean@1212
  1720
                 *   • PEPProtocolVersion
Dean@1095
  1721
                 * 
markus@1337
  1722
                 * This also skips a number of TextMessage properties currently unsupported in PEPMessage.
Dean@1095
  1723
                 */
Dean@1095
  1724
Dean@927
  1725
                // Attachments
Dean@927
  1726
                newMessage.Attachments.Clear();
markus@1347
  1727
                if (msg.Attachments != null)
Dean@927
  1728
                {
markus@1347
  1729
                    for (int i = 0; i < msg.Attachments.Length; i++)
Dean@927
  1730
                    {
markus@1347
  1731
                        newMessage.Attachments.Add(new PEPAttachment((Blob)msg.Attachments.GetValue(i)));
Dean@927
  1732
                    }
Dean@927
  1733
                }
Dean@927
  1734
Dean@1348
  1735
                // Bcc
Dean@1348
  1736
                newMessage.Bcc.Clear();
markus@1347
  1737
                if (msg.Bcc != null)
Dean@927
  1738
                {
markus@1347
  1739
                    for (int i = 0; i < msg.Bcc.Length; i++)
Dean@927
  1740
                    {
Dean@1348
  1741
                        newMessage.Bcc.Add(new PEPIdentity((pEpIdentity)msg.Bcc.GetValue(i)));
Dean@927
  1742
                    }
Dean@927
  1743
                }
Dean@927
  1744
Dean@1348
  1745
                // Cc
Dean@1348
  1746
                newMessage.Cc.Clear();
markus@1347
  1747
                if (msg.Cc != null)
Dean@927
  1748
                {
markus@1347
  1749
                    for (int i = 0; i < msg.Cc.Length; i++)
Dean@927
  1750
                    {
Dean@1348
  1751
                        newMessage.Cc.Add(new PEPIdentity((pEpIdentity)msg.Cc.GetValue(i)));
Dean@927
  1752
                    }
Dean@927
  1753
                }
Dean@927
  1754
markus@1347
  1755
                newMessage.Direction = msg.Dir;
markus@1347
  1756
                newMessage.From = new PEPIdentity(msg.From);
Dean@1348
  1757
                newMessage.Id = msg.Id;
Dean@927
  1758
Dean@927
  1759
                // Keywords
Dean@927
  1760
                newMessage.Keywords.Clear();
markus@1347
  1761
                if (msg.Keywords != null)
Dean@927
  1762
                {
markus@1347
  1763
                    for (int i = 0; i < msg.Keywords.Length; i++)
Dean@927
  1764
                    {
markus@1347
  1765
                        newMessage.Keywords.Add((string)msg.Keywords.GetValue(i));
Dean@927
  1766
                    }
Dean@927
  1767
                }
Dean@927
  1768
markus@1356
  1769
                newMessage.LongMsg = msg.LongMsg;
markus@1356
  1770
                newMessage.LongMsgFormattedHtml = msg.LongMsgFormatted;
Dean@927
  1771
Dean@927
  1772
                // Optional properties
markus@1347
  1773
                if (msg.OptFields != null)
Dean@927
  1774
                {
markus@1347
  1775
                    foreach (StringPair optField in msg.OptFields)
Dean@927
  1776
                    {
markus@1347
  1777
                        if (optField.Name != null)
Dean@1094
  1778
                        {
markus@1347
  1779
                            switch (optField.Name)
Dean@1094
  1780
                            {
Dean@1094
  1781
                                case (PEPMessage.PR_X_ENC_STATUS_NAME):
Dean@1095
  1782
                                    {
Dean@1095
  1783
                                        try
Dean@1095
  1784
                                        {
markus@1347
  1785
                                            newMessage.Rating = AdapterExtensions.ParseRatingString(optField.Value);
Dean@1095
  1786
                                        }
Dean@1095
  1787
                                        catch
Dean@1095
  1788
                                        {
markus@1337
  1789
                                            newMessage.Rating = pEpRating.pEpRatingUndefined;
Dean@1095
  1790
                                        }
Dean@1095
  1791
                                        break;
Dean@1095
  1792
                                    }
Dean@1094
  1793
                                case (PEPMessage.PR_X_KEY_LIST_NAME):
Dean@1095
  1794
                                    {
markus@1347
  1795
                                        newMessage.KeyList = optField.Value;
Dean@1095
  1796
                                        break;
Dean@1095
  1797
                                    }
Dean@1104
  1798
                                case (PEPMessage.PR_X_PEP_NEVER_UNSECURE_NAME):
Dean@1095
  1799
                                    {
Dean@1095
  1800
                                        // If it exists it's true, value doesn't matter
Dean@1104
  1801
                                        newMessage.NeverUnsecure = true;
Dean@1095
  1802
                                        break;
Dean@1095
  1803
                                    }
Dean@1212
  1804
                                case (PEPMessage.PR_X_PEP_PROTOCOL_VERSION_NAME):
Dean@1095
  1805
                                    {
markus@1347
  1806
                                        newMessage.PEPProtocolVersion = optField.Value;
Dean@1095
  1807
                                        break;
Dean@1095
  1808
                                    }
Dean@1094
  1809
                            }
Dean@1094
  1810
                        }
Dean@927
  1811
                    }
Dean@927
  1812
                }
Dean@927
  1813
Dean@927
  1814
                // ReceivedOn
markus@1347
  1815
                if (msg.Recv > 0)
Dean@927
  1816
                {
markus@1347
  1817
                    newMessage.ReceivedOn = new DateTime(1970, 1, 1).AddSeconds(msg.Recv);
Dean@927
  1818
                }
Dean@927
  1819
                else
Dean@927
  1820
                {
Dean@927
  1821
                    newMessage.ReceivedOn = null;
Dean@927
  1822
                }
Dean@927
  1823
Dean@927
  1824
                // SentOn
markus@1347
  1825
                if (msg.Sent > 0)
Dean@927
  1826
                {
markus@1347
  1827
                    newMessage.SentOn = new DateTime(1970, 1, 1).AddSeconds(msg.Sent);
Dean@927
  1828
                }
Dean@927
  1829
                else
Dean@927
  1830
                {
Dean@927
  1831
                    newMessage.SentOn = null;
Dean@927
  1832
                }
Dean@927
  1833
markus@1356
  1834
                newMessage.ShortMsg = msg.ShortMsg;
Dean@927
  1835
Dean@927
  1836
                // To
Dean@927
  1837
                newMessage.To.Clear();
markus@1347
  1838
                if (msg.To != null)
Dean@927
  1839
                {
markus@1347
  1840
                    for (int i = 0; i < msg.To.Length; i++)
Dean@927
  1841
                    {
markus@1347
  1842
                        newMessage.To.Add(new PEPIdentity((pEpIdentity)msg.To.GetValue(i)));
Dean@927
  1843
                    }
Dean@927
  1844
                }
Dean@927
  1845
            }
Dean@927
  1846
            catch (Exception ex)
Dean@927
  1847
            {
Dean@927
  1848
                status = Globals.ReturnStatus.Failure;
Dean@1249
  1849
                Log.Error("PEPMessage.Create: Failure occured, " + ex.ToString());
Dean@927
  1850
            }
Dean@927
  1851
Dean@927
  1852
            createdMessage = newMessage;
Dean@927
  1853
            return (status);
Dean@927
  1854
        }
Dean@927
  1855
Dean@927
  1856
        /// <summary>
Dean@927
  1857
        /// Contructs a new message from the given outlook mail item.
Dean@927
  1858
        /// The output will never be null.
Dean@927
  1859
        /// </summary>
Dean@927
  1860
        /// <param name="omi">The outlook mail item to create the message from.</param>
Dean@927
  1861
        /// <param name="createdMessage">The output newly created message (will never be null).</param>
Dean@927
  1862
        /// <param name="createWithoutContent">Whether to include content such as text body, attachments 
Dean@927
  1863
        /// and optional properties.</param>
Dean@927
  1864
        /// <returns>The status of the method.</returns>
markus@954
  1865
        /// <remarks>In some contexts which call this message, a partial conversion of the
markus@1337
  1866
        /// <see cref="TextMessage"/> is acceptable, non-critical, and better than failure (e. G. displaying
markus@954
  1867
        /// a preview to the user). Thus, this method will always create a <see cref="PEPMessage"/>
markus@954
  1868
        /// which is "best effort", and return the <see cref="Globals.ReturnStatus"/> which can be evaluated
markus@954
  1869
        /// in the more critical contexts. Callers of that message need to be aware of this
markus@954
  1870
        /// and check the result if appropriate.</remarks>
Dean@927
  1871
        public static Globals.ReturnStatus Create(Outlook.MailItem omi,
Dean@927
  1872
                                                  out PEPMessage createdMessage,
Dean@927
  1873
                                                  bool createWithoutContent = false)
Dean@927
  1874
        {
Dean@927
  1875
            byte[] rtfBody;
Dean@927
  1876
            string delim;
Dean@927
  1877
            string[] keywords;
Dean@1094
  1878
            object propValue;
Dean@927
  1879
            DateTime? receivedOn = null;
Dean@927
  1880
            DateTime? sentOn = null;
Dean@927
  1881
            PEPIdentity ident;
Dean@927
  1882
            Outlook.Attachment currAttachment = null;
Dean@927
  1883
            Outlook.Attachments attachments = null;
Dean@927
  1884
            Outlook.Recipient currRecipient = null;
Dean@927
  1885
            Outlook.Recipients recipients = null;
Dean@927
  1886
            Globals.ReturnStatus status = Globals.ReturnStatus.Success;
Dean@927
  1887
            Globals.ReturnStatus sts;
Dean@927
  1888
            PEPMessage newMessage = new PEPMessage();
Dean@927
  1889
Dean@927
  1890
            try
Dean@927
  1891
            {
Dean@1249
  1892
                Log.Verbose("PEPMessage.Create: Creating new PEPMessage from OMI started, calculating recipients.");
Dean@927
  1893
Dean@1095
  1894
                /* Note: Skip the following properties which are not supported in the MailItem
Dean@1095
  1895
                 *   • Direction
Dean@1095
  1896
                 */
Dean@1095
  1897
Dean@927
  1898
                // Calculate recipients
Dean@1348
  1899
                newMessage.Bcc.Clear();
Dean@1348
  1900
                newMessage.Cc.Clear();
Dean@927
  1901
                newMessage.To.Clear();
Dean@927
  1902
                recipients = omi.Recipients;
Dean@927
  1903
                for (int i = 1; i <= recipients.Count; i++)
Dean@927
  1904
                {
Dean@927
  1905
                    currRecipient = recipients[i];
Dean@927
  1906
Dean@927
  1907
                    switch ((Outlook.OlMailRecipientType)currRecipient.Type)
Dean@927
  1908
                    {
Dean@927
  1909
                        case Outlook.OlMailRecipientType.olBCC:
Dean@1095
  1910
                            {
Dean@1202
  1911
                                sts = PEPIdentity.Create(currRecipient, out ident);
Dean@1095
  1912
                                if (sts == Globals.ReturnStatus.Success)
Dean@1095
  1913
                                {
Dean@1348
  1914
                                    newMessage.Bcc.Add(ident);
Dean@1095
  1915
                                }
Dean@1095
  1916
                                else
Dean@1095
  1917
                                {
Dean@1095
  1918
                                    // Update the status, only the first failure type is recorded
Dean@1095
  1919
                                    if (status == Globals.ReturnStatus.Success)
Dean@1095
  1920
                                    {
Dean@1095
  1921
                                        status = sts;
Dean@1095
  1922
                                    }
Dean@1194
  1923
Dean@1348
  1924
                                    Log.Error("PEPMessage.Create: Failure creating 'Bcc' identity.");
Dean@1095
  1925
                                }
Dean@927
  1926
Dean@1095
  1927
                                break;
Dean@1095
  1928
                            }
Dean@1095
  1929
                        case Outlook.OlMailRecipientType.olCC:
Dean@927
  1930
                            {
Dean@1202
  1931
                                sts = PEPIdentity.Create(currRecipient, out ident);
Dean@1095
  1932
                                if (sts == Globals.ReturnStatus.Success)
Dean@1095
  1933
                                {
Dean@1348
  1934
                                    newMessage.Cc.Add(ident);
Dean@1095
  1935
                                }
Dean@1095
  1936
                                else
Dean@1095
  1937
                                {
Dean@1095
  1938
                                    // Update the status, only the first failure type is recorded
Dean@1095
  1939
                                    if (status == Globals.ReturnStatus.Success)
Dean@1095
  1940
                                    {
Dean@1095
  1941
                                        status = sts;
Dean@1095
  1942
                                    }
Dean@1194
  1943
Dean@1348
  1944
                                    Log.Error("PEPMessage.Create: Failure creating 'Cc' identity.");
Dean@1095
  1945
                                }
Dean@1095
  1946
Dean@1095
  1947
                                break;
Dean@927
  1948
                            }
Dean@1095
  1949
                        case Outlook.OlMailRecipientType.olTo:
Dean@927
  1950
                            {
Dean@1202
  1951
                                sts = PEPIdentity.Create(currRecipient, out ident);
Dean@1095
  1952
                                if (sts == Globals.ReturnStatus.Success)
Dean@927
  1953
                                {
Dean@1095
  1954
                                    newMessage.To.Add(ident);
Dean@927
  1955
                                }
Dean@1095
  1956
                                else
Dean@1095
  1957
                                {
Dean@1095
  1958
                                    // Update the status, only the first failure type is recorded
Dean@1095
  1959
                                    if (status == Globals.ReturnStatus.Success)
Dean@1095
  1960
                                    {
Dean@1095
  1961
                                        status = sts;
Dean@1095
  1962
                                    }
Dean@1194
  1963
Dean@1249
  1964
                                    Log.Error("PEPMessage.Create: Failure creating 'To' identity.");
Dean@1095
  1965
                                }
Dean@1095
  1966
Dean@1095
  1967
                                break;
Dean@927
  1968
                            }
Dean@927
  1969
                    }
Dean@927
  1970
Dean@927
  1971
                    Marshal.ReleaseComObject(currRecipient);
Dean@927
  1972
                    currRecipient = null;
Dean@927
  1973
                }
Dean@927
  1974
Dean@1249
  1975
                Log.Verbose("PEPMessage.Create: Recipients calculated, calculating main properties.");
Dean@927
  1976
markus@1337
  1977
                newMessage.Direction = omi.GetIsIncoming() ? pEpMsgDirection.pEpDirIncoming : pEpMsgDirection.pEpDirOutgoing;
Dean@927
  1978
Dean@927
  1979
                // From
Dean@927
  1980
                sts = PEPIdentity.GetFromIdentity(omi, out ident);
Dean@927
  1981
                if (sts == Globals.ReturnStatus.Success)
Dean@927
  1982
                {
Dean@927
  1983
                    newMessage.From = ident;
Dean@927
  1984
                }
Dean@927
  1985
                else
Dean@927
  1986
                {
Dean@927
  1987
                    // Update the status, only the first failure type is recorded
Dean@927
  1988
                    if (status == Globals.ReturnStatus.Success)
Dean@927
  1989
                    {
Dean@927
  1990
                        status = sts;
Dean@927
  1991
                    }
Dean@1194
  1992
Dean@1249
  1993
                    Log.Error("PEPMessage.Create: Failure creating 'From' identity.");
Dean@927
  1994
                }
Dean@927
  1995
Dean@1350
  1996
                newMessage.Id = (string)MapiHelper.GetProperty(omi, MapiProperty.PidTagInternetMessageId, "");
Dean@927
  1997
                newMessage.ShortMsg = omi.Subject;
Dean@927
  1998
Dean@927
  1999
                /* Date & Times
Dean@927
  2000
                 * 
Dean@927
  2001
                 * Note: The mail item date can be invalid:
Dean@927
  2002
                 * For incoming this commonly occurs when creating a mirror -- it's created without a received time.
Dean@927
  2003
                 * For outgoing this commonly occurs for unsent mail.
Dean@927
  2004
                 * In these cases, the invalid date and time will be returned as 1/1/4501 at 12:00AM
Dean@927
  2005
                 * This situation is filtered here and the date is set as null.
Dean@927
  2006
                 */
Dean@927
  2007
                receivedOn = omi.ReceivedTime;
Dean@927
  2008
                if ((receivedOn != null) &&
Dean@927
  2009
                    (((DateTime)receivedOn).Year > 4000)) // ~2000 years from now, no issue
Dean@927
  2010
                {
Dean@927
  2011
                    receivedOn = null;
Dean@927
  2012
                }
Dean@927
  2013
                newMessage.ReceivedOn = receivedOn;
Dean@927
  2014
Dean@927
  2015
                sentOn = omi.SentOn;
Dean@927
  2016
                if ((sentOn != null) &&
Dean@927
  2017
                    (((DateTime)sentOn).Year > 4000)) // ~2000 years from now, no issue
Dean@927
  2018
                {
Dean@927
  2019
                    sentOn = null;
Dean@927
  2020
                }
Dean@927
  2021
                newMessage.SentOn = sentOn;
Dean@927
  2022
Dean@1113
  2023
                // Conversation information
Dean@1348
  2024
                newMessage.ConversationId = omi.ConversationID;
Dean@1113
  2025
                newMessage.ConversationIndex = omi.ConversationIndex;
Dean@1113
  2026
                newMessage.ConversationTopic = omi.ConversationTopic;
Dean@1113
  2027
Dean@1095
  2028
                // ForceUnencrypted
Dean@1303
  2029
                omi.GetPEPProperty(MailItemExtensions.PEPProperty.ForceUnencrypted, out propValue);
Dean@1095
  2030
                newMessage.ForceUnencrypted = (bool)propValue;
Dean@1095
  2031
Dean@1095
  2032
                // KeyList
Dean@1303
  2033
                omi.GetPEPProperty(MailItemExtensions.PEPProperty.KeyList, out propValue);
Dean@1095
  2034
                newMessage.KeyList = (string)propValue;
Dean@1095
  2035
Dean@1104
  2036
                // NeverUnsecure
Dean@1303
  2037
                omi.GetPEPProperty(MailItemExtensions.PEPProperty.NeverUnsecure, out propValue);
Dean@1104
  2038
                newMessage.NeverUnsecure = (bool)propValue;
Dean@1095
  2039
Dean@1212
  2040
                // PEPProtocolVersion
Dean@1303
  2041
                omi.GetPEPProperty(MailItemExtensions.PEPProperty.PEPProtocolVersion, out propValue);
Dean@1212
  2042
                newMessage.PEPProtocolVersion = (string)propValue;
Dean@1095
  2043
Dean@1303
  2044
                // Rating
Dean@1303
  2045
                omi.GetPEPProperty(MailItemExtensions.PEPProperty.Rating, out propValue);
markus@1337
  2046
                newMessage.Rating = (pEpRating)propValue;
Dean@1303
  2047
Dean@1249
  2048
                Log.Verbose("PEPMessage.Create: Main properties calculated, calculating body and attachments.");
Dean@927
  2049
Dean@927
  2050
                // Calculate text body and attachments
Dean@927
  2051
                if (createWithoutContent == false)
Dean@927
  2052
                {
Dean@927
  2053
                    // Body
Dean@927
  2054
                    if (omi.Body != null)
Dean@927
  2055
                    {
Dean@927
  2056
                        newMessage.LongMsg = omi.Body.Replace("\r\n", "\n");
Dean@927
  2057
Dean@927
  2058
                        // Save as RTF
Dean@927
  2059
                        try
Dean@927
  2060
                        {
Dean@927
  2061
                            rtfBody = omi.RTFBody;
Dean@1249
  2062
Dean@1249
  2063
                            if ((rtfBody != null) &&
Dean@1249
  2064
                                (rtfBody.Length > 0))
Dean@1249
  2065
                            {
Dean@1354
  2066
                                newMessage.LongMsgFormattedRtf = Encoding.ASCII.GetString(rtfBody, 0, rtfBody.Length);
Dean@1249
  2067
                            }
Dean@927
  2068
                        }
Dean@927
  2069
                        catch
Dean@927
  2070
                        {
Dean@1249
  2071
                            Log.Warning("PEPMessage.Create: Unable to read RTF body in MailItem.");
Dean@927
  2072
                        }
Dean@927
  2073
Dean@1095
  2074
                        // Force any rich text into HTML
Dean@927
  2075
                        // WARNING: This is technically a modifcation of the original MailItem
Dean@927
  2076
                        // It should be further investigated if this can be removed in the future.
Dean@927
  2077
                        if (omi.BodyFormat == Outlook.OlBodyFormat.olFormatRichText)
Dean@927
  2078
                        {
Dean@927
  2079
                            omi.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
Dean@927
  2080
                        }
Dean@927
  2081
Dean@927
  2082
                        if (omi.BodyFormat == Outlook.OlBodyFormat.olFormatHTML)
Dean@927
  2083
                        {
Dean@1354
  2084
                            newMessage.LongMsgFormattedHtml = omi.HTMLBody;
Dean@927
  2085
                        }
Dean@927
  2086
                    }
Dean@927
  2087
Dean@927
  2088
                    // Attachments
Dean@927
  2089
                    newMessage.Attachments.Clear();
Dean@927
  2090
                    attachments = omi.Attachments;
Dean@927
  2091
                    for (int i = 1; i <= attachments.Count; i++)
Dean@927
  2092
                    {
Dean@927
  2093
                        currAttachment = attachments[i];
Dean@927
  2094
Dean@927
  2095
                        try
Dean@927
  2096
                        {
Dean@927
  2097
                            newMessage.Attachments.Add(new PEPAttachment(currAttachment));
Dean@927
  2098
                        }
Dean@927
  2099
                        catch { }
Dean@927
  2100
Dean@927
  2101
                        Marshal.ReleaseComObject(currAttachment);
Dean@927
  2102
                        currAttachment = null;
Dean@927
  2103
                    }
Dean@927
  2104
Dean@927
  2105
                    // Keywords
Dean@927
  2106
                    if (omi.Categories != null)
Dean@927
  2107
                    {
Dean@927
  2108
                        try
Dean@927
  2109
                        {
Dean@927
  2110
                            newMessage.Keywords.Clear();
Dean@927
  2111
Dean@927
  2112
                            using (RegistryKey key1 = Registry.CurrentUser.OpenSubKey("Control Panel\\International"))
Dean@927
  2113
                            {
Dean@927
  2114
                                delim = key1.GetValue("sList").ToString();
Dean@927
  2115
                                keywords = omi.Categories.Split(delim.ToCharArray());
Dean@927
  2116
Dean@927
  2117
                                for (int i = 0; i < keywords.Length; i++)
Dean@927
  2118
                                {
Dean@927
  2119
                                    newMessage.Keywords.Add(keywords[i]);
Dean@927
  2120
                                }
Dean@927
  2121
                            }
Dean@927
  2122
                        }
Dean@927
  2123
                        catch
Dean@927
  2124
                        {
Dean@927
  2125
                            newMessage.Keywords.Clear();
Dean@1249
  2126
                            Log.Warning("PEPMessage.Create: Unable to set keywords.");
Dean@927
  2127
                        }
Dean@927
  2128
                    }
Dean@927
  2129
                }
Dean@927
  2130
Dean@1249
  2131
                Log.Verbose("PEPMessage.Create: New PEPMessage created from OMI.");
Dean@927
  2132
            }
Dean@927
  2133
            catch (Exception ex)
Dean@927
  2134
            {
Dean@927
  2135
                status = Globals.ReturnStatus.Failure;
Dean@1249
  2136
                Log.Error("PEPMessage.Create: failure occured, " + ex.ToString());
Dean@927
  2137
            }
Dean@927
  2138
            finally
Dean@927
  2139
            {
Dean@927
  2140
                // Free resources
Dean@927
  2141
                if (currAttachment != null)
Dean@927
  2142
                {
Dean@927
  2143
                    Marshal.ReleaseComObject(currAttachment);
Dean@927
  2144
                    currAttachment = null;
Dean@927
  2145
                }
Dean@927
  2146
Dean@927
  2147
                if (attachments != null)
Dean@927
  2148
                {
Dean@927
  2149
                    Marshal.ReleaseComObject(attachments);
Dean@927
  2150
                    attachments = null;
Dean@927
  2151
                }
Dean@927
  2152
Dean@927
  2153
                if (currRecipient != null)
Dean@927
  2154
                {
Dean@927
  2155
                    Marshal.ReleaseComObject(currRecipient);
Dean@927
  2156
                    currRecipient = null;
Dean@927
  2157
                }
Dean@927
  2158
Dean@927
  2159
                if (recipients != null)
Dean@927
  2160
                {
Dean@927
  2161
                    Marshal.ReleaseComObject(recipients);
Dean@927
  2162
                    recipients = null;
Dean@927
  2163
                }
Dean@927
  2164
            }
Dean@927
  2165
Dean@927
  2166
            createdMessage = newMessage;
Dean@972
  2167
Dean@1249
  2168
            Log.Verbose("PEPMessage.Create: ReturnStatus=" + status.ToString());
Dean@927
  2169
            return (status);
Dean@927
  2170
        }
Dean@927
  2171
Dean@927
  2172
        /// <summary>
Dean@927
  2173
        /// Determines if the given text is PGP text based on starting text sequence.
Dean@927
  2174
        /// The starting sequence must be "-----BEGIN PGP MESSAGE-----"
Dean@927
  2175
        /// </summary>
Dean@927
  2176
        /// <param name="text">The text to test if it is PGP text.</param>
Dean@927
  2177
        /// <returns>True if the given text is PGP text, otherwise false.</returns>
Dean@927
  2178
        public static bool IsPGPText(string text)
Dean@927
  2179
        {
Dean@927
  2180
            if (string.IsNullOrEmpty(text) == false)
Dean@927
  2181
            {
Dean@927
  2182
                string pgp_text = text.Trim();
Dean@927
  2183
                return (pgp_text.StartsWith("-----BEGIN PGP MESSAGE-----"));
Dean@927
  2184
            }
Dean@927
  2185
            else
Dean@927
  2186
            {
Dean@927
  2187
                return (false);
Dean@927
  2188
            }
Dean@927
  2189
        }
Dean@927
  2190
Dean@927
  2191
        /// <summary>
Dean@1330
  2192
        /// Determines if the given message is considered secure.
Dean@927
  2193
        /// </summary>
Dean@1330
  2194
        /// <param name="omi">The outlook mail item to check security for.</param>
Dean@1224
  2195
        /// <returns>True if the given message is encrypted, otherwise false.</returns>
Dean@1330
  2196
        public static bool GetIsSecure(Outlook.MailItem omi)
Dean@927
  2197
        {
Dean@927
  2198
            if (omi != null)
Dean@927
  2199
            {
Dean@1224
  2200
                // Partitioned or inline PGP format
Dean@927
  2201
                if (omi.Body != null && PEPMessage.IsPGPText(omi.Body))
Dean@927
  2202
                {
Dean@927
  2203
                    return (true);
Dean@927
  2204
                }
Dean@927
  2205
Dean@1224
  2206
                // PGP/MIME format
Dean@1224
  2207
                if (PEPMessage.GetIsPGPMIMEEncrypted(omi))
Dean@927
  2208
                {
Dean@1224
  2209
                    return (true);
Dean@927
  2210
                }
Dean@927
  2211
            }
Dean@927
  2212
Dean@927
  2213
            return (false);
Dean@927
  2214
        }
Dean@927
  2215
Dean@927
  2216
        /// <summary>
Dean@1224
  2217
        /// Determines if the given message is encrypted in PGP/MIME format.
Dean@1224
  2218
        /// </summary>
Dean@1224
  2219
        /// <param name="msg">The message to check encryption for.</param>
Dean@1224
  2220
        /// <returns>True if the given message is PGP/MIME encrypted, otherwise false.</returns>
Dean@1224
  2221
        public static bool GetIsPGPMIMEEncrypted(Outlook.MailItem omi)
Dean@1224
  2222
        {
Dean@1224
  2223
            bool result = false;
Dean@1224
  2224
            bool versionInfoFound = false;
Dean@1236
  2225
            bool contentFound = false;
Dean@1224
  2226
            PEPAttachment attach;
Dean@1224
  2227
            Outlook.Attachment attachment = null;
Dean@1224
  2228
            Outlook.Attachments attachments = null;
Dean@1224
  2229
Dean@1224
  2230
            try
Dean@1224
  2231
            {
Dean@1224
  2232
                if (omi != null)
Dean@1224
  2233
                {
Dean@1224
  2234
                    attachments = omi.Attachments;
Dean@1224
  2235
Dean@1224
  2236
                    // Require only two attachments (version identification & encrypted content)
Dean@1224
  2237
                    // However, allow the attachments to be in any order
Dean@1224
  2238
                    if (attachments.Count == 2)
Dean@1224
  2239
                    {
Dean@1224
  2240
                        // Note: attachment index starts at 1
Dean@1224
  2241
                        for (int i = 1; i <= attachments.Count; i++)
Dean@1224
  2242
                        {
Dean@1224
  2243
                            attachment = attachments[i];
Dean@1224
  2244
                            attach = new PEPAttachment(attachment);
Dean@1224
  2245
Dean@1236
  2246
                            if (PEPMessage.IsPGPMIMEVersionInfoFormat(attach))
Dean@1224
  2247
                            {
Dean@1236
  2248
                                versionInfoFound = true;
Dean@1224
  2249
                            }
Dean@1236
  2250
                            else if (PEPMessage.IsPGPMIMEContentFormat(attach))
Dean@1224
  2251
                            {
Dean@1236
  2252
                                contentFound = true;
Dean@1224
  2253
                            }
Dean@1224
  2254
Dean@1224
  2255
                            Marshal.ReleaseComObject(attachment);
Dean@1224
  2256
                            attachment = null;
Dean@1224
  2257
                        }
Dean@1224
  2258
Dean@1236
  2259
                        if (versionInfoFound && contentFound)
Dean@1224
  2260
                        {
Dean@1224
  2261
                            result = true;
Dean@1224
  2262
                        }
Dean@1224
  2263
                    }
Dean@1224
  2264
                }
Dean@1224
  2265
            }
Dean@1224
  2266
            catch { }
Dean@1224
  2267
            finally
Dean@1224
  2268
            {
Dean@1224
  2269
                if (attachment != null)
Dean@1224
  2270
                {
Dean@1224
  2271
                    Marshal.ReleaseComObject(attachment);
Dean@1224
  2272
                    attachment = null;
Dean@1224
  2273
                }
Dean@1224
  2274
Dean@1224
  2275
                if (attachments != null)
Dean@1224
  2276
                {
Dean@1224
  2277
                    Marshal.ReleaseComObject(attachments);
Dean@1224
  2278
                    attachments = null;
Dean@1224
  2279
                }
Dean@1224
  2280
            }
Dean@1224
  2281
Dean@1224
  2282
            return (result);
Dean@1224
  2283
        }
Dean@1224
  2284
Dean@1224
  2285
        /// <summary>
Dean@1330
  2286
        /// Determines if the given message is considered secure.
Dean@927
  2287
        /// Currently, only PGP encrypted messages will be detected.
Dean@927
  2288
        /// </summary>
Dean@1330
  2289
        /// <param name="msg">The message to check security for.</param>
Dean@927
  2290
        /// <returns>True if the given message is encrypted, otherwise false.</returns>
Dean@1330
  2291
        public static bool GetIsSecure(PEPMessage msg)
Dean@927
  2292
        {
Dean@927
  2293
            if (msg != null)
Dean@927
  2294
            {
Dean@1224
  2295
                // Partitioned or inline PGP format
Dean@927
  2296
                if (msg.LongMsg != null && PEPMessage.IsPGPText(msg.LongMsg))
Dean@927
  2297
                {
Dean@927
  2298
                    return (true);
Dean@927
  2299
                }
Dean@927
  2300
Dean@1224
  2301
                // PGP/MIME format
Dean@1224
  2302
                if (PEPMessage.GetIsPGPMIMEEncrypted(msg))
Dean@927
  2303
                {
Dean@1224
  2304
                    return (true);
Dean@927
  2305
                }
Dean@927
  2306
            }
Dean@927
  2307
Dean@927
  2308
            return (false);
Dean@927
  2309
        }
Dean@1224
  2310
Dean@1224
  2311
        /// <summary>
Dean@1224
  2312
        /// Determines if the given message is encrypted in PGP/MIME format.
Dean@1224
  2313
        /// </summary>
Dean@1224
  2314
        /// <param name="msg">The message to check encryption for.</param>
Dean@1224
  2315
        /// <returns>True if the given message is PGP/MIME encrypted, otherwise false.</returns>
Dean@1224
  2316
        public static bool GetIsPGPMIMEEncrypted(PEPMessage msg)
Dean@1224
  2317
        {
Dean@1224
  2318
            bool result = false;
Dean@1224
  2319
            bool versionInfoFound = false;
Dean@1236
  2320
            bool contentFound = false;
Dean@1224
  2321
Dean@1224
  2322
            if (msg != null)
Dean@1224
  2323
            {
Dean@1224
  2324
                // Require only two attachments (version identification & encrypted content)
Dean@1224
  2325
                // However, allow the attachments to be in any order
Dean@1224
  2326
                if (msg.Attachments.Count == 2)
Dean@1224
  2327
                {
Dean@1224
  2328
                    foreach (PEPAttachment attach in msg.Attachments)
Dean@1224
  2329
                    {
Dean@1236
  2330
                        if (PEPMessage.IsPGPMIMEVersionInfoFormat(attach))
Dean@1224
  2331
                        {
Dean@1236
  2332
                            versionInfoFound = true;
Dean@1224
  2333
                        }
Dean@1236
  2334
                        else if (PEPMessage.IsPGPMIMEContentFormat(attach))
Dean@1224
  2335
                        {
Dean@1236
  2336
                            contentFound = true;
Dean@1224
  2337
                        }
Dean@1224
  2338
                    }
Dean@1224
  2339
Dean@1236
  2340
                    if (versionInfoFound && contentFound)
Dean@1236
  2341
                    {
Dean@1236
  2342
                        result = true;
Dean@1236
  2343
                    }
Dean@1236
  2344
                }
Dean@1236
  2345
            }
Dean@1236
  2346
Dean@1236
  2347
            return (result);
Dean@1236
  2348
        }
Dean@1236
  2349
Dean@1236
  2350
        /// <summary>
Dean@1236
  2351
        /// Determines if the given attachment is formatted like a PGP/MIME version identification.
Dean@1236
  2352
        /// </summary>
Dean@1236
  2353
        /// <param name="attachment">The attachment to check.</param>
Dean@1236
  2354
        /// <returns>True if the attachment is PGP/MIME version identifcation formatted, otherwise false.</returns>
Dean@1236
  2355
        private static bool IsPGPMIMEVersionInfoFormat(PEPAttachment attachment)
Dean@1236
  2356
        {
Dean@1236
  2357
            bool result = false;
Dean@1236
  2358
Dean@1236
  2359
            if ((attachment != null) &&
Dean@1236
  2360
                (string.IsNullOrEmpty(attachment.MIMEType) == false) &&
Dean@1236
  2361
                (string.Equals(attachment.MIMEType.Trim(), "application/pgp-encrypted", StringComparison.OrdinalIgnoreCase)))
Dean@1236
  2362
            {
Dean@1236
  2363
                // Allow any data
Dean@1236
  2364
                if ((attachment.Data != null) &&
Dean@1236
  2365
                    (attachment.Data.Length > 0))
Dean@1236
  2366
                {
Dean@1236
  2367
                    result = true;
Dean@1236
  2368
                }
Dean@1236
  2369
            }
Dean@1236
  2370
Dean@1236
  2371
            return (result);
Dean@1236
  2372
        }
Dean@1236
  2373
Dean@1236
  2374
        /// <summary>
Dean@1236
  2375
        /// Determines if the given attachment is formatted like PGP/MIME content.
Dean@1236
  2376
        /// </summary>
Dean@1236
  2377
        /// <param name="attachment">The attachment to check.</param>
Dean@1236
  2378
        /// <returns>True if the attachment is PGP/MIME content formatted, otherwise false.</returns>
Dean@1236
  2379
        private static bool IsPGPMIMEContentFormat(PEPAttachment attachment)
Dean@1236
  2380
        {
Dean@1236
  2381
            bool result = false;
Dean@1236
  2382
Dean@1236
  2383
            if ((attachment != null) &&
Dean@1236
  2384
                (string.IsNullOrEmpty(attachment.MIMEType) == false) &&
Dean@1236
  2385
                (string.Equals(attachment.MIMEType.Trim(), "application/octet-stream", StringComparison.OrdinalIgnoreCase)))
Dean@1236
  2386
            {
Dean@1236
  2387
                // Require data to start with PGP text and be at least 100 bytes
Dean@1236
  2388
                if ((attachment.Data != null) &&
Dean@1236
  2389
                    (attachment.Data.Length > 100))
Dean@1236
  2390
                {
Dean@1236
  2391
                    // Check for PGP text after converting to string
Dean@1236
  2392
                    if (PEPMessage.IsPGPText(Encoding.ASCII.GetString(attachment.Data, 0, 100)))
Dean@1224
  2393
                    {
Dean@1224
  2394
                        result = true;
Dean@1224
  2395
                    }
Dean@1224
  2396
                }
Dean@1224
  2397
            }
Dean@1224
  2398
Dean@1224
  2399
            return (result);