PEPMessage.cs
author Thomas
Thu, 01 Mar 2018 10:07:49 +0100
changeset 2025 16d165ee211e
parent 2017 2b8ec8253118
child 2027 bd9dd65f8c6b
child 2030 feaa39471f5f
permissions -rw-r--r--
Use new string instance in copy method
     1 using Microsoft.Win32;
     2 using MimeKit;
     3 using pEpCOMServerAdapterLib;
     4 using System;
     5 using System.Collections.Generic;
     6 using System.ComponentModel;
     7 using System.IO;
     8 using System.Text;
     9 using Outlook = Microsoft.Office.Interop.Outlook;
    10 
    11 namespace pEp
    12 {
    13     /// <summary>
    14     /// Class for a completely in-memory message based on the pEp engine TextMessage.
    15     /// </summary>
    16     internal class PEPMessage : INotifyPropertyChanged,
    17                                 IEquatable<PEPMessage>,
    18                                 Interfaces.ICopy<PEPMessage>,
    19                                 Interfaces.IReset
    20     {
    21         /// <summary>
    22         /// Event raised when a property is changed on a component.
    23         /// </summary>
    24         public event PropertyChangedEventHandler PropertyChanged;
    25 
    26         // pEp named MAPI properties
    27         public const string PR_ENC_STATUS_NAME           = "X-EncStatus";
    28         public const string PR_KEY_LIST_NAME             = "X-KeyList";
    29         public const string PR_PEP_AUTO_CONSUME_NAME     = "pEp-auto-consume";
    30         public const string PR_PEP_FORCE_PROTECTION_NAME = "pEp-force-protection";
    31         public const string PR_PEP_NEVER_UNSECURE_NAME   = "X-pEp-Never-Unsecure";
    32         public const string PR_PEP_PROTOCOL_VERSION_NAME = "X-pEp-Version";
    33 
    34         public const string PR_PEP_NEVER_UNSECURE_VALUE  = "yes";
    35 
    36         public static readonly MapiProperty.MapiProp PidNameEncStatus          = new MapiProperty.MapiProp(PR_ENC_STATUS_NAME,           MapiProperty.PS_INTERNET_HEADERS);
    37         public static readonly MapiProperty.MapiProp PidNameKeyList            = new MapiProperty.MapiProp(PR_KEY_LIST_NAME,             MapiProperty.PS_INTERNET_HEADERS);
    38         public static readonly MapiProperty.MapiProp PidNamePEPAutoConsume     = new MapiProperty.MapiProp(PR_PEP_AUTO_CONSUME_NAME,     MapiProperty.PS_INTERNET_HEADERS);
    39         public static readonly MapiProperty.MapiProp PidNamePEPForceProtection = new MapiProperty.MapiProp(PR_PEP_FORCE_PROTECTION_NAME, MapiProperty.PS_INTERNET_HEADERS);
    40         public static readonly MapiProperty.MapiProp PidNamePEPNeverUnsecure   = new MapiProperty.MapiProp(PR_PEP_NEVER_UNSECURE_NAME,   MapiProperty.PS_INTERNET_HEADERS);
    41         public static readonly MapiProperty.MapiProp PidNamePEPProtocolVersion = new MapiProperty.MapiProp(PR_PEP_PROTOCOL_VERSION_NAME, MapiProperty.PS_INTERNET_HEADERS);
    42 
    43         private static object                     mutexMirror           = new object();
    44         private static Dictionary<string, string> mirrorCache           = new Dictionary<string, string>();
    45 
    46         private List<PEPAttachment> _Attachments;
    47         private string              _AutoConsume;
    48         private List<PEPIdentity>   _Bcc;
    49         private List<PEPIdentity>   _Cc;
    50         private string              _ConversationId;
    51         private string              _ConversationIndex;
    52         private string              _ConversationTopic;
    53         private pEpMsgDirection     _Direction;
    54         private bool                _EnableProtection;
    55         private string              _ForceProtectionId;
    56         private bool                _ForceUnencrypted;
    57         private PEPIdentity         _From;
    58         private string              _Id;
    59         private string              _KeyList;
    60         private List<string>        _Keywords;
    61         private string              _LongMsg;
    62         private string              _LongMsgFormattedHtml;
    63         private string              _LongMsgFormattedRtf;
    64         private bool                _NeverUnsecure;
    65         private string              _PEPProtocolVersion;
    66         private pEpRating           _Rating;
    67         private DateTime?           _ReceivedOn;
    68         private DateTime?           _SentOn;
    69         private string              _ShortMsg;
    70         private List<PEPIdentity>   _To;
    71 
    72         /**************************************************************
    73          * 
    74          * Constructors
    75          * 
    76          *************************************************************/
    77 
    78         /// <summary>
    79         /// Default constructor.
    80         /// </summary>
    81         public PEPMessage()
    82         {
    83             this.Reset();
    84         }
    85 
    86         /**************************************************************
    87          * 
    88          * Property Accessors
    89          * 
    90          *************************************************************/
    91 
    92         #region Property Accessors
    93 
    94         /// <summary>
    95         /// Gets the list of attachements for this message.
    96         ///          MailItem : Corresponds to property 'Attachments' which contains 'Attachment'
    97         /// CryptableMailItem : not exposed
    98         ///       TextMessage : Corresponds to property 'Attachments' which contains 'Blob'
    99         /// </summary>
   100         public List<PEPAttachment> Attachments
   101         {
   102             get { return (this._Attachments); }
   103         }
   104 
   105         /// <summary>
   106         /// Gets or sets whether this message should be automatically consumed by the pEp engine.
   107         /// Note: While this is stored as a header field, it is OK to apply to any MailItem and outgoing messages.
   108         /// It's intended function is only to add to outgoing messages generated by the engine.
   109         ///          MailItem : Corresponds to MAPI Property PidNamePEPAutoConsume (Header field)
   110         /// CryptableMailItem : not supported
   111         ///       TextMessage : Corresponds with the key PR_PEP_AUTO_CONSUME_NAME within 'opt_fields' array.
   112         /// </summary>
   113         public string AutoConsume
   114         {
   115             get { return (this._AutoConsume); }
   116             set
   117             {
   118                 this._AutoConsume = value;
   119                 this.RaisePropertyChangedEvent(nameof(this.AutoConsume));
   120             }
   121         }
   122 
   123         /// <summary>
   124         /// Gets the list of identities to be blind carbon copied on the message.
   125         ///          MailItem : Component of property 'Recipients' which contains 'Recipient'
   126         /// CryptableMailItem : not exposed directly (part of Recipients)
   127         ///       TextMessage : Corresponds to property 'Bcc'
   128         /// </summary>
   129         public List<PEPIdentity> Bcc
   130         {
   131             get { return (this._Bcc); }
   132         }
   133 
   134         /// <summary>
   135         /// Gets the list of identities to be carbon copied on the message.
   136         ///          MailItem : Component of property 'Recipients' which contains 'Recipient'
   137         /// CryptableMailItem : not exposed directly (part of Recipients)
   138         ///       TextMessage : Corresponds to property 'Cc'
   139         /// </summary>
   140         public List<PEPIdentity> Cc
   141         {
   142             get { return (this._Cc); }
   143         }
   144 
   145         /// <summary>
   146         /// Gets or sets the conversation ID of the message.
   147         /// Outlook should normally manage this itself.
   148         ///          MailItem : Corresponds to property 'ConversationID' (Underlying MAPI property PidTagConversationId)
   149         /// CryptableMailItem : Corresponds to property 'ConversationID'
   150         ///       TextMessage : not supported
   151         /// </summary>
   152         public string ConversationId
   153         {
   154             get { return (this._ConversationId); }
   155             set
   156             {
   157                 this._ConversationId = value;
   158                 this.RaisePropertyChangedEvent(nameof(this.ConversationId));
   159             }
   160         }
   161 
   162         /// <summary>
   163         /// Gets or sets the conversation index of the message.
   164         /// Outlook should normally manage this itself.
   165         ///          MailItem : Corresponds to property 'ConversationIndex' (Underlying MAPI property PidTagConversationIndex)
   166         /// CryptableMailItem : Corresponds to property 'ConversationIndex'
   167         ///       TextMessage : not supported
   168         /// </summary>
   169         public string ConversationIndex
   170         {
   171             get { return (this._ConversationIndex); }
   172             set
   173             {
   174                 this._ConversationIndex = value;
   175                 this.RaisePropertyChangedEvent(nameof(this.ConversationIndex));
   176             }
   177         }
   178 
   179         /// <summary>
   180         /// Gets or sets the conversation index of the message.
   181         /// Outlook should normally manage this itself.
   182         ///          MailItem : Corresponds to property 'ConversationTopic' (Underlying MAPI property PidTagConversationTopic)
   183         /// CryptableMailItem : Corresponds to property 'ConversationTopic'
   184         ///       TextMessage : not supported
   185         /// </summary>
   186         public string ConversationTopic
   187         {
   188             get { return (this._ConversationTopic); }
   189             set
   190             {
   191                 this._ConversationTopic = value;
   192                 this.RaisePropertyChangedEvent(nameof(this.ConversationTopic));
   193             }
   194         }
   195 
   196         /// <summary>
   197         /// Gets the date and time when the message was either sent or received.
   198         /// This corresponds with the SentOn and ReceivedOn properties defined separately.
   199         /// </summary>
   200         public DateTime? DateTimeSentOrReceived
   201         {
   202             get
   203             {
   204                 if (this._Direction == pEpMsgDirection.pEpDirIncoming)
   205                 {
   206                     return (this._ReceivedOn);
   207                 }
   208                 else
   209                 {
   210                     return (this._SentOn);
   211                 }
   212             }
   213         }
   214 
   215         /// <summary>
   216         /// Gets or sets the direction (incoming or outgoing) of the message.
   217         ///          MailItem : not supported (calculated from various properties)
   218         /// CryptableMailItem : Corresponds 1-to-1 with property 'IsIncoming'
   219         ///       TextMessage : Corresponds to property 'Dir'
   220         /// </summary>
   221         public pEpMsgDirection Direction
   222         {
   223             get { return (this._Direction); }
   224             set
   225             {
   226                 this._Direction = value;
   227                 this.RaisePropertyChangedEvent(nameof(this.Direction));
   228             }
   229         }
   230 
   231         /// <summary>
   232         /// Gets or sets the enable protection status.
   233         ///          MailItem : Corresponds to UserProperty USER_PROPERTY_KEY_ENABLE_PROTECTION
   234         /// CryptableMailItem : Corresponds to property 'EnableProtection'
   235         ///       TextMessage : not supported
   236         /// </summary>
   237         public bool EnableProtection
   238         {
   239             get { return (this._EnableProtection); }
   240             set
   241             {
   242                 this._EnableProtection = value;
   243                 this.RaisePropertyChangedEvent(nameof(this.EnableProtection));
   244             }
   245         }
   246 
   247         /// <summary>
   248         /// Gets or sets the message's force protection id (if any).
   249         ///          MailItem : Corresponds to MAPI Property PidNamePEPForceProtection (Header field)
   250         /// CryptableMailItem : not supported
   251         ///       TextMessage : Corresponds with the key PR_PEP_FORCE_PROTECTION_NAME within 'opt_fields' array.
   252         /// </summary>
   253         public string ForceProtectionId
   254         {
   255             get { return (this._ForceProtectionId); }
   256             set
   257             {
   258                 this._ForceProtectionId = value;
   259                 this.RaisePropertyChangedEvent(nameof(this.ForceProtectionId));
   260             }
   261         }
   262 
   263         /// <summary>
   264         /// Gets or sets the force unencrypted status.
   265         ///          MailItem : Corresponds to UserProperty USER_PROPERTY_KEY_FORCE_UNENCRYPTED
   266         /// CryptableMailItem : Corresponds to property 'ForceUnencryptedBool'
   267         ///       TextMessage : not supported
   268         /// </summary>
   269         public bool ForceUnencrypted
   270         {
   271             get { return (this._ForceUnencrypted); }
   272             set
   273             {
   274                 this._ForceUnencrypted = value;
   275                 this.RaisePropertyChangedEvent(nameof(this.ForceUnencrypted));
   276             }
   277         }
   278 
   279         /// <summary>
   280         /// Gets or sets the from identity of the message.
   281         /// Warning: this value can be null.
   282         ///          MailItem : not supported (calculated from various properties)
   283         /// CryptableMailItem : Corresponds to property 'From'
   284         ///       TextMessage : Corresponds to property 'From'
   285         /// </summary>
   286         public PEPIdentity From
   287         {
   288             get { return (this._From); }
   289             set
   290             {
   291                 this._From = value;
   292                 this.RaisePropertyChangedEvent(nameof(this.From));
   293             }
   294         }
   295 
   296         /// <summary>
   297         /// Gets or sets the ID of the message.
   298         /// Warning: this value can be null.
   299         ///          MailItem : Corresponds to MAPI property PidTagInternetMessageId
   300         /// CryptableMailItem : not supported
   301         ///       TextMessage : Corresponds to property 'Id'
   302         /// </summary>
   303         public string Id
   304         {
   305             get { return (this._Id); }
   306             set
   307             {
   308                 this._Id = value;
   309                 this.RaisePropertyChangedEvent(nameof(this.Id));
   310             }
   311         }
   312 
   313         /// <summary>
   314         /// Gets whether this message is considered secure.
   315         /// This will forward the call to the static method of the same purpose.
   316         /// </summary>
   317         public bool IsSecure
   318         {
   319             get { return (PEPMessage.GetIsSecure(this)); }
   320         }
   321 
   322         /// <summary>
   323         /// Gets whether this message is secured using PGP/MIME format.
   324         /// This will forward the call to the static method of the same purpose.
   325         /// </summary>
   326         public bool IsPGPMIMEEncrypted
   327         {
   328             get { return (PEPMessage.GetIsPGPMIMEEncrypted(this)); }
   329         }
   330 
   331         /// <summary>
   332         /// Gets or sets the list of keys associated with this message.
   333         /// Commonly this contains the list of decryption keys.
   334         /// Warning: Since this is stored as a header field, care must be taken not to apply this to a MailItem on an 
   335         /// untrusted server or on an outgoing message.
   336         ///          MailItem : Corresponds to MAPI Property PidNameKeyList (Header field)
   337         /// CryptableMailItem : not supported
   338         ///       TextMessage : Corresponds with the key PR_KEY_LIST_NAME within 'opt_fields' array. 
   339         ///                     This is also an out parameter of the decrypt function.
   340         /// </summary>
   341         public string KeyList
   342         {
   343             get { return (this._KeyList); }
   344             set
   345             {
   346                 this._KeyList = value;
   347                 this.RaisePropertyChangedEvent(nameof(this.KeyList));
   348             }
   349         }
   350 
   351         /// <summary>
   352         /// Gets the list of keywords associated with the message.
   353         ///          MailItem : Corresponds to property 'Categories'
   354         /// CryptableMailItem : not supported
   355         ///       TextMessage : Corresponds to property 'Keywords'
   356         /// </summary>
   357         public List<string> Keywords
   358         {
   359             get { return (this._Keywords); }
   360         }
   361 
   362         /// <summary>
   363         /// Gets or sets the plain text long-form (body) of the message.
   364         /// Warning: this value can be null.
   365         ///          MailItem : Corresponds to property 'Body' (also BodyFormat)
   366         /// CryptableMailItem : not exposed (processed internally)
   367         ///       TextMessage : Corresponds to property 'LongMsg'
   368         /// </summary>
   369         public string LongMsg
   370         {
   371             get { return (this._LongMsg); }
   372             set
   373             {
   374                 this._LongMsg = value;
   375                 this.RaisePropertyChangedEvent(nameof(this.LongMsg));
   376             }
   377         }
   378 
   379         /// <summary>
   380         /// Gets or sets the HTML formatted long-form (body) of the message.
   381         /// Warning: this value can be null.
   382         ///          MailItem : Corresponds to property 'HTMLBody' (also BodyFormat)
   383         /// CryptableMailItem : not exposed (processed internally)
   384         ///       TextMessage : Corresponds to property 'LongMsgFormatted'
   385         /// </summary>
   386         public string LongMsgFormattedHtml
   387         {
   388             get { return (this._LongMsgFormattedHtml); }
   389             set
   390             {
   391                 this._LongMsgFormattedHtml = value;
   392                 this.RaisePropertyChangedEvent(nameof(this.LongMsgFormattedHtml));
   393             }
   394         }
   395 
   396         /// <summary>
   397         /// Gets or sets the RTF formatted long-form (body) of the message.
   398         /// Warning: this value can be null, it is only supported when creating from a MailItem. It is
   399         /// not applied to a MailItem during .ApplyTo as it's unsupported by the engine.
   400         ///          MailItem : Corresponds to property 'RTFBody' (also BodyFormat)
   401         /// CryptableMailItem : not exposed (processed internally)
   402         ///       TextMessage : not supported
   403         /// </summary>
   404         public string LongMsgFormattedRtf
   405         {
   406             get { return (this._LongMsgFormattedRtf); }
   407             set
   408             {
   409                 this._LongMsgFormattedRtf = value;
   410                 this.RaisePropertyChangedEvent(nameof(this.LongMsgFormattedRtf));
   411             }
   412         }
   413 
   414         /// <summary>
   415         /// Gets or sets whether this message is marked to never be unsecure.
   416         /// Note: While this is stored as a header field, it is OK to apply to any MailItem and outgoing messages.
   417         /// It's intended function is to flag a message to keep it encrypted.
   418         ///          MailItem : Corresponds to MAPI Property PidNamePEPNeverUnsecure (Header field)
   419         /// CryptableMailItem : Corresponds to property 'NeverUnsecure'
   420         ///       TextMessage : not supported
   421         /// </summary>
   422         public bool NeverUnsecure
   423         {
   424             get { return (this._NeverUnsecure); }
   425             set
   426             {
   427                 this._NeverUnsecure = value;
   428                 this.RaisePropertyChangedEvent(nameof(this.NeverUnsecure));
   429             }
   430         }
   431 
   432         /// <summary>
   433         /// Gets the outgoing rating of this message.
   434         /// </summary>
   435         public pEpRating OutgoingRating
   436         {
   437             get
   438             {
   439                 pEpRating rating = pEpRating.pEpRatingUndefined;
   440 
   441 #if READER_RELEASE_MODE
   442                 // If reader mode, always unencrypted
   443                 rating = pEpRating.pEpRatingUnencrypted;
   444 #else
   445                 if (this.ForceUnencrypted)
   446                 {
   447                     rating = pEpRating.pEpRatingUnencrypted;
   448                 }
   449                 else if (string.IsNullOrEmpty(this.ForceProtectionId) == false)
   450                 {
   451                     rating = pEpRating.pEpRatingReliable;
   452                 }
   453                 else
   454                 {
   455                     PEPMessage workingMessage = this.Copy();
   456                     workingMessage.FlattenAllRecipientIdentities();
   457                     workingMessage.Direction = pEpMsgDirection.pEpDirOutgoing;
   458 
   459                     try
   460                     {
   461                         rating = ThisAddIn.PEPEngine.OutgoingMessageRating(workingMessage.ToCOMType());
   462                     }
   463                     catch (Exception ex)
   464                     {
   465                         rating = pEpRating.pEpRatingUndefined;
   466                         Log.Error("OutgoingRating: Error getting outgoing rating from engine. " + ex.ToString());
   467                     }
   468                 }
   469 #endif
   470                 return rating;
   471             }
   472         }
   473 
   474         /// <summary>
   475         /// Gets or sets the pEp protocol (or 'format') version of this message.
   476         /// This is not to be confused with pEp engine version which can be different.
   477         /// This is commonly set after decryption.
   478         /// Note: While this is stored as a header field, it is OK to apply to any MailItem and outgoing messages.
   479         /// It's intended function is just to show the version of pEp last used to process the message.
   480         ///          MailItem : Corresponds to MAPI Property PidNamePEPProtocolVersion (Header field)
   481         /// CryptableMailItem : not supported
   482         ///       TextMessage : Corresponds with the key PR_PEP_PROTOCOL_VERSION_NAME within 'opt_fields' array. 
   483         /// </summary>
   484         public string PEPProtocolVersion
   485         {
   486             get { return (this._PEPProtocolVersion); }
   487             set
   488             {
   489                 this._PEPProtocolVersion = value;
   490                 this.RaisePropertyChangedEvent(nameof(this.PEPProtocolVersion));
   491             }
   492         }
   493 
   494         /// <summary>
   495         /// Gets or sets the pEp rating of the message.
   496         /// This should corresponds primarily with the decryption rating.
   497         /// Warning: Since this is stored as a header field, care must be taken not to apply this to a MailItem on an 
   498         /// untrusted server or on an outgoing message.
   499         ///          MailItem : Corresponds to MAPI Property PidNameEncStatus (Header field)
   500         /// CryptableMailItem : not exposed (processed internally)
   501         ///       TextMessage : Corresponds with the key PR_ENC_STATUS_NAME within 'opt_fields' array
   502         /// </summary>
   503         public pEpRating Rating
   504         {
   505             get { return (this._Rating); }
   506             set
   507             {
   508                 this._Rating = value;
   509                 this.RaisePropertyChangedEvent(nameof(this.Rating));
   510             }
   511         }
   512 
   513         /// <summary>
   514         /// Gets or sets the date and time when the message was received.
   515         ///          MailItem : Corresponds to property 'ReceivedTime'
   516         /// CryptableMailItem : not supported
   517         ///       TextMessage : Corresponds to property 'recv'
   518         /// </summary>
   519         public DateTime? ReceivedOn
   520         {
   521             get { return (this._ReceivedOn); }
   522             set
   523             {
   524                 this._ReceivedOn = value;
   525                 this.RaisePropertyChangedEvent(nameof(this.ReceivedOn));
   526             }
   527         }
   528 
   529         /// <summary>
   530         /// Gets or sets the date and time when the message was sent.
   531         ///          MailItem : Corresponds to property 'SentOn'
   532         /// CryptableMailItem : not supported
   533         ///       TextMessage : Corresponds to property 'sent'
   534         /// </summary>
   535         public DateTime? SentOn
   536         {
   537             get { return (this._SentOn); }
   538             set
   539             {
   540                 this._SentOn = value;
   541                 this.RaisePropertyChangedEvent(nameof(this.SentOn));
   542             }
   543         }
   544 
   545         /// <summary>
   546         /// Gets or sets the short-form (subject) of the message.
   547         /// Warning: this value can be null.
   548         ///          MailItem : Corresponds to property 'Subject'
   549         /// CryptableMailItem : not supported
   550         ///       TextMessage : Corresponds to property 'Shortmsg'
   551         /// </summary>
   552         public string ShortMsg
   553         {
   554             get { return (this._ShortMsg); }
   555             set
   556             {
   557                 this._ShortMsg = value;
   558                 this.RaisePropertyChangedEvent(nameof(this.ShortMsg));
   559             }
   560         }
   561 
   562         /// <summary>
   563         /// Gets the list of identities to receive the message.
   564         ///          MailItem : Component of property 'Recipients' which contains 'Recipient'
   565         /// CryptableMailItem : not exposed directly (part of Recipients)
   566         ///       TextMessage : Corresponds to property 'To'
   567         /// </summary>
   568         public List<PEPIdentity> To
   569         {
   570             get { return (this._To); }
   571         }
   572 
   573         /// <summary>
   574         /// Gets the total number of all recipients in the message (Bcc, Cc &amp; To).
   575         /// </summary>
   576         public int RecipientCount
   577         {
   578             get { return (this._Bcc.Count + this._Cc.Count + this._To.Count); }
   579         }
   580 
   581         /// <summary>
   582         /// Gets a list of all recipients in the message (Bcc, Cc &amp; To).
   583         /// Each recipient in the returned list is a copy.
   584         /// </summary>
   585         public PEPIdentity[] Recipients
   586         {
   587             get
   588             {
   589                 List<PEPIdentity> recipients = new List<PEPIdentity>();
   590 
   591                 // Bcc
   592                 for (int i = 0; i < this._Bcc.Count; i++)
   593                 {
   594                     recipients.Add(this._Bcc[i].Copy());
   595                 }
   596 
   597                 // Cc
   598                 for (int i = 0; i < this._Cc.Count; i++)
   599                 {
   600                     recipients.Add(this._Cc[i].Copy());
   601                 }
   602 
   603                 // To
   604                 for (int i = 0; i < this._To.Count; i++)
   605                 {
   606                     recipients.Add(this._To[i].Copy());
   607                 }
   608 
   609                 return (recipients.ToArray());
   610             }
   611         }
   612 
   613 #endregion
   614 
   615         /**************************************************************
   616          * 
   617          * Methods
   618          * 
   619          *************************************************************/
   620 
   621         /// <summary>
   622         /// Raises the property changed event, if possible, with the given arguments.
   623         /// </summary>
   624         /// <param name="propertyName">The name of the property that changed.</param>
   625         private void RaisePropertyChangedEvent(string propertyName)
   626         {
   627             this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   628             return;
   629         }
   630         
   631         /// <summary>
   632         /// Returns this pEp message as a new pEp engine TextMessage struct.
   633         /// Warning: Any identity members (groups) are lost, call FlattenAllRecipientIdentities() before this method.
   634         /// </summary>
   635         /// <param name="alwaysAddOptFields">Whether to always add optional fields (default is false). When false, 
   636         /// optional fields will not be added if they are undefined/null/empty in the PEPMessage. This is to prevent 
   637         /// against adding optional fields when they have never been set.
   638         /// </param>
   639         /// <returns>A pEp engine identity.</returns>
   640         public TextMessage ToCOMType(bool alwaysAddOptFields = false)
   641         {
   642             long recvSec;
   643             long sentSec;
   644             TimeSpan span;
   645             List<Blob> attachments = new List<Blob>();
   646             List<pEpIdentity> bcc = new List<pEpIdentity>();
   647             List<pEpIdentity> cc = new List<pEpIdentity>();
   648             List<pEpIdentity> to = new List<pEpIdentity>();
   649             List<StringPair> optionalFields = new List<StringPair>();
   650             StringPair field;
   651             TextMessage result = new TextMessage();
   652 
   653             // Convert attachments
   654             for (int i = 0; i < this._Attachments.Count; i++)
   655             {
   656                 attachments.Add(this._Attachments[i].ToCOMType());
   657             }
   658 
   659             // Convert Bcc
   660             for (int i = 0; i < this._Bcc.Count; i++)
   661             {
   662                 bcc.Add(this._Bcc[i].ToCOMType());
   663             }
   664 
   665             // Convert Cc
   666             for (int i = 0; i < this._Cc.Count; i++)
   667             {
   668                 cc.Add(this._Cc[i].ToCOMType());
   669             }
   670 
   671             // Convert To
   672             for (int i = 0; i < this._To.Count; i++)
   673             {
   674                 to.Add(this._To[i].ToCOMType());
   675             }
   676 
   677             // Create optional fields
   678             if ((alwaysAddOptFields) ||
   679                 (string.IsNullOrEmpty(this._AutoConsume) == false))
   680             {
   681                 field = new StringPair();
   682                 field.Name = PEPMessage.PR_PEP_AUTO_CONSUME_NAME;
   683                 field.Value = this._AutoConsume;
   684                 optionalFields.Add(field);
   685             }
   686 
   687             if ((alwaysAddOptFields) ||
   688                 (string.IsNullOrEmpty(this._ForceProtectionId) == false))
   689             {
   690                 field = new StringPair();
   691                 field.Name = PEPMessage.PR_PEP_FORCE_PROTECTION_NAME;
   692                 field.Value = this._ForceProtectionId;
   693                 optionalFields.Add(field);
   694             }
   695 
   696             if ((alwaysAddOptFields) ||
   697                 (string.IsNullOrEmpty(this._KeyList) == false))
   698             {
   699                 field = new StringPair();
   700                 field.Name = PEPMessage.PR_KEY_LIST_NAME;
   701                 field.Value = this._KeyList;
   702                 optionalFields.Add(field);
   703             }
   704 
   705             if ((alwaysAddOptFields) ||
   706                 (this._NeverUnsecure == true))
   707             {
   708                 field = new StringPair();
   709                 field.Name = PEPMessage.PR_PEP_NEVER_UNSECURE_NAME;
   710                 field.Value = PEPMessage.PR_PEP_NEVER_UNSECURE_VALUE;
   711                 optionalFields.Add(field);
   712             }
   713 
   714             if ((alwaysAddOptFields) ||
   715                 (string.IsNullOrEmpty(this._PEPProtocolVersion) == false))
   716             {
   717                 field = new StringPair();
   718                 field.Name = PEPMessage.PR_PEP_PROTOCOL_VERSION_NAME;
   719                 field.Value = this._PEPProtocolVersion;
   720                 optionalFields.Add(field);
   721             }
   722 
   723             if ((alwaysAddOptFields) ||
   724                 (this._Rating != pEpRating.pEpRatingUndefined))
   725             {
   726                 field = new StringPair();
   727                 field.Name = PEPMessage.PR_ENC_STATUS_NAME;
   728                 field.Value = this._Rating.ToEngineString();
   729                 optionalFields.Add(field);
   730             }
   731 
   732             // ReceivedOn
   733             recvSec = -1;
   734             if (this._ReceivedOn != null)
   735             {
   736                 DateTime receivedOnUtc;
   737                 try
   738                 {
   739                     receivedOnUtc = TimeZoneInfo.ConvertTimeToUtc((DateTime)this._ReceivedOn);
   740                 }
   741                 catch (Exception ex)
   742                 {
   743                     Log.Verbose("PEPMessage.ToCOMType: Error converting received time to UTC. " + ex.ToString());
   744                     receivedOnUtc = (DateTime)this._ReceivedOn;
   745                 }
   746                 span = receivedOnUtc - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
   747                 recvSec = (long)span.TotalSeconds;
   748             }
   749 
   750             // SentOn
   751             sentSec = -1;
   752             if (this._SentOn != null)
   753             {
   754                 DateTime sentOnUtc;
   755                 try
   756                 {
   757                     sentOnUtc = TimeZoneInfo.ConvertTimeToUtc((DateTime)this._SentOn);
   758                 }
   759                 catch (Exception ex)
   760                 {
   761                     Log.Verbose("PEPMessage.ToCOMType: Error converting sent time to UTC. " + ex.ToString());
   762                     sentOnUtc = (DateTime)this._SentOn;
   763                 }
   764                 span = sentOnUtc - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
   765                 sentSec = (long)span.TotalSeconds;
   766             }
   767 
   768             /* Note: Skip the following properties which are not supported in TextMessage
   769              *   • ConversationId
   770              *   • ConversationIndex
   771              *   • ConversationTopic
   772              *   • EnableProtection
   773              *   • ForceUnencrypted
   774              *   • LongMsgFormattedRtf
   775              * 
   776              * Also not the following are handled as optional fields
   777              *   • Rating
   778              *   • ForceProtection
   779              *   • NeverUnsecure
   780              *   • KeyList
   781              *   • PEPProtocolVersion
   782              * 
   783              * This also skips a number of TextMessage properties currently unsupported in PEPMessage.
   784              */
   785             result.Attachments = attachments.ToArray();
   786             result.Bcc = bcc.ToArray();
   787             result.Cc = cc.ToArray();
   788             result.Dir = this._Direction;
   789             result.From = (this._From != null ? this._From.ToCOMType() : result.From);
   790             result.Id = this._Id;
   791             result.Keywords = this._Keywords.ToArray();
   792             result.LongMsg = this._LongMsg;
   793             result.LongMsgFormatted = this._LongMsgFormattedHtml;
   794             result.OptFields = optionalFields.ToArray();
   795             result.Recv = ((recvSec >= 0) ? recvSec : result.Recv);
   796             result.Sent = ((sentSec >= 0) ? sentSec : result.Sent);
   797             result.ShortMsg = this._ShortMsg;
   798             result.To = to.ToArray();
   799 
   800             return (result);
   801         }
   802 
   803         /// <summary>
   804         /// Converts a PEPMessage into a MimeKit.MimeMessage.
   805         /// </summary>
   806         /// <param name="pEpMessage">The PEPMessage to convert.</param>
   807         /// <param name="mimeMessage">The created MimeMessage.</param>
   808         /// <returns>The status of this method.</returns>
   809         public static Globals.ReturnStatus ToMIMEMessage(PEPMessage pEpMessage, out MimeMessage mimeMessage)
   810         {
   811             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
   812             MimeMessage message = new MimeMessage();
   813             BodyBuilder bodyBuilder = new BodyBuilder();
   814 
   815             // Convert TextMessage to MimeMessage
   816             // Add attachments to body builder
   817             for (int i = 0; i < pEpMessage.Attachments?.Count; i++)
   818             {
   819                 try
   820                 {
   821                     PEPAttachment currentAttachment = pEpMessage.Attachments[i];
   822                     MimePart messageAttachment = bodyBuilder.Attachments.Add(currentAttachment.FileName, currentAttachment.Data) as MimePart;
   823 
   824                     // In case of inline attachments, add content id
   825                     if (string.IsNullOrEmpty(currentAttachment.ContentId) == false)
   826                     {
   827                         messageAttachment.ContentId = currentAttachment.ContentId;
   828                         messageAttachment.ContentDisposition = new ContentDisposition(ContentDisposition.Inline);
   829                     }
   830                 }
   831                 catch (Exception ex)
   832                 {
   833                     status = Globals.ReturnStatus.Failure;
   834                     Log.Error("ToMIMEMessage: Error parsing attachment. " + ex.ToString());
   835                 }
   836             }
   837 
   838             // Add body to body builder
   839             bodyBuilder.TextBody = pEpMessage.LongMsg;
   840             bodyBuilder.HtmlBody = pEpMessage.LongMsgFormattedHtml;
   841 
   842             // Add attachments and body to message
   843             message.Body = bodyBuilder.ToMessageBody();
   844 
   845             // Process recipients
   846             // BCC
   847             for (int i = 0; i < pEpMessage.Bcc?.Count; i++)
   848             {
   849                 InternetAddress address;
   850                 try
   851                 {
   852                     if (InternetAddress.TryParse(pEpMessage.Bcc[i].Address, out address))
   853                     {
   854                         address.Name = pEpMessage.Bcc[i].UserName;
   855                         message.Bcc.Add(address);
   856                     }
   857                 }
   858                 catch (Exception ex)
   859                 {
   860                     status = Globals.ReturnStatus.Failure;
   861                     Log.Error("ToMIMEMessage: Error parsing BCC identity. " + ex.ToString());
   862                 }
   863             }
   864 
   865             // CC
   866             for (int i = 0; i < pEpMessage.Cc?.Count; i++)
   867             {
   868                 InternetAddress address;
   869                 try
   870                 {
   871                     if (InternetAddress.TryParse(pEpMessage.Cc[i].Address, out address))
   872                     {
   873                         address.Name = pEpMessage.Cc[i].UserName;
   874                         message.Cc.Add(address);
   875                     }
   876                 }
   877                 catch (Exception ex)
   878                 {
   879                     status = Globals.ReturnStatus.Failure;
   880                     Log.Error("ToMIMEMessage: Error parsing CC identity. " + ex.ToString());
   881                 }
   882             }
   883 
   884             // To
   885             for (int i = 0; i < pEpMessage.To?.Count; i++)
   886             {
   887                 InternetAddress address;
   888                 try
   889                 {
   890                     if (InternetAddress.TryParse(pEpMessage.To[i].Address, out address))
   891                     {
   892                         address.Name = pEpMessage.To[i].UserName;
   893                         message.To.Add(address);
   894                     }
   895                 }
   896                 catch (Exception ex)
   897                 {
   898                     status = Globals.ReturnStatus.Failure;
   899                     Log.Error("ToMIMEMessage: Error parsing To identity. " + ex.ToString());
   900                 }
   901             }
   902 
   903             // From
   904             InternetAddress from;
   905             try
   906             {
   907                 if (InternetAddress.TryParse(pEpMessage.From.Address, out from))
   908                 {
   909                     from.Name = pEpMessage.From.UserName;
   910                     message.From.Add(from);
   911                 }
   912             }
   913             catch (Exception ex)
   914             {
   915                 status = Globals.ReturnStatus.Failure;
   916                 Log.Error("ToMIMEMessage: Error parsing From identity. " + ex.ToString());
   917             }
   918 
   919             // Message ID
   920             if (string.IsNullOrEmpty(pEpMessage.Id) == false)
   921             {
   922                 message.MessageId = pEpMessage.Id;
   923             }
   924 
   925             // Subject
   926             if (string.IsNullOrEmpty(pEpMessage.ShortMsg) == false)
   927             {
   928                 message.Subject = pEpMessage.ShortMsg;
   929             }
   930 
   931             // Add pEp header fields
   932             if (string.IsNullOrEmpty(pEpMessage.AutoConsume) == false)
   933             {
   934                 try
   935                 {
   936                     Header header = new Header(Encoding.Unicode, PEPMessage.PR_PEP_AUTO_CONSUME_NAME, pEpMessage.AutoConsume);
   937                     message.Headers.Add(header);
   938                 }
   939                 catch (Exception ex)
   940                 {
   941                     status = Globals.ReturnStatus.Failure;
   942                     Log.Error("ToMIMEMessage: Error adding AutoConsume header. " + ex.ToString());
   943                 }
   944             }
   945 
   946             if (string.IsNullOrEmpty(pEpMessage.ForceProtectionId) == false)
   947             {
   948                 try
   949                 {
   950                     Header header = new Header(Encoding.Unicode, PEPMessage.PR_PEP_FORCE_PROTECTION_NAME, pEpMessage.ForceProtectionId);
   951                     message.Headers.Add(header);
   952                 }
   953                 catch (Exception ex)
   954                 {
   955                     status = Globals.ReturnStatus.Failure;
   956                     Log.Error("ToMIMEMessage: Error adding ForceProtection header. " + ex.ToString());
   957                 }
   958             }
   959 
   960             if (string.IsNullOrEmpty(pEpMessage.KeyList) == false)
   961             {
   962                 try
   963                 {
   964                     Header header = new Header(Encoding.Unicode, PEPMessage.PR_KEY_LIST_NAME, pEpMessage.KeyList);
   965                     message.Headers.Add(header);
   966                 }
   967                 catch (Exception ex)
   968                 {
   969                     status = Globals.ReturnStatus.Failure;
   970                     Log.Error("ToMIMEMessage: Error adding KeyList header. " + ex.ToString());
   971                 }
   972             }
   973 
   974             if (pEpMessage.NeverUnsecure)
   975             {
   976                 try
   977                 {
   978                     Header header = new Header(Encoding.Unicode, PEPMessage.PR_PEP_NEVER_UNSECURE_NAME, PEPMessage.PR_PEP_NEVER_UNSECURE_VALUE);
   979                     message.Headers.Add(header);
   980                 }
   981                 catch (Exception ex)
   982                 {
   983                     status = Globals.ReturnStatus.Failure;
   984                     Log.Error("ToMIMEMessage: Error adding NeverUnsecure header. " + ex.ToString());
   985                 }
   986             }
   987 
   988             if (string.IsNullOrEmpty(pEpMessage.PEPProtocolVersion) == false)
   989             {
   990                 try
   991                 {
   992                     Header header = new Header(Encoding.Unicode, PEPMessage.PR_PEP_PROTOCOL_VERSION_NAME, pEpMessage.PEPProtocolVersion);
   993                     message.Headers.Add(header);
   994                 }
   995                 catch (Exception ex)
   996                 {
   997                     status = Globals.ReturnStatus.Failure;
   998                     Log.Error("ToMIMEMessage: Error adding AutConsume header. " + ex.ToString());
   999                 }
  1000             }
  1001 
  1002             if (pEpMessage.Rating != pEpRating.pEpRatingUndefined)
  1003             {
  1004                 try
  1005                 {
  1006                     Header header = new Header(Encoding.Unicode, PEPMessage.PR_ENC_STATUS_NAME, pEpMessage.Rating.ToEngineString());
  1007                     message.Headers.Add(header);
  1008                 }
  1009                 catch (Exception ex)
  1010                 {
  1011                     status = Globals.ReturnStatus.Failure;
  1012                     Log.Error("ToMIMEMessage: Error adding AutConsume header. " + ex.ToString());
  1013                 }
  1014             }
  1015 
  1016             // Message date
  1017             try
  1018             {
  1019                 if (pEpMessage.ReceivedOn != null)
  1020                 {
  1021                     message.Date = new DateTimeOffset((DateTime)pEpMessage.ReceivedOn, TimeSpan.Zero);
  1022                 }
  1023                 else if (pEpMessage.SentOn != null)
  1024                 {
  1025                     message.Date = new DateTimeOffset((DateTime)pEpMessage.SentOn, TimeSpan.Zero);
  1026                 }
  1027             }
  1028             catch (Exception ex)
  1029             {
  1030                 status = Globals.ReturnStatus.Failure;
  1031                 Log.Error("ToMIMEMessage: Error setting message date. " + ex.ToString());
  1032             }
  1033 
  1034             // Return MimeMessage
  1035             mimeMessage = message;
  1036 
  1037             return status;
  1038         }
  1039 
  1040         /// <summary>
  1041         /// Serves as a hash function for a particular type.
  1042         /// </summary>
  1043         /// <returns>A hash code for the current object.</returns>
  1044         public override int GetHashCode()
  1045         {
  1046             return base.GetHashCode();
  1047         }
  1048 
  1049         /// <summary>
  1050         /// Indicates whether the current object is equal to another object of the same type.
  1051         /// </summary>
  1052         /// <param name="obj">The object to check equality with.</param>
  1053         /// <returns>True if both objects are considered equal, otherwise false.</returns>
  1054         public override bool Equals(object obj)
  1055         {
  1056             if ((obj == null) ||
  1057                 !(obj is PEPMessage))
  1058             {
  1059                 return (false);
  1060             }
  1061 
  1062             return (this.Equals((PEPMessage)obj));
  1063         }
  1064 
  1065         /// <summary>
  1066         /// Indicates whether the current object is equal to another object of the same type.
  1067         /// </summary>
  1068         /// <param name="obj">The object to check equality with.</param>
  1069         /// <returns>True if both objects are considered equal, otherwise false.</returns>
  1070         public bool Equals(PEPMessage obj)
  1071         {
  1072             if (obj == null)
  1073             {
  1074                 return (false);
  1075             }
  1076 
  1077             if (Comparisons.Equals(this.Attachments, obj.Attachments) &&
  1078                 Comparisons.Equals(this.Bcc, obj.Bcc) &&
  1079                 Comparisons.Equals(this.Cc, obj.Cc) &&
  1080                 Comparisons.Equals(this.ConversationId, obj.ConversationId) &&
  1081                 Comparisons.Equals(this.ConversationIndex, obj.ConversationIndex) &&
  1082                 Comparisons.Equals(this.ConversationTopic, obj.ConversationTopic) &&
  1083                 Comparisons.Equals(this.Direction, obj.Direction) &&
  1084                 Comparisons.Equals(this.EnableProtection, obj.EnableProtection) &&
  1085                 Comparisons.Equals(this.ForceProtectionId, obj.ForceProtectionId) &&
  1086                 Comparisons.Equals(this.ForceUnencrypted, obj.ForceUnencrypted) &&
  1087                 Comparisons.Equals(this.From, obj.From) &&
  1088                 Comparisons.Equals(this.Id, obj.Id) &&
  1089                 Comparisons.Equals(this.KeyList, obj.KeyList) &&
  1090                 Comparisons.Equals(this.Keywords, obj.Keywords) &&
  1091                 Comparisons.Equals(this.LongMsg, obj.LongMsg) &&
  1092                 Comparisons.Equals(this.LongMsgFormattedHtml, obj.LongMsgFormattedHtml) &&
  1093                 Comparisons.Equals(this.LongMsgFormattedRtf, obj.LongMsgFormattedRtf) &&
  1094                 Comparisons.Equals(this.NeverUnsecure, obj.NeverUnsecure) &&
  1095                 Comparisons.Equals(this.PEPProtocolVersion, obj.PEPProtocolVersion) &&
  1096                 Comparisons.Equals(this.Rating, obj.Rating) &&
  1097                 Comparisons.Equals(this.ReceivedOn, obj.ReceivedOn) &&
  1098                 Comparisons.Equals(this.SentOn, obj.SentOn) &&
  1099                 Comparisons.Equals(this.ShortMsg, obj.ShortMsg) &&
  1100                 Comparisons.Equals(this.To, obj.To))
  1101             {
  1102                 return (true);
  1103             }
  1104 
  1105             return (false);
  1106         }
  1107 
  1108         /// <summary>
  1109         /// Indicates whether the content of the current PEPMessage is equal to another one's.
  1110         /// </summary>
  1111         /// <param name="obj">The object to check equality with.</param>
  1112         /// <returns>True if both objects' content is considered equal, otherwise false.</returns>
  1113         public bool EqualsByContent(PEPMessage obj)
  1114         {
  1115             if (obj == null)
  1116             {
  1117                 return false;
  1118             }
  1119 
  1120             if (Comparisons.Equals(this.ConversationTopic, obj.ConversationTopic) &&
  1121                 Comparisons.Equals(this.Keywords, obj.Keywords) &&
  1122                 Comparisons.Equals(this.LongMsg, obj.LongMsg) &&
  1123                 Comparisons.Equals(this.LongMsgFormattedHtml, obj.LongMsgFormattedHtml) &&
  1124                 Comparisons.Equals(this.LongMsgFormattedRtf, obj.LongMsgFormattedRtf) &&
  1125                 Comparisons.Equals(this.ShortMsg, obj.ShortMsg))
  1126             {
  1127                 return true;
  1128             }
  1129             else
  1130             {
  1131                 return false;
  1132             }
  1133         }
  1134 
  1135         /// <summary>
  1136         /// Gets a deep copy of the object and all its data.
  1137         /// </summary>
  1138         /// <returns>The deep copy of the object.</returns>
  1139         public PEPMessage Copy()
  1140         {
  1141             return (this.Copy(false));
  1142         }
  1143 
  1144         /// <summary>
  1145         /// Gets a copy of the PEPMessage with or without data.
  1146         /// </summary>
  1147         /// <param name="createWithoutContent">Whether to include content such as text body, attachments 
  1148         /// and optional properties.</param>
  1149         /// <returns>The copy of the PEPMessage.</returns>
  1150         public PEPMessage Copy(bool createWithoutContent = false)
  1151         {
  1152             PEPMessage copy = new PEPMessage();
  1153 
  1154             // Attachments
  1155             copy.Attachments.Clear();
  1156             if (createWithoutContent == false)
  1157             {
  1158                 for (int i = 0; i < this._Attachments.Count; i++)
  1159                 {
  1160                     copy.Attachments.Add(this._Attachments[i].Copy());
  1161                 }
  1162             }
  1163 
  1164             copy.AutoConsume = (this._AutoConsume == null ? null : string.Copy(this._AutoConsume));
  1165 
  1166             // Bcc
  1167             copy.Bcc.Clear();
  1168             for (int i = 0; i < this._Bcc.Count; i++)
  1169             {
  1170                 copy.Bcc.Add(this._Bcc[i].Copy());
  1171             }
  1172 
  1173             // Cc
  1174             copy.Cc.Clear();
  1175             for (int i = 0; i < this._Cc.Count; i++)
  1176             {
  1177                 copy.Cc.Add(this._Cc[i].Copy());
  1178             }
  1179 
  1180             copy.ConversationId = (this._ConversationId == null ? null : string.Copy(this._ConversationId));
  1181             copy.ConversationIndex = (this._ConversationIndex == null ? null : string.Copy(this._ConversationIndex));
  1182             copy.ConversationTopic = (this._ConversationTopic == null ? null : string.Copy(this._ConversationTopic));
  1183             copy.Direction = this._Direction;
  1184             copy.EnableProtection = this._EnableProtection;
  1185             copy.ForceProtectionId = (this._ForceProtectionId == null ? null : string.Copy(this._ForceProtectionId));
  1186             copy.ForceUnencrypted = this._ForceUnencrypted;
  1187             copy.From = (this._From == null ? null : this._From.Copy());
  1188             copy.Id = (this._Id == null ? null : string.Copy(this._Id));
  1189             copy.KeyList = (this._KeyList == null ? null : string.Copy(this._KeyList));
  1190 
  1191             // Keywords
  1192             copy.Keywords.Clear();
  1193             for (int i = 0; i < this._Keywords.Count; i++)
  1194             {
  1195                 copy.Keywords.Add(this._Keywords[i]);
  1196             }
  1197 
  1198             // Body
  1199             if (createWithoutContent == false)
  1200             {
  1201                 copy.LongMsg = (this._LongMsg == null ? null : string.Copy(this._LongMsg));
  1202                 copy.LongMsgFormattedHtml = (this._LongMsgFormattedHtml == null ? null : string.Copy(this._LongMsgFormattedHtml));
  1203                 copy.LongMsgFormattedRtf = (this._LongMsgFormattedRtf == null ? null : string.Copy(this._LongMsgFormattedRtf));
  1204             }
  1205             else
  1206             {
  1207                 copy.LongMsg = null;
  1208                 copy.LongMsgFormattedHtml = null;
  1209                 copy.LongMsgFormattedRtf = null;
  1210             }
  1211 
  1212             copy.NeverUnsecure = this._NeverUnsecure;
  1213             copy.PEPProtocolVersion = (this._PEPProtocolVersion == null ? null : string.Copy(this._PEPProtocolVersion));
  1214             copy.Rating = this._Rating;
  1215 
  1216             // ReceivedOn
  1217             if (this._ReceivedOn != null)
  1218             {
  1219                 copy.ReceivedOn = new DateTime(((DateTime)this._ReceivedOn).Ticks);
  1220             }
  1221             else
  1222             {
  1223                 copy.ReceivedOn = null;
  1224             }
  1225 
  1226             // SentOn
  1227             if (this._SentOn != null)
  1228             {
  1229                 copy.SentOn = new DateTime(((DateTime)this._SentOn).Ticks);
  1230             }
  1231             else
  1232             {
  1233                 copy.SentOn = null;
  1234             }
  1235 
  1236             copy.ShortMsg = (this._ShortMsg == null ? null : string.Copy(this._ShortMsg));
  1237 
  1238             // To
  1239             copy.To.Clear();
  1240             for (int i = 0; i < this._To.Count; i++)
  1241             {
  1242                 copy.To.Add(this._To[i].Copy());
  1243             }
  1244 
  1245             return (copy);
  1246         }
  1247 
  1248         /// <summary>
  1249         /// Resets the object to its default state/values.
  1250         /// </summary>
  1251         public void Reset()
  1252         {
  1253             this._Attachments = new List<PEPAttachment>();
  1254             this._AutoConsume = null;
  1255             this._Bcc = new List<PEPIdentity>();
  1256             this._Cc = new List<PEPIdentity>();
  1257             this._ConversationId = null;
  1258             this._ConversationIndex = null;
  1259             this._ConversationTopic = null;
  1260             this._Direction = pEpMsgDirection.pEpDirIncoming;
  1261             this._EnableProtection = false;
  1262             this._ForceProtectionId = null;
  1263             this._ForceUnencrypted = false;
  1264             this._From = null;
  1265             this._Id = null;
  1266             this._KeyList = null;
  1267             this._Keywords = new List<string>();
  1268             this._LongMsg = null;
  1269             this._LongMsgFormattedHtml = null;
  1270             this._LongMsgFormattedRtf = null;
  1271             this._NeverUnsecure = false;
  1272             this._PEPProtocolVersion = null;
  1273             this._Rating = pEpRating.pEpRatingUndefined;
  1274             this._ReceivedOn = null;
  1275             this._SentOn = null;
  1276             this._ShortMsg = null;
  1277             this._To = new List<PEPIdentity>();
  1278 
  1279             this.RaisePropertyChangedEvent(nameof(this.Attachments));
  1280             this.RaisePropertyChangedEvent(nameof(this.AutoConsume));
  1281             this.RaisePropertyChangedEvent(nameof(this.Bcc));
  1282             this.RaisePropertyChangedEvent(nameof(this.Cc));
  1283             this.RaisePropertyChangedEvent(nameof(this.ConversationId));
  1284             this.RaisePropertyChangedEvent(nameof(this.ConversationIndex));
  1285             this.RaisePropertyChangedEvent(nameof(this.ConversationTopic));
  1286             this.RaisePropertyChangedEvent(nameof(this.Direction));
  1287             this.RaisePropertyChangedEvent(nameof(this.EnableProtection));
  1288             this.RaisePropertyChangedEvent(nameof(this.ForceProtectionId));
  1289             this.RaisePropertyChangedEvent(nameof(this.ForceUnencrypted));
  1290             this.RaisePropertyChangedEvent(nameof(this.From));
  1291             this.RaisePropertyChangedEvent(nameof(this.Id));
  1292             this.RaisePropertyChangedEvent(nameof(this.KeyList));
  1293             this.RaisePropertyChangedEvent(nameof(this.Keywords));
  1294             this.RaisePropertyChangedEvent(nameof(this.LongMsg));
  1295             this.RaisePropertyChangedEvent(nameof(this.LongMsgFormattedHtml));
  1296             this.RaisePropertyChangedEvent(nameof(this.LongMsgFormattedRtf));
  1297             this.RaisePropertyChangedEvent(nameof(this.NeverUnsecure));
  1298             this.RaisePropertyChangedEvent(nameof(this.PEPProtocolVersion));
  1299             this.RaisePropertyChangedEvent(nameof(this.Rating));
  1300             this.RaisePropertyChangedEvent(nameof(this.ReceivedOn));
  1301             this.RaisePropertyChangedEvent(nameof(this.SentOn));
  1302             this.RaisePropertyChangedEvent(nameof(this.ShortMsg));
  1303             this.RaisePropertyChangedEvent(nameof(this.To));
  1304 
  1305             return;
  1306         }
  1307 
  1308         /// <summary>
  1309         /// Copies main properties that are unsupported by the engine from the given message into this message.
  1310         /// This is commonly used to restore information that would otherwise be lost in conversions to/from 
  1311         /// TextMessage during engine processing.
  1312         /// Key properties that should be set separately for encryption concerns (such as LongMsgFormattedRtf) are ignored.
  1313         /// </summary>
  1314         /// <param name="msg">The message to copy over properties from.</param>
  1315         public void SetNonEnginePropertiesFrom(PEPMessage msg)
  1316         {
  1317             /* Include the following properties:
  1318              *   • AutoConsume
  1319              *   • ConversationId
  1320              *   • ConversationIndex
  1321              *   • ConversationTopic
  1322              *   • EnableProtection
  1323              *   • ForceProtection
  1324              *   • ForceUnencrypted
  1325              *   • NeverUnsecure
  1326              * 
  1327              * Also note:
  1328              *              Rating: This is handled by the decrypt function of the engine only. It should not be
  1329              *                      added back separately so is skipped.
  1330              *             KeyList: This is handled by the decrypt function of the engine only. It should not be
  1331              *                      added back separately so is skipped.
  1332              * LongMsgFormattedRtf: This is completely unsupported by the engine but otherwise should be encrypted.
  1333              *                      Due to this, it's completely skipped.
  1334              *  PEPProtocolVersion: This is handled in both the decrypt and encrypt functions of the engine.
  1335              *                      It should almost always be set within the TextMessage there isn't needed to add back.
  1336              */
  1337 
  1338             if (msg != null)
  1339             {
  1340                 this._AutoConsume = msg.AutoConsume;
  1341                 this._ConversationId = msg.ConversationId;
  1342                 this._ConversationIndex = msg.ConversationIndex;
  1343                 this._ConversationTopic = msg.ConversationTopic;
  1344                 this._EnableProtection = msg.EnableProtection;
  1345                 this._ForceProtectionId = msg.ForceProtectionId;
  1346                 this._ForceUnencrypted = msg.ForceUnencrypted;
  1347                 this._NeverUnsecure = msg.NeverUnsecure;
  1348 
  1349                 this.RaisePropertyChangedEvent(nameof(this.AutoConsume));
  1350                 this.RaisePropertyChangedEvent(nameof(this.ConversationId));
  1351                 this.RaisePropertyChangedEvent(nameof(this.ConversationIndex));
  1352                 this.RaisePropertyChangedEvent(nameof(this.ConversationTopic));
  1353                 this.RaisePropertyChangedEvent(nameof(this.ForceProtectionId));
  1354                 this.RaisePropertyChangedEvent(nameof(this.EnableProtection));
  1355                 this.RaisePropertyChangedEvent(nameof(this.ForceUnencrypted));
  1356                 this.RaisePropertyChangedEvent(nameof(this.NeverUnsecure));
  1357             }
  1358 
  1359             return;
  1360         }
  1361 
  1362         /// <summary>
  1363         /// Sets all pEp properties at once. This method improves performance as the MimeKit 
  1364         /// message headers are only retrieved once and used for all properties.
  1365         /// </summary>
  1366         /// <param name="omi">The Outlook mail item to read the pEp properties from.</param>
  1367         /// <param name="direction">Whether it is an incoming or outgoing message.</param>
  1368         /// <param name="secureMessage">Whether the message is secure (encrypted) or not.</param>
  1369         /// <param name="messageHeaders">The message headers of this message.</param>
  1370         public void SetPEPProperties(Outlook.MailItem omi, HeaderList messageHeaders)
  1371         {
  1372             object propValue = null;
  1373 
  1374             // AutoConsume
  1375             omi.GetPEPProperty(MailItemExtensions.PEPProperty.AutoConsume, out propValue, MailItemExtensions.GetPEPPropertyDefault(MailItemExtensions.PEPProperty.AutoConsume), messageHeaders);
  1376             this.AutoConsume = propValue as string;
  1377 
  1378             // PEPProtocolVersion
  1379             omi.GetPEPProperty(MailItemExtensions.PEPProperty.PEPProtocolVersion, out propValue, MailItemExtensions.GetPEPPropertyDefault(MailItemExtensions.PEPProperty.PEPProtocolVersion), messageHeaders);
  1380             this.PEPProtocolVersion = propValue as string;
  1381 
  1382             // Rating
  1383             omi.GetPEPProperty(MailItemExtensions.PEPProperty.Rating, out propValue);
  1384             this.Rating = (pEpRating)propValue;
  1385 
  1386             // KeyList
  1387             omi.GetPEPProperty(MailItemExtensions.PEPProperty.KeyList, out propValue);
  1388             this.KeyList = propValue as string;
  1389 
  1390             // EnableProtection
  1391             omi.GetPEPProperty(MailItemExtensions.PEPProperty.EnableProtection, out propValue);
  1392             this.EnableProtection = (bool)propValue;
  1393 
  1394             // ForceUnencrypted
  1395             omi.GetPEPProperty(MailItemExtensions.PEPProperty.ForceUnencrypted, out propValue);
  1396             this.ForceUnencrypted = propValue as bool? ?? false;
  1397 
  1398             // ForceProtection
  1399             omi.GetPEPProperty(MailItemExtensions.PEPProperty.ForceProtection, out propValue, MailItemExtensions.GetPEPPropertyDefault(MailItemExtensions.PEPProperty.ForceProtection), messageHeaders);
  1400             this.ForceProtectionId = propValue as string;
  1401 
  1402             // NeverUnsecure
  1403             omi.GetPEPProperty(MailItemExtensions.PEPProperty.NeverUnsecure, out propValue, MailItemExtensions.GetPEPPropertyDefault(MailItemExtensions.PEPProperty.NeverUnsecure), messageHeaders);
  1404             this.NeverUnsecure = (bool)propValue;
  1405 
  1406             Log.Verbose("SetPEPProperties: Values retrieved");
  1407         }
  1408 
  1409 
  1410         /// <summary>
  1411         /// Gets the outgoing pEp rating of the given message based on the recipients force unencrypted status.
  1412         /// The IsForceUnencrypted property of each recipient must have already been set in the message.
  1413         /// This property is copied from any associated Outlook contact's force unencrypted user property.
  1414         /// </summary>
  1415         /// <param name="message">The message to get the rating for.</param>
  1416         /// <returns>The rating: undefined, reliable, unencrypted or unencrypted_for_some.
  1417         /// Will return undefined by default or if no recipients exist.</returns>
  1418         public pEpRating GetOutgoingRatingByRecipients()
  1419         {
  1420             // TODO: Check whether this method should be part of the engine?
  1421             int unencryptedCount = 0;
  1422             pEpRating rating = pEpRating.pEpRatingUndefined;
  1423             List<PEPIdentity> forceUnencryptedList = new List<PEPIdentity>();
  1424             PEPIdentity[] recipients;
  1425             PEPMessage workingMessage;
  1426 
  1427             workingMessage = this.Copy(true);
  1428             workingMessage.FlattenAllRecipientIdentities();
  1429 
  1430             recipients = workingMessage.Recipients;
  1431 
  1432             if (recipients.Length > 0)
  1433             {
  1434                 // Calculate for all recipients
  1435                 for (int i = 0; i < recipients.Length; i++)
  1436                 {
  1437                     if (recipients[i].IsForceUnencryptedBool)
  1438                     {
  1439                         unencryptedCount++;
  1440                     }
  1441                 }
  1442 
  1443                 // Final rating determination
  1444                 if (unencryptedCount == recipients.Length)
  1445                 {
  1446                     rating = pEpRating.pEpRatingUnencrypted;
  1447                 }
  1448                 else if (unencryptedCount == 0)
  1449                 {
  1450                     // Assume encrypted (will use engine rating in the end)
  1451                     rating = pEpRating.pEpRatingReliable;
  1452                 }
  1453                 else if (unencryptedCount < recipients.Length)
  1454                 {
  1455                     rating = pEpRating.pEpRatingUnencryptedForSome;
  1456                 }
  1457                 else
  1458                 {
  1459                     // Should never get here
  1460                     rating = pEpRating.pEpRatingUndefined;
  1461                 }
  1462             }
  1463 
  1464             return (rating);
  1465         }
  1466 
  1467         /// <summary>
  1468         /// Applies this pEp message's data to the given Outlook item.
  1469         /// </summary>
  1470         /// <param name="omi">The Outlook mail item to apply this pEp message's data to.</param>
  1471         /// <param name="setInternalHeaderFields">Whether to set internal header fields (Stored as MAPI properites) such as 
  1472         /// Rating and KeyList. Warning: Never apply these to outgoing messages -- used only for mirrors or internal MailItems.
  1473         /// Note that some header fields will be applied regardless such as NeverUnsecure or PEPProtocolVersion. 
  1474         /// That is because these are not considered internal only and don't contain sensitive information.</param>
  1475         /// /// <param name="setSender">Whether to set the sender manually. This is only needed when creating a sent message. 
  1476         /// Important: Setting this to true for outgoing messages can cause problems with Exchange accounts.</param>
  1477         /// <param name="setRecipients">Whether to set the message recipients manually. This should normally be set to true
  1478         /// for all common messages and makes only sense to be omitted in special cases like a FPP reply message.</param>
  1479         /// <returns>The status of the method.</returns>
  1480         public Globals.ReturnStatus ApplyTo(Outlook.MailItem omi,
  1481                                             bool setInternalHeaderFields,
  1482                                             bool setSender,
  1483                                             bool setRecipients = true)
  1484         {
  1485             bool fromRecipientRemoved = false;
  1486             bool isPGPMIMEMsg = this.IsPGPMIMEEncrypted;
  1487             byte[] bytes;
  1488             Outlook.Attachments attachments = null;
  1489             Outlook.Recipient newRecipient = null;
  1490             Outlook.Recipients recipients = null;
  1491             Outlook.Account currAccount = null;
  1492             Outlook.Account sendUsingAccount = null;
  1493             Outlook.Accounts accounts = null;
  1494             Outlook.Recipient fromRecipient = null;
  1495             Outlook.NameSpace ns = null;
  1496             PEPIdentity ident;
  1497             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
  1498 
  1499             try
  1500             {
  1501                 ns = Globals.ThisAddIn.Application.Session;
  1502 
  1503                 if (setRecipients)
  1504                 {
  1505                     // Remove all existing recipients
  1506                     recipients = omi.Recipients;
  1507                     while (recipients.Count > 0)
  1508                     {
  1509                         recipients.Remove(1);
  1510                     }
  1511 
  1512                     // Set recipients
  1513                     for (int i = 0; i < this._Bcc.Count; i++)
  1514                     {
  1515                         if (string.IsNullOrWhiteSpace(this._Bcc[i].Address) == false)
  1516                         {
  1517                             // Add by address
  1518                             newRecipient = recipients.Add(this._Bcc[i].Address);
  1519                             newRecipient.Type = (int)Outlook.OlMailRecipientType.olBCC;
  1520 
  1521                             // Marshal.ReleaseComObject(newRecipient);
  1522                             newRecipient = null;
  1523                         }
  1524                         else if (string.IsNullOrWhiteSpace(this._Bcc[i].UserName) == false)
  1525                         {
  1526                             // Add by user name (required for distribution lists)
  1527                             newRecipient = recipients.Add(this._Bcc[i].UserName);
  1528                             newRecipient.Type = (int)Outlook.OlMailRecipientType.olBCC;
  1529 
  1530                             // Marshal.ReleaseComObject(newRecipient);
  1531                             newRecipient = null;
  1532                         }
  1533                     }
  1534 
  1535                     for (int i = 0; i < this._Cc.Count; i++)
  1536                     {
  1537                         if (string.IsNullOrWhiteSpace(this._Cc[i].Address) == false)
  1538                         {
  1539                             // Add by address
  1540                             newRecipient = recipients.Add(this._Cc[i].Address);
  1541                             newRecipient.Type = (int)Outlook.OlMailRecipientType.olCC;
  1542 
  1543                             // Marshal.ReleaseComObject(newRecipient);
  1544                             newRecipient = null;
  1545                         }
  1546                         else if (string.IsNullOrWhiteSpace(this._Cc[i].UserName) == false)
  1547                         {
  1548                             // Add by user name (required for distribution lists)
  1549                             newRecipient = recipients.Add(this._Cc[i].UserName);
  1550                             newRecipient.Type = (int)Outlook.OlMailRecipientType.olCC;
  1551 
  1552                             // Marshal.ReleaseComObject(newRecipient);
  1553                             newRecipient = null;
  1554                         }
  1555                     }
  1556 
  1557                     for (int i = 0; i < this._To.Count; i++)
  1558                     {
  1559                         if (string.IsNullOrWhiteSpace(this._To[i].Address) == false)
  1560                         {
  1561                             // Add by address
  1562                             newRecipient = recipients.Add(this._To[i].Address);
  1563                             newRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
  1564 
  1565                             // Marshal.ReleaseComObject(newRecipient);
  1566                             newRecipient = null;
  1567                         }
  1568                         else if (string.IsNullOrWhiteSpace(this._To[i].UserName) == false)
  1569                         {
  1570                             // Add by user name (required for distribution lists)
  1571                             newRecipient = recipients.Add(this._To[i].UserName);
  1572                             newRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
  1573 
  1574                             // Marshal.ReleaseComObject(newRecipient);
  1575                             newRecipient = null;
  1576                         }
  1577                     }
  1578                 }
  1579 
  1580                 /* Add the pEp From identity as its own recipient. This will be removed later.
  1581                  * However, here it simplifies getting the Sender/From AddressEntry, which will be set later.
  1582                  * 
  1583                  * Note: Outlook will not allow the Sender to be set for received mail items.
  1584                  * If you try to set the sender in this situation, it will always throw an error.
  1585                  * To mitigate this, the Sender should only be set to the MailItem if one is not already existing that already
  1586                  * matches the Sender we would otherwise try to set. This is considered good enough as determining
  1587                  * if a mail item is received is not completely reliable.
  1588                  */
  1589                 if (setSender)
  1590                 {
  1591                     if ((this._From != null) &&
  1592                         ((omi.Sender == null) ||
  1593                          ((omi.Sender != null) &&
  1594                           (this._From != null) &&
  1595                           (this._From.EqualsByAddress(omi.Sender.Address) == false))))
  1596                     {
  1597                         if (string.IsNullOrWhiteSpace(this._From.Address) == false)
  1598                         {
  1599                             // Add by address
  1600                             fromRecipient = recipients.Add(this._From.Address);
  1601                             fromRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
  1602                         }
  1603                         else if (string.IsNullOrWhiteSpace(this._From.UserName) == false)
  1604                         {
  1605                             // Add by user name (required for distribution lists)
  1606                             fromRecipient = recipients.Add(this._From.UserName);
  1607                             fromRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
  1608                         }
  1609                     }
  1610 
  1611                     try
  1612                     {
  1613                         recipients.ResolveAll();
  1614                     }
  1615                     catch { }
  1616 
  1617                     /* Set sender account
  1618                      * Note that if this fails and the account is null, it will eventually use the default account.
  1619                      * If the send using account is already populated, this usually cannot be re-set. (Exception for unsaved drafts)
  1620                      */
  1621                     sendUsingAccount = omi.SendUsingAccount;
  1622 
  1623                     if ((this._From != null) &&
  1624                         (this._From.Address != null))
  1625                     {
  1626                         accounts = ns.Accounts;
  1627 
  1628                         // Note: Index starts at 1
  1629                         for (int i = 1; i <= accounts.Count; i++)
  1630                         {
  1631                             currAccount = accounts[i];
  1632                             var sts = PEPIdentity.GetOwnIdentity(currAccount, out ident);
  1633 
  1634                             if ((ident != null) &&
  1635                                 (ident.EqualsByAddress(this._From)))
  1636                             {
  1637                                 /* Try to set the SendUsingAccount
  1638                                  * This will fail if the mail item is already marked as sent or the SendUsingAccount is not null, etc...
  1639                                  * If it fails, Outlook will in the end just use the default account.
  1640                                  * This property should ideally be set before a mail item is saved.
  1641                                  */
  1642                                 try
  1643                                 {
  1644                                     omi.SendUsingAccount = currAccount;
  1645                                 }
  1646                                 catch { }
  1647 
  1648                                 // Marshal.ReleaseComObject(currAccount);
  1649                                 currAccount = null;
  1650 
  1651                                 break;
  1652                             }
  1653 
  1654                             // Marshal.ReleaseComObject(currAccount);
  1655                             currAccount = null;
  1656                         }
  1657                     }
  1658 
  1659                     /* Set Sender
  1660                      * Outlook takes the Sender property to display the respective name in the Sent folder.
  1661                      * This Sender property is based on the name, the email address and the EntryID MAPI properties. As the EntryID can't be accessed
  1662                      * directly with MAPI methods, we have to set these properties indirectly through the Sender.
  1663                      */
  1664                     if (fromRecipient != null)
  1665                     {
  1666                         try
  1667                         {
  1668                             omi.Sender = fromRecipient.AddressEntry;
  1669                         }
  1670                         catch
  1671                         {
  1672                             Log.Warning("PEPMessage.ApplyTo: Failed to set the Sender.");
  1673                         }
  1674 
  1675                         // Remove the From recipient from the Recipients collection by searching for a match by EntryID
  1676                         for (int i = 1; i <= recipients.Count; i++)
  1677                         {
  1678                             newRecipient = recipients[i];
  1679                             if (newRecipient != null)
  1680                             {
  1681                                 if ((string.IsNullOrEmpty(newRecipient.EntryID) == false) &&
  1682                                     (string.IsNullOrEmpty(fromRecipient.EntryID) == false) &&
  1683                                     (newRecipient.EntryID == fromRecipient.EntryID))
  1684                                 {
  1685                                     try
  1686                                     {
  1687                                         recipients.Remove(i);
  1688                                         fromRecipientRemoved = true;
  1689                                     }
  1690                                     catch
  1691                                     {
  1692                                         Log.Warning("PEPMessage.ApplyTo: Error removing 'From' recipient from Recipients collection.");
  1693                                     }
  1694                                     break;
  1695                                 }
  1696 
  1697                                 // Marshal.ReleaseComObject(newRecipient);
  1698                                 newRecipient = null;
  1699                             }
  1700                         }
  1701 
  1702                         // Fallback solution in case there is no EntryID match
  1703                         if (fromRecipientRemoved == false)
  1704                         {
  1705                             try
  1706                             {
  1707                                 recipients.Remove(recipients.Count);
  1708                                 Log.Warning("PEPMessage.ApplyTo: No EntryID match for 'From' recipient in Recipients collection. Removed using fallback solution.");
  1709                             }
  1710                             catch
  1711                             {
  1712                                 Log.Error("PEPMessage.ApplyTo: No EntryID match for 'From' recipient in Recipients collection. Fallback solution also failed.");
  1713                             }
  1714                         }
  1715 
  1716                         // Set Sender's name and email properties via MAPIProperty (won't be created automatically when assigníng Sender)
  1717                         try
  1718                         {
  1719                             MapiHelper.SetProperty(omi, MapiProperty.PidTagSenderName, this._From.UserName);
  1720                             MapiHelper.SetProperty(omi, MapiProperty.PidTagSenderEmailAddress, this._From.Address);
  1721                         }
  1722                         catch (Exception ex)
  1723                         {
  1724                             Log.Warning("PEPMessage.ApplyTo: Unable to set sender's name or email address via MAPIProperty. " + ex.Message);
  1725                         }
  1726 
  1727                         // Marshal.ReleaseComObject(fromRecipient);
  1728                         fromRecipient = null;
  1729                     }
  1730                 }
  1731                 else
  1732                 {
  1733                     try
  1734                     {
  1735                         recipients.ResolveAll();
  1736                     }
  1737                     catch { }
  1738                 }
  1739 
  1740                 /* Set the encoding to UTF-8
  1741                  * See: https://msdn.microsoft.com/en-us/library/office/ff860730.aspx
  1742                  * All PEPMessages should be UTF especially those coming from the engine.
  1743                  * 
  1744                  * It is important that the encoding is set BEFORE the body and other text of the 
  1745                  * mail item. This is necessary to avoid character encoding issues where
  1746                  * Outlook will not respect the UTF encoding of strings set to the body/subject/etc.
  1747                  * It will instead try to use the strings with the encoding of the codepage.
  1748                  */
  1749                 try
  1750                 {
  1751                     MapiHelper.SetProperty(omi, MapiProperty.PidTagInternetCodepage, 65001);
  1752                     MapiHelper.SetProperty(omi, MapiProperty.PidTagMessageCodepage, 65001);
  1753                 }
  1754                 catch
  1755                 {
  1756                     Log.Warning("PEPMessage.ApplyTo: Failed to set UTF-8 encoding.");
  1757                 }
  1758 
  1759                 // Set the subject
  1760                 omi.Subject = this._ShortMsg;
  1761 
  1762                 /* Set the body
  1763                  * PGP/MIME format must be handled specially.
  1764                  * In addition, RTF format is ignored. Only HTML is used the same as the pEp engine.
  1765                  */
  1766                 if (isPGPMIMEMsg)
  1767                 {
  1768                     /* Clear any old body content, these cannot exist for PGP/MIME encrypted messages.
  1769                      * Note that setting the RTFBody to null or an empty byte array throws an exception.
  1770                      * Therefore, it is sent to a single zero byte to be constant.
  1771                      * The RTFBody is ignored when sending the actual message.
  1772                      */
  1773                     omi.BodyFormat = Outlook.OlBodyFormat.olFormatPlain;
  1774                     omi.Body = null;
  1775                     omi.HTMLBody = null;
  1776                     omi.RTFBody = new byte[] { 0x00 };
  1777 
  1778                     // Now, set the mail mime type to S/MIME multipart/signed, this is used later to add a special attachment
  1779                     try
  1780                     {
  1781                         MapiHelper.SetProperty(omi, MapiProperty.PidTagMessageClass, MapiPropertyValue.PidTagMessageClassSMIMEMultipartSigned);
  1782                     }
  1783                     catch
  1784                     {
  1785                         status = Globals.ReturnStatus.Failure;
  1786                         throw new Exception("Failure to set message class for PGP/MIME message.");
  1787                     }
  1788                 }
  1789                 else if (string.IsNullOrWhiteSpace(this._LongMsgFormattedHtml))
  1790                 {
  1791                     omi.Body = this._LongMsg;
  1792                     omi.BodyFormat = Outlook.OlBodyFormat.olFormatPlain;
  1793                 }
  1794                 else
  1795                 {
  1796                     omi.HTMLBody = this._LongMsgFormattedHtml;
  1797                     omi.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
  1798                 }
  1799 
  1800                 // Remove any previous attachments
  1801                 attachments = omi.Attachments;
  1802                 while (attachments.Count > 0)
  1803                 {
  1804                     /* OUT-193: We cannot use attachments.Remove() here, as it
  1805                      * seems to be buggy in Outlook 2013. Let's hope that
  1806                      * using Attachment.Delete() has no bad side effects on
  1807                      * other Outlook versions.
  1808                      * OUT-292: The Delete() method can fail under rare circumstances.
  1809                      * In this case, just log the error and break. 
  1810                      * NOTE: We can NOT use a for loop here, as this leads to unforeseeable 
  1811                      * issues with actual attachments (supposedly due to race conditions on 
  1812                      * multithreaded decryption operations).
  1813                      */
  1814                     try
  1815                     {
  1816                         attachments[attachments.Count].Delete();
  1817                     }
  1818                     catch (Exception ex)
  1819                     {
  1820                         Log.Error("Applyto: Error deleting attachment. " + ex.ToString());
  1821                         break;
  1822                     }
  1823                 }
  1824 
  1825                 if (isPGPMIMEMsg)
  1826                 {
  1827                     PEPAttachment pgpMIME = this.ConvertToPGPMIMEAttachment();
  1828 
  1829                     if (pgpMIME != null)
  1830                     {
  1831                         try
  1832                         {
  1833                             pgpMIME.AddTo(attachments);
  1834                         }
  1835                         catch (Exception ex)
  1836                         {
  1837                             Log.Verbose("Failed to add PGP/MIME attachment. " + ex.ToString());
  1838 
  1839                             /* If adding the attachment fails, check if it is because of file size limitations
  1840                              * and throw dedicated exception in that case (needed for user feedback).
  1841                              */
  1842                             var pgpgMIMESizeInKB = pgpMIME.Data.Length / 1000; // Get size in kB
  1843                             if (pgpgMIMESizeInKB > omi.GetMaxMailSize())
  1844                             {
  1845                                 throw new AttachmentSizeException(string.Format("Failed to add PGP/MIME attachment due to size restrictions. Allowed size: {0} kB. Actual size: {1} kB.", omi.GetMaxMailSize(), pgpgMIMESizeInKB));
  1846                             }
  1847                             else
  1848                             {
  1849                                 throw;
  1850                             }
  1851                         }
  1852                     }
  1853                     else
  1854                     {
  1855                         throw new Exception("Failed to create and add PGP/MIME attachment.");
  1856                     }
  1857                 }
  1858                 else
  1859                 {
  1860                     for (int i = 0; i < this._Attachments.Count; i++)
  1861                     {
  1862                         try
  1863                         {
  1864                             this._Attachments[i].AddTo(attachments, ("attachment" + i.ToString()));
  1865                         }
  1866                         catch (Exception ex)
  1867                         {
  1868                             Log.Error("ApplyTo: Error adding attachment. " + ex.ToString());
  1869                         }
  1870                     }
  1871                 }
  1872 
  1873                 // ID
  1874                 try
  1875                 {
  1876                     MapiHelper.SetProperty(omi, MapiProperty.PidTagInternetMessageId, this._Id);
  1877                 }
  1878                 catch { }
  1879 
  1880                 // Conversation information
  1881                 try
  1882                 {
  1883                     /* Note: PidTagConversationId cannot be set even through the MAPI accessor.
  1884                      * This is by design since this property is computed automatically from other properties.
  1885                      * See: https://msdn.microsoft.com/en-us/library/ee204279.aspx
  1886                      */
  1887                     bytes = MapiHelper.StringToPtypBinary(this._ConversationIndex);
  1888                     if ((bytes != null) &&
  1889                         (bytes.Length > 0))
  1890                     {
  1891                         MapiHelper.SetProperty(omi, MapiProperty.PidTagConversationIndex, bytes);
  1892                     }
  1893 
  1894                     MapiHelper.SetProperty(omi, MapiProperty.PidTagConversationIndexTracking, false);
  1895 
  1896                     if (string.IsNullOrEmpty(this._ConversationTopic))
  1897                     {
  1898                         // Clear the property value even if null
  1899                         MapiHelper.SetProperty(omi, MapiProperty.PidTagConversationTopic, string.Empty);
  1900                     }
  1901                     else
  1902                     {
  1903                         MapiHelper.SetProperty(omi, MapiProperty.PidTagConversationTopic, this._ConversationTopic);
  1904                     }
  1905                 }
  1906                 catch
  1907                 {
  1908                     Log.Warning("PEPMessage.ApplyTo: Failure setting conversation information.");
  1909                 }
  1910 
  1911                 /* Add internal properties
  1912                  * Note: there is no need to check for defaults as it's all handled within the SetInterpretedProperty method.
  1913                  * Also, .Save() must never be called here as the .ApplyTo() method can be used on MailItems that need to change properties before save.
  1914                  */
  1915                 if (setInternalHeaderFields)
  1916                 {
  1917                     // Note: ignore return status
  1918                     omi.SetPEPProperty(MailItemExtensions.PEPProperty.KeyList, this._KeyList);
  1919 
  1920                     // Only store rating once and never change it
  1921                     object storedRating = null;
  1922                     omi.GetPEPProperty(MailItemExtensions.PEPProperty.Rating, out storedRating);
  1923                     if ((pEpRating)storedRating == pEpRating.pEpRatingUndefined)
  1924                     {
  1925                         omi.SetPEPProperty(MailItemExtensions.PEPProperty.Rating, this._Rating);
  1926                     }
  1927                 }
  1928 
  1929                 // Note: ignore return status
  1930                 omi.SetPEPProperty(MailItemExtensions.PEPProperty.AutoConsume, this._AutoConsume);
  1931                 omi.SetPEPProperty(MailItemExtensions.PEPProperty.ForceProtection, this._ForceProtectionId);
  1932                 omi.SetPEPProperty(MailItemExtensions.PEPProperty.NeverUnsecure, this._NeverUnsecure);
  1933                 omi.SetPEPProperty(MailItemExtensions.PEPProperty.PEPProtocolVersion, this._PEPProtocolVersion);
  1934             }
  1935             catch (Exception ex)
  1936             {
  1937                 status = Globals.ReturnStatus.Failure;
  1938                 Log.Error("PEPMessage.ApplyTo: Failure occured, " + ex.ToString());
  1939                 throw;
  1940             }
  1941             finally
  1942             {
  1943                 if (attachments != null)
  1944                 {
  1945                     // Marshal.ReleaseComObject(attachments);
  1946                     attachments = null;
  1947                 }
  1948 
  1949                 if (newRecipient != null)
  1950                 {
  1951                     // Marshal.ReleaseComObject(newRecipient);
  1952                     newRecipient = null;
  1953                 }
  1954 
  1955                 if (recipients != null)
  1956                 {
  1957                     // Marshal.ReleaseComObject(recipients);
  1958                     recipients = null;
  1959                 }
  1960 
  1961                 if (currAccount != null)
  1962                 {
  1963                     // Marshal.ReleaseComObject(currAccount);
  1964                     currAccount = null;
  1965                 }
  1966 
  1967                 if (sendUsingAccount != null)
  1968                 {
  1969                     // Marshal.ReleaseComObject(sendUsingAccount);
  1970                     sendUsingAccount = null;
  1971                 }
  1972 
  1973                 if (accounts != null)
  1974                 {
  1975                     // Marshal.ReleaseComObject(accounts);
  1976                     accounts = null;
  1977                 }
  1978 
  1979                 if (fromRecipient != null)
  1980                 {
  1981                     // Marshal.ReleaseComObject(fromRecipient);
  1982                     fromRecipient = null;
  1983                 }
  1984 
  1985                 if (ns != null)
  1986                 {
  1987                     // Marshal.ReleaseComObject(ns);
  1988                     ns = null;
  1989                 }
  1990             }
  1991 
  1992             return (status);
  1993         }
  1994 
  1995         /// <summary>
  1996         /// Converts this message into a single PGP/MIME attachment.
  1997         /// This is necessary in order for Outlook and MAPI to correctly transport PGP/MIME messages.
  1998         /// The attachment will report itself as S/MIME to MAPI, S/MIME attachments are passed-through as unmodified text.
  1999         /// Since we can insert unmodified text, this allows us to build a correct PGP/MIME message from existing attachments.
  2000         /// Warning: This can return null if a failure occured.
  2001         /// </summary>
  2002         /// <returns>This message converted to a single PGP/MIME attachment, otherwise null.</returns>
  2003         private PEPAttachment ConvertToPGPMIMEAttachment()
  2004         {
  2005             string boundary;
  2006             byte[] bytes;
  2007             bool versionInfoFound = false;
  2008             bool contentFound = false;
  2009             PEPAttachment newAttachment = null;
  2010             List<byte> data = new List<byte>();
  2011 
  2012             if (this._Attachments.Count == 2)
  2013             {
  2014                 boundary = "----=_NextPart_" + Guid.NewGuid().ToString().Replace('-', '_');
  2015 
  2016                 /* See below for an example PGP/MIME formatted message.
  2017                  * this is from RFC 3156.  
  2018                  *    
  2019                  *  Content-Type: multipart/encrypted; boundary=foo;
  2020                  *     protocol="application/pgp-encrypted"
  2021                  * 
  2022                  *  --foo
  2023                  *  Content-Type: application/pgp-encrypted
  2024                  * 
  2025                  *  Version: 1
  2026                  * 
  2027                  *  --foo
  2028                  *  Content-Type: application/octet-stream
  2029                  * 
  2030                  *  -----BEGIN PGP MESSAGE-----
  2031                  *  Version: 2.6.2
  2032                  * 
  2033                  *  hIwDY32hYGCE8MkBA/wOu7d45aUxF4Q0RKJprD3v5Z9K1YcRJ2fve87lMlDlx4Oj
  2034                  *  eW4GDdBfLbJE7VUpp13N19GL8e/AqbyyjHH4aS0YoTk10QQ9nnRvjY8nZL3MPXSZ
  2035                  *  g9VGQxFeGqzykzmykU6A26MSMexR4ApeeON6xzZWfo+0yOqAq6lb46wsvldZ96YA
  2036                  *  AABH78hyX7YX4uT1tNCWEIIBoqqvCeIMpp7UQ2IzBrXg6GtukS8NxbukLeamqVW3
  2037                  *  1yt21DYOjuLzcMNe/JNsD9vDVCvOOG3OCi8=
  2038                  *  =zzaA
  2039                  *  -----END PGP MESSAGE-----
  2040                  * 
  2041                  *  --foo--
  2042                  */
  2043 
  2044                 // Add internet headers (these are not added by default for S/MIME messages which allows them to be customized)
  2045                 bytes = Encoding.UTF8.GetBytes("MIME-Version: 1.0" + Environment.NewLine +
  2046                                                "Content-Type: multipart/encrypted;" + Environment.NewLine +
  2047                                                "\tprotocol=\"application/pgp-encrypted\";" + Environment.NewLine +
  2048                                                "\tboundary=\"" + boundary + "\"" + Environment.NewLine);
  2049                 data.AddRange(bytes);
  2050 
  2051                 // Add the version identification attachment
  2052                 bytes = Encoding.UTF8.GetBytes(Environment.NewLine +
  2053                                                "--" + boundary + Environment.NewLine +
  2054                                                "Content-Type: application/pgp-encrypted" + Environment.NewLine +
  2055                                                "Content-Description: PGP/MIME version identification" + Environment.NewLine +
  2056                                                Environment.NewLine);
  2057                 data.AddRange(bytes);
  2058 
  2059                 foreach (PEPAttachment attach in this._Attachments)
  2060                 {
  2061                     if (attach.IsPGPMIMEVersionInfoFormat)
  2062                     {
  2063                         data.AddRange(attach.Data);
  2064                         versionInfoFound = true;
  2065                         break;
  2066                     }
  2067                 }
  2068 
  2069                 // Add the content attachment
  2070                 bytes = Encoding.UTF8.GetBytes(Environment.NewLine +
  2071                                                Environment.NewLine +
  2072                                                "--" + boundary + Environment.NewLine +
  2073                                                "Content-Type: application/octet-stream;" + Environment.NewLine +
  2074                                                "\tname=\"msg.asc\"" + Environment.NewLine +
  2075                                                Environment.NewLine);
  2076                 data.AddRange(bytes);
  2077 
  2078                 foreach (PEPAttachment attach in this._Attachments)
  2079                 {
  2080                     if (attach.IsPGPMIMEContentFormat)
  2081                     {
  2082                         data.AddRange(attach.Data);
  2083                         contentFound = true;
  2084                         break;
  2085                     }
  2086                 }
  2087 
  2088                 bytes = Encoding.UTF8.GetBytes(Environment.NewLine +
  2089                                                "--" + boundary + "--" + Environment.NewLine);
  2090                 data.AddRange(bytes);
  2091 
  2092                 // Create the new attachment
  2093                 if (versionInfoFound && contentFound)
  2094                 {
  2095                     newAttachment = new PEPAttachment()
  2096                     {
  2097                         Data = data.ToArray(),
  2098                         MimeType = "multipart/signed",
  2099                         Tag = MapiPropertyValue.PidTagAttachTagMIME
  2100                     };
  2101                 }
  2102             }
  2103 
  2104             return (newAttachment);
  2105         }
  2106 
  2107         /// <summary>
  2108         /// Recursivley converts all "Bcc", "Cc", and "To" identities into a 'flat' list of any members.
  2109         /// This will remove groups (hierarchy) and convert a group into it's members.
  2110         /// </summary>
  2111         public void FlattenAllRecipientIdentities()
  2112         {
  2113             this._Bcc = PEPIdentity.ToFlatList(this._Bcc);
  2114             this._Cc = PEPIdentity.ToFlatList(this._Cc);
  2115             this._To = PEPIdentity.ToFlatList(this._To);
  2116 
  2117             return;
  2118         }
  2119 
  2120         /// <summary>
  2121         /// Add a disclaimer to the message if needed.
  2122         /// Note: This should only be applied to outgoing messages. As the method doesn't 
  2123         /// save the mail item after adding a disclaimer, it's the caller's responsibility
  2124         /// to save if necessary.
  2125         /// </summary>
  2126         /// <param name="outgoingRating">The outgoing rating for this message.</param>
  2127         public void AddDisclaimer(pEpRating outgoingRating)
  2128         {
  2129             // We only add disclaimers to outgoing messages
  2130             if (this.Direction == pEpMsgDirection.pEpDirOutgoing)
  2131             {
  2132                 try
  2133                 {
  2134                     // Get settings for From account
  2135                     var accountSettings = Globals.ThisAddIn.Settings.GetAccountSettings(this.From?.Address);
  2136 
  2137                     /* Add disclaimer if needed:
  2138                      *      - if set to add to all messages
  2139                      *      - if set to add to encrypted messages and outgoing rating is
  2140                      *        at least reliable.
  2141                      */
  2142                     if ((accountSettings.AddDisclaimer == PEPSettings.Disclaimer.AllMessages) ||
  2143                         ((accountSettings.AddDisclaimer == PEPSettings.Disclaimer.OnlyEncryptedMessages) && outgoingRating >= pEpRating.pEpRatingReliable))
  2144                     {
  2145                         // Add to plain text
  2146                         if (string.IsNullOrEmpty(this._LongMsg) == false)
  2147                         {
  2148                             this._LongMsg += Environment.NewLine +
  2149                                              Environment.NewLine +
  2150                                              accountSettings.DisclaimerText;
  2151                         }
  2152 
  2153                         // Add to html
  2154                         if (string.IsNullOrEmpty(this._LongMsgFormattedHtml) == false)
  2155                         {
  2156                             this._LongMsgFormattedHtml += "<br><br><p>" +
  2157                                                           accountSettings.DisclaimerText +
  2158                                                           "</p>";
  2159                         }
  2160 
  2161                         // Add to Rtf
  2162                         if (string.IsNullOrEmpty(this._LongMsgFormattedRtf) == false)
  2163                         {
  2164                             System.Windows.Forms.RichTextBox rtb = new System.Windows.Forms.RichTextBox();
  2165                             rtb.Text = this._LongMsgFormattedRtf;
  2166                             rtb.AppendText(Environment.NewLine +
  2167                                            Environment.NewLine +
  2168                                            accountSettings.DisclaimerText);
  2169                             this._LongMsgFormattedRtf = rtb.ToString();
  2170                         }
  2171                     }
  2172                 }
  2173                 catch (Exception ex)
  2174                 {
  2175                     Log.Error("AddDisclaimer: Error adding disclaimer to message. " + ex.ToString());
  2176                 }
  2177             }
  2178             else
  2179             {
  2180                 Log.Verbose("AddDisclaimer: Skipped. Message is incoming.");
  2181             }
  2182         }
  2183 
  2184         /**************************************************************
  2185          * 
  2186          * Static Methods
  2187          * 
  2188          *************************************************************/
  2189 
  2190         /// <summary>
  2191         /// Constructs a new message from the given pEp engine TextMessage.
  2192         /// The output will never be null.
  2193         /// </summary>
  2194         /// <param name="msg">The TextMessage to construct from.</param>
  2195         /// <param name="createdMessage">The output newly created message (will never be null).</param>
  2196         /// <returns>The status of the method.</returns>
  2197         /// <remarks>In some contexts which call this message, a partial conversion of the
  2198         /// <see cref="TextMessage"/> is acceptable, non-critical, and better than failure (e. G. displaying
  2199         /// a preview to the user). Thus, this method will always create a <see cref="PEPMessage"/>
  2200         /// which is "best effort", and return the <see cref="Globals.ReturnStatus"/> which can be evaluated
  2201         /// in the more critical contexts. Callers of that message need to be aware of this
  2202         /// and check the result if appropriate.</remarks>
  2203         public static Globals.ReturnStatus Create(TextMessage msg,
  2204                                                   out PEPMessage createdMessage)
  2205         {
  2206             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
  2207             PEPMessage newMessage = new PEPMessage();
  2208 
  2209             try
  2210             {
  2211                 /* Note: Skip the following properties which are not supported in the TextMessage
  2212                  *   • ConversationID
  2213                  *   • ConversationIndex
  2214                  *   • ConversationTopic
  2215                  *   • EnableProtection
  2216                  *   • ForceUnencrypted
  2217                  *   • LongMsgFormattedRtf
  2218                  * 
  2219                  * Also note the following are handled as optional fields
  2220                  *   • AutoConsume
  2221                  *   • ForceProtection
  2222                  *   • KeyList
  2223                  *   • NeverUnsecure
  2224                  *   • PEPProtocolVersion
  2225                  *   • Rating
  2226                  * 
  2227                  * This also skips a number of TextMessage properties currently unsupported in PEPMessage.
  2228                  */
  2229 
  2230                 // Attachments
  2231                 newMessage.Attachments.Clear();
  2232                 if (msg.Attachments != null)
  2233                 {
  2234                     for (int i = 0; i < msg.Attachments.Length; i++)
  2235                     {
  2236                         newMessage.Attachments.Add(new PEPAttachment((Blob)msg.Attachments.GetValue(i)));
  2237                     }
  2238                 }
  2239 
  2240                 // Bcc
  2241                 newMessage.Bcc.Clear();
  2242                 if (msg.Bcc != null)
  2243                 {
  2244                     for (int i = 0; i < msg.Bcc.Length; i++)
  2245                     {
  2246                         newMessage.Bcc.Add(new PEPIdentity((pEpIdentity)msg.Bcc.GetValue(i)));
  2247                     }
  2248                 }
  2249 
  2250                 // Cc
  2251                 newMessage.Cc.Clear();
  2252                 if (msg.Cc != null)
  2253                 {
  2254                     for (int i = 0; i < msg.Cc.Length; i++)
  2255                     {
  2256                         newMessage.Cc.Add(new PEPIdentity((pEpIdentity)msg.Cc.GetValue(i)));
  2257                     }
  2258                 }
  2259 
  2260                 newMessage.Direction = msg.Dir;
  2261                 newMessage.From = new PEPIdentity(msg.From);
  2262                 newMessage.Id = msg.Id;
  2263 
  2264                 // Keywords
  2265                 newMessage.Keywords.Clear();
  2266                 if (msg.Keywords != null)
  2267                 {
  2268                     for (int i = 0; i < msg.Keywords.Length; i++)
  2269                     {
  2270                         newMessage.Keywords.Add((string)msg.Keywords.GetValue(i));
  2271                     }
  2272                 }
  2273 
  2274                 newMessage.LongMsg = msg.LongMsg;
  2275                 newMessage.LongMsgFormattedHtml = msg.LongMsgFormatted;
  2276 
  2277                 // Optional properties
  2278                 if (msg.OptFields != null)
  2279                 {
  2280                     foreach (StringPair optField in msg.OptFields)
  2281                     {
  2282                         if (optField.Name != null)
  2283                         {
  2284                             switch (optField.Name)
  2285                             {
  2286                                 case PEPMessage.PR_PEP_AUTO_CONSUME_NAME:
  2287                                     {
  2288                                         newMessage.AutoConsume = optField.Value;
  2289                                         break;
  2290                                     }
  2291                                 case PEPMessage.PR_PEP_FORCE_PROTECTION_NAME:
  2292                                     {
  2293                                         newMessage.ForceProtectionId = optField.Value;
  2294                                         break;
  2295                                     }
  2296                                 case PEPMessage.PR_KEY_LIST_NAME:
  2297                                     {
  2298                                         newMessage.KeyList = optField.Value;
  2299                                         break;
  2300                                     }
  2301                                 case PEPMessage.PR_PEP_NEVER_UNSECURE_NAME:
  2302                                     {
  2303                                         // If it exists it's true, value doesn't matter
  2304                                         newMessage.NeverUnsecure = true;
  2305                                         break;
  2306                                     }
  2307                                 case PEPMessage.PR_PEP_PROTOCOL_VERSION_NAME:
  2308                                     {
  2309                                         newMessage.PEPProtocolVersion = optField.Value;
  2310                                         break;
  2311                                     }
  2312                                 case PEPMessage.PR_ENC_STATUS_NAME:
  2313                                     {
  2314                                         try
  2315                                         {
  2316                                             newMessage.Rating = AdapterExtensions.ParseRatingString(optField.Value);
  2317                                         }
  2318                                         catch
  2319                                         {
  2320                                             newMessage.Rating = pEpRating.pEpRatingUndefined;
  2321                                         }
  2322                                         break;
  2323                                     }
  2324                             }
  2325                         }
  2326                     }
  2327                 }
  2328 
  2329                 // ReceivedOn
  2330                 if (msg.Recv > 0)
  2331                 {
  2332                     DateTime receivedOnUtc = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(msg.Recv);
  2333                     try
  2334                     {
  2335                         newMessage.ReceivedOn = TimeZoneInfo.ConvertTimeFromUtc(receivedOnUtc, TimeZoneInfo.Local);
  2336                     }
  2337                     catch (Exception ex)
  2338                     {
  2339                         Log.Error("PEPMessage.Create: Error converting received time to local time. " + ex.ToString());
  2340                         newMessage.ReceivedOn = new DateTime(1970, 1, 1).AddSeconds(msg.Recv);
  2341                     }
  2342                 }
  2343                 else
  2344                 {
  2345                     newMessage.ReceivedOn = null;
  2346                 }
  2347 
  2348                 // SentOn
  2349                 if (msg.Sent > 0)
  2350                 {
  2351                     DateTime sentOnUtc = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(msg.Sent);
  2352                     try
  2353                     {
  2354                         newMessage.SentOn = TimeZoneInfo.ConvertTimeFromUtc(sentOnUtc, TimeZoneInfo.Local);
  2355                     }
  2356                     catch (Exception ex)
  2357                     {
  2358                         Log.Error("PEPMessage.Create: Error converting sent time to local time. " + ex.ToString());
  2359                         newMessage.SentOn = new DateTime(1970, 1, 1).AddSeconds(msg.Sent);
  2360                     }
  2361                 }
  2362                 else
  2363                 {
  2364                     newMessage.SentOn = null;
  2365                 }
  2366 
  2367                 newMessage.ShortMsg = msg.ShortMsg;
  2368 
  2369                 // To
  2370                 newMessage.To.Clear();
  2371                 if (msg.To != null)
  2372                 {
  2373                     for (int i = 0; i < msg.To.Length; i++)
  2374                     {
  2375                         newMessage.To.Add(new PEPIdentity((pEpIdentity)msg.To.GetValue(i)));
  2376                     }
  2377                 }
  2378             }
  2379             catch (Exception ex)
  2380             {
  2381                 status = Globals.ReturnStatus.Failure;
  2382                 Log.Error("PEPMessage.Create: Failure occured, " + ex.ToString());
  2383             }
  2384 
  2385             createdMessage = newMessage;
  2386             return (status);
  2387         }
  2388 
  2389         /// <summary>
  2390         /// Contructs a new message from the given outlook mail item.
  2391         /// The output will never be null.
  2392         /// </summary>
  2393         /// <param name="omi">The outlook mail item to create the message from.</param>
  2394         /// <param name="createdMessage">The output newly created message (will never be null).</param>
  2395         /// <param name="onlyRecipientsAndDirection">Whether to only set recipients and direction. Only set to
  2396         /// true in special cases, e.g. if the message is only needed to calculate the outgoing rating.</param>
  2397         /// <param name="createContacts">Whether or not to create contacts from the mail items's recipients during
  2398         /// processing of the message. Only set to false in special cases like for outgoing rating.</param>
  2399         /// <returns>The status of the method.</returns>
  2400         /// <remarks>In some contexts which call this message, a partial conversion of the
  2401         /// <see cref="TextMessage"/> is acceptable, non-critical, and better than failure (e. G. displaying
  2402         /// a preview to the user). Thus, this method will always create a <see cref="PEPMessage"/>
  2403         /// which is "best effort", and return the <see cref="Globals.ReturnStatus"/> which can be evaluated
  2404         /// in the more critical contexts. Callers of that message need to be aware of this
  2405         /// and check the result if appropriate.</remarks>
  2406         public static Globals.ReturnStatus Create(Outlook.MailItem omi,
  2407                                                   out PEPMessage createdMessage,
  2408                                                   bool onlyRecipientsAndDirection = false,
  2409                                                   bool createContacts = true)
  2410         {
  2411             byte[] rtfBody;
  2412             string delim;
  2413             string[] keywords;
  2414             DateTime? receivedOn = null;
  2415             DateTime? sentOn = null;
  2416             PEPIdentity ident;
  2417             Outlook.Attachment currAttachment = null;
  2418             Outlook.Attachments attachments = null;
  2419             Outlook.Recipient currRecipient = null;
  2420             Outlook.Recipients recipients = null;
  2421             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
  2422             Globals.ReturnStatus sts;
  2423             PEPMessage newMessage = new PEPMessage();
  2424 
  2425             try
  2426             {
  2427                 Log.Verbose("PEPMessage.Create: Creating new PEPMessage from OMI started.");
  2428 
  2429                 newMessage.Direction = omi.GetIsIncoming() ? pEpMsgDirection.pEpDirIncoming : pEpMsgDirection.pEpDirOutgoing;
  2430                 Log.Verbose("PEPMessage.Create: direction calculated.");
  2431 
  2432                 // Calculate recipients
  2433                 newMessage.Bcc.Clear();
  2434                 newMessage.Cc.Clear();
  2435                 newMessage.To.Clear();
  2436                 recipients = omi.Recipients;
  2437                 for (int i = 1; i <= recipients.Count; i++)
  2438                 {
  2439                     currRecipient = recipients[i];
  2440 
  2441                     switch ((Outlook.OlMailRecipientType)currRecipient.Type)
  2442                     {
  2443                         case Outlook.OlMailRecipientType.olBCC:
  2444                             {
  2445                                 sts = PEPIdentity.Create(currRecipient, out ident);
  2446                                 if (sts == Globals.ReturnStatus.Success)
  2447                                 {
  2448                                     newMessage.Bcc.Add(ident);
  2449                                 }
  2450                                 else
  2451                                 {
  2452                                     // Update the status, only the first failure type is recorded
  2453                                     if (status == Globals.ReturnStatus.Success)
  2454                                     {
  2455                                         status = sts;
  2456                                     }
  2457 
  2458                                     Log.Error("PEPMessage.Create: Failure creating 'Bcc' identity.");
  2459                                 }
  2460 
  2461                                 break;
  2462                             }
  2463                         case Outlook.OlMailRecipientType.olCC:
  2464                             {
  2465                                 sts = PEPIdentity.Create(currRecipient, out ident, (createContacts && (newMessage.Direction == pEpMsgDirection.pEpDirOutgoing)));
  2466                                 if (sts == Globals.ReturnStatus.Success)
  2467                                 {
  2468                                     newMessage.Cc.Add(ident);
  2469                                 }
  2470                                 else
  2471                                 {
  2472                                     // Update the status, only the first failure type is recorded
  2473                                     if (status == Globals.ReturnStatus.Success)
  2474                                     {
  2475                                         status = sts;
  2476                                     }
  2477 
  2478                                     Log.Error("PEPMessage.Create: Failure creating 'Cc' identity.");
  2479                                 }
  2480 
  2481                                 break;
  2482                             }
  2483                         case Outlook.OlMailRecipientType.olTo:
  2484                             {
  2485                                 sts = PEPIdentity.Create(currRecipient, out ident, (createContacts && (newMessage.Direction == pEpMsgDirection.pEpDirOutgoing)));
  2486                                 if (sts == Globals.ReturnStatus.Success)
  2487                                 {
  2488                                     newMessage.To.Add(ident);
  2489                                 }
  2490                                 else
  2491                                 {
  2492                                     // Update the status, only the first failure type is recorded
  2493                                     if (status == Globals.ReturnStatus.Success)
  2494                                     {
  2495                                         status = sts;
  2496                                     }
  2497 
  2498                                     Log.Error("PEPMessage.Create: Failure creating 'To' identity.");
  2499                                 }
  2500 
  2501                                 break;
  2502                             }
  2503                     }
  2504 
  2505                     // Marshal.ReleaseComObject(currRecipient);
  2506                     currRecipient = null;
  2507                 }
  2508 
  2509                 Log.Verbose("PEPMessage.Create: Recipients calculated, calculating main properties.");
  2510 
  2511                 if (onlyRecipientsAndDirection == false)
  2512                 {
  2513                     // From
  2514                     sts = PEPIdentity.GetFromIdentity(omi, out ident);
  2515                     if (sts == Globals.ReturnStatus.Success)
  2516                     {
  2517                         newMessage.From = ident;
  2518                     }
  2519                     else
  2520                     {
  2521                         // Update the status, only the first failure type is recorded
  2522                         if (status == Globals.ReturnStatus.Success)
  2523                         {
  2524                             status = sts;
  2525                         }
  2526 
  2527                         Log.Error("PEPMessage.Create: Failure creating 'From' identity.");
  2528                     }
  2529                     Log.Verbose("PEPMessage.Create: From identity calculated.");
  2530 
  2531                     newMessage.Id = MapiHelper.GetProperty(omi, MapiProperty.PidTagInternetMessageId, "") as string;
  2532                     newMessage.ShortMsg = omi.Subject;
  2533 
  2534                     /* Date & Times
  2535                      * 
  2536                      * Note: The mail item date can be invalid:
  2537                      * For incoming this commonly occurs when creating a mirror -- it's created without a received time.
  2538                      * For outgoing this commonly occurs for unsent mail.
  2539                      * In these cases, the invalid date and time will be returned as 1/1/4501 at 12:00AM
  2540                      * This situation is filtered here and the date is set as null.
  2541                      */
  2542                     receivedOn = omi.ReceivedTime;
  2543                     if ((receivedOn != null) &&
  2544                         (((DateTime)receivedOn).Year > 4000)) // ~2000 years from now, no issue
  2545                     {
  2546                         receivedOn = null;
  2547                     }
  2548                     newMessage.ReceivedOn = receivedOn;
  2549 
  2550                     sentOn = omi.SentOn;
  2551                     if ((sentOn != null) &&
  2552                         (((DateTime)sentOn).Year > 4000)) // ~2000 years from now, no issue
  2553                     {
  2554                         sentOn = null;
  2555                     }
  2556                     newMessage.SentOn = sentOn;
  2557                     Log.Verbose("PEPMessage.Create: Dates calculated.");
  2558 
  2559                     // Conversation information
  2560                     newMessage.ConversationId = omi.ConversationID;
  2561                     newMessage.ConversationIndex = omi.ConversationIndex;
  2562                     newMessage.ConversationTopic = omi.ConversationTopic;
  2563 
  2564                     Log.Verbose("PEPMessage.Create: Main properties calculated, calculating body and attachments.");
  2565 
  2566                     // Calculate text body and attachments
  2567                     // Body
  2568                     if (omi.Body != null)
  2569                     {
  2570                         newMessage.LongMsg = omi.Body.Replace("\r\n", "\n");
  2571 
  2572                         // Save as RTF
  2573                         try
  2574                         {
  2575                             rtfBody = omi.RTFBody;
  2576 
  2577                             if ((rtfBody != null) &&
  2578                                 (rtfBody.Length > 0))
  2579                             {
  2580                                 newMessage.LongMsgFormattedRtf = Encoding.ASCII.GetString(rtfBody, 0, rtfBody.Length);
  2581                             }
  2582                         }
  2583                         catch
  2584                         {
  2585                             Log.Warning("PEPMessage.Create: Unable to read RTF body in MailItem.");
  2586                         }
  2587 
  2588                         // Force any rich text into HTML
  2589                         // WARNING: This is technically a modifcation of the original MailItem
  2590                         // It should be further investigated if this can be removed in the future.
  2591                         if (omi.BodyFormat == Outlook.OlBodyFormat.olFormatRichText)
  2592                         {
  2593                             omi.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
  2594                         }
  2595 
  2596                         if (omi.BodyFormat == Outlook.OlBodyFormat.olFormatHTML)
  2597                         {
  2598                             newMessage.LongMsgFormattedHtml = omi.HTMLBody;
  2599                         }
  2600                     }
  2601 
  2602                     // Attachments
  2603                     newMessage.Attachments.Clear();
  2604                     attachments = omi.Attachments;
  2605                     for (int i = 1; i <= attachments.Count; i++)
  2606                     {
  2607                         currAttachment = attachments[i];
  2608 
  2609                         try
  2610                         {
  2611                             var newAttachment = new PEPAttachment(currAttachment);
  2612                             if (newAttachment?.Data != null)
  2613                             {
  2614                                 newMessage.Attachments.Add(newAttachment);
  2615                             }
  2616                         }
  2617                         catch { }
  2618 
  2619                         // Marshal.ReleaseComObject(currAttachment);
  2620                         currAttachment = null;
  2621                     }
  2622 
  2623                     // Keywords
  2624                     if (omi.Categories != null)
  2625                     {
  2626                         try
  2627                         {
  2628                             newMessage.Keywords.Clear();
  2629 
  2630                             using (RegistryKey key1 = Registry.CurrentUser.OpenSubKey("Control Panel\\International"))
  2631                             {
  2632                                 delim = key1.GetValue("sList").ToString();
  2633                                 keywords = omi.Categories.Split(delim.ToCharArray());
  2634 
  2635                                 for (int i = 0; i < keywords.Length; i++)
  2636                                 {
  2637                                     newMessage.Keywords.Add(keywords[i]);
  2638                                 }
  2639                             }
  2640                         }
  2641                         catch
  2642                         {
  2643                             newMessage.Keywords.Clear();
  2644                             Log.Warning("PEPMessage.Create: Unable to set keywords.");
  2645                         }
  2646                     }
  2647 
  2648                     // Set PEP properties
  2649                     HeaderList headers = omi.GetParsedTransportMessageHeaders();
  2650                     newMessage.SetPEPProperties(omi, headers);
  2651                 }
  2652 
  2653                 Log.Verbose("PEPMessage.Create: New PEPMessage created from OMI.");
  2654             }
  2655             catch (Exception ex)
  2656             {
  2657                 status = Globals.ReturnStatus.Failure;
  2658                 Log.Error("PEPMessage.Create: failure occured, " + ex.ToString());
  2659             }
  2660             finally
  2661             {
  2662                 // Free resources
  2663                 if (currAttachment != null)
  2664                 {
  2665                     // Marshal.ReleaseComObject(currAttachment);
  2666                     currAttachment = null;
  2667                 }
  2668 
  2669                 if (attachments != null)
  2670                 {
  2671                     // Marshal.ReleaseComObject(attachments);
  2672                     attachments = null;
  2673                 }
  2674 
  2675                 if (currRecipient != null)
  2676                 {
  2677                     // Marshal.ReleaseComObject(currRecipient);
  2678                     currRecipient = null;
  2679                 }
  2680 
  2681                 if (recipients != null)
  2682                 {
  2683                     // Marshal.ReleaseComObject(recipients);
  2684                     recipients = null;
  2685                 }
  2686             }
  2687 
  2688             createdMessage = newMessage;
  2689 
  2690             Log.Verbose("PEPMessage.Create: ReturnStatus=" + status.ToString());
  2691             return (status);
  2692         }
  2693 
  2694         /// <summary>
  2695         /// Creates a PEPMessage from a MimeMessage.
  2696         /// </summary>
  2697         /// <param name="mimeMessage">The MimeMessage to create a PEPMessage from.</param>
  2698         /// <param name="createdMessage">The created PEPMessage.</param>
  2699         /// <returns>The creation status.</returns>
  2700         public static Globals.ReturnStatus Create(MimeMessage mimeMessage,
  2701                                                   out PEPMessage createdMessage)
  2702         {
  2703             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
  2704             PEPMessage pEpMessage = new PEPMessage();
  2705 
  2706             try
  2707             {
  2708                 // Attachments
  2709                 if (mimeMessage.Attachments != null)
  2710                 {
  2711                     foreach (var attachment in mimeMessage.Attachments)
  2712                     {
  2713                         // Cast attachment to MimePart and check if casting was successful
  2714                         MimePart currentAttachment = attachment as MimePart;
  2715                         if (currentAttachment == null)
  2716                         {
  2717                             Log.Error("PEPMessage.Create: Error casting attachment.");
  2718                             continue;
  2719                         }
  2720 
  2721                         PEPAttachment pEpAttachment = new PEPAttachment();
  2722 
  2723                         // Assign content type
  2724                         pEpAttachment.MimeType = currentAttachment.ContentType?.MimeType;
  2725                         pEpAttachment.FileName = currentAttachment.FileName;
  2726 
  2727                         // Decode content object and copy to data property
  2728                         using (var memStream = new MemoryStream())
  2729                         {
  2730                             currentAttachment.ContentObject?.DecodeTo(memStream);
  2731                             pEpAttachment.Data = memStream.ToArray();
  2732                         }
  2733 
  2734                         // Add attachment to PEPMessage.Attachments collection
  2735                         pEpMessage.Attachments.Add(pEpAttachment);
  2736                     }
  2737                 }
  2738 
  2739                 // Check for inline content
  2740                 foreach (var bodyPart in mimeMessage.BodyParts)
  2741                 {
  2742                     // If inline content is found, add it as attachment
  2743                     if (bodyPart.ContentDisposition?.Disposition == ContentDisposition.Inline)
  2744                     {
  2745                         var pEpAttachment = new PEPAttachment
  2746                         {
  2747                             ContentId = bodyPart.ContentId,
  2748                             MimeType = bodyPart.ContentType?.MimeType
  2749                         };
  2750 
  2751                         // Decode content object and copy to data property
  2752                         using (var memStream = new MemoryStream())
  2753                         {
  2754                             (bodyPart as MimePart)?.ContentObject?.DecodeTo(memStream);
  2755                             pEpAttachment.Data = memStream.ToArray();
  2756                         }
  2757 
  2758                         // Add inline content as attachment
  2759                         pEpMessage.Attachments.Add(pEpAttachment);
  2760                     }
  2761                 }
  2762 
  2763                 // Body
  2764                 pEpMessage.LongMsg = mimeMessage.TextBody;
  2765                 pEpMessage.LongMsgFormattedHtml = mimeMessage.HtmlBody;
  2766 
  2767                 // Subject
  2768                 pEpMessage.ShortMsg = mimeMessage.Subject;
  2769 
  2770                 // Process recipients
  2771                 // BCC
  2772                 for (int i = 0; i < mimeMessage.Bcc?.Count; i++)
  2773                 {
  2774                     PEPIdentity ident = new PEPIdentity();
  2775                     ident.Address = (mimeMessage.Bcc[i] as MailboxAddress)?.Address;
  2776                     ident.UserName = (mimeMessage.Bcc[i] as MailboxAddress)?.Name;
  2777 
  2778                     pEpMessage.Bcc.Add(ident);
  2779                 }
  2780 
  2781                 // CC
  2782                 for (int i = 0; i < mimeMessage.Cc?.Count; i++)
  2783                 {
  2784                     PEPIdentity ident = new PEPIdentity();
  2785                     ident.Address = (mimeMessage.Cc[i] as MailboxAddress)?.Address;
  2786                     ident.UserName = (mimeMessage.Cc[i] as MailboxAddress)?.Name;
  2787 
  2788                     pEpMessage.Cc.Add(ident);
  2789                 }
  2790 
  2791                 // To
  2792                 for (int i = 0; i < mimeMessage.To?.Count; i++)
  2793                 {
  2794                     PEPIdentity ident = new PEPIdentity();
  2795                     ident.Address = (mimeMessage.To[i] as MailboxAddress)?.Address;
  2796                     ident.UserName = (mimeMessage.To[i] as MailboxAddress)?.Name;
  2797 
  2798                     pEpMessage.To.Add(ident);
  2799                 }
  2800 
  2801                 // From
  2802                 if (mimeMessage.Sender != null)
  2803                 {
  2804                     PEPIdentity ident = new PEPIdentity();
  2805                     ident.Address = mimeMessage.Sender?.Address;
  2806                     ident.UserName = mimeMessage.Sender?.Name;
  2807 
  2808                     pEpMessage.From = ident;
  2809                 }
  2810                 else if (mimeMessage.From?.Count == 1)
  2811                 {
  2812                     PEPIdentity ident = new PEPIdentity();
  2813                     ident.Address = (mimeMessage.From[0] as MailboxAddress)?.Address;
  2814                     ident.UserName = (mimeMessage.From[0] as MailboxAddress)?.Name;
  2815 
  2816                     pEpMessage.From = ident;
  2817                 }
  2818 
  2819                 // pEp header fields
  2820                 if (mimeMessage.Headers != null)
  2821                 {
  2822                     foreach (var header in mimeMessage.Headers)
  2823                     {
  2824                         if (header?.Field != null)
  2825                         {
  2826                             if (string.Equals(header.Field.Trim(), PEPMessage.PR_KEY_LIST_NAME, StringComparison.OrdinalIgnoreCase))
  2827                             {
  2828                                 pEpMessage.KeyList = header.Value;
  2829                             }
  2830                             else if (string.Equals(header.Field.Trim(), PEPMessage.PR_PEP_AUTO_CONSUME_NAME, StringComparison.OrdinalIgnoreCase))
  2831                             {
  2832                                 pEpMessage.AutoConsume = header.Value;
  2833                             }
  2834                             else if (string.Equals(header.Field.Trim(), PEPMessage.PR_PEP_FORCE_PROTECTION_NAME, StringComparison.OrdinalIgnoreCase))
  2835                             {
  2836                                 pEpMessage.ForceProtectionId = header.Value;
  2837                             }
  2838                             else if (string.Equals(header.Field.Trim(), PEPMessage.PR_PEP_NEVER_UNSECURE_NAME, StringComparison.OrdinalIgnoreCase))
  2839                             {
  2840                                 pEpMessage.NeverUnsecure = true;
  2841                             }
  2842                             else if (string.Equals(header.Field.Trim(), PEPMessage.PR_PEP_PROTOCOL_VERSION_NAME, StringComparison.OrdinalIgnoreCase))
  2843                             {
  2844                                 pEpMessage.PEPProtocolVersion = header.Value;
  2845                             }
  2846                         }
  2847                     }
  2848                 }
  2849 
  2850                 // Message date
  2851                 if (mimeMessage.Date != null)
  2852                 {
  2853                     pEpMessage.SentOn = mimeMessage.Date.DateTime;
  2854                 }
  2855             }
  2856             catch (Exception ex)
  2857             {
  2858                 status = Globals.ReturnStatus.Failure;
  2859                 Log.Error("Create: Error creating PEPMessage from MimeMessage. " + ex.ToString());
  2860             }
  2861 
  2862             createdMessage = pEpMessage;
  2863 
  2864             return status;
  2865         }
  2866 
  2867         /// <summary>
  2868         /// Returns the mirror of a given Outlook mail item based on its EntryId or MessageId
  2869         /// and the sender's user name.
  2870         /// </summary>
  2871         /// <param name="id">The entry id of the original encrypted mail item.</param>
  2872         /// <param name="userName">The user name of the original's sender.</param>
  2873         /// <returns>The mirror mail item if successful. Otherwise null.</returns>
  2874         public static Outlook.MailItem GetMirror(string id, string userName)
  2875         {
  2876             string mirrorID;
  2877             string origEntryID;
  2878             Outlook.MailItem mirrorItem = null;
  2879             Outlook.MailItem currMirror = null;
  2880             Outlook.NameSpace ns = Globals.ThisAddIn.Application.Session;
  2881             Outlook.Store pEpStore = Globals.ThisAddIn.PEPStoreRootFolder.Store;
  2882             Outlook.Folder targetFolder = null;
  2883             Outlook.Items targetFolderItems = null;
  2884 
  2885             if (string.IsNullOrEmpty(id) == false)
  2886             {
  2887                 // Try to find in cache
  2888                 try
  2889                 {
  2890                     lock (mutexMirror)
  2891                     {
  2892                         mirrorID = mirrorCache[id];
  2893                     }
  2894 
  2895                     // Note: If messageId was stored, this method returns null
  2896                     mirrorItem = (Outlook.MailItem)ns.GetItemFromID(mirrorID, pEpStore.StoreID);
  2897                 }
  2898                 catch
  2899                 {
  2900                     mirrorItem = null;
  2901                 }
  2902 
  2903                 // Try to find same EntryID
  2904                 if (mirrorItem == null)
  2905                 {
  2906                     try
  2907                     {
  2908                         // Note: If messageId was stored, this method returns null
  2909                         mirrorItem = (Outlook.MailItem)ns.GetItemFromID(id, pEpStore.StoreID);
  2910                     }
  2911                     catch
  2912                     {
  2913                         mirrorItem = null;
  2914                     }
  2915                 }
  2916 
  2917                 // Try to find by user property
  2918                 if (mirrorItem == null)
  2919                 {
  2920                     Outlook.UserDefinedProperties up = null;
  2921 
  2922                     try
  2923                     {
  2924                         targetFolder = PEPMessage.GetMirrorFolder(userName);
  2925                         targetFolderItems = targetFolder.Items;
  2926 
  2927                         /* Add user property to folder if not there already:
  2928                          * "If you are trying to use the Find or Restrict methods with user-defined fields,
  2929                          * the fields must be defined in the folder, otherwise an error will occur."
  2930                          * See: https://msdn.microsoft.com/en-us/library/office/ff869662.aspx
  2931                         */
  2932                         up = targetFolder.UserDefinedProperties as Outlook.UserDefinedProperties;
  2933                         if (up.Find(MailItemExtensions.USER_PROPERTY_KEY_ORIG_ENTRY_ID) == null)
  2934                         {
  2935                             up.Add(MailItemExtensions.USER_PROPERTY_KEY_ORIG_ENTRY_ID, Outlook.OlUserPropertyType.olText);
  2936                         }
  2937 
  2938                         string filter = string.Format("[{0}] = '{1}'", MailItemExtensions.USER_PROPERTY_KEY_ORIG_ENTRY_ID, id);
  2939                         mirrorItem = targetFolderItems.Find(filter) as Outlook.MailItem;
  2940 
  2941                         if (mirrorItem != null)
  2942                         {
  2943                             lock (mutexMirror)
  2944                             {
  2945                                 mirrorCache[id] = mirrorItem.EntryID;
  2946                             }
  2947                         }
  2948                     }
  2949                     catch (Exception e)
  2950                     {
  2951                         mirrorItem = null;
  2952                         Log.Error("GetMirror: Error searching for mirror omi. " + e.Message);
  2953                     }
  2954                     finally
  2955                     {
  2956                         up = null;
  2957                     }
  2958                 }
  2959 
  2960                 // Nothing worked, search each item by user property (slow)
  2961                 if (mirrorItem == null)
  2962                 {
  2963                     targetFolder = PEPMessage.GetMirrorFolder(userName);
  2964                     targetFolderItems = targetFolder.Items;
  2965 
  2966                     // Note: index starts at 1
  2967                     for (int i = 1; i <= targetFolderItems.Count; i++)
  2968                     {
  2969                         currMirror = null;
  2970 
  2971                         try
  2972                         {
  2973                             currMirror = (Outlook.MailItem)targetFolderItems[i];
  2974                             origEntryID = (string)currMirror.GetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_ORIG_ENTRY_ID, "");
  2975 
  2976                             if (origEntryID == id)
  2977                             {
  2978                                 lock (mutexMirror)
  2979                                 {
  2980                                     mirrorCache[id] = currMirror.EntryID;
  2981                                 }
  2982 
  2983                                 mirrorItem = currMirror;
  2984                                 break;
  2985                             }
  2986                         }
  2987                         catch { }
  2988 
  2989                         currMirror = null;
  2990                     }
  2991                 }
  2992             }
  2993             else
  2994             {
  2995                 Log.Error("MsgProcessor.GetMirror: EntryId is null or empty.");
  2996             }
  2997 
  2998             /* Release objects
  2999              * Note: Do not release mirror or currMirror (which may point to mirror).
  3000              * These are returned for external use.
  3001              */
  3002             ns = null;
  3003             pEpStore = null;
  3004             targetFolder = null;
  3005             targetFolderItems = null;
  3006 
  3007             return mirrorItem;
  3008         }
  3009 
  3010         /// <summary>
  3011         /// Gets the mirror folder based on a user name.
  3012         /// </summary>
  3013         /// <param name="userName">The user name.</param>
  3014         /// <returns>The Outlook folder for this user name.</returns>
  3015         public static Outlook.Folder GetMirrorFolder(string userName)
  3016         {
  3017             string[] specialChars;
  3018             Outlook.Folder folder;
  3019             Outlook.Folders folders = Globals.ThisAddIn.PEPStoreRootFolder.Folders;
  3020             StringBuilder strBuilder = new StringBuilder();
  3021 
  3022             if (userName != null)
  3023             {
  3024                 /* Remove special characters from folder name.
  3025                  * See: https://msdn.microsoft.com/en-us/library/aa493942(v=exchg.80).aspx
  3026                  */
  3027                 specialChars = new string[] { "[", "]", "/", "\\", "&", "~", "?", "*", "|", "<", ">", "\"", ";", ":", "+" };
  3028                 if (userName != null)
  3029                 {
  3030                     strBuilder.Append(userName);
  3031                     for (int i = 0; i < specialChars.Length; i++)
  3032                     {
  3033                         strBuilder.Replace(specialChars[i], "");
  3034                     }
  3035                     userName = strBuilder.ToString();
  3036                 }
  3037 
  3038                 // Use unknown if invalid
  3039                 if (string.IsNullOrWhiteSpace(userName))
  3040                 {
  3041                     userName = MailItemExtensions.UNKNOWN_SENDER;
  3042                 }
  3043             }
  3044             else
  3045             {
  3046                 userName = MailItemExtensions.UNKNOWN_SENDER;
  3047             }
  3048 
  3049             try
  3050             {
  3051                 folder = (Outlook.Folder)folders[userName];
  3052                 Log.Verbose("GetUnencryptedFolder: Using existing folder.");
  3053             }
  3054             catch
  3055             {
  3056                 folder = (Outlook.Folder)folders.Add(userName);
  3057                 Log.Verbose("GetUnencryptedFolder: Creating new folder.");
  3058             }
  3059 
  3060             folders = null;
  3061 
  3062             return (folder);
  3063         }
  3064 
  3065         /// <summary>
  3066         /// Determines if the given text is PGP text based on starting text sequence.
  3067         /// The starting sequence must be "-----BEGIN PGP MESSAGE-----"
  3068         /// </summary>
  3069         /// <param name="text">The text to test if it is PGP text.</param>
  3070         /// <returns>True if the given text is PGP text, otherwise false.</returns>
  3071         public static bool IsPGPText(string text)
  3072         {
  3073             if (string.IsNullOrEmpty(text) == false)
  3074             {
  3075                 string pgp_text = text.Trim();
  3076                 return (pgp_text.StartsWith("-----BEGIN PGP MESSAGE-----"));
  3077             }
  3078             else
  3079             {
  3080                 return (false);
  3081             }
  3082         }
  3083 
  3084         /// <summary>
  3085         /// Determines if the given message is considered secure.
  3086         /// Currently, only PGP encrypted messages will be detected.
  3087         /// </summary>
  3088         /// <param name="msg">The message to check security for.</param>
  3089         /// <returns>True if the given message is encrypted, otherwise false.</returns>
  3090         public static bool GetIsSecure(PEPMessage msg)
  3091         {
  3092             if (msg != null)
  3093             {
  3094                 // Partitioned or inline PGP format
  3095                 if (msg.LongMsg != null && PEPMessage.IsPGPText(msg.LongMsg))
  3096                 {
  3097                     return (true);
  3098                 }
  3099 
  3100                 // PGP/MIME format
  3101                 if (PEPMessage.GetIsPGPMIMEEncrypted(msg))
  3102                 {
  3103                     return (true);
  3104                 }
  3105             }
  3106 
  3107             return (false);
  3108         }
  3109 
  3110         /// <summary>
  3111         /// Determines if the given message is encrypted in PGP/MIME format.
  3112         /// </summary>
  3113         /// <param name="msg">The message to check encryption for.</param>
  3114         /// <returns>True if the given message is PGP/MIME encrypted, otherwise false.</returns>
  3115         public static bool GetIsPGPMIMEEncrypted(PEPMessage msg)
  3116         {
  3117             bool result = false;
  3118             bool versionInfoFound = false;
  3119             bool contentFound = false;
  3120 
  3121             if (msg != null)
  3122             {
  3123                 // Require only two attachments (version identification & encrypted content)
  3124                 // However, allow the attachments to be in any order
  3125                 if (msg.Attachments.Count == 2)
  3126                 {
  3127                     foreach (PEPAttachment attach in msg.Attachments)
  3128                     {
  3129                         if (attach.IsPGPMIMEVersionInfoFormat)
  3130                         {
  3131                             versionInfoFound = true;
  3132                         }
  3133                         else if (attach.IsPGPMIMEContentFormat)
  3134                         {
  3135                             contentFound = true;
  3136                         }
  3137                     }
  3138 
  3139                     if (versionInfoFound && contentFound)
  3140                     {
  3141                         result = true;
  3142                     }
  3143                 }
  3144             }
  3145 
  3146             return (result);
  3147         }
  3148     }
  3149 
  3150     /// <summary>
  3151     /// Custom exception for when the PGP/MIME attachment exceeds the server's
  3152     /// allowable limit.
  3153     /// </summary>
  3154     [Serializable]
  3155     internal class AttachmentSizeException : Exception
  3156     {
  3157         public AttachmentSizeException()
  3158         {
  3159         }
  3160 
  3161         public AttachmentSizeException(string message)
  3162             : base(message)
  3163         {
  3164         }
  3165 
  3166         public AttachmentSizeException(string message, Exception inner)
  3167             : base(message, inner)
  3168         {
  3169         }
  3170     }
  3171 }