PEPMessage.cs
author Markus Schaber <markus@pep-security.net>
Sat, 28 May 2016 10:49:40 +0200
branchunregister_callbacks
changeset 940 f349ba4cdcba
parent 927 ce3aac0a61e8
child 954 3dc2c7c6bcc8
permissions -rw-r--r--
Closed the wrongly named branch.
     1 ´╗┐using Microsoft.Win32;
     2 using pEpCOMServerAdapterLib;
     3 using System;
     4 using System.Collections.Generic;
     5 using System.IO;
     6 using System.Runtime.InteropServices;
     7 using Outlook = Microsoft.Office.Interop.Outlook;
     8 
     9 namespace pEp
    10 {
    11     /// <summary>
    12     /// Class for a completely in-memory message based on the pEp engine text_message.
    13     /// </summary>
    14     internal class PEPMessage : Interfaces.ICopy<PEPMessage>
    15     {
    16         // pEp properties
    17         public const string PR_OPT_FIELD     = "http://schemas.microsoft.com/mapi/string/{00020386-0000-0000-C000-000000000046}/";
    18         public const string PR_X_ENC_STATUS  = "http://schemas.microsoft.com/mapi/string/{00020386-0000-0000-C000-000000000046}/X-EncStatus";
    19         public const string PR_X_KEY_LIST    = "http://schemas.microsoft.com/mapi/string/{00020386-0000-0000-C000-000000000046}/X-KeyList";
    20         public const string PR_X_PEP_VERSION = "http://schemas.microsoft.com/mapi/string/{00020386-0000-0000-C000-000000000046}/X-pEp-Version";
    21 
    22         public const string PR_X_ENC_STATUS_NAME  = "X-EncStatus";
    23         public const string PR_X_KEY_LIST_NAME    = "X-KeyList";
    24         public const string PR_X_PEP_VERSION_NAME = "X-pEp-Version";
    25 
    26         private List<PEPAttachment> _Attachments;
    27         private List<PEPIdentity>   _BCC;
    28         private List<PEPIdentity>   _CC;
    29         private _pEp_msg_direction  _Direction;
    30         private PEPIdentity         _From;
    31         private string              _ID;
    32         private List<string>        _Keywords;
    33         private string              _LongMsg;
    34         private string              _LongMsgFormattedHTML;
    35         private string              _LongMsgFormattedRTF;
    36         private List<opt_field>     _OptionalProperties;
    37         private DateTime?           _ReceivedOn;
    38         private DateTime?           _SentOn;
    39         private string              _ShortMsg;
    40         private List<PEPIdentity>   _To;
    41 
    42         /**************************************************************
    43          * 
    44          * Constructors
    45          * 
    46          *************************************************************/
    47 
    48         /// <summary>
    49         /// Default constructor.
    50         /// </summary>
    51         public PEPMessage()
    52         {
    53             this._Attachments = new List<PEPAttachment>();
    54             this._BCC = new List<PEPIdentity>();
    55             this._CC = new List<PEPIdentity>();
    56             this._Direction = _pEp_msg_direction.pEp_dir_incoming;
    57             this._From = null;
    58             this._ID = null;
    59             this._Keywords = new List<string>();
    60             this._LongMsg = null;
    61             this._LongMsgFormattedHTML = null;
    62             this._LongMsgFormattedRTF = null;
    63             this._OptionalProperties = new List<opt_field>();
    64             this._ReceivedOn = null;
    65             this._SentOn = null;
    66             this._ShortMsg = null;
    67             this._To = new List<PEPIdentity>();
    68         }
    69 
    70         /**************************************************************
    71          * 
    72          * Property Accessors
    73          * 
    74          *************************************************************/
    75 
    76         #region Property Accessors
    77 
    78         /// <summary>
    79         /// Gets the list of attachements for this message.
    80         /// </summary>
    81         public List<PEPAttachment> Attachments
    82         {
    83             get { return (this._Attachments); }
    84         }
    85 
    86         /// <summary>
    87         /// Gets the list of identities to be blind carbon copied on the message.
    88         /// </summary>
    89         public List<PEPIdentity> BCC
    90         {
    91             get { return (this._BCC); }
    92         }
    93 
    94         /// <summary>
    95         /// Gets the list of identities to be carbon copied on the message.
    96         /// </summary>
    97         public List<PEPIdentity> CC
    98         {
    99             get { return (this._CC); }
   100         }
   101 
   102         /// <summary>
   103         /// Gets the date and time when the message was either sent or received.
   104         /// This corresponds with the SentOn and ReceivedOn properties.
   105         /// </summary>
   106         public DateTime? DateTimeSentOrReceived
   107         {
   108             get
   109             {
   110                 if (this._Direction == _pEp_msg_direction.pEp_dir_incoming)
   111                 {
   112                     return (this._ReceivedOn);
   113                 }
   114                 else
   115                 {
   116                     return (this._SentOn);
   117                 }
   118             }
   119         }
   120 
   121         /// <summary>
   122         /// Gets or sets the direction (incoming or outgoing) of the message.
   123         /// </summary>
   124         public _pEp_msg_direction Direction
   125         {
   126             get { return (this._Direction); }
   127             set { this._Direction = value; }
   128         }
   129 
   130         /// <summary>
   131         /// Gets or sets the from identity of the message.
   132         /// Warning: this value can be null.
   133         /// </summary>
   134         public PEPIdentity From
   135         {
   136             get { return (this._From); }
   137             set { this._From = value; }
   138         }
   139 
   140         /// <summary>
   141         /// Gets or sets the ID of the message.
   142         /// Warning: this value can be null.
   143         /// </summary>
   144         public string ID
   145         {
   146             get { return (this._ID); }
   147             set { this._ID = value; }
   148         }
   149 
   150         /// <summary>
   151         /// Gets whether this message is encrypted.
   152         /// This will forward the call to the static method of the same purpose.
   153         /// </summary>
   154         public bool IsEncrypted
   155         {
   156             get { return (PEPMessage.GetIsEncrypted(this)); }
   157         }
   158 
   159         /// <summary>
   160         /// Gets the list of keywords associated with the message.
   161         /// </summary>
   162         public List<string> Keywords
   163         {
   164             get { return (this._Keywords); }
   165         }
   166 
   167         /// <summary>
   168         /// Gets or sets the long-form (body) of the message.
   169         /// This should be plain text.
   170         /// Warning: this value can be null.
   171         /// </summary>
   172         public string LongMsg
   173         {
   174             get { return (this._LongMsg); }
   175             set { this._LongMsg = value; }
   176         }
   177 
   178         /// <summary>
   179         /// Gets or sets the HTML formatted long-form (body) of the message.
   180         /// Warning: this value can be null.
   181         /// </summary>
   182         public string LongMsgFormattedHTML
   183         {
   184             get { return (this._LongMsgFormattedHTML); }
   185             set { this._LongMsgFormattedHTML = value; }
   186         }
   187 
   188         /// <summary>
   189         /// Gets or sets the RTF formatted long-form (body) of the message.
   190         /// Warning: this value can be null.
   191         /// </summary>
   192         public string LongMsgFormattedRTF
   193         {
   194             get { return (this._LongMsgFormattedRTF); }
   195             set { this._LongMsgFormattedRTF = value; }
   196         }
   197 
   198         /// <summary>
   199         /// Gets the list of optional properties associated with the message.
   200         /// </summary>
   201         public List<opt_field> OptionalProperties
   202         {
   203             get { return (this._OptionalProperties); }
   204         }
   205 
   206         /// <summary>
   207         /// Gets or sets the date and time when the message was received.
   208         /// </summary>
   209         public DateTime? ReceivedOn
   210         {
   211             get { return (this._ReceivedOn); }
   212             set { this._ReceivedOn = value; }
   213         }
   214 
   215         /// <summary>
   216         /// Gets or sets the date and time when the message was sent.
   217         /// </summary>
   218         public DateTime? SentOn
   219         {
   220             get { return (this._SentOn); }
   221             set { this._SentOn = value; }
   222         }
   223 
   224         /// <summary>
   225         /// Gets or sets the short-form (subject) of the message.
   226         /// Warning: this value can be null.
   227         /// </summary>
   228         public string ShortMsg
   229         {
   230             get { return (this._ShortMsg); }
   231             set { this._ShortMsg = value; }
   232         }
   233 
   234         /// <summary>
   235         /// Gets the list of identities to receive the message.
   236         /// </summary>
   237         public List<PEPIdentity> To
   238         {
   239             get { return (this._To); }
   240         }
   241 
   242         /// <summary>
   243         /// Gets the total number of all recipients in the message (BCC, CC &amp; To).
   244         /// </summary>
   245         public int RecipientCount
   246         {
   247             get { return (this._BCC.Count + this._CC.Count + this._To.Count); }
   248         }
   249 
   250         /// <summary>
   251         /// Gets a list of all recipients in the message (BCC, CC &amp; To).
   252         /// Each recipient in the returned list is a copy.
   253         /// </summary>
   254         public PEPIdentity[] Recipients
   255         {
   256             get
   257             {
   258                 List<PEPIdentity> recipients = new List<PEPIdentity>();
   259 
   260                 // BCC
   261                 for (int i = 0; i < this._BCC.Count; i++)
   262                 {
   263                     recipients.Add(this._BCC[i].Copy());
   264                 }
   265 
   266                 // CC
   267                 for (int i = 0; i < this._CC.Count; i++)
   268                 {
   269                     recipients.Add(this._CC[i].Copy());
   270                 }
   271 
   272                 // To
   273                 for (int i = 0; i < this._To.Count; i++)
   274                 {
   275                     recipients.Add(this._To[i].Copy());
   276                 }
   277 
   278                 return (recipients.ToArray());
   279             }
   280         }
   281 
   282         #endregion
   283 
   284         /**************************************************************
   285          * 
   286          * Methods
   287          * 
   288          *************************************************************/
   289 
   290         /// <summary>
   291         /// Returns this pEp message as a new pEp engine text_message.
   292         /// Warning: Any identity members (groups) are lost, call FlattenAllRecipientIdentities() before this method.
   293         /// </summary>
   294         /// <returns>A pEp engine identity.</returns>
   295         public text_message ToCOMType()
   296         {
   297             List<blob> attachments = new List<blob>();
   298             List<pEp_identity_s> bcc = new List<pEp_identity_s>();
   299             List<pEp_identity_s> cc = new List<pEp_identity_s>();
   300             List<pEp_identity_s> to = new List<pEp_identity_s>();
   301             text_message result = new text_message();
   302 
   303             // Convert attachments
   304             for (int i = 0; i < this._Attachments.Count; i++)
   305             {
   306                 attachments.Add(this._Attachments[i].ToCOMType());
   307             }
   308 
   309             // Convert BCC
   310             for (int i = 0; i < this._BCC.Count; i++)
   311             {
   312                 bcc.Add(this._BCC[i].ToCOMType());
   313             }
   314 
   315             // Convert CC
   316             for (int i = 0; i < this._CC.Count; i++)
   317             {
   318                 cc.Add(this._CC[i].ToCOMType());
   319             }
   320 
   321             // Convert To
   322             for (int i = 0; i < this._To.Count; i++)
   323             {
   324                 to.Add(this._To[i].ToCOMType());
   325             }
   326 
   327             /* Set properties
   328              * 
   329              * Note: Skip DateTimeSentOrReceived and LongMsgFormattedRTF which
   330              * are not supported in the pEp engine.
   331              */
   332             result.attachments = attachments.ToArray();
   333             result.bcc = bcc.ToArray();
   334             result.cc = cc.ToArray();
   335             result.dir = this._Direction;
   336             result.from = (this._From == null ? new pEp_identity_s() : this._From.ToCOMType());
   337             result.id = this._ID;
   338             result.keywords = this._Keywords.ToArray();
   339             result.longmsg = this._LongMsg;
   340             result.longmsg_formatted = this._LongMsgFormattedHTML;
   341             result.opt_fields = this._OptionalProperties.ToArray();
   342             result.shortmsg = this._ShortMsg;
   343             result.to = to.ToArray();
   344 
   345             return (result);
   346         }
   347 
   348         /// <summary>
   349         /// Gets a deep copy of the object and all it's data.
   350         /// </summary>
   351         /// <returns>The deep copy of the object.</returns>
   352         public PEPMessage Copy()
   353         {
   354             return (this.Copy(false));
   355         }
   356 
   357         /// <summary>
   358         /// Gets a copy of the PEPMessage with or without data.
   359         /// </summary>
   360         /// <param name="createWithoutContent">Whether to include content such as text body, attachments 
   361         /// and optional properties.</param>
   362         /// <returns>The copy of the PEPMessage.</returns>
   363         public PEPMessage Copy(bool createWithoutContent = false)
   364         {
   365             PEPMessage copy = new PEPMessage();
   366 
   367             // Attachments
   368             copy.Attachments.Clear();
   369             if (createWithoutContent == false)
   370             {
   371                 for (int i = 0; i < this._Attachments.Count; i++)
   372                 {
   373                     copy.Attachments.Add(this._Attachments[i].Copy());
   374                 }
   375             }
   376 
   377             // BCC
   378             copy.BCC.Clear();
   379             for (int i = 0; i < this._BCC.Count; i++)
   380             {
   381                 copy.BCC.Add(this._BCC[i].Copy());
   382             }
   383 
   384             // CC
   385             copy.CC.Clear();
   386             for (int i = 0; i < this._CC.Count; i++)
   387             {
   388                 copy.CC.Add(this._CC[i].Copy());
   389             }
   390 
   391             copy.Direction = this._Direction;
   392             copy.From = (this._From == null ? null : this._From.Copy());
   393             copy.ID = this._ID;
   394 
   395             // Keywords
   396             copy.Keywords.Clear();
   397             for (int i = 0; i < this._Keywords.Count; i++)
   398             {
   399                 copy.Keywords.Add(this._Keywords[i]);
   400             }
   401 
   402             // Body
   403             if (createWithoutContent == false)
   404             {
   405                 copy.LongMsg = this._LongMsg;
   406                 copy.LongMsgFormattedHTML = this._LongMsgFormattedHTML;
   407                 copy.LongMsgFormattedRTF = this._LongMsgFormattedRTF;
   408             }
   409             else
   410             {
   411                 copy.LongMsg = null;
   412                 copy.LongMsgFormattedHTML = null;
   413                 copy.LongMsgFormattedRTF = null;
   414             }
   415 
   416             // OptionalFields
   417             copy.OptionalProperties.Clear();
   418             if (createWithoutContent == false)
   419             {
   420                 for (int i = 0; i < this._OptionalProperties.Count; i++)
   421                 {
   422                     copy.OptionalProperties.Add(this._OptionalProperties[i]);
   423                 }
   424             }
   425 
   426             // ReceivedOn
   427             if (this._ReceivedOn != null)
   428             {
   429                 copy.ReceivedOn = new DateTime(((DateTime)this._ReceivedOn).Ticks);
   430             }
   431             else
   432             {
   433                 copy.ReceivedOn = null;
   434             }
   435 
   436             // SentOn
   437             if (this._SentOn != null)
   438             {
   439                 copy.SentOn = new DateTime(((DateTime)this._SentOn).Ticks);
   440             }
   441             else
   442             {
   443                 copy.SentOn = null;
   444             }
   445 
   446             copy.ShortMsg = this._ShortMsg;
   447 
   448             // To
   449             copy.To.Clear();
   450             for (int i = 0; i < this._To.Count; i++)
   451             {
   452                 copy.To.Add(this._To[i].Copy());
   453             }
   454 
   455             return (copy);
   456         }
   457 
   458         /// <summary>
   459         /// Applies this pEp message's data to the given Outlook item.
   460         /// </summary>
   461         /// <param name="omi">The Outlook mail item to apply this pEp message's data to.</param>
   462         /// <param name="includeOptionalProperties">Whether to include optional properites such as 
   463         /// color rating, pEp version etc...</param>
   464         /// <returns>The status of the method.</returns>
   465         public Globals.ReturnStatus ApplyTo(Outlook.MailItem omi,
   466                                             bool includeOptionalProperties = false)
   467         {
   468             Outlook.Attachments attachments = null;
   469             Outlook.Recipient newRecipient = null;
   470             Outlook.Recipients recipients = null;
   471             Outlook.Account currAccount = null;
   472             Outlook.Account sendUsingAccount = null;
   473             Outlook.Accounts accounts = null;
   474             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
   475 
   476             try
   477             {
   478                 // Remove all recipients
   479                 recipients = omi.Recipients;
   480                 while (recipients.Count > 0)
   481                 {
   482                     recipients.Remove(1);
   483                 }
   484 
   485                 // Set recipients
   486                 for (int i = 0; i < this._BCC.Count; i++)
   487                 {
   488                     if (string.IsNullOrWhiteSpace(this._BCC[i].Address) == false)
   489                     {
   490                         // Add by address
   491                         newRecipient = recipients.Add(this._BCC[i].Address);
   492                         newRecipient.Type = (int)Outlook.OlMailRecipientType.olBCC;
   493 
   494                         Marshal.ReleaseComObject(newRecipient);
   495                         newRecipient = null;
   496                     }
   497                     else if (string.IsNullOrWhiteSpace(this._BCC[i].Username) == false)
   498                     {
   499                         // Add by username (required for distribution lists)
   500                         newRecipient = recipients.Add(this._BCC[i].Username);
   501                         newRecipient.Type = (int)Outlook.OlMailRecipientType.olBCC;
   502 
   503                         Marshal.ReleaseComObject(newRecipient);
   504                         newRecipient = null;
   505                     }
   506                 }
   507 
   508                 for (int i = 0; i < this._CC.Count; i++)
   509                 {
   510                     if (string.IsNullOrWhiteSpace(this._CC[i].Address) == false)
   511                     {
   512                         // Add by address
   513                         newRecipient = recipients.Add(this._CC[i].Address);
   514                         newRecipient.Type = (int)Outlook.OlMailRecipientType.olCC;
   515 
   516                         Marshal.ReleaseComObject(newRecipient);
   517                         newRecipient = null;
   518                     }
   519                     else if (string.IsNullOrWhiteSpace(this._CC[i].Username) == false)
   520                     {
   521                         // Add by username (required for distribution lists)
   522                         newRecipient = recipients.Add(this._CC[i].Username);
   523                         newRecipient.Type = (int)Outlook.OlMailRecipientType.olCC;
   524 
   525                         Marshal.ReleaseComObject(newRecipient);
   526                         newRecipient = null;
   527                     }
   528                 }
   529 
   530                 for (int i = 0; i < this._To.Count; i++)
   531                 {
   532                     if (string.IsNullOrWhiteSpace(this._To[i].Address) == false)
   533                     {
   534                         // Add by address
   535                         newRecipient = recipients.Add(this._To[i].Address);
   536                         newRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
   537 
   538                         Marshal.ReleaseComObject(newRecipient);
   539                         newRecipient = null;
   540                     }
   541                     else if (string.IsNullOrWhiteSpace(this._To[i].Username) == false)
   542                     {
   543                         // Add by username (required for distribution lists)
   544                         newRecipient = recipients.Add(this._To[i].Username);
   545                         newRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
   546 
   547                         Marshal.ReleaseComObject(newRecipient);
   548                         newRecipient = null;
   549                     }
   550                 }
   551 
   552                 try
   553                 {
   554                     recipients.ResolveAll();
   555                 }
   556                 catch { }
   557 
   558                 /* Set sender
   559                  * Note that if fails, will be empty which eventually will use the default send account
   560                  * If the send using account is already populated, this cannot be re-set.
   561                  * So far this doesn't appear to be an issue as it occurs when applying unencrypted data to a mirror.
   562                  * However, the mirror SendUsingAccount is already correct at this point and doesn't need to be set.
   563                  */
   564                 sendUsingAccount = omi.SendUsingAccount;
   565 
   566                 if ((this._From != null) &&
   567                     (this._From.Address != null) &&
   568                     (sendUsingAccount == null))
   569                 {
   570                     accounts = Globals.ThisAddIn.Application.Session.Accounts;
   571 
   572                     // Note: Index starts at 1
   573                     for (int i = 1; i <= accounts.Count; i++)
   574                     {
   575                         currAccount = accounts[i];
   576 
   577                         if ((currAccount.SmtpAddress != null) &&
   578                             (currAccount.SmtpAddress.ToUpper() == this._From.Address.ToUpper()))
   579                         {
   580                             /* Try to set the SendUsingAccount
   581                              * This will fail if the mail item is already marked as sent or the SendUsingAccount is not null, etc...
   582                              * If it fails, Outlook will in the end just use the default account.
   583                              * This property should ideally be set before a mail item is saved.
   584                              */
   585                             try
   586                             {
   587                                 omi.SendUsingAccount = currAccount;
   588                             }
   589                             catch { }
   590 
   591                             Marshal.ReleaseComObject(currAccount);
   592                             currAccount = null;
   593 
   594                             break;
   595                         }
   596 
   597                         Marshal.ReleaseComObject(currAccount);
   598                         currAccount = null;
   599                     }
   600                 }
   601 
   602                 // Set the subject
   603                 omi.Subject = this._ShortMsg;
   604 
   605                 // Set the body (skip RTF format, only use HTML which is what the engine uses)
   606                 if (string.IsNullOrWhiteSpace(this._LongMsgFormattedHTML))
   607                 {
   608                     omi.BodyFormat = Outlook.OlBodyFormat.olFormatPlain;
   609                     omi.Body = this._LongMsg;
   610                 }
   611                 else
   612                 {
   613                     omi.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
   614                     omi.HTMLBody = this._LongMsgFormattedHTML;
   615                 }
   616 
   617                 /* Set the encoding to UTF-8
   618                  * See: https://msdn.microsoft.com/en-us/library/office/ff860730.aspx
   619                  * All PEPMessages should be UTF even those coming from the engine.
   620                  */
   621                 try
   622                 {
   623                     MAPIHelper.SetProperty(omi, MAPIProperty.PidTagInternetCodepage, 65001);
   624                     MAPIHelper.SetProperty(omi, MAPIProperty.PidTagMessageCodepage, 65001);
   625                 }
   626                 catch
   627                 {
   628                     Globals.Log("PEPMessage.ApplyTo: Failed to set UTF-8 encoding.");
   629                 }
   630 
   631                 // Remove any previous attachments
   632                 attachments = omi.Attachments;
   633                 while (attachments.Count > 0)
   634                 {
   635                     attachments.Remove(1);
   636                 }
   637 
   638                 // Add new attachments
   639                 for (int i = 0; i < this._Attachments.Count; i++)
   640                 {
   641                     this._Attachments[i].AddTo(attachments, ("attachment" + i.ToString()));
   642                 }
   643 
   644                 // Optional properties
   645                 if (includeOptionalProperties)
   646                 {
   647                     for (int i = 0; i < this._OptionalProperties.Count; i++)
   648                     {
   649                         MAPIHelper.SetProperty(omi,
   650                                                PEPMessage.PR_OPT_FIELD + this._OptionalProperties[i].name,
   651                                                this._OptionalProperties[i].value);
   652                     }
   653                 }
   654             }
   655             catch (Exception ex)
   656             {
   657                 status = Globals.ReturnStatus.Failure;
   658                 Globals.Log("PEPMessage.ApplyTo: Failure occured, " + ex.ToString());
   659             }
   660             finally
   661             {
   662                 if (attachments != null)
   663                 {
   664                     Marshal.ReleaseComObject(attachments);
   665                     attachments = null;
   666                 }
   667 
   668                 if (newRecipient != null)
   669                 {
   670                     Marshal.ReleaseComObject(newRecipient);
   671                     newRecipient = null;
   672                 }
   673 
   674                 if (recipients != null)
   675                 {
   676                     Marshal.ReleaseComObject(recipients);
   677                     recipients = null;
   678                 }
   679 
   680                 if (currAccount != null)
   681                 {
   682                     Marshal.ReleaseComObject(currAccount);
   683                     currAccount = null;
   684                 }
   685 
   686                 if (sendUsingAccount != null)
   687                 {
   688                     Marshal.ReleaseComObject(sendUsingAccount);
   689                     sendUsingAccount = null;
   690                 }
   691 
   692                 if (accounts != null)
   693                 {
   694                     Marshal.ReleaseComObject(accounts);
   695                     accounts = null;
   696                 }
   697             }
   698 
   699             return (status);
   700         }
   701 
   702         /// <summary>
   703         /// Recursivley converts all "BCC", "CC", and "To" identities into a 'flat' list of any members.
   704         /// This will remove groups (hierarchy) and convert a group into it's members.
   705         /// </summary>
   706         public void FlattenAllRecipientIdentities()
   707         {
   708             this._BCC = PEPIdentity.ToFlatList(this._BCC);
   709             this._CC = PEPIdentity.ToFlatList(this._CC);
   710             this._To = PEPIdentity.ToFlatList(this._To);
   711 
   712             return;
   713         }
   714 
   715         /// <summary>
   716         /// Processes all content IDs in the image attachments as well as HTML body.
   717         /// This will convert the CID's to the image filename only.
   718         /// </summary>
   719         public void NormalizeContentIDs()
   720         {
   721             int currImgTagIndex;
   722             int currImgTagStopIndex;
   723             int currImgTagCIDIndex;
   724             int currImgTagCIDStopIndex;
   725             int tempIndex;
   726             string currCID;
   727             string newCID;
   728             string beginning;
   729             string ending;
   730             string currFileName;
   731             string workingHTML;
   732             Dictionary<string, string> modifiedCIDs = new Dictionary<string, string>();
   733 
   734             // Modify CIDs for all attachments and store modified CIDs in the dictionary
   735             for (int i = 0; i < this._Attachments.Count; i++)
   736             {
   737                 currCID = this._Attachments[i].ContentID;
   738                 currFileName = this._Attachments[i].FileName;
   739 
   740                 if (string.IsNullOrEmpty(currCID))
   741                 {
   742                     /* Just use the file name (even if file name is null or empty)
   743                      * It's doesn't matter as it shouldn't be replaced in HTML.
   744                      * This situation should mean either the attachment isn't an embedded image, or
   745                      * the filename is already used as the CID in the HTML.
   746                      */
   747                     newCID = currFileName;
   748                 }
   749                 else
   750                 {
   751                     if (string.IsNullOrEmpty(currFileName))
   752                     {
   753                         // A name must exist for HTML so create one
   754                         newCID = "attachment" + i.ToString();
   755                     }
   756                     else
   757                     {
   758                         newCID = currFileName;
   759                     }
   760 
   761                     modifiedCIDs.Add(currCID, newCID);
   762                 }
   763 
   764                 this._Attachments[i].ContentID = newCID;
   765             }
   766 
   767             /* Process the HTML body replacing all modified CIDs
   768              * Since no HTML document library is provided in standard C#, text operations are done.
   769              * This should work ok, but there are some potential issue with malformed HTML that might render correctly.
   770              * Only " is supported instead of ' as well.
   771              * This shouldn't be an issue since the method is primarily run on mail items generated internally.
   772              */
   773             workingHTML = this._LongMsgFormattedHTML;
   774             if (string.IsNullOrEmpty(workingHTML) == false)
   775             {
   776                 tempIndex = workingHTML.IndexOf("<img", 0);
   777                 currImgTagIndex = (tempIndex > -1 ? (tempIndex + 4) : -1); // MUST start index after "<img"
   778 
   779                 while (currImgTagIndex > -1)
   780                 {
   781                     try
   782                     {
   783                         tempIndex = workingHTML.IndexOf("src=\"cid:", currImgTagIndex);
   784                         currImgTagCIDIndex = (tempIndex > -1 ? (tempIndex + 9) : -1);
   785                         currImgTagCIDStopIndex = workingHTML.IndexOf("\"", currImgTagCIDIndex);
   786                         currImgTagStopIndex = workingHTML.IndexOf(">", currImgTagIndex);
   787                     }
   788                     catch
   789                     {
   790                         Globals.Log("NormalizeContentIDs: Incorrect index detected when calculating CID position, skipping current img.");
   791 
   792                         // Likely System.ArgumentOutOfRangeException from incorrect index
   793                         // Just invalidate all locations and will not change this <img>
   794                         currImgTagCIDIndex = -1;
   795                         currImgTagCIDStopIndex = -1;
   796                         currImgTagStopIndex = -1;
   797                     }
   798 
   799                     // Validate relative index positions
   800                     if ((currImgTagCIDIndex < currImgTagStopIndex) &&
   801                         (currImgTagCIDStopIndex < currImgTagStopIndex) &&
   802                         ((currImgTagCIDStopIndex - currImgTagCIDIndex) >= 1))
   803                     {
   804                         // Split the HTML at the CID and modify if necessary
   805                         try
   806                         {
   807                             beginning = workingHTML.Substring(0, currImgTagCIDIndex);
   808                             ending = workingHTML.Substring(currImgTagCIDStopIndex);
   809                             currCID = workingHTML.Substring(currImgTagCIDIndex, (currImgTagCIDStopIndex - currImgTagCIDIndex));
   810                         }
   811                         catch
   812                         {
   813                             Globals.Log("NormalizeContentIDs: Error splitting HTML at CID, skipping current img.");
   814 
   815                             beginning = null;
   816                             ending = null;
   817                             currCID = null;
   818                         }
   819 
   820                         // Lookup the new CID
   821                         newCID = null;
   822                         try
   823                         {
   824                             if (currCID != null)
   825                             {
   826                                 newCID = modifiedCIDs[currCID];
   827                             }
   828                         }
   829                         catch
   830                         {
   831                             newCID = null;
   832                         }
   833 
   834                         // Replace
   835                         if ((beginning != null) &&
   836                             (string.IsNullOrEmpty(newCID) == false) &&
   837                             (ending != null))
   838                         {
   839                             workingHTML = beginning + newCID + ending;
   840                         }
   841                     }
   842 
   843                     try
   844                     {
   845                         tempIndex = workingHTML.IndexOf("<img", currImgTagIndex);
   846                         currImgTagIndex = (tempIndex > -1 ? (tempIndex + 4) : -1); // MUST start index after "<img"
   847                     }
   848                     catch
   849                     {
   850                         Globals.Log("NormalizeContentIDs: Incorrect index detected, stopping calculation.");
   851 
   852                         // Likely System.ArgumentOutOfRangeException from incorrect index
   853                         // Stop processing the HTML and use whatever is processed up to this point
   854                         currImgTagIndex = -1;
   855                     }
   856                 }
   857             }
   858             this._LongMsgFormattedHTML = workingHTML;
   859 
   860             return;
   861         }
   862 
   863         /**************************************************************
   864          * 
   865          * Static Methods
   866          * 
   867          *************************************************************/
   868 
   869         /// <summary>
   870         /// Constructs a new message from the given pEp engine text_message.
   871         /// The output will never be null.
   872         /// </summary>
   873         /// <param name="msg">The text_message to construct from.</param>
   874         /// <param name="createdMessage">The output newly created message (will never be null).</param>
   875         /// <returns>The status of the method.</returns>
   876         public static Globals.ReturnStatus Create(text_message msg,
   877                                                   out PEPMessage createdMessage)
   878         {
   879             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
   880             PEPMessage newMessage = new PEPMessage();
   881 
   882             try
   883             {
   884                 // Attachments
   885                 newMessage.Attachments.Clear();
   886                 if (msg.attachments != null)
   887                 {
   888                     for (int i = 0; i < msg.attachments.Length; i++)
   889                     {
   890                         newMessage.Attachments.Add(new PEPAttachment((blob)msg.attachments.GetValue(i)));
   891                     }
   892                 }
   893 
   894                 // BCC
   895                 newMessage.BCC.Clear();
   896                 if (msg.bcc != null)
   897                 {
   898                     for (int i = 0; i < msg.bcc.Length; i++)
   899                     {
   900                         newMessage.BCC.Add(new PEPIdentity((pEp_identity_s)msg.bcc.GetValue(i)));
   901                     }
   902                 }
   903 
   904                 // CC
   905                 newMessage.CC.Clear();
   906                 if (msg.cc != null)
   907                 {
   908                     for (int i = 0; i < msg.cc.Length; i++)
   909                     {
   910                         newMessage.CC.Add(new PEPIdentity((pEp_identity_s)msg.cc.GetValue(i)));
   911                     }
   912                 }
   913 
   914                 newMessage.Direction = msg.dir;
   915                 newMessage.From = new PEPIdentity(msg.from);
   916                 newMessage.ID = msg.id;
   917 
   918                 // Keywords
   919                 newMessage.Keywords.Clear();
   920                 if (msg.keywords != null)
   921                 {
   922                     for (int i = 0; i < msg.keywords.Length; i++)
   923                     {
   924                         newMessage.Keywords.Add((string)msg.keywords.GetValue(i));
   925                     }
   926                 }
   927 
   928                 newMessage.LongMsg = msg.longmsg;
   929                 newMessage.LongMsgFormattedHTML = msg.longmsg_formatted;
   930                 newMessage.LongMsgFormattedRTF = null;
   931 
   932                 // Optional properties
   933                 newMessage.OptionalProperties.Clear();
   934                 if (msg.opt_fields != null)
   935                 {
   936                     for (int i = 0; i < msg.opt_fields.Length; i++)
   937                     {
   938                         newMessage.OptionalProperties.Add((opt_field)msg.opt_fields.GetValue(i));
   939                     }
   940                 }
   941 
   942                 // ReceivedOn
   943                 if (msg.recv > 0)
   944                 {
   945                     newMessage.ReceivedOn = new DateTime(1970, 1, 1).AddSeconds(msg.recv);
   946                 }
   947                 else
   948                 {
   949                     newMessage.ReceivedOn = null;
   950                 }
   951 
   952                 // SentOn
   953                 if (msg.sent > 0)
   954                 {
   955                     newMessage.SentOn = new DateTime(1970, 1, 1).AddSeconds(msg.sent);
   956                 }
   957                 else
   958                 {
   959                     newMessage.SentOn = null;
   960                 }
   961 
   962                 newMessage.ShortMsg = msg.shortmsg;
   963 
   964                 // To
   965                 newMessage.To.Clear();
   966                 if (msg.to != null)
   967                 {
   968                     for (int i = 0; i < msg.to.Length; i++)
   969                     {
   970                         newMessage.To.Add(new PEPIdentity((pEp_identity_s)msg.to.GetValue(i)));
   971                     }
   972                 }
   973             }
   974             catch (Exception ex)
   975             {
   976                 status = Globals.ReturnStatus.Failure;
   977                 Globals.Log("PEPMessage.Create: Failure occured, " + ex.ToString());
   978             }
   979 
   980             createdMessage = newMessage;
   981             return (status);
   982         }
   983 
   984         /// <summary>
   985         /// Contructs a new message from the given outlook mail item.
   986         /// The output will never be null.
   987         /// </summary>
   988         /// <param name="omi">The outlook mail item to create the message from.</param>
   989         /// <param name="createdMessage">The output newly created message (will never be null).</param>
   990         /// <param name="createWithoutContent">Whether to include content such as text body, attachments 
   991         /// and optional properties.</param>
   992         /// <returns>The status of the method.</returns>
   993         public static Globals.ReturnStatus Create(Outlook.MailItem omi,
   994                                                   out PEPMessage createdMessage,
   995                                                   bool createWithoutContent = false)
   996         {
   997             byte[] rtfBody;
   998             string delim;
   999             string[] keywords;
  1000             object propertyValue;
  1001             opt_field newProp;
  1002             DateTime? receivedOn = null;
  1003             DateTime? sentOn = null;
  1004             PEPIdentity ident;
  1005             Outlook.Attachment currAttachment = null;
  1006             Outlook.Attachments attachments = null;
  1007             Outlook.Recipient currRecipient = null;
  1008             Outlook.Recipients recipients = null;
  1009             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
  1010             Globals.ReturnStatus sts;
  1011             PEPMessage newMessage = new PEPMessage();
  1012 
  1013             try
  1014             {
  1015                 Globals.LogVerbose("PEPMessage.Create: Creating new PEPMessage from OMI started, calculating recipients.");
  1016 
  1017                 // Calculate recipients
  1018                 newMessage.BCC.Clear();
  1019                 newMessage.CC.Clear();
  1020                 newMessage.To.Clear();
  1021                 recipients = omi.Recipients;
  1022                 for (int i = 1; i <= recipients.Count; i++)
  1023                 {
  1024                     currRecipient = recipients[i];
  1025 
  1026                     switch ((Outlook.OlMailRecipientType)currRecipient.Type)
  1027                     {
  1028                         case Outlook.OlMailRecipientType.olBCC:
  1029 
  1030                             sts = PEPIdentity.ToIdentity(currRecipient, out ident);
  1031                             if (sts == Globals.ReturnStatus.Success)
  1032                             {
  1033                                 newMessage.BCC.Add(ident);
  1034                             }
  1035                             else
  1036                             {
  1037                                 // Update the status, only the first failure type is recorded
  1038                                 if (status == Globals.ReturnStatus.Success)
  1039                                 {
  1040                                     status = sts;
  1041                                 }
  1042                             }
  1043 
  1044                             break;
  1045                         case Outlook.OlMailRecipientType.olCC:
  1046 
  1047                             sts = PEPIdentity.ToIdentity(currRecipient, out ident);
  1048                             if (sts == Globals.ReturnStatus.Success)
  1049                             {
  1050                                 newMessage.CC.Add(ident);
  1051                             }
  1052                             else
  1053                             {
  1054                                 // Update the status, only the first failure type is recorded
  1055                                 if (status == Globals.ReturnStatus.Success)
  1056                                 {
  1057                                     status = sts;
  1058                                 }
  1059                             }
  1060 
  1061                             break;
  1062                         case Outlook.OlMailRecipientType.olTo:
  1063 
  1064                             sts = PEPIdentity.ToIdentity(currRecipient, out ident);
  1065                             if (sts == Globals.ReturnStatus.Success)
  1066                             {
  1067                                 newMessage.To.Add(ident);
  1068                             }
  1069                             else
  1070                             {
  1071                                 // Update the status, only the first failure type is recorded
  1072                                 if (status == Globals.ReturnStatus.Success)
  1073                                 {
  1074                                     status = sts;
  1075                                 }
  1076                             }
  1077 
  1078                             break;
  1079                     }
  1080 
  1081                     Marshal.ReleaseComObject(currRecipient);
  1082                     currRecipient = null;
  1083                 }
  1084 
  1085                 Globals.LogVerbose("PEPMessage.Create: Recipients calculated, calculating main properties.");
  1086 
  1087                 newMessage.Direction = CryptableMailItem.GetIsIncoming(omi) ? _pEp_msg_direction.pEp_dir_incoming : _pEp_msg_direction.pEp_dir_outgoing;
  1088 
  1089                 // From
  1090                 sts = PEPIdentity.GetFromIdentity(omi, out ident);
  1091                 if (sts == Globals.ReturnStatus.Success)
  1092                 {
  1093                     newMessage.From = ident;
  1094                 }
  1095                 else
  1096                 {
  1097                     // Update the status, only the first failure type is recorded
  1098                     if (status == Globals.ReturnStatus.Success)
  1099                     {
  1100                         status = sts;
  1101                     }
  1102                 }
  1103 
  1104                 newMessage.ID = (string)MAPIHelper.GetProperty(omi, MAPIProperty.PidTagInternetMessageId, "");
  1105                 newMessage.ShortMsg = omi.Subject;
  1106 
  1107                 /* Date & Times
  1108                  * 
  1109                  * Note: The mail item date can be invalid:
  1110                  * For incoming this commonly occurs when creating a mirror -- it's created without a received time.
  1111                  * For outgoing this commonly occurs for unsent mail.
  1112                  * In these cases, the invalid date and time will be returned as 1/1/4501 at 12:00AM
  1113                  * This situation is filtered here and the date is set as null.
  1114                  */
  1115                 receivedOn = omi.ReceivedTime;
  1116                 if ((receivedOn != null) &&
  1117                     (((DateTime)receivedOn).Year > 4000)) // ~2000 years from now, no issue
  1118                 {
  1119                     receivedOn = null;
  1120                 }
  1121                 newMessage.ReceivedOn = receivedOn;
  1122 
  1123                 sentOn = omi.SentOn;
  1124                 if ((sentOn != null) &&
  1125                     (((DateTime)sentOn).Year > 4000)) // ~2000 years from now, no issue
  1126                 {
  1127                     sentOn = null;
  1128                 }
  1129                 newMessage.SentOn = sentOn;
  1130 
  1131                 Globals.LogVerbose("PEPMessage.Create: Main properties calculated, calculating body and attachments.");
  1132 
  1133                 // Calculate text body and attachments
  1134                 if (createWithoutContent == false)
  1135                 {
  1136                     // Body
  1137                     if (omi.Body != null)
  1138                     {
  1139                         newMessage.LongMsg = omi.Body.Replace("\r\n", "\n");
  1140 
  1141                         // Save as RTF
  1142                         try
  1143                         {
  1144                             rtfBody = omi.RTFBody;
  1145                             newMessage.LongMsgFormattedRTF = System.Text.Encoding.ASCII.GetString(rtfBody, 0, rtfBody.Length);
  1146                         }
  1147                         catch
  1148                         {
  1149                             Globals.Log("PEPMessage.Create: Unable to read RTF body in MailItem.");
  1150                         }
  1151 
  1152                         // Force rich text into HTML
  1153                         // WARNING: This is technically a modifcation of the original MailItem
  1154                         // It should be further investigated if this can be removed in the future.
  1155                         if (omi.BodyFormat == Outlook.OlBodyFormat.olFormatRichText)
  1156                         {
  1157                             omi.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
  1158                         }
  1159 
  1160                         if (omi.BodyFormat == Outlook.OlBodyFormat.olFormatHTML)
  1161                         {
  1162                             newMessage.LongMsgFormattedHTML = omi.HTMLBody;
  1163                         }
  1164                     }
  1165 
  1166                     // Attachments
  1167                     newMessage.Attachments.Clear();
  1168                     attachments = omi.Attachments;
  1169                     for (int i = 1; i <= attachments.Count; i++)
  1170                     {
  1171                         currAttachment = attachments[i];
  1172 
  1173                         try
  1174                         {
  1175                             newMessage.Attachments.Add(new PEPAttachment(currAttachment));
  1176                         }
  1177                         catch { }
  1178 
  1179                         Marshal.ReleaseComObject(currAttachment);
  1180                         currAttachment = null;
  1181                     }
  1182 
  1183                     // Keywords
  1184                     if (omi.Categories != null)
  1185                     {
  1186                         try
  1187                         {
  1188                             newMessage.Keywords.Clear();
  1189 
  1190                             using (RegistryKey key1 = Registry.CurrentUser.OpenSubKey("Control Panel\\International"))
  1191                             {
  1192                                 delim = key1.GetValue("sList").ToString();
  1193                                 keywords = omi.Categories.Split(delim.ToCharArray());
  1194 
  1195                                 for (int i = 0; i < keywords.Length; i++)
  1196                                 {
  1197                                     newMessage.Keywords.Add(keywords[i]);
  1198                                 }
  1199                             }
  1200                         }
  1201                         catch
  1202                         {
  1203                             newMessage.Keywords.Clear();
  1204                             Globals.Log("PEPMessage.Create: Unable to set keywords.");
  1205                         }
  1206                     }
  1207 
  1208                     // Optional properties
  1209                     newMessage.OptionalProperties.Clear();
  1210 
  1211                     propertyValue = MAPIHelper.GetProperty(omi, PEPMessage.PR_X_ENC_STATUS);
  1212                     if (propertyValue != null)
  1213                     {
  1214                         newProp = new opt_field();
  1215                         newProp.name = PEPMessage.PR_X_ENC_STATUS_NAME;
  1216                         newProp.value = propertyValue.ToString();
  1217 
  1218                         newMessage.OptionalProperties.Add(newProp);
  1219                     }
  1220 
  1221                     propertyValue = MAPIHelper.GetProperty(omi, PEPMessage.PR_X_KEY_LIST);
  1222                     if (propertyValue != null)
  1223                     {
  1224                         newProp = new opt_field();
  1225                         newProp.name = PEPMessage.PR_X_KEY_LIST_NAME;
  1226                         newProp.value = propertyValue.ToString();
  1227 
  1228                         newMessage.OptionalProperties.Add(newProp);
  1229                     }
  1230 
  1231                     propertyValue = MAPIHelper.GetProperty(omi, PEPMessage.PR_X_PEP_VERSION);
  1232                     if (propertyValue != null)
  1233                     {
  1234                         newProp = new opt_field();
  1235                         newProp.name = PEPMessage.PR_X_PEP_VERSION_NAME;
  1236                         newProp.value = propertyValue.ToString();
  1237 
  1238                         newMessage.OptionalProperties.Add(newProp);
  1239                     }
  1240                 }
  1241 
  1242                 Globals.LogVerbose("PEPMessage.Create: New PEPMessage created from OMI.");
  1243             }
  1244             catch (Exception ex)
  1245             {
  1246                 status = Globals.ReturnStatus.Failure;
  1247                 Globals.Log("PEPMessage.Create: failure occured, " + ex.ToString());
  1248             }
  1249             finally
  1250             {
  1251                 // Free resources
  1252                 if (currAttachment != null)
  1253                 {
  1254                     Marshal.ReleaseComObject(currAttachment);
  1255                     currAttachment = null;
  1256                 }
  1257 
  1258                 if (attachments != null)
  1259                 {
  1260                     Marshal.ReleaseComObject(attachments);
  1261                     attachments = null;
  1262                 }
  1263 
  1264                 if (currRecipient != null)
  1265                 {
  1266                     Marshal.ReleaseComObject(currRecipient);
  1267                     currRecipient = null;
  1268                 }
  1269 
  1270                 if (recipients != null)
  1271                 {
  1272                     Marshal.ReleaseComObject(recipients);
  1273                     recipients = null;
  1274                 }
  1275             }
  1276 
  1277             createdMessage = newMessage;
  1278             return (status);
  1279         }
  1280 
  1281         /// <summary>
  1282         /// Determines if the given text is PGP text based on starting text sequence.
  1283         /// The starting sequence must be "-----BEGIN PGP MESSAGE-----"
  1284         /// </summary>
  1285         /// <param name="text">The text to test if it is PGP text.</param>
  1286         /// <returns>True if the given text is PGP text, otherwise false.</returns>
  1287         public static bool IsPGPText(string text)
  1288         {
  1289             if (string.IsNullOrEmpty(text) == false)
  1290             {
  1291                 string pgp_text = text.Trim();
  1292                 return (pgp_text.StartsWith("-----BEGIN PGP MESSAGE-----"));
  1293             }
  1294             else
  1295             {
  1296                 return (false);
  1297             }
  1298         }
  1299 
  1300         /// <summary>
  1301         /// Determines if the given outlook mail item is encrypted.
  1302         /// Currently, only PGP encrypted messages will be detected.
  1303         /// </summary>
  1304         /// <param name="omi">The outlook mail item to check encryption for.</param>
  1305         /// <returns>True if the given outlook mail item is encrypted, otherwise false.</returns>
  1306         public static bool GetIsEncrypted(Outlook.MailItem omi)
  1307         {
  1308             byte[] data;
  1309             byte[] copy;
  1310             string ignored1 = "";
  1311             string ignored2 = "";
  1312 
  1313             if (omi != null)
  1314             {
  1315                 // Plain PGP message
  1316                 if (omi.Body != null && PEPMessage.IsPGPText(omi.Body))
  1317                 {
  1318                     return (true);
  1319                 }
  1320 
  1321                 // PGP/MIME
  1322                 if (omi.Attachments.Count == 2)
  1323                 {
  1324                     try
  1325                     {
  1326                         // Note: attachment index starts at 1 for a MailItem
  1327                         data = PEPAttachment.GetData(omi.Attachments[2], ref ignored1, ref ignored2);
  1328 
  1329                         // Data must be at least 100 bytes long
  1330                         if ((data == null) ||
  1331                             (data.Length < 100))
  1332                         {
  1333                             return (false);
  1334                         }
  1335 
  1336                         // Create a copy
  1337                         copy = new byte[100];
  1338                         for (int i = 0; i < 100; i++)
  1339                         {
  1340                             copy[i] = data[i];
  1341                         }
  1342 
  1343                         // Check for PGP text after converting to string
  1344                         if (PEPMessage.IsPGPText(System.Text.Encoding.ASCII.GetString(copy)))
  1345                         {
  1346                             return (true);
  1347                         }
  1348                     }
  1349                     catch { }
  1350                 }
  1351             }
  1352 
  1353             return (false);
  1354         }
  1355 
  1356         /// <summary>
  1357         /// Determines if the given PEPMessage is encrypted.
  1358         /// Currently, only PGP encrypted messages will be detected.
  1359         /// </summary>
  1360         /// <param name="msg">The message to check encryption for.</param>
  1361         /// <returns>True if the given message is encrypted, otherwise false.</returns>
  1362         public static bool GetIsEncrypted(PEPMessage msg)
  1363         {
  1364             byte[] data;
  1365             byte[] copy;
  1366 
  1367             if (msg != null)
  1368             {
  1369                 // Plain PGP message
  1370                 if (msg.LongMsg != null && PEPMessage.IsPGPText(msg.LongMsg))
  1371                 {
  1372                     return (true);
  1373                 }
  1374 
  1375                 // PGP/MIME
  1376                 if (msg.Attachments.Count == 2)
  1377                 {
  1378                     try
  1379                     {
  1380                         // Note: attachment index starts at 0 for a PEPMessage
  1381                         data = msg.Attachments[1].Data;
  1382 
  1383                         // Data must be at least 100 bytes long
  1384                         if ((data == null) ||
  1385                             (data.Length < 100))
  1386                         {
  1387                             return (false);
  1388                         }
  1389 
  1390                         // Create a copy
  1391                         copy = new byte[100];
  1392                         for (int i = 0; i < 100; i++)
  1393                         {
  1394                             copy[i] = data[i];
  1395                         }
  1396 
  1397                         // Check for PGP text after converting to string
  1398                         if (PEPMessage.IsPGPText(System.Text.Encoding.ASCII.GetString(copy)))
  1399                         {
  1400                             return (true);
  1401                         }
  1402                     }
  1403                     catch { }
  1404                 }
  1405             }
  1406 
  1407             return (false);
  1408         }
  1409     }
  1410 }