示例#1
0
        /// <summary>
        /// Decrypt the encrypted S/MIME envelope.
        /// </summary>
        /// <param name="contentType">Content Type of the outermost MIME part.</param>
        /// <param name="contentTransferEncoding">Encoding of the outermost MIME part.</param>
        /// <param name="envelopeText">The MIME envelope.</param>
        /// <param name="processingFlags">Flags determining whether specialized properties are returned with a MailMessage.</param>
        /// <param name="depth">The nesting layer of this MIME part.</param>
        public static List <MimePart> ReturnSmimeDecryptedMimeParts(string contentType, string contentTransferEncoding, string envelopeText, MailMessageProcessingFlags processingFlags, int depth)
        {
            try
            {
                // Hydrate the envelope CMS object.
                EnvelopedCms envelope = new EnvelopedCms();

                // Attempt to decrypt the envelope.
                envelope.Decode(Convert.FromBase64String(envelopeText));
                envelope.Decrypt();

                string body        = Encoding.UTF8.GetString(envelope.ContentInfo.Content);
                int    divider     = body.IndexOf("\r\n\r\n");
                string mimeHeaders = body.Substring(0, divider);
                body = body.Substring(divider + 4);

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

                // Recurse through embedded MIME parts.
                List <MimePart> mimeParts = ExtractMIMEParts(mimeContentType, mimeCharSet, mimeContentTransferEncoding, body, processingFlags, depth + 1);
                foreach (MimePart mimePart in mimeParts)
                {
                    mimePart.SmimeEncryptedEnvelope = true;
                }

                return(mimeParts);
            }
            catch (Exception)
            {
                // If unable to decrypt the body, return null.
                return(null);
            }
        }
示例#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="depth">The nesting layer of this MIME part.</param>
        /// <param name="processingFlags">Flags determining whether specialized properties are returned with a MailMessage.</param>
        public static List <MimePart> ExtractMIMEParts(string contentType, string charSet, string contentTransferEncoding, string body, MailMessageProcessingFlags 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 & MailMessageProcessingFlags.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 & MailMessageProcessingFlags.IncludeSmimeEncryptedEnvelopeData) == 0;

                                    // Decrypt the MIME part and recurse through embedded MIME parts.
                                    List <MimePart> returnedMIMEParts = ReturnSmimeDecryptedMimeParts(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 & MailMessageProcessingFlags.IncludeWinMailData) > 0)
                                    {
                                        processed = false;
                                    }
                                    else
                                    {
                                        // Unless a flag has been set to include this winmail.dat block, exclude it from attachments.
                                        if ((processingFlags & MailMessageProcessingFlags.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 & MailMessageProcessingFlags.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 (VerifySmimeSignature(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 & MailMessageProcessingFlags.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 & MailMessageProcessingFlags.IncludeSmimeEncryptedEnvelopeData) > 0)
                    {
                        mimeParts.Add(new MimePart("smime.p7m", contentType, "", "", "", body));
                    }

                    // Decrypt the MIME part and recurse through embedded MIME parts.
                    List <MimePart> returnedMIMEParts = ReturnSmimeDecryptedMimeParts(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);
        }
示例#3
0
        /// <summary>
        /// Decrypt the encrypted S/MIME envelope.
        /// </summary>
        /// <param name="contentType">Content Type of the outermost MIME part.</param>
        /// <param name="contentTransferEncoding">Encoding of the outermost MIME part.</param>
        /// <param name="envelopeText">The MIME envelope.</param>
        /// <param name="processingFlags">Flags determining whether specialized properties are returned with a MailMessage.</param>
        /// <param name="depth">The nesting layer of this MIME part.</param>
        public static List<MimePart> ReturnSmimeDecryptedMimeParts(string contentType, string contentTransferEncoding, string envelopeText, MailMessageProcessingFlags processingFlags, int depth)
        {
            try
            {
                // Hydrate the envelope CMS object.
                EnvelopedCms envelope = new EnvelopedCms();

                // Attempt to decrypt the envelope.
                envelope.Decode(Convert.FromBase64String(envelopeText));
                envelope.Decrypt();

                string body = Encoding.UTF8.GetString(envelope.ContentInfo.Content);
                int divider = body.IndexOf("\r\n\r\n");
                string mimeHeaders = body.Substring(0, divider);
                body = body.Substring(divider + 4);

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

                // Recurse through embedded MIME parts.
                List<MimePart> mimeParts = ExtractMIMEParts(mimeContentType, mimeCharSet, mimeContentTransferEncoding, body, processingFlags, depth + 1);
                foreach (MimePart mimePart in mimeParts)
                    mimePart.SmimeEncryptedEnvelope = true;

                return mimeParts;
            }
            catch (Exception)
            {
                // If unable to decrypt the body, return null.
                return null;
            }
        }
示例#4
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 MailMessage.</param>
        public static List<MimePart> ExtractMIMEParts(string contentType, string charSet, string contentTransferEncoding, string body, MailMessageProcessingFlags 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 & MailMessageProcessingFlags.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 & MailMessageProcessingFlags.IncludeSmimeEncryptedEnvelopeData) == 0;

                                    // Decrypt the MIME part and recurse through embedded MIME parts.
                                    List<MimePart> returnedMIMEParts = ReturnSmimeDecryptedMimeParts(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 & MailMessageProcessingFlags.IncludeWinMailData) > 0)
                                        processed = false;
                                    else
                                    {
                                        // Unless a flag has been set to include this winmail.dat block, exclude it from attachments.
                                        if ((processingFlags & MailMessageProcessingFlags.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 & MailMessageProcessingFlags.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 (VerifySmimeSignature(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 & MailMessageProcessingFlags.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 & MailMessageProcessingFlags.IncludeSmimeEncryptedEnvelopeData) > 0)
                        mimeParts.Add(new MimePart("smime.p7m", contentType, "", "", "", body));

                    // Decrypt the MIME part and recurse through embedded MIME parts.
                    List<MimePart> returnedMIMEParts = ReturnSmimeDecryptedMimeParts(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;
        }
示例#5
0
        /// <summary>
        /// Initializes a populated instance of the OpaqueMail.MailMessage class representing the message text passed in with attachments procesed according to the attachment filter flags.
        /// </summary>
        /// <param name="messageText">The raw contents of the email message.</param>
        /// <param name="processingFlags">Flags determining whether specialized properties are returned with a MailMessage.</param>
        /// <param name="parseExtendedHeaders">Whether to populate the ExtendedHeaders object.</param>
        public MailMessage(string messageText, MailMessageProcessingFlags processingFlags, bool parseExtendedHeaders)
            : this()
        {
            // Default to no MIME boundary.
            MimeBoundaryName = null;

            // Prepare an object to track advanced headers.
            if (parseExtendedHeaders)
                ExtendedProperties = new ExtendedProperties();

            // Remember which specialized attachments to include.
            ProcessingFlags = processingFlags;

            // Fix messages whose carriage returns have been stripped.
            if (messageText.IndexOf("\r") < 0)
                messageText = messageText.Replace("\n", "\r\n");

            // Separate the headers for processing.
            string headers;
            int cutoff = messageText.IndexOf("\r\n\r\n");
            if (cutoff > -1)
                headers = messageText.Substring(0, cutoff);
            else
                headers = messageText;

            // Calculate the size of the message.
            Size = messageText.Length;

            // Temporary header variables to be processed by Functions.FromMailAddressString() later.
            string fromText = "";
            string toText = "";
            string ccText = "";
            string bccText = "";
            string replyToText = "";

            // Temporary header variables to be processed later.
            List<string> receivedChain = new List<string>();
            string receivedText = "";

            // Unfold any unneeded whitespace, then loop through each line of the headers.
            string[] headersList = Functions.UnfoldWhitespace(headers).Replace("\r", "").Split('\n');
            foreach (string header in headersList)
            {
                // Split header {name:value} pairs by the first colon found.
                int colonPos = header.IndexOf(":");
                if (colonPos > -1 && colonPos < header.Length - 1)
                {
                    string[] headerParts = new string[] { header.Substring(0, colonPos), header.Substring(colonPos + 1).TrimStart(new char[] { ' ' }) };
                    string headerType = headerParts[0].ToLower();
                    string headerValue = headerParts[1];

                    // Set header variables for common headers.
                    if (!string.IsNullOrEmpty(headerType) && !string.IsNullOrEmpty(headerValue))
                        Headers[headerType] = headerValue;

                    switch (headerType)
                    {
                        case "cc":
                            if (ccText.Length > 0)
                                ccText += ", ";
                            ccText += headerValue;
                            break;
                        case "content-transfer-encoding":
                            ContentTransferEncoding = headerValue;
                            switch (headerValue.ToLower())
                            {
                                case "base64":
                                    BodyTransferEncoding = TransferEncoding.Base64;
                                    break;
                                case "quoted-printable":
                                    BodyTransferEncoding = TransferEncoding.QuotedPrintable;
                                    break;
                                case "7bit":
                                    BodyTransferEncoding = TransferEncoding.SevenBit;
                                    break;
                                case "8bit":
                                    BodyTransferEncoding = TransferEncoding.EightBit;
                                    break;
                                default:
                                    BodyTransferEncoding = TransferEncoding.Unknown;
                                    break;
                            }
                            break;
                        case "content-language":
                            ContentLanguage = headerValue;
                            break;
                        case "content-type":
                            // If multiple content-types are passed, only process the first.
                            if (string.IsNullOrEmpty(ContentType))
                            {
                                ContentType = headerValue.Trim();
                                CharSet = Functions.ExtractMimeParameter(ContentType, "charset");
                                MimeBoundaryName = Functions.ExtractMimeParameter(ContentType, "boundary");

                                BodyContentType = ContentType.ToLower();
                                if (BodyContentType.IndexOf(";") > -1)
                                    BodyContentType = BodyContentType.Substring(0, BodyContentType.IndexOf(";"));

                                IsBodyHtml = BodyContentType.StartsWith("text/html");
                            }
                            break;
                        case "date":
                            string dateString = headerValue;

                            // Ignore extraneous datetime information.
                            int dateStringParenthesis = dateString.IndexOf("(");
                            if (dateStringParenthesis > -1)
                                dateString = dateString.Substring(0, dateStringParenthesis - 1);

                            // Remove timezone suffix.
                            if (dateString.Substring(dateString.Length - 4, 1) == " ")
                                dateString = dateString.Substring(0, dateString.Length - 4);

                            DateTime date;
                            DateTime.TryParse(dateString, out date);
                            Date = date;
                            break;
                        case "delivered-to":
                            DeliveredTo = headerValue;
                            break;
                        case "from":
                            fromText = headerValue;
                            break;
                        case "importance":
                            Importance = headerValue;
                            break;
                        case "in-reply-to":
                            InReplyTo = headerValue;

                            // Loop through references.
                            List<string> inReplyTo = new List<string>();
                            int inReplyToPos = 0;
                            while (inReplyToPos > -1)
                            {
                                inReplyToPos = headerValue.IndexOf("<", inReplyToPos);
                                if (inReplyToPos > -1)
                                {
                                    int referencesPos2 = headerValue.IndexOf(">", inReplyToPos);
                                    if (referencesPos2 > -1)
                                    {
                                        inReplyTo.Add(headerValue.Substring(inReplyToPos + 1, referencesPos2 - inReplyToPos - 1));
                                        inReplyToPos = referencesPos2 + 1;
                                    }
                                    else
                                        inReplyToPos = -1;
                                }
                            }
                            // If any references were found, apply to the message's array.
                            if (inReplyTo.Count > 0)
                                InReplyToMessageIDs = inReplyTo.ToArray();
                            else
                            {
                                if (!string.IsNullOrEmpty(headerValue))
                                    InReplyToMessageIDs = new string[] {headerValue};
                            }

                            break;
                        case "message-id":
                            // Ignore opening and closing <> characters.
                            MessageId = headerValue;
                            if (MessageId.StartsWith("<"))
                                MessageId = MessageId.Substring(1);
                            if (MessageId.EndsWith(">"))
                                MessageId = MessageId.Substring(0, MessageId.Length - 1);
                            break;
                        case "received":
                        case "x-received":
                            if (!string.IsNullOrEmpty(receivedText))
                                receivedChain.Add(receivedText);

                            receivedText = headerValue;
                            break;
                        case "references":
                            // Loop through references.
                            List<string> references = new List<string>();
                            int referencesPos = 0;
                            while (referencesPos > -1)
                            {
                                referencesPos = headerValue.IndexOf("<", referencesPos);
                                if (referencesPos > -1)
                                {
                                    int referencesPos2 = headerValue.IndexOf(">", referencesPos);
                                    if (referencesPos2 > -1)
                                    {
                                        references.Add(headerValue.Substring(referencesPos + 1, referencesPos2 - referencesPos - 1));
                                        referencesPos = referencesPos2 + 1;
                                    }
                                    else
                                        referencesPos = -1;
                                }
                            }
                            // If any references were found, apply to the message's array.
                            if (references.Count > 0)
                                References = references.ToArray();

                            break;
                        case "replyto":
                        case "reply-to":
                            replyToText = headerValue;
                            break;
                        case "return-path":
                            // Ignore opening and closing <> characters.
                            ReturnPath = headerValue;
                            if (ReturnPath.StartsWith("<"))
                                ReturnPath = ReturnPath.Substring(1);
                            if (ReturnPath.EndsWith(">"))
                                ReturnPath = ReturnPath.Substring(0, ReturnPath.Length - 1);
                            break;
                        case "sender":
                        case "x-sender":
                            if (headerValue.Length > 0)
                            {
                                MailAddressCollection senderCollection = MailAddressCollection.Parse(headerValue);
                                if (senderCollection.Count > 0)
                                    this.Sender = senderCollection[0];
                            }
                            break;
                        case "subject":
                            // Decode international strings and remove escaped linebreaks.
                            Subject = Functions.DecodeMailHeader(headerValue).Replace("\r", "").Replace("\n", "");
                            break;
                        case "to":
                            if (toText.Length > 0)
                                toText += ", ";
                            toText += headerValue;
                            break;
                        case "x-priority":
                            switch (headerValue.ToUpper())
                            {
                                case "LOW":
                                    Priority = MailPriority.Low;
                                    break;
                                case "NORMAL":
                                    Priority = MailPriority.Normal;
                                    break;
                                case "HIGH":
                                    Priority = MailPriority.High;
                                    break;
                            }
                            break;
                        case "x-subject-encryption":
                            bool subjectEncryption;
                            bool.TryParse(headerValue, out subjectEncryption);
                            SubjectEncryption = subjectEncryption;
                            break;
                        default:
                            break;
                    }

                    // Set header variables for advanced headers.
                    if (parseExtendedHeaders)
                    {
                        switch (headerType)
                        {
                            case "acceptlanguage":
                            case "accept-language":
                                ExtendedProperties.AcceptLanguage = headerValue;
                                break;
                            case "authentication-results":
                                ExtendedProperties.AuthenticationResults = headerValue;
                                break;
                            case "bounces-to":
                            case "bounces_to":
                                ExtendedProperties.BouncesTo = headerValue;
                                break;
                            case "content-description":
                                ExtendedProperties.ContentDescription = headerValue;
                                break;
                            case "dispositionnotificationto":
                            case "disposition-notification-to":
                                ExtendedProperties.DispositionNotificationTo = headerValue;
                                break;
                            case "dkim-signature":
                            case "domainkey-signature":
                            case "x-google-dkim-signature":
                                ExtendedProperties.DomainKeySignature = headerValue;
                                break;
                            case "domainkey-status":
                                ExtendedProperties.DomainKeyStatus = headerValue;
                                break;
                            case "errors-to":
                                ExtendedProperties.ErrorsTo = headerValue;
                                break;
                            case "list-unsubscribe":
                            case "x-list-unsubscribe":
                                ExtendedProperties.ListUnsubscribe = headerValue;
                                break;
                            case "mailer":
                            case "x-mailer":
                                ExtendedProperties.Mailer = headerValue;
                                break;
                            case "organization":
                            case "x-originator-org":
                            case "x-originatororg":
                            case "x-organization":
                                ExtendedProperties.OriginatorOrg = headerValue;
                                break;
                            case "original-messageid":
                            case "x-original-messageid":
                                ExtendedProperties.OriginalMessageId = headerValue;
                                break;
                            case "originating-email":
                            case "x-originating-email":
                                ExtendedProperties.OriginatingEmail = headerValue;
                                break;
                            case "precedence":
                                ExtendedProperties.Precedence = headerValue;
                                break;
                            case "received-spf":
                                ExtendedProperties.ReceivedSPF = headerValue;
                                break;
                            case "references":
                                ExtendedProperties.References = headerValue;
                                break;
                            case "resent-date":
                                string dateString = headerValue;

                                // Ignore extraneous datetime information.
                                int dateStringParenthesis = dateString.IndexOf("(");
                                if (dateStringParenthesis > -1)
                                    dateString = dateString.Substring(0, dateStringParenthesis - 1);

                                // Remove timezone suffix.
                                if (dateString.Substring(dateString.Length - 4) == " ")
                                    dateString = dateString.Substring(0, dateString.Length - 4);

                                DateTime.TryParse(dateString, out ExtendedProperties.ResentDate);
                                break;
                            case "resent-from":
                                ExtendedProperties.ResentFrom = headerValue;
                                break;
                            case "resent-message-id":
                                ExtendedProperties.ResentMessageID = headerValue;
                                break;
                            case "thread-index":
                                ExtendedProperties.ThreadIndex = headerValue;
                                break;
                            case "thread-topic":
                                ExtendedProperties.ThreadTopic = headerValue;
                                break;
                            case "user-agent":
                            case "useragent":
                                ExtendedProperties.UserAgent = headerValue;
                                break;
                            case "x-auto-response-suppress":
                                ExtendedProperties.AutoResponseSuppress = headerValue;
                                break;
                            case "x-campaign":
                            case "x-campaign-id":
                            case "x-campaignid":
                            case "x-mllistcampaign":
                            case "x-rpcampaign":
                                ExtendedProperties.CampaignID = headerValue;
                                break;
                            case "x-delivery-context":
                                ExtendedProperties.DeliveryContext = headerValue;
                                break;
                            case "x-maillist-id":
                                ExtendedProperties.MailListId = headerValue;
                                break;
                            case "x-msmail-priority":
                                ExtendedProperties.MSMailPriority = headerValue;
                                break;
                            case "x-originalarrivaltime":
                            case "x-original-arrival-time":
                                dateString = headerValue;

                                // Ignore extraneous datetime information.
                                dateStringParenthesis = dateString.IndexOf("(");
                                if (dateStringParenthesis > -1)
                                    dateString = dateString.Substring(0, dateStringParenthesis - 1);

                                // Remove timezone suffix.
                                if (dateString.Substring(dateString.Length - 4) == " ")
                                    dateString = dateString.Substring(0, dateString.Length - 4);

                                DateTime.TryParse(dateString, out ExtendedProperties.OriginalArrivalTime);
                                break;
                            case "x-originating-ip":
                                ExtendedProperties.OriginatingIP = headerValue;
                                break;
                            case "x-rcpt-to":
                                if (headerValue.Length > 1)
                                    ExtendedProperties.RcptTo = headerValue.Substring(1, headerValue.Length - 2);
                                break;
                            case "x-csa-complaints":
                            case "x-complaints-to":
                            case "x-reportabuse":
                            case "x-report-abuse":
                            case "x-mail_abuse_inquiries":
                                ExtendedProperties.ReportAbuse = headerValue;
                                break;
                            case "x-spam-score":
                                ExtendedProperties.SpamScore = headerValue;
                                break;
                            default:
                                break;
                        }
                    }
                }
            }

            // Track all Received and X-Received headers.
            if (!string.IsNullOrEmpty(receivedText))
                receivedChain.Add(receivedText);
            ReceivedChain = receivedChain.ToArray();

            // Process the body if it's passed in.
            string body = "";

            // If common headers are unsent, we're parsing the body only.
            if (string.IsNullOrEmpty(fromText) && string.IsNullOrEmpty(Subject))
                body = messageText;
            else
            {
                HasHeaders = true;

                // Set the raw headers property if requested.
                if ((processingFlags & MailMessageProcessingFlags.IncludeRawHeaders) > 0)
                    RawHeaders = headers;

                if (cutoff > -1)
                {
                    body = messageText.Substring(cutoff + 4);

                    if (body.Length > 0)
                    {
                        // Ignore closing message indicators.
                        int closePos = body.Length;
                        if (body.EndsWith(")"))
                            closePos--;

                        // Ignore trailing linebreaks.
                        while (closePos > 1 && body.Substring(closePos - 2, 2) == "\r\n")
                            closePos -= 2;

                        if (closePos != body.Length)
                            body = body.Substring(0, closePos);
                    }
                }
            }

            if (!string.IsNullOrEmpty(body))
            {
                HasBody = true;

                // Only process MIME parts if a boundary name is passed in.
                if (!string.IsNullOrEmpty(MimeBoundaryName))
                {
                    // Parse body into MIME parts and process them.
                    MimeParts = MimePart.ExtractMIMEParts(ContentType, CharSet, ContentTransferEncoding, body, ProcessingFlags, 0);
                    if (MimeParts.Count > 0)
                        Task.Run(() => ProcessMimeParts()).Wait();
                    else
                    {
                        // We can no longer trust the external encoding, so infer if this body is Base-64 or Quoted-Printable encoded.
                        if (Functions.AppearsBase64(body))
                            ContentTransferEncoding = "base64";
                        else if (Functions.AppearsQuotedPrintable(body))
                            ContentTransferEncoding = "quoted-printable";

                        string encodedBody = body;
                        Body = Functions.Decode(body, ContentTransferEncoding, CharSet, BodyEncoding);
                        if (Body != encodedBody)
                            BodyDecoded = true;

                        if (Body.StartsWith("-----BEGIN PGP MESSAGE-----"))
                            pgpEncrypted = true;
                        else if (Body.StartsWith("-----BEGIN PGP SIGNED MESSAGE-----"))
                            pgpSigned = true;

                        BodyContentType = ContentType;
                        if (BodyContentType.IndexOf(";") > -1)
                            BodyContentType = BodyContentType.Substring(0, BodyContentType.IndexOf(";"));

                        // Infer if text/html when no content type is specified.
                        if (BodyContentType != "text/html" && Body.ToUpper().Contains("<BODY"))
                            BodyContentType = "text/html";
                    }

                    if (!((processingFlags & MailMessageProcessingFlags.IncludeMIMEParts) > 0))
                        MimeParts = null;
                }
                else
                {
                    // If no encoding is specified (such as when requesting partial headers only), infer if this body is Base-64 or Quoted-Printable encoded.
                    if (string.IsNullOrEmpty(ContentTransferEncoding))
                    {
                        if (Functions.AppearsBase64(body))
                            ContentTransferEncoding = "base64";
                        else if (Functions.AppearsQuotedPrintable(body))
                            ContentTransferEncoding = "quoted-printable";
                    }

                    string encodedBody = body;
                    Body = Functions.Decode(body, ContentTransferEncoding, CharSet, BodyEncoding);
                    if (Body != encodedBody)
                        BodyDecoded = true;

                    if (Body.StartsWith("-----BEGIN PGP MESSAGE-----"))
                        pgpEncrypted = true;
                    else if (Body.StartsWith("-----BEGIN PGP SIGNED MESSAGE-----"))
                        pgpSigned = true;

                    BodyContentType = ContentType;
                    if (BodyContentType.IndexOf(";") > -1)
                        BodyContentType = BodyContentType.Substring(0, BodyContentType.IndexOf(";"));

                    // Infer if text/html when no content type is specified.
                    if (BodyContentType != "text/html" && Body.ToUpper().Contains("<BODY"))
                        BodyContentType = "text/html";
                }

                // Set the raw body property if requested.
                if ((processingFlags & MailMessageProcessingFlags.IncludeRawBody) > 0)
                    RawBody = body;
            }

            // Parse String representations of addresses into MailAddress objects.
            if (fromText.Length > 0)
            {
                MailAddressCollection fromAddresses = MailAddressCollection.Parse(fromText);
                if (fromAddresses.Count > 0)
                    From = fromAddresses[0];
            }

            if (toText.Length > 0)
            {
                To = MailAddressCollection.Parse(toText);

                // Add the address to the AllRecipients collection.
                foreach (MailAddress toAddress in To)
                {
                    if (!AllRecipients.Contains(toAddress.Address))
                        AllRecipients.Add(toAddress.Address);
                }
            }

            if (ccText.Length > 0)
            {
                CC = MailAddressCollection.Parse(ccText);

                // Add the address to the AllRecipients collection.
                foreach (MailAddress ccAddress in CC)
                {
                    if (!AllRecipients.Contains(ccAddress.Address))
                        AllRecipients.Add(ccAddress.Address);
                }
            }

            if (bccText.Length > 0)
            {
                Bcc = MailAddressCollection.Parse(bccText);

                // Add the address to the AllRecipients collection.
                foreach (MailAddress bccAddress in Bcc)
                {
                    if (!AllRecipients.Contains(bccAddress.Address))
                        AllRecipients.Add(bccAddress.Address);
                }
            }

            if (replyToText.Length > 0)
            {
                ReplyToList = MailAddressCollection.Parse(replyToText);
            }
        }
示例#6
0
 /// <summary>
 /// Initializes a populated instance of the OpaqueMail.MailMessage class representing the message text passed in with attachments procesed according to the attachment filter flags.
 /// </summary>
 /// <param name="messageText">The raw contents of the email message.</param>
 /// <param name="processingFlags">Flags determining whether specialized properties are returned with a MailMessage.</param>
 public MailMessage(string messageText, MailMessageProcessingFlags processingFlags)
     : this(messageText, processingFlags, false)
 {
 }