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