using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Text; using System.IO; using System.Net.Mime; using System.Net.Mail; namespace Ant.Service.Utilities { /// /// This class represents a Mime entity. /// 这个类表示一个MIME实体 /// public class MimeEntity { private StringBuilder _encodedMessage; /// /// Gets the encoded message. /// 获取编码的消息。 /// /// /// 编码的消息 /// The encoded message. /// public StringBuilder EncodedMessage { get { return _encodedMessage; } } private List _children; /// /// Gets the children. /// /// The children. public List Children { get { return _children; } } private ContentType _contentType; /// /// Gets the type of the content. /// /// The type of the content. public ContentType ContentType { get { return _contentType; } } private string _mediaSubType; /// /// Gets the type of the media sub. /// /// The type of the media sub. public string MediaSubType { get { return _mediaSubType; } } private string _mediaMainType; /// /// Gets the type of the media main. /// /// The type of the media main. public string MediaMainType { get { return _mediaMainType; } } private NameValueCollection _headers; /// /// Gets the headers. /// /// The headers. public NameValueCollection Headers { get { return _headers; } } private string _mimeVersion; /// /// Gets or sets the MIME version. /// /// The MIME version. public string MimeVersion { get { return _mimeVersion; } set { _mimeVersion = value; } } private string _contentId; /// /// Gets or sets the content id. /// /// The content id. public string ContentId { get { return _contentId; } set { _contentId = value; } } private string _contentDescription; /// /// Gets or sets the content description. /// /// The content description. public string ContentDescription { get { return _contentDescription; } set { _contentDescription = value; } } private ContentDisposition _contentDisposition; /// /// Gets or sets the content disposition. /// /// The content disposition. public ContentDisposition ContentDisposition { get { return _contentDisposition; } set { _contentDisposition = value; } } private string _transferEncoding; /// /// Gets or sets the transfer encoding. /// /// The transfer encoding. public string TransferEncoding { get { return _transferEncoding; } set { _transferEncoding = value; } } private TransferEncoding _contentTransferEncoding; /// /// Gets or sets the content transfer encoding. /// /// The content transfer encoding. public TransferEncoding ContentTransferEncoding { get { return _contentTransferEncoding; } set { _contentTransferEncoding = value; } } /// /// Gets a value indicating whether this instance has boundary. /// /// /// true if this instance has boundary; otherwise, false. /// internal bool HasBoundary { get { return (!string.IsNullOrEmpty(_contentType.Boundary)) || (!string.IsNullOrEmpty(_startBoundary)); } } private string _startBoundary; /// /// Gets the start boundary. /// /// The start boundary. public string StartBoundary { get { if (string.IsNullOrEmpty(_startBoundary) || !string.IsNullOrEmpty(_contentType.Boundary)) { return string.Concat("--", _contentType.Boundary); } return _startBoundary; } } /// /// Gets the end boundary. /// /// The end boundary. public string EndBoundary { get { return string.Concat(StartBoundary, "--"); } } private MimeEntity _parent; /// /// Gets or sets the parent. /// /// The parent. public MimeEntity Parent { get { return _parent; } set { _parent = value; } } private MemoryStream _content; /// /// Gets or sets the content. /// /// The content. public MemoryStream Content { get { return _content; } internal set { _content = value; } } /// /// Initializes a new instance of the class. /// public MimeEntity() { _children = new List(); _headers = new NameValueCollection(); _contentType = MimeReader.GetContentType(string.Empty); _parent = null; _encodedMessage = new StringBuilder(); } /// /// Initializes a new instance of the class. /// /// The parent. public MimeEntity(MimeEntity parent) : this() { if (parent == null) { throw new ArgumentNullException("parent"); } _parent = parent; _startBoundary = parent.StartBoundary; } /// /// Sets the type of the content. /// /// Type of the content. internal void SetContentType(ContentType contentType) { _contentType = contentType; _contentType.MediaType = MimeReader.GetMediaType(contentType.MediaType); _mediaMainType = MimeReader.GetMediaMainType(contentType.MediaType); _mediaSubType = MimeReader.GetMediaSubType(contentType.MediaType); } /// /// Toes the mail message ex. /// /// public MailMessageEx ToMailMessageEx() { return ToMailMessageEx(this); } /// /// Toes the mail message ex. /// /// The entity. /// private MailMessageEx ToMailMessageEx(MimeEntity entity) { if (entity == null) { //throw new ArgumentNullException("entity"); return null; } //parse standard headers and create base email. MailMessageEx message = MailMessageEx.CreateMailMessageFromEntity(entity); if (!string.IsNullOrEmpty(entity.ContentType.Boundary)) { message = MailMessageEx.CreateMailMessageFromEntity(entity); BuildMultiPartMessage(entity, message); }//parse multipart message into sub parts. else if (string.Equals(entity.ContentType.MediaType, MediaTypes.MessageRfc822, StringComparison.InvariantCultureIgnoreCase)) { //use the first child to create the multipart message. if (entity.Children.Count < 0) { throw new Pop3Exception("Invalid child count on message/rfc822 entity."); } //create the mail message from the first child because it will //contain all of the mail headers. The entity in this state //only contains simple content type headers indicating, disposition, type and description. //This means we can't create the mail message from this type as there is no //internet mail headers attached to this entity. message = MailMessageEx.CreateMailMessageFromEntity(entity.Children[0]); BuildMultiPartMessage(entity, message); } //parse nested message. else { message = MailMessageEx.CreateMailMessageFromEntity(entity); BuildSinglePartMessage(entity, message); } //Create single part message. return message; } /// /// Builds the single part message. /// /// The entity. /// The message. private void BuildSinglePartMessage(MimeEntity entity, MailMessageEx message) { SetMessageBody(message, entity); } /// /// Gets the body encoding. /// /// Type of the content. public Encoding GetEncoding() { if (string.IsNullOrEmpty(this.ContentType.CharSet)) { return Encoding.ASCII; } else { try { return Encoding.GetEncoding(this.ContentType.CharSet); } catch (ArgumentException) { return Encoding.ASCII; } } } /// /// Builds the multi part message. /// /// The entity. /// The message. private void BuildMultiPartMessage(MimeEntity entity, MailMessageEx message) { foreach (MimeEntity child in entity.Children) { if (child == null) { continue; } if (string.Equals(child.ContentType.MediaType, MediaTypes.MultipartAlternative, StringComparison.InvariantCultureIgnoreCase) || string.Equals(child.ContentType.MediaType, MediaTypes.MultipartMixed, StringComparison.InvariantCultureIgnoreCase)) { BuildMultiPartMessage(child, message); } //if the message is mulitpart/alternative or multipart/mixed then the entity will have children needing parsed. else if (!IsAttachment(child) && (string.Equals(child.ContentType.MediaType, MediaTypes.TextPlain) || string.Equals(child.ContentType.MediaType, MediaTypes.TextHtml))) { message.AlternateViews.Add(CreateAlternateView(child)); SetMessageBody(message, child); } //add the alternative views. else if (string.Equals(child.ContentType.MediaType, MediaTypes.MessageRfc822, StringComparison.InvariantCultureIgnoreCase) && string.Equals(child.ContentDisposition.DispositionType, DispositionTypeNames.Attachment, StringComparison.InvariantCultureIgnoreCase)) { message.Children.Add(ToMailMessageEx(child)); } //create a child message and else if (IsAttachment(child)) { message.Attachments.Add(CreateAttachment(child)); } } } private static bool IsAttachment(MimeEntity child) { return (child.ContentDisposition != null) && (string.Equals(child.ContentDisposition.DispositionType, DispositionTypeNames.Attachment, StringComparison.InvariantCultureIgnoreCase)); } /// /// Sets the message body. /// /// The message. /// The child. private void SetMessageBody(MailMessageEx message, MimeEntity child) { Encoding encoding = child.GetEncoding(); message.Body = DecodeBytes(child.Content.ToArray(), encoding); message.BodyEncoding = encoding; message.IsBodyHtml = string.Equals(MediaTypes.TextHtml, child.ContentType.MediaType, StringComparison.InvariantCultureIgnoreCase); } /// /// Decodes the bytes. /// /// The buffer. /// The encoding. /// private string DecodeBytes(byte[] buffer, Encoding encoding) { if (buffer == null) { return null; } if (encoding == null) { encoding = Encoding.UTF7; } //email defaults to 7bit. return encoding.GetString(buffer); } /// /// Creates the alternate view. /// /// The view. /// private AlternateView CreateAlternateView(MimeEntity view) { AlternateView alternateView = new AlternateView(view.Content, view.ContentType); alternateView.TransferEncoding = view.ContentTransferEncoding; alternateView.ContentId = TrimBrackets(view.ContentId); return alternateView; } /// /// Trims the brackets. /// /// The value. /// public static string TrimBrackets(string value) { if (value == null) { return value; } if (value.StartsWith("<") && value.EndsWith(">")) { return value.Trim('<', '>'); } return value; } /// /// Creates the attachment. /// /// The entity. /// private Attachment CreateAttachment(MimeEntity entity) { Attachment attachment = new Attachment(entity.Content, entity.ContentType); if (entity.ContentDisposition != null) { attachment.ContentDisposition.Parameters.Clear(); foreach (string key in entity.ContentDisposition.Parameters.Keys) { attachment.ContentDisposition.Parameters.Add(key, entity.ContentDisposition.Parameters[key]); } attachment.ContentDisposition.CreationDate = entity.ContentDisposition.CreationDate; attachment.ContentDisposition.DispositionType = entity.ContentDisposition.DispositionType; attachment.ContentDisposition.FileName = entity.ContentDisposition.FileName; attachment.ContentDisposition.Inline = entity.ContentDisposition.Inline; attachment.ContentDisposition.ModificationDate = entity.ContentDisposition.ModificationDate; attachment.ContentDisposition.ReadDate = entity.ContentDisposition.ReadDate; attachment.ContentDisposition.Size = entity.ContentDisposition.Size; } if (!string.IsNullOrEmpty(entity.ContentId)) { attachment.ContentId = TrimBrackets(entity.ContentId); } attachment.TransferEncoding = entity.ContentTransferEncoding; return attachment; } } }