Example #1
0
        /// <summary>
        /// Extract a list of MIME parts from a multipart/* MIME encoded message.
        /// </summary>
        /// <param name="contentType">Content Type of the outermost MIME part.</param>
        /// <param name="charSet">Character set of the outermost MIME part.</param>
        /// <param name="contentTransferEncoding">Encoding of the outermost MIME part.</param>
        /// <param name="body">The outermost MIME part's contents.</param>
        /// <param name="depth">The nesting layer of this MIME part.</param>
        /// <param name="processingFlags">Flags determining whether specialized properties are returned with a ReadOnlyMailMessage.</param>
        public static List <MimePart> ExtractMIMEParts(string contentType, string charSet, string contentTransferEncoding, string body, ReadOnlyMailMessageProcessingFlags processingFlags, int depth)
        {
            List <MimePart> mimeParts = new List <MimePart>();

            string contentTypeToUpper = contentType.ToUpper();

            if (contentTypeToUpper.StartsWith("MULTIPART/"))
            {
                // Prepare to process each part of the multipart/* message.
                int cursor = 0;

                // Prepend and append to the body with a carriage return and linefeed for consistent boundary matching.
                body = "\r\n" + body + "\r\n";

                // Determine the outermost boundary name.
                string boundaryName       = Functions.ExtractMimeParameter(contentType, "boundary");
                int    boundaryNameLength = boundaryName.Length;

                // Variables used for record keeping with signed S/MIME parts.
                int           signatureBlock = -1;
                List <string> mimeBlocks     = new List <string>();

                cursor = body.IndexOf("\r\n--" + boundaryName, 0, StringComparison.Ordinal);
                while (cursor > -1)
                {
                    // Calculate the end boundary of the current MIME part.
                    int mimeStartPosition = cursor + boundaryNameLength + 4;
                    int mimeEndPosition   = body.IndexOf("\r\n--" + boundaryName, mimeStartPosition, StringComparison.Ordinal);
                    if (mimeEndPosition > -1 && (mimeEndPosition + boundaryNameLength + 6 <= body.Length))
                    {
                        string afterBoundaryEnd = body.Substring(mimeEndPosition + 4 + boundaryNameLength, 2);
                        if (afterBoundaryEnd == "\r\n" || afterBoundaryEnd == "--")
                        {
                            string mimeContents = body.Substring(mimeStartPosition, mimeEndPosition - mimeStartPosition);

                            // Extract the header portion of the current MIME part.
                            int    mimeDivider = mimeContents.IndexOf("\r\n\r\n");
                            string mimeHeaders, mimeBody;
                            if (mimeDivider > -1)
                            {
                                mimeHeaders = mimeContents.Substring(0, mimeDivider);
                                mimeBody    = mimeContents.Substring(mimeDivider + 4);
                            }
                            else
                            {
                                // The following is a workaround to handle malformed MIME bodies.
                                mimeHeaders = mimeContents;
                                mimeBody    = "";

                                int linePos = 0, lastLinePos = 0;
                                while (linePos > -1)
                                {
                                    lastLinePos = linePos;
                                    linePos     = mimeHeaders.IndexOf("\r\n", lastLinePos);
                                    if (linePos > -1)
                                    {
                                        string currentLine = mimeContents.Substring(lastLinePos, linePos - lastLinePos);
                                        if (currentLine.Length > 0 && currentLine.IndexOf(":") < 0)
                                        {
                                            mimeBody = mimeContents.Substring(lastLinePos + 2, mimeContents.Length - lastLinePos - 4);
                                            linePos  = -1;
                                        }
                                        else
                                        {
                                            linePos += 2;
                                        }
                                    }
                                }
                            }

                            mimeBlocks.Add(mimeContents);

                            // Divide the MIME part's headers into its components.
                            string mimeCharSet = "", mimeContentDisposition = "", mimeContentID = "", mimeContentType = "", mimeContentTransferEncoding = "", mimeFileName = "";
                            ExtractMimeHeaders(mimeHeaders, out mimeContentType, out mimeCharSet, out mimeContentTransferEncoding, out mimeContentDisposition, out mimeFileName, out mimeContentID);

                            string mimeContentTypeToUpper = mimeContentType.ToUpper();
                            if (mimeContentTypeToUpper.StartsWith("MULTIPART/"))
                            {
                                // Recurse through embedded MIME parts.
                                List <MimePart> returnedMIMEParts = ExtractMIMEParts(mimeContentType, mimeCharSet, mimeContentTransferEncoding, mimeBody, processingFlags, depth + 1);
                                foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                                {
                                    mimeParts.Add(returnedMIMEPart);
                                }
                            }
                            else
                            {
                                // Keep track of whether this MIME part's body has already been processed.
                                bool processed = false;

                                if (mimeContentTypeToUpper.StartsWith("APPLICATION/PKCS7-SIGNATURE") || mimeContentTypeToUpper.StartsWith("APPLICATION/X-PKCS7-SIGNATURE"))
                                {
                                    // Unless a flag has been set to include this *.p7s block, exclude it from attachments.
                                    if ((processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeSmimeSignedData) == 0)
                                    {
                                        processed = true;
                                    }

                                    // Remember the signature block to use for later verification.
                                    signatureBlock = mimeBlocks.Count() - 1;
                                }
                                else if (mimeContentTypeToUpper.StartsWith("APPLICATION/PKCS7-MIME") || mimeContentTypeToUpper.StartsWith("APPLICATION/X-PKCS7-MIME"))
                                {
                                    // Unless a flag has been set to include this *.p7m block, exclude it from attachments.
                                    processed = (processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeSmimeEncryptedEnvelopeData) == 0;

                                    // Decrypt the MIME part and recurse through embedded MIME parts.
                                    List <MimePart> returnedMIMEParts = ReturnDecryptedMimeParts(mimeContentType, mimeContentTransferEncoding, mimeBody, processingFlags, depth + 1);
                                    if (returnedMIMEParts != null)
                                    {
                                        foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                                        {
                                            mimeParts.Add(returnedMIMEPart);
                                        }
                                    }
                                    else
                                    {
                                        // If we were unable to decrypt, return this MIME part as-is.
                                        processed = false;
                                    }
                                }
                                else if (mimeContentTypeToUpper.StartsWith("APPLICATION/MS-TNEF") || mimeFileName.ToUpper() == "WINMAIL.DAT")
                                {
                                    // Process the TNEF encoded message.
                                    processed = true;
                                    TnefEncoding tnef = new TnefEncoding(Convert.FromBase64String(mimeBody));

                                    // If we were unable to extract content from this MIME, include it as an attachment.
                                    if ((tnef.Body.Length < 1 && tnef.MimeAttachments.Count < 1) || (processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeWinMailData) > 0)
                                    {
                                        processed = false;
                                    }
                                    else
                                    {
                                        // Unless a flag has been set to include this winmail.dat block, exclude it from attachments.
                                        if ((processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeWinMailData) > 0)
                                        {
                                            if (!string.IsNullOrEmpty(tnef.Body))
                                            {
                                                mimeParts.Add(new MimePart("winmail.dat", tnef.ContentType, "", "", mimeContentTransferEncoding, Encoding.UTF8.GetBytes(tnef.Body)));
                                            }
                                        }

                                        foreach (MimePart mimePart in tnef.MimeAttachments)
                                        {
                                            mimeParts.Add(mimePart);
                                        }
                                    }
                                }
                                else if (mimeContentTypeToUpper == "MESSAGE/RFC822")
                                {
                                    if ((processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeNestedRFC822Messages) > 0)
                                    {
                                        // Recurse through the RFC822 container.
                                        processed = true;

                                        mimeDivider = mimeBody.IndexOf("\r\n\r\n");
                                        if (mimeDivider > -1)
                                        {
                                            mimeHeaders = Functions.UnfoldWhitespace(mimeBody.Substring(0, mimeDivider));
                                            mimeBody    = mimeBody.Substring(mimeDivider + 4);

                                            mimeContentType             = Functions.ReturnBetween(mimeHeaders, "Content-Type:", "\r\n").Trim();
                                            mimeContentTransferEncoding = Functions.ReturnBetween(mimeHeaders, "Content-Transfer-Encoding:", "\r\n").Trim();
                                            mimeCharSet = Functions.ExtractMimeParameter(mimeContentType, "charset").Replace("\"", "");

                                            List <MimePart> returnedMIMEParts = ExtractMIMEParts(mimeContentType, mimeCharSet, mimeContentTransferEncoding, mimeBody, processingFlags, depth + 1);
                                            foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                                            {
                                                mimeParts.Add(returnedMIMEPart);
                                            }
                                        }
                                    }
                                }

                                if (!processed)
                                {
                                    // Decode and add the message to the MIME parts collection.
                                    switch (mimeContentTransferEncoding.ToLower())
                                    {
                                    case "base64":
                                        mimeBody = mimeBody.Replace("\r\n", "");
                                        if (mimeBody.Length % 4 != 0)
                                        {
                                            mimeBody += new String('=', 4 - (mimeBody.Length % 4));
                                        }

                                        mimeParts.Add(new MimePart(mimeFileName, mimeContentType, mimeCharSet, mimeContentID, mimeContentTransferEncoding, Convert.FromBase64String(mimeBody)));
                                        break;

                                    case "quoted-printable":
                                        mimeParts.Add(new MimePart(mimeFileName, mimeContentType, mimeCharSet, mimeContentID, mimeContentTransferEncoding, Functions.FromQuotedPrintable(mimeBody, mimeCharSet, null)));
                                        break;

                                    case "binary":
                                    case "7bit":
                                    case "8bit":
                                    default:
                                        mimeParts.Add(new MimePart(mimeFileName, mimeContentType, mimeCharSet, mimeContentID, mimeContentTransferEncoding, mimeBody));
                                        break;
                                    }
                                }
                            }
                        }
                        cursor = mimeEndPosition;
                    }
                    else
                    {
                        cursor = -1;
                    }
                }

                // If a PKCS signature was found and there's one other MIME part, verify the signature.
                if (signatureBlock > -1 && mimeBlocks.Count == 2)
                {
                    // Verify the signature and track the signing certificates.
                    X509Certificate2Collection signingCertificates;
                    if (VerifySignature(mimeBlocks[signatureBlock], mimeBlocks[1 - signatureBlock], out signingCertificates))
                    {
                        // Stamp each MIME part found so far as signed, and if relevant, triple wrapped.
                        foreach (MimePart mimePart in mimeParts)
                        {
                            mimePart.SmimeSigningCertificates = signingCertificates;

                            if (mimePart.SmimeSigned && mimePart.SmimeEncryptedEnvelope)
                            {
                                mimePart.SmimeTripleWrapped = true;
                            }

                            mimePart.SmimeSigned = true;
                        }
                    }
                }
            }
            else if (contentTypeToUpper.StartsWith("APPLICATION/MS-TNEF"))
            {
                // Process the TNEF encoded message.
                TnefEncoding tnef = new TnefEncoding(Convert.FromBase64String(body));

                // Unless a flag has been set to include this winmail.dat block, exclude it from attachments.
                if ((processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeWinMailData) > 0)
                {
                    if (!string.IsNullOrEmpty(tnef.Body))
                    {
                        mimeParts.Add(new MimePart("winmail.dat", tnef.ContentType, "", "", "", Encoding.UTF8.GetBytes(tnef.Body)));
                    }
                }

                foreach (MimePart mimePart in tnef.MimeAttachments)
                {
                    mimeParts.Add(mimePart);
                }
            }
            else if (contentTypeToUpper.StartsWith("APPLICATION/PKCS7-MIME") || contentTypeToUpper.StartsWith("APPLICATION/X-PKCS7-MIME"))
            {
                // Don't attempt to decrypt if this is a signed message only.
                if (contentType.IndexOf("smime-type=signed-data") < 0)
                {
                    // Unless a flag has been set to include this *.p7m block, exclude it from attachments.
                    if ((processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeSmimeEncryptedEnvelopeData) > 0)
                    {
                        mimeParts.Add(new MimePart("smime.p7m", contentType, "", "", "", body));
                    }

                    // Decrypt the MIME part and recurse through embedded MIME parts.
                    List <MimePart> returnedMIMEParts = ReturnDecryptedMimeParts(contentType, contentTransferEncoding, body, processingFlags, depth + 1);
                    if (returnedMIMEParts != null)
                    {
                        foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                        {
                            mimeParts.Add(returnedMIMEPart);
                        }
                    }
                    else
                    {
                        // If we were unable to decrypt the message, pass it along as-is.
                        mimeParts.Add(new MimePart(Functions.ReturnBetween(contentType + ";", "name=", ";").Replace("\"", ""), contentType, "", "", contentTransferEncoding, body));
                    }
                }
                else
                {
                    // Hydrate the signature CMS object.
                    SignedCms signedCms = new SignedCms();

                    try
                    {
                        // Attempt to decode the signature block and verify the passed in signature.
                        signedCms.Decode(Convert.FromBase64String(body));
                        signedCms.CheckSignature(true);

                        string mimeContents = Encoding.UTF8.GetString(signedCms.ContentInfo.Content);

                        int    mimeDivider = mimeContents.IndexOf("\r\n\r\n");
                        string mimeHeaders;
                        if (mimeDivider > -1)
                        {
                            mimeHeaders = mimeContents.Substring(0, mimeDivider);
                        }
                        else
                        {
                            mimeHeaders = mimeContents;
                        }

                        if (mimeHeaders.Length > 0)
                        {
                            // Extract the body portion of the current MIME part.
                            string mimeBody = mimeContents.Substring(mimeDivider + 4);

                            string mimeCharSet = "", mimeContentDisposition = "", mimeContentID = "", mimeContentType = "", mimeContentTransferEncoding = "", mimeFileName = "";
                            ExtractMimeHeaders(mimeHeaders, out mimeContentType, out mimeCharSet, out mimeContentTransferEncoding, out mimeContentDisposition, out mimeFileName, out mimeContentID);

                            List <MimePart> returnedMIMEParts = ExtractMIMEParts(mimeContentType, mimeCharSet, mimeContentTransferEncoding, mimeBody, processingFlags, depth + 1);
                            foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                            {
                                mimeParts.Add(returnedMIMEPart);
                            }
                        }
                    }
                    catch
                    {
                        // If an exception occured, the signature could not be verified.
                    }
                }
            }
            else if (contentTypeToUpper == "MESSAGE/RFC822")
            {
                int mimeDivider = body.IndexOf("\r\n\r\n");
                if (mimeDivider > -1)
                {
                    string mimeHeaders = Functions.UnfoldWhitespace(body.Substring(0, mimeDivider));
                    string mimeBody    = body.Substring(mimeDivider + 4);

                    string mimeContentType             = Functions.ReturnBetween(mimeHeaders, "Content-Type:", "\r\n").Trim();
                    string mimeContentTransferEncoding = Functions.ReturnBetween(mimeHeaders, "Content-Transfer-Encoding:", "\r\n").Trim();
                    string mimeCharSet = Functions.ExtractMimeParameter(mimeContentType, "charset");

                    List <MimePart> returnedMIMEParts = ExtractMIMEParts(mimeContentType, mimeCharSet, mimeContentTransferEncoding, mimeBody, processingFlags, depth + 1);
                    foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                    {
                        mimeParts.Add(returnedMIMEPart);
                    }
                }
            }
            else
            {
                // Decode the message.
                switch (contentTransferEncoding.ToLower())
                {
                case "base64":
                    body = Functions.FromBase64(body);
                    break;

                case "quoted-printable":
                    body = Functions.FromQuotedPrintable(body, charSet, null);
                    break;

                case "binary":
                case "7bit":
                case "8bit":
                    break;
                }

                // If we're beyond the first layer, process the MIME part.  Otherwise, the message isn't MIME encoded.
                if (depth > 0)
                {
                    // Extract the headers from this MIME part.
                    string mimeHeaders;
                    int    mimeDivider = body.IndexOf("\r\n\r\n");
                    if (mimeDivider > -1)
                    {
                        mimeHeaders = body.Substring(0, mimeDivider);
                    }
                    else
                    {
                        mimeHeaders = body;
                    }

                    // Divide the MIME part's headers into its components.
                    string mimeCharSet = "", mimeContentDisposition = "", mimeContentID = "", mimeContentType = "", mimeContentTransferEncoding = "", mimeFileName = "";
                    ExtractMimeHeaders(mimeHeaders, out mimeContentType, out mimeCharSet, out mimeContentTransferEncoding, out mimeContentDisposition, out mimeFileName, out mimeContentID);

                    // If this MIME part's content type is null, fall back to the overall content type.
                    if ((string.IsNullOrEmpty(mimeContentType) && !string.IsNullOrEmpty(contentType)) || (contentTypeToUpper.StartsWith("MESSAGE/PARTIAL")))
                    {
                        mimeCharSet     = charSet;
                        mimeContentType = contentType;
                    }
                    else
                    {
                        if (body.Length > (mimeDivider + 4))
                        {
                            body = body.Substring(mimeDivider + 4);
                        }
                        else
                        {
                            body = "";
                        }
                    }

                    // Add the message to the MIME parts collection.
                    mimeParts.Add(new MimePart(mimeFileName, mimeContentType, mimeCharSet, mimeContentID, mimeContentTransferEncoding, body));
                }
                else
                {
                    // If the content type contains a character set, extract it.
                    charSet = Functions.NormalizeCharSet(Functions.ExtractMimeParameter(contentType, "charset"));

                    int semicolonPos = contentType.IndexOf(";");
                    if (semicolonPos > -1)
                    {
                        contentType = contentType.Substring(0, semicolonPos);
                    }

                    // Add the message as-is.
                    mimeParts.Add(new MimePart("", contentType, charSet, "", contentTransferEncoding, body));
                }
            }

            return(mimeParts);
        }
Example #2
0
        /// <summary>
        /// Extract a list of MIME parts from a multipart/* MIME encoded message.
        /// </summary>
        /// <param name="contentType">Content Type of the outermost MIME part.</param>
        /// <param name="charSet">Character set of the outermost MIME part.</param>
        /// <param name="contentTransferEncoding">Encoding of the outermost MIME part.</param>
        /// <param name="body">The outermost MIME part's contents.</param>
        /// <param name="processingFlags">Flags determining whether specialized properties are returned with a ReadOnlyMailMessage.</param>
        public static List<MimePart> ExtractMIMEParts(string contentType, string charSet, string contentTransferEncoding, string body, ReadOnlyMailMessageProcessingFlags processingFlags)
        {
            List<MimePart> mimeParts = new List<MimePart>();

            string contentTypeToUpper = contentType.ToUpper();
            if (contentTypeToUpper.StartsWith("MULTIPART/"))
            {
                // Prepare to process each part of the multipart/* message.
                int cursor = 0;

                // Prepend the body with a carriage return and linefeed for consistent boundary matching.
                body = "\r\n" + body;

                // Determine the outermost boundary name.
                string boundaryName = Functions.ReturnBetween(contentType, "boundary=\"", "\"");
                if (boundaryName.Length < 1)
                {
                    cursor = contentType.IndexOf("boundary=", StringComparison.OrdinalIgnoreCase);
                    if (cursor > -1)
                        boundaryName = contentType.Substring(cursor + 9);
                    cursor = boundaryName.IndexOf(";");
                    if (cursor > -1)
                        boundaryName = boundaryName.Substring(0, cursor);

                    if (boundaryName.StartsWith("\"") && boundaryName.EndsWith("\""))
                        boundaryName = boundaryName.Substring(1, boundaryName.Length - 2);
                    else
                    {
                        // Remove linear whitespace from boundary names.
                        boundaryName = boundaryName.Trim();
                    }
                }
                int boundaryNameLength = boundaryName.Length;

                // Variables used for record keeping with signed S/MIME parts.
                int signatureBlock = -1;
                List<string> mimeBlocks = new List<string>();

                cursor = body.IndexOf("\r\n--" + boundaryName, 0, StringComparison.Ordinal);
                while (cursor > -1)
                {
                    // Calculate the end boundary of the current MIME part.
                    int mimeStartPosition = cursor + boundaryNameLength + 4;
                    int mimeEndPosition = body.IndexOf("\r\n--" + boundaryName, mimeStartPosition, StringComparison.Ordinal);
                    if (mimeEndPosition > -1)
                    {
                        string afterBoundaryEnd = body.Substring(mimeEndPosition + 4 + boundaryNameLength, 2);
                        if (afterBoundaryEnd == "\r\n" || afterBoundaryEnd == "--")
                        {
                            string mimeContents = body.Substring(mimeStartPosition, mimeEndPosition - mimeStartPosition);

                            // Extract the header portion of the current MIME part.
                            int mimeDivider = mimeContents.IndexOf("\r\n\r\n");
                            string mimeHeaders, mimeBody;
                            if (mimeDivider > -1)
                            {
                                mimeHeaders = mimeContents.Substring(0, mimeDivider);
                                mimeBody = mimeContents.Substring(mimeDivider + 4);
                            }
                            else
                            {
                                // The following is a workaround to handle malformed MIME bodies.
                                mimeHeaders = mimeContents;
                                mimeBody = "";

                                int linePos = 0, lastLinePos = 0;
                                while (linePos > -1)
                                {
                                    lastLinePos = linePos;
                                    linePos = mimeHeaders.IndexOf("\r\n", lastLinePos);
                                    if (linePos > -1)
                                    {
                                        string currentLine = mimeContents.Substring(lastLinePos, linePos - lastLinePos);
                                        if (currentLine.Length > 0 && currentLine.IndexOf(":") < 0)
                                        {
                                            mimeBody = mimeContents.Substring(lastLinePos + 2, mimeContents.Length - lastLinePos - 4);
                                            linePos = -1;
                                        }
                                        else
                                            linePos += 2;
                                    }
                                }
                            }

                            mimeBlocks.Add(mimeContents);

                            // Divide the MIME part's headers into its components.
                            string mimeCharSet = "", mimeContentDisposition = "", mimeContentID = "", mimeContentType = "", mimeContentTransferEncoding = "", mimeFileName = "";
                            ExtractMimeHeaders(mimeHeaders, out mimeContentType, out mimeCharSet, out mimeContentTransferEncoding, out mimeContentDisposition, out mimeFileName, out mimeContentID);

                            string mimeContentTypeToUpper = mimeContentType.ToUpper();
                            if (mimeContentTypeToUpper.StartsWith("MULTIPART/"))
                            {
                                // Recurse through embedded MIME parts.
                                List<MimePart> returnedMIMEParts = ExtractMIMEParts(mimeContentType, mimeCharSet, mimeContentTransferEncoding, mimeBody, processingFlags);
                                foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                                    mimeParts.Add(returnedMIMEPart);
                            }
                            else
                            {
                                // Keep track of whether this MIME part's body has already been processed.
                                bool processed = false;

                                if (mimeContentTypeToUpper.StartsWith("APPLICATION/PKCS7-SIGNATURE") || mimeContentTypeToUpper.StartsWith("APPLICATION/X-PKCS7-SIGNATURE"))
                                {
                                    // Unless a flag has been set to include this *.p7s block, exclude it from attachments.
                                    if ((processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeSmimeSignedData) == 0)
                                        processed = true;

                                    // Remember the signature block to use for later verification.
                                    signatureBlock = mimeBlocks.Count() - 1;
                                }
                                else if (mimeContentTypeToUpper.StartsWith("APPLICATION/PKCS7-MIME") || mimeContentTypeToUpper.StartsWith("APPLICATION/X-PKCS7-MIME"))
                                {
                                    // Unless a flag has been set to include this *.p7m block, exclude it from attachments.
                                    processed = (processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeSmimeEncryptedEnvelopeData) == 0;

                                    // Decrypt the MIME part and recurse through embedded MIME parts.
                                    List<MimePart> returnedMIMEParts = ReturnDecryptedMimeParts(mimeContentType, mimeContentTransferEncoding, mimeBody, processingFlags);
                                    if (returnedMIMEParts != null)
                                    {
                                        foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                                            mimeParts.Add(returnedMIMEPart);
                                    }
                                    else
                                    {
                                        // If we were unable to decrypt, return this MIME part as-is.
                                        processed = false;
                                    }
                                }
                                else if (mimeContentTypeToUpper.StartsWith("APPLICATION/MS-TNEF") || mimeFileName.ToUpper() == "WINMAIL.DAT")
                                {
                                    // Process the TNEF encoded message.
                                    processed = true;
                                    TnefEncoding tnef = new TnefEncoding(Convert.FromBase64String(mimeBody));

                                    // If we were unable to extract content from this MIME, include it as an attachment.
                                    if ((tnef.Body.Length < 1 && tnef.MimeAttachments.Count < 1) || (processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeWinMailData) > 0)
                                        processed = false;
                                    else
                                    {
                                        // Unless a flag has been set to include this winmail.dat block, exclude it from attachments.
                                        if ((processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeWinMailData) > 0)
                                        {
                                            if (!string.IsNullOrEmpty(tnef.Body))
                                                mimeParts.Add(new MimePart("winmail.dat", tnef.ContentType, "", "", mimeContentTransferEncoding, Encoding.UTF8.GetBytes(tnef.Body)));
                                        }

                                        foreach (MimePart mimePart in tnef.MimeAttachments)
                                            mimeParts.Add(mimePart);
                                    }
                                }
                                else if (mimeContentTypeToUpper == "MESSAGE/RFC822")
                                {
                                    if ((processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeNestedRFC822Messages) > 0)
                                    {
                                        // Recurse through the RFC822 container.
                                        processed = true;

                                        mimeDivider = mimeBody.IndexOf("\r\n\r\n");
                                        if (mimeDivider > -1)
                                        {
                                            mimeHeaders = Functions.UnfoldWhitespace(mimeBody.Substring(0, mimeDivider));
                                            mimeBody = mimeBody.Substring(mimeDivider + 4);

                                            mimeContentType = Functions.ReturnBetween(mimeHeaders, "Content-Type:", "\r\n").Trim();
                                            mimeContentTransferEncoding = Functions.ReturnBetween(mimeHeaders, "Content-Transfer-Encoding:", "\r\n").Trim();
                                            mimeCharSet = Functions.ExtractMimeParameter(mimeContentType, "charset").Replace("\"", "");

                                            List<MimePart> returnedMIMEParts = ExtractMIMEParts(mimeContentType, mimeCharSet, mimeContentTransferEncoding, mimeBody, processingFlags);
                                            foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                                                mimeParts.Add(returnedMIMEPart);
                                        }
                                    }
                                }

                                if (!processed)
                                {
                                    // Decode and add the message to the MIME parts collection.
                                    switch (mimeContentTransferEncoding.ToLower())
                                    {
                                        case "base64":
                                            mimeBody = mimeBody.Replace("\r\n", "");
                                            if (mimeBody.Length % 4 != 0)
                                                mimeBody += new String('=', 4 - (mimeBody.Length % 4));

                                            mimeParts.Add(new MimePart(mimeFileName, mimeContentType, mimeCharSet, mimeContentID, mimeContentTransferEncoding, Convert.FromBase64String(mimeBody)));
                                            break;
                                        case "quoted-printable":
                                            mimeParts.Add(new MimePart(mimeFileName, mimeContentType, mimeCharSet, mimeContentID, mimeContentTransferEncoding, Functions.FromQuotedPrintable(mimeBody, mimeCharSet, null)));
                                            break;
                                        case "binary":
                                        case "7bit":
                                        case "8bit":
                                        default:
                                            mimeParts.Add(new MimePart(mimeFileName, mimeContentType, mimeCharSet, mimeContentID, mimeContentTransferEncoding, mimeBody));
                                            break;
                                    }
                                }
                            }
                        }
                        cursor = mimeEndPosition;
                    }
                    else
                        cursor = -1;
                }

                // If a PKCS signature was found and there's one other MIME part, verify the signature.
                if (signatureBlock > -1 && mimeBlocks.Count == 2)
                {
                    // Verify the signature and track the signing certificates.
                    X509Certificate2Collection signingCertificates;
                    if (VerifySignature(mimeBlocks[signatureBlock], mimeBlocks[1 - signatureBlock], out signingCertificates))
                    {
                        // Stamp each MIME part found so far as signed, and if relevant, triple wrapped.
                        foreach (MimePart mimePart in mimeParts)
                        {
                            mimePart.SmimeSigningCertificates = signingCertificates;

                            if (mimePart.SmimeSigned && mimePart.SmimeEncryptedEnvelope)
                                mimePart.SmimeTripleWrapped = true;

                            mimePart.SmimeSigned = true;
                        }
                    }
                }
            }
            else if (contentTypeToUpper.StartsWith("APPLICATION/MS-TNEF"))
            {
                // Process the TNEF encoded message.
                TnefEncoding tnef = new TnefEncoding(Convert.FromBase64String(body));

                // Unless a flag has been set to include this winmail.dat block, exclude it from attachments.
                if ((processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeWinMailData) > 0)
                {
                    if (!string.IsNullOrEmpty(tnef.Body))
                        mimeParts.Add(new MimePart("winmail.dat", tnef.ContentType, "", "", "", Encoding.UTF8.GetBytes(tnef.Body)));
                }

                foreach (MimePart mimePart in tnef.MimeAttachments)
                    mimeParts.Add(mimePart);
            }
            else if (contentTypeToUpper.StartsWith("APPLICATION/PKCS7-MIME") || contentTypeToUpper.StartsWith("APPLICATION/X-PKCS7-MIME"))
            {
                // Don't attempt to decrypt if this is a signed message only.
                if (contentType.IndexOf("smime-type=signed-data") < 0)
                {
                    // Unless a flag has been set to include this *.p7m block, exclude it from attachments.
                    if ((processingFlags & ReadOnlyMailMessageProcessingFlags.IncludeSmimeEncryptedEnvelopeData) > 0)
                        mimeParts.Add(new MimePart("smime.p7m", contentType, "", "", "", body));

                    // Decrypt the MIME part and recurse through embedded MIME parts.
                    List<MimePart> returnedMIMEParts = ReturnDecryptedMimeParts(contentType, contentTransferEncoding, body, processingFlags);
                    if (returnedMIMEParts != null)
                    {
                        foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                            mimeParts.Add(returnedMIMEPart);
                    }
                    else
                    {
                        // If we were unable to decrypt the message, pass it along as-is.
                        mimeParts.Add(new MimePart(Functions.ReturnBetween(contentType + ";", "name=", ";").Replace("\"", ""), contentType, "", "", contentTransferEncoding, body));
                    }
                }
                else
                {
                    // Hydrate the signature CMS object.
                    SignedCms signedCms = new SignedCms();

                    try
                    {
                        // Attempt to decode the signature block and verify the passed in signature.
                        signedCms.Decode(Convert.FromBase64String(body));
                        signedCms.CheckSignature(true);

                        string mimeContents = Encoding.UTF8.GetString(signedCms.ContentInfo.Content);

                        int mimeDivider = mimeContents.IndexOf("\r\n\r\n");
                        string mimeHeaders;
                        if (mimeDivider > -1)
                            mimeHeaders = mimeContents.Substring(0, mimeDivider);
                        else
                            mimeHeaders = mimeContents;

                        if (mimeHeaders.Length > 0)
                        {
                            // Extract the body portion of the current MIME part.
                            string mimeBody = mimeContents.Substring(mimeDivider + 4);

                            string mimeCharSet = "", mimeContentDisposition = "", mimeContentID = "", mimeContentType = "", mimeContentTransferEncoding = "", mimeFileName = "";
                            ExtractMimeHeaders(mimeHeaders, out mimeContentType, out mimeCharSet, out mimeContentTransferEncoding, out mimeContentDisposition, out mimeFileName, out mimeContentID);

                            List<MimePart> returnedMIMEParts = ExtractMIMEParts(mimeContentType, mimeCharSet, mimeContentTransferEncoding, mimeBody, processingFlags);
                            foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                                mimeParts.Add(returnedMIMEPart);
                        }
                    }
                    catch
                    {
                        // If an exception occured, the signature could not be verified.
                    }
                }
            }
            else if (contentTypeToUpper == "MESSAGE/RFC822")
            {
                int mimeDivider = body.IndexOf("\r\n\r\n");
                if (mimeDivider > -1)
                {
                    string mimeHeaders = Functions.UnfoldWhitespace(body.Substring(0, mimeDivider));
                    string mimeBody = body.Substring(mimeDivider + 4);

                    string mimeContentType = Functions.ReturnBetween(mimeHeaders, "Content-Type:", "\r\n").Trim();
                    string mimeContentTransferEncoding = Functions.ReturnBetween(mimeHeaders, "Content-Transfer-Encoding:", "\r\n").Trim();
                    string mimeCharSet = Functions.ExtractMimeParameter(mimeContentType, "charset");

                    List<MimePart> returnedMIMEParts = ExtractMIMEParts(mimeContentType, mimeCharSet, mimeContentTransferEncoding, mimeBody, processingFlags);
                    foreach (MimePart returnedMIMEPart in returnedMIMEParts)
                        mimeParts.Add(returnedMIMEPart);
                }
            }
            else
            {
                // Decode the message.
                switch (contentTransferEncoding.ToLower())
                {
                    case "base64":
                        body = Functions.FromBase64(body);
                        break;
                    case "quoted-printable":
                        body = Functions.FromQuotedPrintable(body, charSet, null);
                        break;
                    case "binary":
                    case "7bit":
                    case "8bit":
                        break;
                }

                // Extract the headers from this MIME part.
                string mimeHeaders;
                int mimeDivider = body.IndexOf("\r\n\r\n");
                if (mimeDivider > -1 )
                    mimeHeaders = body.Substring(0, mimeDivider);
                else
                    mimeHeaders = body;

                // Divide the MIME part's headers into its components.
                string mimeCharSet = "", mimeContentDisposition = "", mimeContentID = "", mimeContentType = "", mimeContentTransferEncoding = "", mimeFileName = "";
                ExtractMimeHeaders(mimeHeaders, out mimeContentType, out mimeCharSet, out mimeContentTransferEncoding, out mimeContentDisposition, out mimeFileName, out mimeContentID);

                // If this MIME part's content type is null, fall back to the overall content type.
                if ((string.IsNullOrEmpty(mimeContentType) && !string.IsNullOrEmpty(contentType)) || (contentTypeToUpper.StartsWith("MESSAGE/PARTIAL")))
                {
                    mimeCharSet = charSet;
                    mimeContentType = contentType;
                }
                else
                {
                    if (body.Length > (mimeDivider + 4))
                        body = body.Substring(mimeDivider + 4);
                    else
                        body = "";
                }

                // Add the message to the MIME parts collection.
                mimeParts.Add(new MimePart(mimeFileName, mimeContentType, mimeCharSet, mimeContentID, mimeContentTransferEncoding, body));
            }
            
            return mimeParts;
        }