Further clean up, and synchronize changes from the "default" branch. outlook_mime_support
authorMarkus Schaber <markus@pep-security.net>
Thu, 01 Sep 2016 23:13:19 +0200
branchoutlook_mime_support
changeset 1228711bd3c38d94
parent 1227 91d2f524ac24
child 1229 983cfec975e3
Further clean up, and synchronize changes from the "default" branch.
.hgignore
MAPIProperty.cs
MAPIPropertyValue.cs
PEPAttachment.cs
PEPMessage.cs
     1.1 --- a/.hgignore	Thu Sep 01 21:48:01 2016 +0200
     1.2 +++ b/.hgignore	Thu Sep 01 23:13:19 2016 +0200
     1.3 @@ -7,5 +7,3 @@
     1.4  Thumbs.db
     1.5  *.user
     1.6  
     1.7 -pEpForOutlookNativeHelper/Debug
     1.8 -pEpForOutlookNativeHelper/x64
     2.1 --- a/MAPIProperty.cs	Thu Sep 01 21:48:01 2016 +0200
     2.2 +++ b/MAPIProperty.cs	Thu Sep 01 23:13:19 2016 +0200
     2.3 @@ -4062,12 +4062,6 @@
     2.4          public static readonly MAPIProp PidTagAttachMimeTag = new MAPIProp(MAPIDataType.PtypString, 0x370E);
     2.5  
     2.6          /// <summary>
     2.7 -        /// Contains a content-type MIME header.
     2.8 -        /// Data type: PtypString
     2.9 -        /// </summary>
    2.10 -        public const string PidTagAttachMimeTagA = "http://schemas.microsoft.com/mapi/proptag/0x370E001E";
    2.11 -
    2.12 -        /// <summary>
    2.13          /// Identifies the Attachment object within its Message object.
    2.14          /// Data type: PtypInteger32
    2.15          /// </summary>
    2.16 @@ -4109,8 +4103,6 @@
    2.17          /// </summary>
    2.18          public static readonly MAPIProp PidTagAttachTag = new MAPIProp(MAPIDataType.PtypBinary, 0x370A);
    2.19  
    2.20 -        public static readonly byte[] PidTagAttachTag_Value_OID_MIME =     {0x2A, 0x86, 0x48, 0x86, 0xf7, 0x14, 0x03, 0x0a, 0x04};
    2.21 -
    2.22          /// <summary>
    2.23          /// Contains the name of an attachment file, modified so that it can be correlated with TNEF messages.
    2.24          /// Data type: PtypString
     3.1 --- a/MAPIPropertyValue.cs	Thu Sep 01 21:48:01 2016 +0200
     3.2 +++ b/MAPIPropertyValue.cs	Thu Sep 01 23:13:19 2016 +0200
     3.3 @@ -93,5 +93,44 @@
     3.4              /// </summary>
     3.5              mfUntrusted = 0x00008000
     3.6          }
     3.7 +
     3.8 +        /// <summary>
     3.9 +        /// Value for PidTagAttachTag which specifies the attachment is TNEF.
    3.10 +        /// </summary>
    3.11 +        public static readonly byte[] PidTagAttachTagTNEF = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x14, 0x03, 0x0A, 0x01};
    3.12 +
    3.13 +        /// <summary>
    3.14 +        /// Value for PidTagAttachTag which specifies the attachment is in an application-specific format.
    3.15 +        /// </summary>
    3.16 +        public static readonly byte[] PidTagAttachTagAfStorage = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x14, 0x03, 0x0A, 0x03, 0x02, 0x01};
    3.17 +
    3.18 +        /// <summary>
    3.19 +        /// Value for PidTagAttachTag which specifies the attachment is MIME.
    3.20 +        /// </summary>
    3.21 +        public static readonly byte[] PidTagAttachTagMIME = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x14, 0x03, 0x0A, 0x04};
    3.22 +
    3.23 +        /// <summary>
    3.24 +        /// Value for PidTagMessageClass indicating a normal e-mail message.
    3.25 +        /// See: https://msdn.microsoft.com/en-us/library/ee200767
    3.26 +        /// </summary>
    3.27 +        public const string PidTagMessageClassNormalMessage = "IPM.Note";
    3.28 +
    3.29 +        /// <summary>
    3.30 +        /// Value for PidTagMessageClass indicating the message is secure (S/MIME) and can also be signed.
    3.31 +        /// See: https://msdn.microsoft.com/en-us/library/ee200767
    3.32 +        /// </summary>
    3.33 +        public const string PidTagMessageClassSMIME = "IPM.Note.SMIME";
    3.34 +
    3.35 +        /// <summary>
    3.36 +        /// Value for PidTagMessageClass indicating the message is secure (S/MIME) and clear signed.
    3.37 +        /// See: https://msdn.microsoft.com/en-us/library/ee200767
    3.38 +        /// </summary>
    3.39 +        public const string PidTagMessageClassSMIMEMultipartSigned = "IPM.Note.SMIME.MultipartSigned";
    3.40 +
    3.41 +        /// <summary>
    3.42 +        /// Value for PidTagMessageClass indicating the message is a secure read receipt
    3.43 +        /// See: https://msdn.microsoft.com/en-us/library/ee200767
    3.44 +        /// </summary>
    3.45 +        public const string PidTagMessageClassSMIMEReceipt = "IPM.Note.Receipt.SMIME";
    3.46      }
    3.47  }
     4.1 --- a/PEPAttachment.cs	Thu Sep 01 21:48:01 2016 +0200
     4.2 +++ b/PEPAttachment.cs	Thu Sep 01 23:13:19 2016 +0200
     4.3 @@ -434,8 +434,10 @@
     4.4          /// <param name="attachments">The Outlook attachments list to add this pEp attachments's data to.</param>
     4.5          /// <param name="defaultFileName">The default file name to use for the attachment if this filename is null 
     4.6          /// or whitespace.</param>
     4.7 +        /// <param name="tag">The binary value to apply to the PidTagAttachTag property. Set to null to skip.</param>
     4.8          public void AddTo(Outlook.Attachments attachments,
     4.9 -                          string defaultFileName = "attachment")
    4.10 +                          string defaultFileName = "attachment",
    4.11 +                          byte[] tag = null)
    4.12          {
    4.13              string tempDir;
    4.14              string tempFile;
    4.15 @@ -473,22 +475,50 @@
    4.16                  File.WriteAllBytes(tempFile, data);
    4.17  
    4.18                  // Create a new attachment from the file on disk (also sets the file name)
    4.19 -                newAttachment = attachments.Add(tempFile);
    4.20 +                newAttachment = attachments.Add(tempFile, Outlook.OlAttachmentType.olByValue);
    4.21  
    4.22 -                if (string.IsNullOrWhiteSpace(this._MIMEType))
    4.23 +                // Set the MIME type
    4.24 +                try
    4.25                  {
    4.26 -                    // Undefined
    4.27 -                    MAPIHelper.SetProperty(newAttachment, MAPIProperty.PidTagAttachMimeTag, "application/octet-stream");
    4.28 +                    if (string.IsNullOrWhiteSpace(this._MIMEType))
    4.29 +                    {
    4.30 +                        // Undefined
    4.31 +                        MAPIHelper.SetProperty(newAttachment, MAPIProperty.PidTagAttachMimeTag, "application/octet-stream");
    4.32 +                    }
    4.33 +                    else
    4.34 +                    {
    4.35 +                        MAPIHelper.SetProperty(newAttachment, MAPIProperty.PidTagAttachMimeTag, this._MIMEType);
    4.36 +                    }
    4.37                  }
    4.38 -                else
    4.39 +                catch
    4.40                  {
    4.41 -                    MAPIHelper.SetProperty(newAttachment, MAPIProperty.PidTagAttachMimeTag, this._MIMEType);
    4.42 +                    Globals.Log("PEPAttachment.AddTo: Failed to set MIME type");
    4.43 +                }
    4.44 +
    4.45 +                // Set the attachment tag if necessary
    4.46 +                try
    4.47 +                {
    4.48 +                    if (tag != null)
    4.49 +                    {
    4.50 +                        MAPIHelper.SetProperty(newAttachment, MAPIProperty.PidTagAttachTag, tag);
    4.51 +                    }
    4.52 +                }
    4.53 +                catch
    4.54 +                {
    4.55 +                    Globals.Log("PEPAttachment.AddTo: Failed to set tag");
    4.56                  }
    4.57  
    4.58                  // Set the content ID
    4.59 -                if (string.IsNullOrWhiteSpace(this._ContentID) == false)
    4.60 +                try
    4.61                  {
    4.62 -                    MAPIHelper.SetProperty(newAttachment, MAPIProperty.PidTagAttachContentId, this._ContentID);
    4.63 +                    if (string.IsNullOrWhiteSpace(this._ContentID) == false)
    4.64 +                    {
    4.65 +                        MAPIHelper.SetProperty(newAttachment, MAPIProperty.PidTagAttachContentId, this._ContentID);
    4.66 +                    }
    4.67 +                }
    4.68 +                catch
    4.69 +                {
    4.70 +                    Globals.Log("PEPAttachment.AddTo: Failed to set content ID");
    4.71                  }
    4.72  
    4.73                  // Delete temp directory for attachments
     5.1 --- a/PEPMessage.cs	Thu Sep 01 21:48:01 2016 +0200
     5.2 +++ b/PEPMessage.cs	Thu Sep 01 23:13:19 2016 +0200
     5.3 @@ -7,7 +7,6 @@
     5.4  using Outlook = Microsoft.Office.Interop.Outlook;
     5.5  using System.IO;
     5.6  using System.Text;
     5.7 -using System.Security.AccessControl;
     5.8  
     5.9  namespace pEp
    5.10  {
    5.11 @@ -33,7 +32,7 @@
    5.12          public static readonly MAPIProperty.MAPIProp XPidNameKeyList            = new MAPIProperty.MAPIProp(PR_X_KEY_LIST_NAME,             MAPIProperty.PS_INTERNET_HEADERS);
    5.13          public static readonly MAPIProperty.MAPIProp XPidNamePEPNeverUnsecure   = new MAPIProperty.MAPIProp(PR_X_PEP_NEVER_UNSECURE_NAME,   MAPIProperty.PS_INTERNET_HEADERS);
    5.14          public static readonly MAPIProperty.MAPIProp XPidNamePEPProtocolVersion = new MAPIProperty.MAPIProp(PR_X_PEP_PROTOCOL_VERSION_NAME, MAPIProperty.PS_INTERNET_HEADERS);
    5.15 -        
    5.16 +
    5.17          private List<PEPAttachment> _Attachments;
    5.18          private List<PEPIdentity>   _BCC;
    5.19          private List<PEPIdentity>   _CC;
    5.20 @@ -277,6 +276,15 @@
    5.21          }
    5.22  
    5.23          /// <summary>
    5.24 +        /// Gets whether this message is secured using PGP/MIME format.
    5.25 +        /// This will forward the call to the static method of the same purpose.
    5.26 +        /// </summary>
    5.27 +        public bool IsPGPMIMEEncrypted
    5.28 +        {
    5.29 +            get { return (PEPMessage.GetIsPGPMIMEEncrypted(this)); }
    5.30 +        }
    5.31 +
    5.32 +        /// <summary>
    5.33          /// Gets or sets the list of keys associated with this message.
    5.34          /// Commonly this contains the list of decryption keys.
    5.35          /// Warning: Since this is stored as a header field, care must be taken not to apply this to a MailItem on an 
    5.36 @@ -1130,23 +1138,23 @@
    5.37                  // Set the subject
    5.38                  omi.Subject = this._ShortMsg;
    5.39  
    5.40 -                bool is_pgp_mime_message = this._Attachments.Count == 2
    5.41 -                    && this.Attachments[0].MIMEType == "application/pgp-encrypted"
    5.42 -                    && this.Attachments[0].Data != null
    5.43 -                    && this.Attachments[0].Data.Length > 0
    5.44 -                    && this.Attachments[1].MIMEType == "application/octet-stream"
    5.45 -                    && this.Attachments[1].Data != null
    5.46 -                    && this.Attachments[1].Data.Length > 0;
    5.47 +                bool is_pgp_mime_message = this.IsPGPMIMEEncrypted;
    5.48  
    5.49 -                // Set the body (skip RTF format, only use HTML which is what the engine uses)
    5.50 +                // Set the body (skip RTF format, only use HTML or plain text which is what the engine uses)
    5.51                  if (is_pgp_mime_message)
    5.52                  {
    5.53 -                    // We intentionally do not set any body, as this will break
    5.54 -                    // the mime structure.
    5.55 +                    // We need to drop the old bodies, to make sure they're not left somewhere in
    5.56 +                    // the message objects stored on untrusted servers.
    5.57 +                    omi.Body = "";
    5.58 +                    omi.BodyFormat = Outlook.OlBodyFormat.olFormatPlain;
    5.59 +                    omi.HTMLBody = null;
    5.60 +                    // To investigate: this throws an exception: omi.RTFBody = null;
    5.61 +
    5.62 +                    // Now, we set the mail mime type to multipart/signed.
    5.63                      var accessor = omi.PropertyAccessor;
    5.64                      try
    5.65                      {
    5.66 -                        accessor.SetProperty(MAPIProperty.PidTagMessageClass, "IPM.Note.SMIME.MultipartSigned");
    5.67 +                        accessor.SetProperty(MAPIProperty.PidTagMessageClass.DASLName, "IPM.Note.SMIME.MultipartSigned");
    5.68                      }
    5.69                      finally
    5.70                      {
    5.71 @@ -1173,19 +1181,25 @@
    5.72  
    5.73                  if (is_pgp_mime_message)
    5.74                  {
    5.75 +
    5.76                      AddPgpMimeAttachment(attachments);
    5.77                  }
    5.78                  else
    5.79                  {
    5.80 -                    // Add new attachments
    5.81                      for (int i = 0; i < this._Attachments.Count; i++)
    5.82                      {
    5.83 -                        this._Attachments[i].AddTo(attachments, ("attachment" + i.ToString()));
    5.84 +                        this._Attachments[i].AddTo(attachments,
    5.85 +                                                   ("attachment" + i.ToString()));
    5.86 +
    5.87                      }
    5.88                  }
    5.89  
    5.90                  // ID
    5.91 -                MAPIHelper.SetProperty(omi, MAPIProperty.PidTagInternetMessageId, this._ID);
    5.92 +                try
    5.93 +                {
    5.94 +                    MAPIHelper.SetProperty(omi, MAPIProperty.PidTagInternetMessageId, this._ID);
    5.95 +                }
    5.96 +                catch { }
    5.97  
    5.98                  // Conversation information
    5.99                  try
   5.100 @@ -1305,14 +1319,14 @@
   5.101                  var tempFile = Path.Combine(tempDir, "attachment");
   5.102                  using (var stream = File.OpenWrite(tempFile))
   5.103                  {
   5.104 -                    WriteAsciiString(stream,
   5.105 -                        "MIME-Version: 1.0\r\n",
   5.106 -                              "Content-Type: multipart/encrypted;\r\n",
   5.107 -                              "\tprotocol=\"application/pgp-encrypted\";\r\n",
   5.108 +                    WriteAsciiString(stream,
   5.109 +                        "MIME-Version: 1.0\r\n",
   5.110 +                              "Content-Type: multipart/encrypted;\r\n",
   5.111 +                              "\tprotocol=\"application/pgp-encrypted\";\r\n",
   5.112                                "\tboundary=\"", boundary, "\"\r\n");
   5.113  
   5.114 -                    WriteAsciiString(stream, "\r\n--", boundary, "\r\n",
   5.115 -                        "Content-Type: application/pgp-encrypted\r\n",
   5.116 +                    WriteAsciiString(stream, "\r\n--", boundary, "\r\n",
   5.117 +                        "Content-Type: application/pgp-encrypted\r\n",
   5.118                                "\r\n");
   5.119  
   5.120                      var first = this._Attachments[0].Data;
   5.121 @@ -1324,7 +1338,7 @@
   5.122                      WriteAsciiString(stream,
   5.123                          "\r\n",
   5.124                          "\r\n--", boundary, "\r\n",
   5.125 -                        "Content-Type: application/octet-stream\r\n",
   5.126 +                        "Content-Type: application/octet-stream\r\n",
   5.127                          "\r\n");
   5.128  
   5.129                      var second = this._Attachments[1].Data;
   5.130 @@ -1338,7 +1352,7 @@
   5.131                      Outlook.OlAttachmentType.olByValue);
   5.132                  try
   5.133                  {
   5.134 -                    MAPIHelper.SetProperty(newAttachment, MAPIProperty.PidTagAttachTag, MAPIProperty.PidTagAttachTag_Value_OID_MIME);
   5.135 +                    MAPIHelper.SetProperty(newAttachment, MAPIProperty.PidTagAttachTag, MAPIPropertyValue.PidTagAttachTagMIME);
   5.136                      MAPIHelper.SetProperty(newAttachment, MAPIProperty.PidTagAttachMimeTag, "multipart/signed");
   5.137                  }
   5.138                  finally
   5.139 @@ -2039,55 +2053,25 @@
   5.140          }
   5.141  
   5.142          /// <summary>
   5.143 -        /// Determines if the given outlook mail item is encrypted.
   5.144 +        /// Determines if the given message is encrypted.
   5.145          /// Currently, only PGP encrypted messages will be detected.
   5.146          /// </summary>
   5.147          /// <param name="omi">The outlook mail item to check encryption for.</param>
   5.148 -        /// <returns>True if the given outlook mail item is encrypted, otherwise false.</returns>
   5.149 +        /// <returns>True if the given message is encrypted, otherwise false.</returns>
   5.150          public static bool GetIsEncrypted(Outlook.MailItem omi)
   5.151          {
   5.152 -            byte[] data;
   5.153 -            byte[] copy;
   5.154 -            string ignored1 = "";
   5.155 -            string ignored2 = "";
   5.156 -
   5.157              if (omi != null)
   5.158              {
   5.159 -                // Plain PGP message
   5.160 +                // Partitioned or inline PGP format
   5.161                  if (omi.Body != null && PEPMessage.IsPGPText(omi.Body))
   5.162                  {
   5.163                      return (true);
   5.164                  }
   5.165  
   5.166 -                // PGP/MIME
   5.167 -                if (omi.Attachments.Count == 2)
   5.168 +                // PGP/MIME format
   5.169 +                if (PEPMessage.GetIsPGPMIMEEncrypted(omi))
   5.170                  {
   5.171 -                    try
   5.172 -                    {
   5.173 -                        // Note: attachment index starts at 1 for a MailItem
   5.174 -                        data = PEPAttachment.GetData(omi.Attachments[2], ref ignored1, ref ignored2);
   5.175 -
   5.176 -                        // Data must be at least 100 bytes long
   5.177 -                        if ((data == null) ||
   5.178 -                            (data.Length < 100))
   5.179 -                        {
   5.180 -                            return (false);
   5.181 -                        }
   5.182 -
   5.183 -                        // Create a copy
   5.184 -                        copy = new byte[100];
   5.185 -                        for (int i = 0; i < 100; i++)
   5.186 -                        {
   5.187 -                            copy[i] = data[i];
   5.188 -                        }
   5.189 -
   5.190 -                        // Check for PGP text after converting to string
   5.191 -                        if (PEPMessage.IsPGPText(System.Text.Encoding.ASCII.GetString(copy)))
   5.192 -                        {
   5.193 -                            return (true);
   5.194 -                        }
   5.195 -                    }
   5.196 -                    catch { }
   5.197 +                    return (true);
   5.198                  }
   5.199              }
   5.200  
   5.201 @@ -2095,6 +2079,102 @@
   5.202          }
   5.203  
   5.204          /// <summary>
   5.205 +        /// Determines if the given message is encrypted in PGP/MIME format.
   5.206 +        /// </summary>
   5.207 +        /// <param name="msg">The message to check encryption for.</param>
   5.208 +        /// <returns>True if the given message is PGP/MIME encrypted, otherwise false.</returns>
   5.209 +        public static bool GetIsPGPMIMEEncrypted(Outlook.MailItem omi)
   5.210 +        {
   5.211 +            bool result = false;
   5.212 +            bool versionInfoFound = false;
   5.213 +            bool encryptedContentFound = false;
   5.214 +            byte[] copy;
   5.215 +            PEPAttachment attach;
   5.216 +            Outlook.Attachment attachment = null;
   5.217 +            Outlook.Attachments attachments = null;
   5.218 +
   5.219 +            try
   5.220 +            {
   5.221 +                if (omi != null)
   5.222 +                {
   5.223 +                    attachments = omi.Attachments;
   5.224 +
   5.225 +                    // Require only two attachments (version identification & encrypted content)
   5.226 +                    // However, allow the attachments to be in any order
   5.227 +                    if (attachments.Count == 2)
   5.228 +                    {
   5.229 +                        // Note: attachment index starts at 1
   5.230 +                        for (int i = 1; i <= attachments.Count; i++)
   5.231 +                        {
   5.232 +                            attachment = attachments[i];
   5.233 +                            attach = new PEPAttachment(attachment);
   5.234 +
   5.235 +                            // Check for version identification
   5.236 +                            if ((string.IsNullOrEmpty(attach.MIMEType) == false) &&
   5.237 +                                (string.Equals(attach.MIMEType.Trim(), "application/pgp-encrypted", StringComparison.OrdinalIgnoreCase)))
   5.238 +                            {
   5.239 +                                // Allow any data
   5.240 +                                if ((attach.Data != null) &&
   5.241 +                                    (attach.Data.Length > 0))
   5.242 +                                {
   5.243 +                                    versionInfoFound = true;
   5.244 +                                }
   5.245 +                            }
   5.246 +
   5.247 +                            // Check for encrypted content
   5.248 +                            if ((string.IsNullOrEmpty(attach.MIMEType) == false) &&
   5.249 +                                (string.Equals(attach.MIMEType.Trim(), "application/octet-stream", StringComparison.OrdinalIgnoreCase)))
   5.250 +                            {
   5.251 +                                // Require data to start with PGP text and be at least 100 bytes
   5.252 +                                if ((attach.Data != null) &&
   5.253 +                                    (attach.Data.Length > 100))
   5.254 +                                {
   5.255 +                                    // Create a copy
   5.256 +                                    copy = new byte[100];
   5.257 +                                    for (int j = 0; j < 100; j++)
   5.258 +                                    {
   5.259 +                                        copy[j] = attach.Data[j];
   5.260 +                                    }
   5.261 +
   5.262 +                                    // Check for PGP text after converting to string
   5.263 +                                    if (PEPMessage.IsPGPText(System.Text.Encoding.ASCII.GetString(copy)))
   5.264 +                                    {
   5.265 +                                        encryptedContentFound = true;
   5.266 +                                    }
   5.267 +                                }
   5.268 +                            }
   5.269 +
   5.270 +                            Marshal.ReleaseComObject(attachment);
   5.271 +                            attachment = null;
   5.272 +                        }
   5.273 +
   5.274 +                        if (versionInfoFound && encryptedContentFound)
   5.275 +                        {
   5.276 +                            result = true;
   5.277 +                        }
   5.278 +                    }
   5.279 +                }
   5.280 +            }
   5.281 +            catch { }
   5.282 +            finally
   5.283 +            {
   5.284 +                if (attachment != null)
   5.285 +                {
   5.286 +                    Marshal.ReleaseComObject(attachment);
   5.287 +                    attachment = null;
   5.288 +                }
   5.289 +
   5.290 +                if (attachments != null)
   5.291 +                {
   5.292 +                    Marshal.ReleaseComObject(attachments);
   5.293 +                    attachments = null;
   5.294 +                }
   5.295 +            }
   5.296 +
   5.297 +            return (result);
   5.298 +        }
   5.299 +
   5.300 +        /// <summary>
   5.301          /// Determines if the given PEPMessage is encrypted.
   5.302          /// Currently, only PGP encrypted messages will be detected.
   5.303          /// </summary>
   5.304 @@ -2102,50 +2182,80 @@
   5.305          /// <returns>True if the given message is encrypted, otherwise false.</returns>
   5.306          public static bool GetIsEncrypted(PEPMessage msg)
   5.307          {
   5.308 -            byte[] data;
   5.309 -            byte[] copy;
   5.310 -
   5.311              if (msg != null)
   5.312              {
   5.313 -                // Plain PGP message
   5.314 +                // Partitioned or inline PGP format
   5.315                  if (msg.LongMsg != null && PEPMessage.IsPGPText(msg.LongMsg))
   5.316                  {
   5.317                      return (true);
   5.318                  }
   5.319  
   5.320 -                // PGP/MIME
   5.321 -                if (msg.Attachments.Count == 2)
   5.322 +                // PGP/MIME format
   5.323 +                if (PEPMessage.GetIsPGPMIMEEncrypted(msg))
   5.324                  {
   5.325 -                    try
   5.326 -                    {
   5.327 -                        // Note: attachment index starts at 0 for a PEPMessage
   5.328 -                        data = msg.Attachments[1].Data;
   5.329 -
   5.330 -                        // Data must be at least 100 bytes long
   5.331 -                        if ((data == null) ||
   5.332 -                            (data.Length < 100))
   5.333 -                        {
   5.334 -                            return (false);
   5.335 -                        }
   5.336 -
   5.337 -                        // Create a copy
   5.338 -                        copy = new byte[100];
   5.339 -                        for (int i = 0; i < 100; i++)
   5.340 -                        {
   5.341 -                            copy[i] = data[i];
   5.342 -                        }
   5.343 -
   5.344 -                        // Check for PGP text after converting to string
   5.345 -                        if (PEPMessage.IsPGPText(System.Text.Encoding.ASCII.GetString(copy)))
   5.346 -                        {
   5.347 -                            return (true);
   5.348 -                        }
   5.349 -                    }
   5.350 -                    catch { }
   5.351 +                    return (true);
   5.352                  }
   5.353              }
   5.354  
   5.355              return (false);
   5.356          }
   5.357 +
   5.358 +        /// <summary>
   5.359 +        /// Determines if the given message is encrypted in PGP/MIME format.
   5.360 +        /// </summary>
   5.361 +        /// <param name="msg">The message to check encryption for.</param>
   5.362 +        /// <returns>True if the given message is PGP/MIME encrypted, otherwise false.</returns>
   5.363 +        public static bool GetIsPGPMIMEEncrypted(PEPMessage msg)
   5.364 +        {
   5.365 +            bool result = false;
   5.366 +            bool versionInfoFound = false;
   5.367 +            bool encryptedContentFound = false;
   5.368 +
   5.369 +            if (msg != null)
   5.370 +            {
   5.371 +                // Require only two attachments (version identification & encrypted content)
   5.372 +                // However, allow the attachments to be in any order
   5.373 +                if (msg.Attachments.Count == 2)
   5.374 +                {
   5.375 +                    foreach (PEPAttachment attach in msg.Attachments)
   5.376 +                    {
   5.377 +                        // Check for version identification
   5.378 +                        if ((string.IsNullOrEmpty(attach.MIMEType) == false) &&
   5.379 +                            (string.Equals(attach.MIMEType.Trim(), "application/pgp-encrypted", StringComparison.OrdinalIgnoreCase)))
   5.380 +                        {
   5.381 +                            // Allow any data
   5.382 +                            if ((attach.Data != null) &&
   5.383 +                                (attach.Data.Length > 0))
   5.384 +                            {
   5.385 +                                versionInfoFound = true;
   5.386 +                            }
   5.387 +                        }
   5.388 +
   5.389 +                        // Check for encrypted content
   5.390 +                        if ((string.IsNullOrEmpty(attach.MIMEType) == false) &&
   5.391 +                            (string.Equals(attach.MIMEType.Trim(), "application/octet-stream", StringComparison.OrdinalIgnoreCase)))
   5.392 +                        {
   5.393 +                            // Require data to start with PGP text and be at least 100 bytes
   5.394 +                            if ((attach.Data != null) &&
   5.395 +                                (attach.Data.Length > 100))
   5.396 +                            {
   5.397 +                                // Check for PGP text after converting to string
   5.398 +                                if (IsPGPText(Encoding.ASCII.GetString(attach.Data, 0, 100)))
   5.399 +                                {
   5.400 +                                    encryptedContentFound = true;
   5.401 +                                }
   5.402 +                            }
   5.403 +                        }
   5.404 +                    }
   5.405 +
   5.406 +                    if (versionInfoFound && encryptedContentFound)
   5.407 +                    {
   5.408 +                        result = true;
   5.409 +                    }
   5.410 +                }
   5.411 +            }
   5.412 +
   5.413 +            return (result);
   5.414 +        }
   5.415      }
   5.416  }