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;
}
}
}