Esempio n. 1
0
        /// <summary>
        /// Process a MIME entity
        /// 
        /// A MIME entity consists of header and body.
        /// Separator lines in the body might mark children MIME entities
        /// </summary>
        private MimeEntityReturnCode ProcessMimeEntity(RxMailMessage message, string parentBoundaryStart)
        {
            bool hasParentBoundary = parentBoundaryStart.Length > 0;
            string parentBoundaryEnd = parentBoundaryStart + "--";
            MimeEntityReturnCode boundaryMimeReturnCode;

            //some format fields are inherited from parent, only the default for
            //ContentType needs to be set here, otherwise the boundary parameter would be
            //inherited too !
            message.SetContentTypeFields("text/plain; charset=us-ascii");

            //get header
            //----------
            string completeHeaderField = null;     //consists of one start line and possibly several continuation lines
            string response;

            // read header lines until empty line is found (end of header)
            while (true)
            {
                if (!readMultiLine(out response))
                {
                    //POP3 server has not send any more lines
                    callGetEmailWarning("incomplete MIME entity header received");
                    //empty this message
                    while (readMultiLine(out response)) { }
                    System.Diagnostics.Debugger.Break(); //didn't have a sample email to test this
                    return MimeEntityReturnCode.problem;
                }

                if (response.Length < 1)
                {
                    //empty line found => end of header
                    if (completeHeaderField != null)
                    {
                        ProcessHeaderField(message, completeHeaderField);
                    }
                    else
                    {
                        //there was only an empty header.
                    }
                    break;
                }

                //check if there is a parent boundary in the header (wrong format!)
                if (hasParentBoundary && parentBoundaryFound(response, parentBoundaryStart, parentBoundaryEnd, out boundaryMimeReturnCode))
                {
                    callGetEmailWarning("MIME entity header  prematurely ended by parent boundary");
                    //empty this message
                    while (readMultiLine(out response)) { }
                    System.Diagnostics.Debugger.Break(); //didn't have a sample email to test this
                    return boundaryMimeReturnCode;
                }
                //read header field
                //one header field can extend over one start line and multiple continuation lines
                //a continuation line starts with at least 1 blank (' ') or tab
                if (response[0] == ' ' || response[0] == '\t')
                {
                    //continuation line found.
                    if (completeHeaderField == null)
                    {
                        callGetEmailWarning("Email header starts with continuation line");
                        //empty this message
                        while (readMultiLine(out response)) { }
                        System.Diagnostics.Debugger.Break(); //didn't have a sample email to test this
                        return MimeEntityReturnCode.problem;
                    }
                    else
                    {
                        // append space, if needed, and continuation line
                        if (completeHeaderField[completeHeaderField.Length - 1] != ' ')
                        {
                            //previous line did not end with a whitespace
                            //need to replace CRLF with a ' '
                            completeHeaderField += ' ' + response.TrimStart(WhiteSpaceChars);
                        }
                        else
                        {
                            //previous line did end with a whitespace
                            completeHeaderField += response.TrimStart(WhiteSpaceChars);
                        }
                    }

                }
                else
                {
                    //a new header field line found
                    if (completeHeaderField == null)
                    {
                        //very first field, just copy it and then check for continuation lines
                        completeHeaderField = response;
                    }
                    else
                    {
                        //new header line found
                        ProcessHeaderField(message, completeHeaderField);

                        //save the beginning of the next line
                        completeHeaderField = response;
                    }
                }
            }//end while read header lines

            //process body
            //------------

            MimeEntitySB.Length = 0;  //empty StringBuilder. For speed reasons, reuse StringBuilder defined as member of class
            string BoundaryDelimiterLineStart = null;
            bool isBoundaryDefined = false;
            if (message.ContentType.Boundary != null)
            {
                isBoundaryDefined = true;
                BoundaryDelimiterLineStart = "--" + message.ContentType.Boundary;
            }
            //prepare return code for the case there is no boundary in the body
            boundaryMimeReturnCode = MimeEntityReturnCode.bodyComplete;

            //read body lines
            while (readMultiLine(out response))
            {
                //check if there is a boundary line from this entity itself in the body
                if (isBoundaryDefined && response.TrimEnd() == BoundaryDelimiterLineStart)
                {
                    //boundary line found.
                    //stop the processing here and start a delimited body processing
                    return ProcessDelimitedBody(message, BoundaryDelimiterLineStart, parentBoundaryStart, parentBoundaryEnd);
                }

                //check if there is a parent boundary in the body
                if (hasParentBoundary &&
                  parentBoundaryFound(response, parentBoundaryStart, parentBoundaryEnd, out boundaryMimeReturnCode))
                {
                    //a parent boundary is found. Decode the content of the body received so far, then end this MIME entity
                    //note that boundaryMimeReturnCode is set here, but used in the return statement
                    break;
                }

                //process next line
                MimeEntitySB.Append(response + CRLF);
            }

            //a complete MIME body read
            //convert received US ASCII characters to .NET string (Unicode)
            string TransferEncodedMessage = MimeEntitySB.ToString();
            bool isAttachmentSaved = false;
            switch (message.ContentTransferEncoding)
            {
                case TransferEncoding.SevenBit:
                    //nothing to do
                    saveMessageBody(message, TransferEncodedMessage);
                    break;

                case TransferEncoding.Base64:
                    //convert base 64 -> byte[]
                    byte[] bodyBytes = System.Convert.FromBase64String(TransferEncodedMessage);
                    message.ContentStream = new MemoryStream(bodyBytes, false);

                    if (message.MediaMainType == "text")
                    {
                        //convert byte[] -> string
                        message.Body = DecodeByteArryToString(bodyBytes, message.BodyEncoding);

                    }
                    else if (message.MediaMainType == "image" || message.MediaMainType == "application")
                    {
                        SaveAttachment(message);
                        isAttachmentSaved = true;
                    }
                    break;

                case TransferEncoding.QuotedPrintable:
                    saveMessageBody(message, QuotedPrintable.Decode(TransferEncodedMessage));
                    break;

                default:
                    saveMessageBody(message, TransferEncodedMessage);
                    //no need to raise a warning here, the warning was done when analising the header
                    break;
            }

            if (message.ContentDisposition != null && message.ContentDisposition.DispositionType.ToLowerInvariant() == "attachment" && !isAttachmentSaved)
            {
                SaveAttachment(message);
                isAttachmentSaved = true;
            }
            return boundaryMimeReturnCode;
        }
Esempio n. 2
0
        /// <summary>
        /// Convert one MIME header field and update message accordingly
        /// </summary>
        private void ProcessHeaderField(RxMailMessage message, string headerField)
        {
            string headerLineType;
            string headerLineContent;
            int separatorPosition = headerField.IndexOf(':');
            if (separatorPosition < 1)
            {
                // header field type not found, skip this line
                callGetEmailWarning("character ':' missing in header format field: '{0}'", headerField);
            }
            else
            {

                //process header field type
                headerLineType = headerField.Substring(0, separatorPosition).ToLowerInvariant();
                headerLineContent = headerField.Substring(separatorPosition + 1).Trim(WhiteSpaceChars);
                if (headerLineType == "" || headerLineContent == "")
                {
                    //1 of the 2 parts missing, drop the line
                    return;
                }
                // add header line to headers
                message.Headers.Add(headerLineType, headerLineContent);

                //interpret if possible
                switch (headerLineType)
                {
                    case "bcc":
                        AddMailAddresses(headerLineContent, message.Bcc);
                        break;
                    case "cc":
                        AddMailAddresses(headerLineContent, message.CC);
                        break;
                    case "content-description":
                        message.ContentDescription = headerLineContent;
                        break;
                    case "content-disposition":
                        message.ContentDisposition = new ContentDisposition(headerLineContent);
                        break;
                    case "content-id":
                        message.ContentId = headerLineContent;
                        break;
                    case "content-transfer-encoding":
                        message.TransferType = headerLineContent;
                        message.ContentTransferEncoding = ConvertToTransferEncoding(headerLineContent);
                        break;
                    case "content-type":
                        message.SetContentTypeFields(headerLineContent);
                        break;
                    case "date":
                        message.DeliveryDate = ConvertToDateTime(headerLineContent);
                        break;
                    case "delivered-to":
                        message.DeliveredTo = ConvertToMailAddress(headerLineContent);
                        break;
                    case "from":
                        MailAddress address = ConvertToMailAddress(headerLineContent);
                        if (address != null)
                        {
                            message.From = address;
                        }
                        break;
                    case "message-id":
                        message.MessageId = headerLineContent;
                        break;
                    case "mime-version":
                        message.MimeVersion = headerLineContent;
                        //message.BodyEncoding = new Encoding();
                        break;
                    case "sender":
                        message.Sender = ConvertToMailAddress(headerLineContent);
                        break;
                    case "subject":
                        message.Subject = headerLineContent;
                        break;
                    case "received":
                        //throw mail routing information away
                        break;
                    case "reply-to":
                        message.ReplyToList.Add(ConvertToMailAddress(headerLineContent));
                        break;
                    case "return-path":
                        message.ReturnPath = ConvertToMailAddress(headerLineContent);
                        break;
                    case "to":
                        AddMailAddresses(headerLineContent, message.To);
                        break;
                    default:
                        message.UnknowHeaderlines.Add(headerField);
                        if (isCollectUnknowHeaderLines)
                        {
                            AllUnknowHeaderLines.Add(headerField);
                        }
                        break;
                }
            }
        }