/// <summary> /// Decode the recieved content /// </summary> /// <returns></returns> public static string DecodeString(MimeEntity entity) { switch (entity.ContentTransferEncoding) { case TransferEncoding.Base64: return DecodeBase64(entity.ContentLines, entity.ContentType.CharSet); case TransferEncoding.QuotedPrintable: return DecodeQuotedPrintable(entity); case TransferEncoding.SevenBit: default: { StringBuilder sb = new StringBuilder(); foreach (byte[] line in entity.ContentLines) { if (sb.Length > 0) { sb.AppendLine(); } sb.Append(DecodeBytesWithSpecificCharset(line, entity.ContentType.CharSet)); } return sb.ToString(); } } }
/// <summary> /// Initializes a new instance of the <see cref="MimeEntity"/> class. /// </summary> /// <param name="parent">The parent.</param> public MimeEntity(MimeEntity parent) : this() { if (parent == null) { throw new ArgumentNullException("parent"); } Parent = parent; _startBoundary = parent.StartBoundary; }
/// <summary> /// Sets the decoded content stream by decoding the EncodedMessage /// and writing it to the entity content stream. /// </summary> public static byte[] DecodeBytes(MimeEntity entity) { switch (entity.ContentTransferEncoding) { case TransferEncoding.Base64: byte[] decodedBytes = CustomBinarySixtyFourDecoder.FromBinaryString(Encoding.ASCII.GetString(entity.ContentBytes)); return decodedBytes; case TransferEncoding.QuotedPrintable: return QuotedPrintableEncoding.Decode(entity.ContentLines); case TransferEncoding.SevenBit: default: return entity.ContentBytes; } }
/// <summary> /// Initializes a new instance of the <see cref="MimeReader"/> class. /// </summary> /// <param name="entity">The entity.</param> /// <param name="lines">The lines.</param> private MimeReader(MimeEntity entity, Queue<byte[]> lines, bool throwOnInvalidContentType) : this(throwOnInvalidContentType) { if (entity == null) { throw new ArgumentNullException("entity"); } if (lines == null) { throw new ArgumentNullException("lines"); } _lines = lines; _entity = new MimeEntity(entity); }
/// <summary> /// Initializes a new instance of the <see cref="MimeReader"/> class. /// </summary> /// <param name="entity">The entity.</param> /// <param name="lines">The lines.</param> private MimeReader(MimeEntity entity, Queue<string> lines) : this() { if (entity == null) { throw new ArgumentNullException("entity"); } if (lines == null) { throw new ArgumentNullException("lines"); } _lines = lines; _entity = new MimeEntity(entity); }
/// <summary> /// Creates the alternate view. /// </summary> /// <param name="view">The view.</param> /// <returns></returns> private AlternateView CreateAlternateView(MimeEntity view) { MemoryStream stream = new MemoryStream(ContentDecoder.DecodeBytes(view), false); AlternateView alternateView = new AlternateView(stream, view.ContentType); alternateView.TransferEncoding = view.ContentTransferEncoding; alternateView.ContentId = TrimBrackets(view.ContentId); return alternateView; }
/// <summary> /// Creates the attachment. /// </summary> /// <param name="entity">The entity.</param> /// <returns></returns> 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; }
/// <summary> /// Creates the alternate view. /// </summary> /// <param name="view">The view.</param> /// <returns></returns> private AlternateView CreateAlternateView(MimeEntity view) { AlternateView alternateView = new AlternateView(view.Content, view.ContentType); alternateView.TransferEncoding = view.ContentTransferEncoding; alternateView.ContentId = TrimBrackets(view.ContentId); return alternateView; }
/// <summary> /// Builds the single part message. /// </summary> /// <param name="entity">The entity.</param> /// <param name="message">The message.</param> private void BuildSinglePartMessage(MimeEntity entity, SidePOPMailMessage message) { SetMessageBody(message, entity); }
/// <summary> /// Builds the multi part message. /// </summary> /// <param name="entity">The entity.</param> /// <param name="message">The message.</param> private void BuildMultiPartMessage(MimeEntity entity, SidePOPMailMessage message) { foreach (MimeEntity child in entity.Children) { 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)); }
/// <summary> /// Decode the content into the given charset /// </summary> private static string DecodeQuotedPrintable(MimeEntity entity) { byte[] decodedBytes = QuotedPrintableEncoding.Decode(entity.ContentLines); if (entity.ContentType.CharSet != null) { return DecodeBytesWithSpecificCharset(decodedBytes, entity.ContentType.CharSet); } else { // by default, a text/plain ContentType without Specific Charset must default to ISO-8859-1. // Other text/* ContentType without Specific charset must default to utf-8 // See http://tools.ietf.org/html/rfc6657#page-3 string encodingName = "utf-8"; if (entity.ContentType != null && string.Compare(entity.ContentType.MediaType, "text/plain", true) == 0) { encodingName = "ISO-8859-1"; } string decodedBytesString = Encoding.GetEncoding(encodingName).GetString(decodedBytes); return decodedBytesString; } }
/// <summary> /// Initializes a new instance of the <see cref="MimeReader"/> class. /// </summary> private MimeReader(bool throwOnInvalidContentType) { _entity = new MimeEntity(); _throwOnInvalidContentType = throwOnInvalidContentType; }
/// <summary> /// Adds the child entity. /// </summary> /// <param name="entity">The entity.</param> private void AddChildEntity(MimeEntity entity, Queue<byte[]> lines) { /*if (entity == null) { return; } if (lines == null) { return; }*/ MimeReader reader = new MimeReader(entity, lines, _throwOnInvalidContentType); MimeEntity childEntity = reader.CreateMimeEntity(); entity.Children.Add(childEntity); byte[] innerBytes = childEntity.RawContent.ToArray(); entity.RawContent.Write(innerBytes, 0, innerBytes.Length); }
/// <summary> /// Sets the message body. /// </summary> /// <param name="message">The message.</param> /// <param name="child">The child.</param> private void SetMessageBody(SidePOPMailMessage 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); }
/// <summary> /// .Ctor() /// </summary> public MimeParserException(byte[] rawBytes, string message, MimeEntity partialMimeEntity, Exception innerException) : base(message, innerException) { PartialMimeEntity = partialMimeEntity; RawBytes = rawBytes; }
/// <summary> /// Initializes a new instance of the <see cref="MimeReader"/> class. /// </summary> private MimeReader() { _entity = new MimeEntity(); }
/// <summary> /// Adds the child entity. /// </summary> /// <param name="entity">The entity.</param> private void AddChildEntity(MimeEntity entity, Queue<string> lines) { /*if (entity == null) { return; } if (lines == null) { return; }*/ MimeReader reader = new MimeReader(entity, lines); entity.Children.Add(reader.CreateMimeEntity()); }
/// <summary> /// Toes the mail message ex. /// </summary> /// <param name="entity">The entity.</param> /// <returns></returns> private SidePOPMailMessage ToMailMessageEx(MimeEntity entity) { if (entity == null) { throw new ArgumentNullException("entity"); } //parse standard headers and create base email. SidePOPMailMessage message = SidePOPMailMessage.CreateMailMessageFromEntity(entity); if (!string.IsNullOrEmpty(entity.ContentType.Boundary)) { message = SidePOPMailMessage.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 = SidePOPMailMessage.CreateMailMessageFromEntity(entity.Children[0]); BuildMultiPartMessage(entity, message); } //parse nested message. else { message = SidePOPMailMessage.CreateMailMessageFromEntity(entity); BuildSinglePartMessage(entity, message); } //Create single part message. return message; }
/// <summary> /// Creates the attachment. /// </summary> /// <param name="entity">The entity.</param> /// <returns></returns> private Attachment CreateAttachment(MimeEntity entity) { MemoryStream memoryStream = new MemoryStream(ContentDecoder.DecodeBytes(entity), false); Attachment attachment = new Attachment(memoryStream, entity.ContentType); if (entity.ContentDisposition != null) { attachment.ContentDisposition.Parameters.Clear(); foreach (string key in entity.ContentDisposition.Parameters.Keys) { //Skip values that will be strongly typed copied after this loop: it happens that date string //will not be parsed correctly when going through Parameters.Add but are already parsed into entity.ContentDisposition if (key == "creation-date" || key == "modification-date" || key == "read-date" || key == "size" || key == "filename") { continue; } 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; }
/// <summary> /// Decodes the specified part if needed. /// </summary> private Queue<byte[]> DecodePartIfNeeded(MimeEntity parentEntity, Queue<byte[]> lines) { if (parentEntity.ContentTransferEncoding == TransferEncoding.Base64) { try { byte[] encodedBytes = lines.SelectMany(childEntityLine => childEntityLine).ToArray(); string encodedString = ConvertBytesToStringWithDefaultEncoding(encodedBytes); byte[] decodedBytes = Convert.FromBase64String(encodedString); return new Queue<byte[]>(SplitByteArrayWithCrLf(decodedBytes)); } catch { //It happens that invalid transfer encoding is specified, just consider that this part is not encoded with Base64 return lines; } } return lines; }
/// <summary> /// Creates the mail message from entity. /// </summary> /// <param name="entity">The entity.</param> /// <returns></returns> public static SidePOPMailMessage CreateMailMessageFromEntity(MimeEntity entity) { SidePOPMailMessage message = new SidePOPMailMessage(); string value; foreach (string key in entity.Headers.AllKeys) { value = entity.Headers[key]; if (value.Equals(string.Empty)) { value = " "; } message.Headers.Add(key.ToLowerInvariant(), value); switch (key.ToLowerInvariant()) { case MailHeaders.Bcc: PopulateAddressList(value, message.Bcc); break; case MailHeaders.Cc: PopulateAddressList(value, message.CC); break; case MailHeaders.From: message.From = CreateMailAddress(value); break; case MailHeaders.ReplyTo: message.ReplyToList.Add(CreateMailAddress(value)); break; case MailHeaders.Subject: message.Subject = value; break; case MailHeaders.To: PopulateAddressList(value, message.To); break; } } return message; }