/// <summary> /// Add alternate views and attachments to the existing message object. /// Each component is separated boundary text defined in the contentType.Boundary variable. /// Each component within a boundary has its own headers defining the payload. /// Media types text/plain and text/html go into AlternateView objects. /// All other media types will be treated as attachments. /// </summary> /// <param name="message"></param> /// <param name="reader"></param> /// <param name="contentType"></param> private static void LoadBodyMultipart(ReceivedMessage message, TextReader reader, ContentType contentType) { /* http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html */ //each component of the multipart message will be separated by a boundary. //each section of text within a boundary should have headers to describe it or in the absence of headers //assumed to be ASCII text/plain. //a valid message component is preceded and followed by a the boundary text //which is two hyphens followed by the value Boundary property of the main ContentType object. //UNDONE: Unsupported for now: In the case of multipart/related, the attachments should be done as LinkedResources of the text/html AlternativeView. //This is a nice to have but not critical. Last time I checked, Gmail didn't even support these kinds //of internal references, either. /******************************************************************************************* * the text following the message headers and preceding the first boundary is to be ignored. * any text at after the final boundary is also to be ignored. * /*******************************************************************************************/ foreach (var part in LoadMultipart(reader, contentType)) { if (part is AlternateView) { message.AlternateViews.Add(part as AlternateView); } else if (part is Attachment) { message.Attachments.Add(part as Attachment); } } //scroll past epilogue string buff; while (null != (buff = reader.ReadLine()) && (LineType.EOF != GetLineType(buff, contentType))) { ; } }
/// <summary> /// Parse a TextReader. The calling code is responsible for closing the reader and the stream. /// </summary> /// <param name="reader"></param> /// <returns>Returns a ReceivedMessage object loaded from the TextReader. Return null if the TextReader is null.</returns> public ReceivedMessage Parse( TextReader reader ) { if( reader == null ) return null; ReceivedMessage message = new ReceivedMessage(); if( !LoadHeaders( reader, message.ReceivedHeaders, message.RawReceivedHeaders ) ) throw new InvalidDataException( "MIME Stream must contain headers." ); LoadMailAddresses( message ); Debug.WriteLine( message.ReceivedHeaders["subject"] ); message.Subject = message.ReceivedHeaders["subject"]; //Load message body and attachments /* * http://tools.ietf.org/html/rfc2046 * http://en.wikipedia.org/wiki/MIME#Content-Type * * fully supported simple messages: * text/plain, text/html (text/html is not compliant, should be multipart/alternative) * text/(unrecognized subtype) treated as application/octet-stream * * supported multipart messages: * multipart/mixed, multipart/alternative * multipart/digest * * partially supported * multipart/related => treat as multipart/mixed. related parts are not linked. * multipart/signed -> treat as multipart/mixed * multipart/encrypted -> treat as multipart/mixed * mutipart/form-data -> treat as multipart/mixed * multipart/report -> treat as multipart/mixed * multipart/appledouble (RFC 1740) -> resource fork is discarded and the data fork becomes an attachment. */ //Load text/plain and text/html as a simple body text. Otherwise, treat the message as multipart. //Anything that is text/plain or text/html should become an alternative view. Everything else becomes an attachment. ContentType contentType = GetContentType( message.ReceivedHeaders ); if( 0 == string.Compare( contentType.MediaType, MediaTypeNames.Text.Plain, true ) || 0 == string.Compare( contentType.MediaType, MediaTypeNames.Text.Html, true ) ) { LoadBodyText( message, reader, contentType ); } else { LoadBodyMultipart( message, reader, contentType ); } return message; }
/// <summary> /// Loads From, To, CC and Bcc properties using the values stored in the headers. /// </summary> /// <param name="message"></param> private static void LoadMailAddresses( ReceivedMessage message ) { try { if( !string.IsNullOrEmpty( message.ReceivedHeaders["from"] ) ) message.From = new MailAddress( message.ReceivedHeaders["from"] ); if( !string.IsNullOrEmpty( message.ReceivedHeaders["reply-to"] ) ) #if NET_FX4 message.ReplyToList.Add(new MailAddress(message.ReceivedHeaders["reply-to"])); #else message.ReplyTo = new MailAddress( message.ReceivedHeaders["reply-to"] ); #endif } catch( Exception ex ) { //swallow exception thrown by bad data. if( !(ex is FormatException) ) throw ex; } LoadMailAddresses( message.To, message.ReceivedHeaders["to"] ); LoadMailAddresses( message.CC, message.ReceivedHeaders["cc"] ); LoadMailAddresses( message.Bcc, message.ReceivedHeaders["bcc"] ); }
private static void LoadBodyText( ReceivedMessage message, TextReader reader, ContentType contentType ) { LineType lastLineType; //not used, because the message is not multipart message.Body = LoadBodyString( reader, message.ReceivedHeaders, contentType, out lastLineType ); message.IsBodyHtml = (contentType.MediaType == MediaTypeNames.Text.Html); }
/// <summary> /// Add alternate views and attachments to the existing message object. /// Each component is separated boundary text defined in the contentType.Boundary variable. /// Each component within a boundary has its own headers defining the payload. /// Media types text/plain and text/html go into AlternateView objects. /// All other media types will be treated as attachments. /// </summary> /// <param name="message"></param> /// <param name="reader"></param> /// <param name="contentType"></param> private static void LoadBodyMultipart( ReceivedMessage message, TextReader reader, ContentType contentType ) { /* http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html */ //each component of the multipart message will be separated by a boundary. //each section of text within a boundary should have headers to describe it or in the absence of headers //assumed to be ASCII text/plain. //a valid message component is preceded and followed by a the boundary text //which is two hyphens followed by the value Boundary property of the main ContentType object. //UNDONE: Unsupported for now: In the case of multipart/related, the attachments should be done as LinkedResources of the text/html AlternativeView. //This is a nice to have but not critical. Last time I checked, Gmail didn't even support these kinds //of internal references, either. /******************************************************************************************* * the text following the message headers and preceding the first boundary is to be ignored. * any text at after the final boundary is also to be ignored. /*******************************************************************************************/ foreach( var part in LoadMultipart( reader, contentType ) ) { if( part is AlternateView ) message.AlternateViews.Add( part as AlternateView ); else if( part is Attachment ) message.Attachments.Add( part as Attachment ); } //scroll past epilogue string buff; while( null != (buff = reader.ReadLine()) && (LineType.EOF != GetLineType( buff, contentType )) ); }