/// <summary> /// Add all attachments and alternative views from child to the parent /// </summary> /// <param name="child">child Object</param> /// <param name="parent">parent Object</param> private static void AddChildPartsToParent(MailMessageParser child, MailMessageParser parent) { ////add the child itself to the parent parent.Entities.Add(child); ////add the alternative views of the child to the parent if (null != child.AlternateViews) { foreach (AlternateView childView in child.AlternateViews) { parent.AlternateViews.Add(childView); } } ////add the body of the child as alternative view to parent ////this should be the last view attached here, because the POP 3 MIME client ////is supposed to display the last alternative view if (child.MediaMainType == ServiceConstants.TEXT_MEDIA_MAIN_TYPE && null != child.ContentStream && null != child.Parent.ContentType && child.Parent.ContentType.MediaType.ToUpperInvariant() == ServiceConstants.MULTI_PART_MEDIA_TYPE) { AlternateView thisAlternateView = new AlternateView(child.ContentStream); thisAlternateView.ContentId = RemoveBrackets(child.ContentId); thisAlternateView.ContentType = child.ContentType; thisAlternateView.TransferEncoding = child.ContentTransferEncoding; parent.AlternateViews.Add(thisAlternateView); } ////add the attachments of the child to the parent if (null != child.Attachments) { foreach (Attachment childAttachment in child.Attachments) { parent.Attachments.Add(childAttachment); } } }
/// <summary> /// Copies the content found for the MIME entity to the MailMessageParser body and creates /// a stream which can be used to create attachments and alternative views. /// </summary> /// <param name="message">Parsed mail message</param> /// <param name="contentString">mail content string</param> private static void SaveMessageBody(MailMessageParser message, string contentString) { message.Body = contentString; System.Text.Encoding ascii = System.Text.Encoding.ASCII; MemoryStream bodyStream = new MemoryStream(ascii.GetBytes(contentString), 0, contentString.Length); message.ContentStream = bodyStream; }
/// <summary> /// Creates an empty child MIME entity from the parent MIME entity. /// An email can consist of several MIME entities. A entity has the same structure of email. /// </summary> /// <returns>Mail message child object</returns> public MailMessageParser CreateChildEntity() { MailMessageParser child = new MailMessageParser(); child.Parent = this; child.TopParent = this.TopParent; child.ContentTransferEncoding = this.ContentTransferEncoding; return(child); }
/// <summary> /// Gets an email from the supplied Email Stream and processes it. /// </summary> /// <param name="mailPath">string that designates the path to a .EML file</param> /// <returns>MailMessageParser or null if email not properly formatted</returns> public MailMessageParser GetEmail(string mailPath) { MailMessageParser messageParser = null; using (Stream emailStream = File.Open(mailPath, FileMode.Open)) { messageParser = this.GetEmail(emailStream); } return(messageParser); }
/// <summary> /// Creates Mail message object from file. /// </summary> /// <param name="mimeDecoder">read MIME object</param> /// <param name="mailPath">EML file path.</param> /// <returns>Mime decoded email object</returns> public static MailMessageParser CreateFromFile(MailMimeReader mimeDecoder, string mailPath) { MailMessageParser result = null; if (null != mimeDecoder) { result = mimeDecoder.GetEmail(mailPath); } else { result = null; } return(result); }
/// <summary> /// Creates Mail message object from stream. /// </summary> /// <param name="mimeDecoder">read MIME object</param> /// <param name="emailStream">The email stream.</param> /// <returns>Mime decoded email object</returns> public static MailMessageParser CreateFromStream(MailMimeReader mimeDecoder, Stream emailStream) { MailMessageParser result = null; if (mimeDecoder != null) { result = mimeDecoder.GetEmail(emailStream); } else { result = null; } return(result); }
/// <summary> /// Processes the delimited body of Mail Mime Entity. /// </summary> /// <param name="message">Parsed Mail Message.</param> /// <param name="boundaryStart">boundary start identifier</param> /// <param name="parentBoundaryStart">parent boundary start identifier</param> /// <param name="parentBoundaryEnd">parent boundary end identifier</param> /// <returns>MimeEntity process result code</returns> private MimeEntityReturnCode ProcessDelimitedBody(MailMessageParser message, string boundaryStart, string parentBoundaryStart, string parentBoundaryEnd) { string response; if (boundaryStart.Trim() == parentBoundaryStart.Trim()) { while (this.ReadMultipleLine(out response)) { continue; } return(MimeEntityReturnCode.problem); } MimeEntityReturnCode returnCode; do { this.mimeEntitySB.Length = 0; MailMessageParser childPart = message.CreateChildEntity(); ////recursively call MIME part processing returnCode = this.ProcessMimeEntity(childPart, boundaryStart); if (returnCode == MimeEntityReturnCode.problem) { return(MimeEntityReturnCode.problem); } ////add the newly found child MIME part to the parent AddChildPartsToParent(childPart, message); }while (returnCode != MimeEntityReturnCode.parentBoundaryEndFound); MimeEntityReturnCode boundaryMimeReturnCode; bool hasParentBoundary = parentBoundaryStart.Length > 0; while (this.ReadMultipleLine(out response)) { if (hasParentBoundary && ParentBoundaryFound(response, parentBoundaryStart, parentBoundaryEnd, out boundaryMimeReturnCode)) { return(boundaryMimeReturnCode); } } return(MimeEntityReturnCode.bodyComplete); }
/// <summary> /// each attachment is stored in its own MIME entity and read into this entity's /// ContentStream. SaveAttachment creates an attachment out of the ContentStream /// and attaches it to the parent MIME entity. /// </summary> /// <param name="message">Parsed mail message</param> private static void SaveAttachment(MailMessageParser message) { if (null != message.Parent) { Attachment thisAttachment = new Attachment(message.ContentStream, message.ContentType); if (null != message.ContentDisposition) { ContentDisposition messageContentDisposition = message.ContentDisposition; ContentDisposition attachmentContentDisposition = thisAttachment.ContentDisposition; if (messageContentDisposition.CreationDate > DateTime.MinValue) { attachmentContentDisposition.CreationDate = messageContentDisposition.CreationDate; } attachmentContentDisposition.DispositionType = messageContentDisposition.DispositionType; attachmentContentDisposition.FileName = messageContentDisposition.FileName; attachmentContentDisposition.Inline = messageContentDisposition.Inline; if (messageContentDisposition.ModificationDate > DateTime.MinValue) { attachmentContentDisposition.ModificationDate = messageContentDisposition.ModificationDate; } if (messageContentDisposition.ReadDate > DateTime.MinValue) { attachmentContentDisposition.ReadDate = messageContentDisposition.ReadDate; } if (0 < messageContentDisposition.Size) { attachmentContentDisposition.Size = messageContentDisposition.Size; } } string contentIdString = message.ContentId; if (null != contentIdString) { thisAttachment.ContentId = RemoveBrackets(contentIdString); } thisAttachment.TransferEncoding = message.ContentTransferEncoding; message.Parent.Attachments.Add(thisAttachment); } }
/// <summary> /// Gets an email from the supplied Email Stream and processes it. /// </summary> /// <param name="emailStream">The email stream object</param> /// <returns> /// MailMessageParser or null if email not properly formatted /// </returns> public MailMessageParser GetEmail(Stream emailStream) { this.EmailStreamReader = new StreamReader(emailStream, Encoding.ASCII); ////prepare message, set defaults as specified in RFC 2046 MailMessageParser message = new MailMessageParser(); message.ContentTransferEncoding = TransferEncoding.SevenBit; MailMessageParser result = null; MimeEntityReturnCode messageMimeReturnCode = this.ProcessMimeEntity(message, string.Empty); if (messageMimeReturnCode == MimeEntityReturnCode.bodyComplete || messageMimeReturnCode == MimeEntityReturnCode.parentBoundaryEndFound) { if (0 == message.To.Count) { string toField = message.Headers[ServiceConstants.Mail_Message_Receiver_Header]; if (!string.IsNullOrEmpty(toField)) { message.To.Add(toField); } } if (null == message.From) { string mailFrom = message.Headers[ServiceConstants.Mail_Message_Sender_Header]; if (!string.IsNullOrEmpty(mailFrom)) { message.From = new MailAddress(mailFrom); } } result = message; } return(result); }
/// <summary> /// Processes the delimited body of Mail Mime Entity. /// </summary> /// <param name="message">Parsed Mail Message.</param> /// <param name="boundaryStart">boundary start identifier</param> /// <param name="parentBoundaryStart">parent boundary start identifier</param> /// <param name="parentBoundaryEnd">parent boundary end identifier</param> /// <returns>MimeEntity process result code</returns> private MimeEntityReturnCode ProcessDelimitedBody(MailMessageParser message, string boundaryStart, string parentBoundaryStart, string parentBoundaryEnd) { string response; if (boundaryStart.Trim() == parentBoundaryStart.Trim()) { while (this.ReadMultipleLine(out response)) { continue; } return MimeEntityReturnCode.problem; } MimeEntityReturnCode returnCode; do { this.mimeEntitySB.Length = 0; MailMessageParser childPart = message.CreateChildEntity(); ////recursively call MIME part processing returnCode = this.ProcessMimeEntity(childPart, boundaryStart); if (returnCode == MimeEntityReturnCode.problem) { return MimeEntityReturnCode.problem; } ////add the newly found child MIME part to the parent AddChildPartsToParent(childPart, message); } while (returnCode != MimeEntityReturnCode.parentBoundaryEndFound); MimeEntityReturnCode boundaryMimeReturnCode; bool hasParentBoundary = parentBoundaryStart.Length > 0; while (this.ReadMultipleLine(out response)) { if (hasParentBoundary && ParentBoundaryFound(response, parentBoundaryStart, parentBoundaryEnd, out boundaryMimeReturnCode)) { return boundaryMimeReturnCode; } } return MimeEntityReturnCode.bodyComplete; }
/// <summary> /// Add all attachments and alternative views from child to the parent /// </summary> /// <param name="child">child Object</param> /// <param name="parent">parent Object</param> private static void AddChildPartsToParent(MailMessageParser child, MailMessageParser parent) { ////add the child itself to the parent parent.Entities.Add(child); ////add the alternative views of the child to the parent if (null != child.AlternateViews) { foreach (AlternateView childView in child.AlternateViews) { parent.AlternateViews.Add(childView); } } ////add the body of the child as alternative view to parent ////this should be the last view attached here, because the POP 3 MIME client ////is supposed to display the last alternative view if (child.MediaMainType == ServiceConstants.TEXT_MEDIA_MAIN_TYPE && null != child.ContentStream && null != child.Parent.ContentType && child.Parent.ContentType.MediaType.ToUpperInvariant() == ServiceConstants.MULTI_PART_MEDIA_TYPE) { AlternateView thisAlternateView = new AlternateView(child.ContentStream); thisAlternateView.ContentId = RemoveBrackets(child.ContentId); thisAlternateView.ContentType = child.ContentType; thisAlternateView.TransferEncoding = child.ContentTransferEncoding; parent.AlternateViews.Add(thisAlternateView); } ////add the attachments of the child to the parent if (null != child.Attachments) { foreach (Attachment childAttachment in child.Attachments) { parent.Attachments.Add(childAttachment); } } }
/// <summary> /// each attachment is stored in its own MIME entity and read into this entity's /// ContentStream. SaveAttachment creates an attachment out of the ContentStream /// and attaches it to the parent MIME entity. /// </summary> /// <param name="message">Parsed mail message</param> private static void SaveAttachment(MailMessageParser message) { if (null != message.Parent) { Attachment thisAttachment = new Attachment(message.ContentStream, message.ContentType); if (null != message.ContentDisposition) { ContentDisposition messageContentDisposition = message.ContentDisposition; ContentDisposition attachmentContentDisposition = thisAttachment.ContentDisposition; if (messageContentDisposition.CreationDate > DateTime.MinValue) { attachmentContentDisposition.CreationDate = messageContentDisposition.CreationDate; } attachmentContentDisposition.DispositionType = messageContentDisposition.DispositionType; attachmentContentDisposition.FileName = messageContentDisposition.FileName; attachmentContentDisposition.Inline = messageContentDisposition.Inline; if (messageContentDisposition.ModificationDate > DateTime.MinValue) { attachmentContentDisposition.ModificationDate = messageContentDisposition.ModificationDate; } if (messageContentDisposition.ReadDate > DateTime.MinValue) { attachmentContentDisposition.ReadDate = messageContentDisposition.ReadDate; } if (0 < messageContentDisposition.Size) { attachmentContentDisposition.Size = messageContentDisposition.Size; } } string contentIdString = message.ContentId; if (null != contentIdString) { thisAttachment.ContentId = RemoveBrackets(contentIdString); } thisAttachment.TransferEncoding = message.ContentTransferEncoding; message.Parent.Attachments.Add(thisAttachment); } }
/// <summary> /// Copies the content found for the MIME entity to the MailMessageParser body and creates /// a stream which can be used to create attachments and alternative views. /// </summary> /// <param name="message">Parsed mail message</param> /// <param name="contentString">mail content string</param> private static void SaveMessageBody(MailMessageParser message, string contentString) { message.Body = contentString; System.Text.Encoding ascii = System.Text.Encoding.ASCII; MemoryStream bodyStream = new MemoryStream(ascii.GetBytes(contentString), 0, contentString.Length); message.ContentStream = bodyStream; }
/// <summary> /// Gets an email from the supplied Email Stream and processes it. /// </summary> /// <param name="emailStream">The email stream object</param> /// <returns> /// MailMessageParser or null if email not properly formatted /// </returns> public MailMessageParser GetEmail(Stream emailStream) { this.EmailStreamReader = new StreamReader(emailStream, Encoding.ASCII); ////prepare message, set defaults as specified in RFC 2046 MailMessageParser message = new MailMessageParser(); message.ContentTransferEncoding = TransferEncoding.SevenBit; MailMessageParser result = null; MimeEntityReturnCode messageMimeReturnCode = this.ProcessMimeEntity(message, string.Empty); if (messageMimeReturnCode == MimeEntityReturnCode.bodyComplete || messageMimeReturnCode == MimeEntityReturnCode.parentBoundaryEndFound) { if (0 == message.To.Count) { string toField = message.Headers[ServiceConstants.Mail_Message_Receiver_Header]; if (!string.IsNullOrEmpty(toField)) { message.To.Add(toField); } } if (null == message.From) { string mailFrom = message.Headers[ServiceConstants.Mail_Message_Sender_Header]; if (!string.IsNullOrEmpty(mailFrom)) { message.From = new MailAddress(mailFrom); } } result = message; } return result; }
/// <summary> /// Creates an empty child MIME entity from the parent MIME entity. /// An email can consist of several MIME entities. A entity has the same structure of email. /// </summary> /// <returns>Mail message child object</returns> public MailMessageParser CreateChildEntity() { MailMessageParser child = new MailMessageParser(); child.Parent = this; child.TopParent = this.TopParent; child.ContentTransferEncoding = this.ContentTransferEncoding; return child; }
/// <summary> /// Gets the uploaded email file properties.... /// </summary> /// <param name="fileStream">The file stream.</param> /// <param name="mailProperties">The mail properties.</param> /// <returns>Dictionary string key value pair for mail properties</returns> public static Dictionary <string, string> GetMailFileProperties(System.IO.Stream fileStream, Dictionary <string, string> mailProperties) { if (null != mailProperties && mailProperties.ContainsKey(ServiceConstants.MAIL_FILE_EXTENSION_KEY)) { if (string.Equals(mailProperties[ServiceConstants.MAIL_FILE_EXTENSION_KEY], ServiceConstants.EMAIL_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase)) { MailMimeReader mime = new MailMimeReader(); MailMessageParser messageParser = mime.GetEmail(fileStream); string fromDisplayName = Convert.ToString(messageParser.From.DisplayName, CultureInfo.InvariantCulture); mailProperties[ServiceConstants.MAIL_SENDER_KEY] = String.Concat(Convert.ToString(messageParser.From.Address, CultureInfo.InvariantCulture), ServiceConstants.SEMICOLON, fromDisplayName.Replace(Convert.ToString(messageParser.From.Address, CultureInfo.InvariantCulture), string.Empty).Replace(ServiceConstants.OPENING_BRACKET + ServiceConstants.CLOSING_BRACKET, string.Empty)); mailProperties[ServiceConstants.MAIL_SEARCH_EMAIL_SUBJECT] = messageParser.Subject; mailProperties[ServiceConstants.MAIL_SENT_DATE_KEY] = Convert.ToString(messageParser.DeliveryDate, CultureInfo.InvariantCulture); mailProperties[ServiceConstants.MAIL_RECEIVED_DATEKEY] = Convert.ToString(messageParser.ReceivedDate, CultureInfo.InvariantCulture); mailProperties[ServiceConstants.MAIL_ORIGINAL_NAME] = messageParser.Subject; StringBuilder mailReceiver = new StringBuilder(); StringBuilder mailCCAddress = new StringBuilder(); StringBuilder attachmentName = new StringBuilder(); foreach (MailAddress toItem in messageParser.To) { string toMailAlias = Convert.ToString(toItem.Address, CultureInfo.InvariantCulture); string toMailName = Convert.ToString(toItem.DisplayName, CultureInfo.InvariantCulture); if (toMailName.Contains(toMailAlias)) { toMailName = toMailName.Replace(toMailAlias, string.Empty).Replace(ServiceConstants.OPENING_BRACKET + ServiceConstants.CLOSING_BRACKET, string.Empty); } mailReceiver.Append(toMailAlias + ServiceConstants.SEMICOLON + toMailName + ServiceConstants.SEMICOLON); } mailProperties[ServiceConstants.MAIL_RECEIVER_KEY] = Convert.ToString(mailReceiver, CultureInfo.InvariantCulture); foreach (MailAddress itemCC in messageParser.CC) { string mailCCAlias = Convert.ToString(itemCC.Address, CultureInfo.InvariantCulture); string mailCCName = Convert.ToString(itemCC.DisplayName, CultureInfo.InvariantCulture); if (mailCCName.Contains(mailCCAlias)) { mailCCName = mailCCName.Replace(mailCCAlias, string.Empty).Replace(ServiceConstants.OPENING_BRACKET + ServiceConstants.CLOSING_BRACKET, string.Empty); } mailCCAddress.Append(mailCCAlias + ServiceConstants.SEMICOLON + mailCCName + ServiceConstants.SEMICOLON); } mailProperties[ServiceConstants.MAIL_CC_ADDRESS_KEY] = Convert.ToString(mailCCAddress, CultureInfo.InvariantCulture); foreach (System.Net.Mail.Attachment itemAttachment in messageParser.Attachments) { if (!string.IsNullOrWhiteSpace(itemAttachment.Name)) { attachmentName.Append(itemAttachment.Name + ServiceConstants.SEMICOLON); } } for (int mailEntitiesCount = 0; mailEntitiesCount < messageParser.Entities.Count; mailEntitiesCount++) { if (string.Equals(messageParser.Entities[mailEntitiesCount].MediaMainType, ServiceConstants.MAIL_ATTACHMENT_MEDIA_MAINT_YPE, StringComparison.CurrentCultureIgnoreCase)) { attachmentName.Append(messageParser.Entities[mailEntitiesCount].ContentDescription + ServiceConstants.SEMICOLON); } } mailProperties[ServiceConstants.MAIL_ATTACHMENT_KEY] = Convert.ToString(attachmentName, CultureInfo.InvariantCulture); // Setting email importance mailProperties[ServiceConstants.MAIL_IMPORTANCE_KEY] = (!string.IsNullOrWhiteSpace(messageParser.MailImportance) ? messageParser.MailImportance : ServiceConstants.MAIL_DEFAULT_IMPORTANCE); // Setting email categories mailProperties[ServiceConstants.MAIL_CATEGORIES_KEY] = (!string.IsNullOrWhiteSpace(messageParser.MailCategories) ? messageParser.MailCategories.Replace(ServiceConstants.COMMA, ServiceConstants.SEMICOLON) : string.Empty); } } return(mailProperties); }
/// <summary> /// Process a MIME entity /// A MIME entity consists of header and body. /// Separator lines in the body might mark children MIME entities /// </summary> /// <param name="message">Mail Message.</param> /// <param name="parentBoundaryStart">The parent boundary start value</param> /// <returns>Mime entity return code for parsed mail message</returns> private MimeEntityReturnCode ProcessMimeEntity(MailMessageParser message, string parentBoundaryStart) { bool hasParentBoundary = parentBoundaryStart.Length > 0; string parentBoundaryEnd = parentBoundaryStart + ServiceConstants.HYPHEN + ServiceConstants.HYPHEN; 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 as well message.SetContentTypeFields(ServiceConstants.MAIL_CONTENT_TYPE); 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 (!this.ReadMultipleLine(out response)) { while (this.ReadMultipleLine(out response)) { continue; } return MimeEntityReturnCode.problem; } if (1 > response.Length) { ////empty line found => end of header if (completeHeaderField != null) { this.ProcessHeaderField(message, completeHeaderField); } break; } if (hasParentBoundary && ParentBoundaryFound(response, parentBoundaryStart, parentBoundaryEnd, out boundaryMimeReturnCode)) { while (this.ReadMultipleLine(out response)) { continue; } 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 (ServiceConstants.SPACE == Convert.ToString(response[0], CultureInfo.InvariantCulture) || ServiceConstants.HORIZONTAL_TAB == Convert.ToString(response[0], CultureInfo.InvariantCulture)) { if (completeHeaderField == null) { while (this.ReadMultipleLine(out response)) { continue; } return MimeEntityReturnCode.problem; } else { if (ServiceConstants.SPACE != Convert.ToString(completeHeaderField[completeHeaderField.Length - 1], CultureInfo.InvariantCulture)) { completeHeaderField += ServiceConstants.SPACE + response.TrimStart(whiteSpaceChars); } else { completeHeaderField += response.TrimStart(whiteSpaceChars); } } } else { if (null == completeHeaderField) { completeHeaderField = response; } else { this.ProcessHeaderField(message, completeHeaderField); completeHeaderField = response; } } } this.mimeEntitySB.Length = 0; string boundaryDelimiterLineStart = null; bool isBoundaryDefined = false; if (null != message.ContentType.Boundary) { 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 (this.ReadMultipleLine(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 this.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 this.mimeEntitySB.Append(response + this.CRLF); } ////a complete MIME body read ////convert received US ASCII characters to .NET string (Unicode) string transferEncodedMessage = Convert.ToString(this.mimeEntitySB, CultureInfo.InvariantCulture); bool isAttachmentSaved = false; switch (message.ContentTransferEncoding) { case TransferEncoding.SevenBit: SaveMessageBody(message, transferEncodedMessage); break; case TransferEncoding.Base64: byte[] bodyBytes = System.Convert.FromBase64String(transferEncodedMessage); message.ContentStream = new MemoryStream(bodyBytes, false); if (message.MediaMainType == ServiceConstants.TEXT_MEDIA_MAIN_TYPE) { message.Body = DecodeByteArrayToString(bodyBytes, message.BodyEncoding); } else if (message.MediaMainType == ServiceConstants.IMAGE_MEDIA_MAIN_TYPE || message.MediaMainType == ServiceConstants.APPLICATION_MEDIA_MAIN_TYPE || message.MediaMainType == ServiceConstants.MESSAGE_MEDIA_MAIN_TYPE) { SaveAttachment(message); isAttachmentSaved = true; } break; case TransferEncoding.QuotedPrintable: SaveMessageBody(message, QuotedPrintable.Decode(transferEncodedMessage)); break; default: SaveMessageBody(message, transferEncodedMessage); break; } if (null != message.ContentDisposition && message.ContentDisposition.DispositionType.ToUpperInvariant() == ServiceConstants.MailAttributes.ATTACHMENT && !isAttachmentSaved) { SaveAttachment(message); isAttachmentSaved = true; } return boundaryMimeReturnCode; }
/// <summary> /// Convert one MIME header field and update message accordingly /// </summary> /// <param name="message">Parsed message object</param> /// <param name="headerField">header field</param> private void ProcessHeaderField(MailMessageParser message, string headerField) { string headerLineType; string headerLineContent; int separatorPosition = headerField.IndexOf(ServiceConstants.COLON, StringComparison.CurrentCulture); if (0 < separatorPosition) { ////process header field type headerLineType = headerField.Substring(0, separatorPosition).ToUpperInvariant(); headerLineContent = headerField.Substring(separatorPosition + 1).Trim(whiteSpaceChars); if (string.IsNullOrEmpty(headerLineType) || string.IsNullOrEmpty(headerLineContent)) { ////mail header parts missing, exist function return; } //// add header line to headers message.Headers.Add(headerLineType, headerLineContent); switch (headerLineType) { case ServiceConstants.MailAttributes.BCC: AddMailAddresses(headerLineContent, message.Bcc); break; case ServiceConstants.MailAttributes.CC: AddMailAddresses(headerLineContent, message.CC); break; case ServiceConstants.MailAttributes.CONTENT_DESCRIPTION: message.ContentDescription = headerLineContent; break; case ServiceConstants.MailAttributes.CONTENT_DISPOSITION: message.SetContentDisposition(headerLineContent); break; case ServiceConstants.MailAttributes.CONTENT_ID: message.ContentId = headerLineContent; break; case ServiceConstants.MailAttributes.CONTENT_TRANSFER_ENCODING: message.ContentTransferEncoding = ConvertToTransferEncoding(headerLineContent); break; case ServiceConstants.MailAttributes.CONTENT_TYPE: message.SetContentTypeFields(headerLineContent); break; case ServiceConstants.MailAttributes.DATE: message.DeliveryDate = this.ConvertToDateTime(headerLineContent); break; case ServiceConstants.MailAttributes.FROM: MailAddress address = ConvertToMailAddress(headerLineContent); if (null != address) { message.From = address; } break; case ServiceConstants.MailAttributes.SENDER: message.Sender = ConvertToMailAddress(headerLineContent); break; case ServiceConstants.MailAttributes.SUBJECT: message.Subject = headerLineContent; break; case ServiceConstants.MailAttributes.TO: AddMailAddresses(headerLineContent, message.To); break; case ServiceConstants.MailAttributes.IMPORTANCE: message.MailImportance = headerLineContent; break; case ServiceConstants.MailAttributes.CATEGORIES: message.MailCategories = headerLineContent; break; case ServiceConstants.MailAttributes.RECEIVED: if (message.ReceivedDate.Year.Equals(1)) { message.ReceivedDate = this.ProcessReceivedDate(headerLineContent); } break; default: message.UnknowHeaderlines.Add(headerField); if (IsCollectHiddenHeaderLines) { AllHiddenHeaderLines.Add(headerField); } break; } } }
/// <summary> /// Process a MIME entity /// A MIME entity consists of header and body. /// Separator lines in the body might mark children MIME entities /// </summary> /// <param name="message">Mail Message.</param> /// <param name="parentBoundaryStart">The parent boundary start value</param> /// <returns>Mime entity return code for parsed mail message</returns> private MimeEntityReturnCode ProcessMimeEntity(MailMessageParser message, string parentBoundaryStart) { bool hasParentBoundary = parentBoundaryStart.Length > 0; string parentBoundaryEnd = parentBoundaryStart + ServiceConstants.HYPHEN + ServiceConstants.HYPHEN; 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 as well message.SetContentTypeFields(ServiceConstants.MAIL_CONTENT_TYPE); 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 (!this.ReadMultipleLine(out response)) { while (this.ReadMultipleLine(out response)) { continue; } return(MimeEntityReturnCode.problem); } if (1 > response.Length) { ////empty line found => end of header if (completeHeaderField != null) { this.ProcessHeaderField(message, completeHeaderField); } break; } if (hasParentBoundary && ParentBoundaryFound(response, parentBoundaryStart, parentBoundaryEnd, out boundaryMimeReturnCode)) { while (this.ReadMultipleLine(out response)) { continue; } 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 (ServiceConstants.SPACE == Convert.ToString(response[0], CultureInfo.InvariantCulture) || ServiceConstants.HORIZONTAL_TAB == Convert.ToString(response[0], CultureInfo.InvariantCulture)) { if (completeHeaderField == null) { while (this.ReadMultipleLine(out response)) { continue; } return(MimeEntityReturnCode.problem); } else { if (ServiceConstants.SPACE != Convert.ToString(completeHeaderField[completeHeaderField.Length - 1], CultureInfo.InvariantCulture)) { completeHeaderField += ServiceConstants.SPACE + response.TrimStart(whiteSpaceChars); } else { completeHeaderField += response.TrimStart(whiteSpaceChars); } } } else { if (null == completeHeaderField) { completeHeaderField = response; } else { this.ProcessHeaderField(message, completeHeaderField); completeHeaderField = response; } } } this.mimeEntitySB.Length = 0; string boundaryDelimiterLineStart = null; bool isBoundaryDefined = false; if (null != message.ContentType.Boundary) { 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 (this.ReadMultipleLine(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(this.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 this.mimeEntitySB.Append(response + this.CRLF); } ////a complete MIME body read ////convert received US ASCII characters to .NET string (Unicode) string transferEncodedMessage = Convert.ToString(this.mimeEntitySB, CultureInfo.InvariantCulture); bool isAttachmentSaved = false; switch (message.ContentTransferEncoding) { case TransferEncoding.SevenBit: SaveMessageBody(message, transferEncodedMessage); break; case TransferEncoding.Base64: byte[] bodyBytes = System.Convert.FromBase64String(transferEncodedMessage); message.ContentStream = new MemoryStream(bodyBytes, false); if (message.MediaMainType == ServiceConstants.TEXT_MEDIA_MAIN_TYPE) { message.Body = DecodeByteArrayToString(bodyBytes, message.BodyEncoding); } else if (message.MediaMainType == ServiceConstants.IMAGE_MEDIA_MAIN_TYPE || message.MediaMainType == ServiceConstants.APPLICATION_MEDIA_MAIN_TYPE || message.MediaMainType == ServiceConstants.MESSAGE_MEDIA_MAIN_TYPE) { SaveAttachment(message); isAttachmentSaved = true; } break; case TransferEncoding.QuotedPrintable: SaveMessageBody(message, QuotedPrintable.Decode(transferEncodedMessage)); break; default: SaveMessageBody(message, transferEncodedMessage); break; } if (null != message.ContentDisposition && message.ContentDisposition.DispositionType.ToUpperInvariant() == ServiceConstants.MailAttributes.ATTACHMENT && !isAttachmentSaved) { SaveAttachment(message); isAttachmentSaved = true; } return(boundaryMimeReturnCode); }
/// <summary> /// Convert one MIME header field and update message accordingly /// </summary> /// <param name="message">Parsed message object</param> /// <param name="headerField">header field</param> private void ProcessHeaderField(MailMessageParser message, string headerField) { string headerLineType; string headerLineContent; int separatorPosition = headerField.IndexOf(ServiceConstants.COLON, StringComparison.CurrentCulture); if (0 < separatorPosition) { ////process header field type headerLineType = headerField.Substring(0, separatorPosition).ToUpperInvariant(); headerLineContent = headerField.Substring(separatorPosition + 1).Trim(whiteSpaceChars); if (string.IsNullOrEmpty(headerLineType) || string.IsNullOrEmpty(headerLineContent)) { ////mail header parts missing, exist function return; } //// add header line to headers message.Headers.Add(headerLineType, headerLineContent); switch (headerLineType) { case ServiceConstants.MailAttributes.BCC: AddMailAddresses(headerLineContent, message.Bcc); break; case ServiceConstants.MailAttributes.CC: AddMailAddresses(headerLineContent, message.CC); break; case ServiceConstants.MailAttributes.CONTENT_DESCRIPTION: message.ContentDescription = headerLineContent; break; case ServiceConstants.MailAttributes.CONTENT_DISPOSITION: message.SetContentDisposition(headerLineContent); break; case ServiceConstants.MailAttributes.CONTENT_ID: message.ContentId = headerLineContent; break; case ServiceConstants.MailAttributes.CONTENT_TRANSFER_ENCODING: message.ContentTransferEncoding = ConvertToTransferEncoding(headerLineContent); break; case ServiceConstants.MailAttributes.CONTENT_TYPE: message.SetContentTypeFields(headerLineContent); break; case ServiceConstants.MailAttributes.DATE: message.DeliveryDate = this.ConvertToDateTime(headerLineContent); break; case ServiceConstants.MailAttributes.FROM: MailAddress address = ConvertToMailAddress(headerLineContent); if (null != address) { message.From = address; } break; case ServiceConstants.MailAttributes.SENDER: message.Sender = ConvertToMailAddress(headerLineContent); break; case ServiceConstants.MailAttributes.SUBJECT: message.Subject = headerLineContent; break; case ServiceConstants.MailAttributes.TO: AddMailAddresses(headerLineContent, message.To); break; case ServiceConstants.MailAttributes.IMPORTANCE: message.MailImportance = headerLineContent; break; case ServiceConstants.MailAttributes.CATEGORIES: message.MailCategories = headerLineContent; break; case ServiceConstants.MailAttributes.RECEIVED: if (message.ReceivedDate.Year.Equals(1)) { message.ReceivedDate = this.ProcessReceivedDate(headerLineContent); } break; default: message.UnknowHeaderlines.Add(headerField); if (IsCollectHiddenHeaderLines) { AllHiddenHeaderLines.Add(headerField); } break; } } }
/// <summary> /// Gets the uploaded email file properties. /// </summary> /// <param name="fileStream">The file stream.</param> /// <param name="mailProperties">The mail properties.</param> /// <returns>Dictionary string key value pair for mail properties</returns> public static Dictionary <string, string> GetMailFileProperties(System.IO.Stream fileStream, Dictionary <string, string> mailProperties) { if (null != mailProperties && mailProperties.ContainsKey(ConstantStrings.MailFileExtensionKey)) { if (string.Equals(mailProperties[ConstantStrings.MailFileExtensionKey], ConstantStrings.EmailFileExtension, StringComparison.OrdinalIgnoreCase)) { MailMimeReader mime = new MailMimeReader(); MailMessageParser messageParser = mime.GetEmail(fileStream); string fromDisplayName = Convert.ToString(messageParser.From.DisplayName, CultureInfo.InvariantCulture); mailProperties[ConstantStrings.MailSenderKey] = String.Concat(Convert.ToString(messageParser.From.Address, CultureInfo.InvariantCulture), ConstantStrings.Semicolon, fromDisplayName.Replace(Convert.ToString(messageParser.From.Address, CultureInfo.InvariantCulture), string.Empty).Replace(ConstantStrings.OpeningBracket + ConstantStrings.ClosingBracket, string.Empty)); mailProperties[ConstantStrings.MailSearchEmailSubject] = messageParser.Subject; mailProperties[ConstantStrings.MailSentDateKey] = Convert.ToString(messageParser.DeliveryDate, CultureInfo.InvariantCulture); mailProperties[ConstantStrings.MailReceivedDateKey] = Convert.ToString(messageParser.ReceivedDate, CultureInfo.InvariantCulture); mailProperties[ConstantStrings.MailOriginalName] = messageParser.Subject; StringBuilder mailReceiver = new StringBuilder(); StringBuilder mailCCAddress = new StringBuilder(); StringBuilder attachmentName = new StringBuilder(); foreach (MailAddress toItem in messageParser.To) { string toMailAlias = Convert.ToString(toItem.Address, CultureInfo.InvariantCulture); string toMailName = Convert.ToString(toItem.DisplayName, CultureInfo.InvariantCulture); if (toMailName.Contains(toMailAlias)) { toMailName = toMailName.Replace(toMailAlias, string.Empty).Replace(ConstantStrings.OpeningBracket + ConstantStrings.ClosingBracket, string.Empty); } mailReceiver.Append(toMailAlias + ConstantStrings.Semicolon + toMailName + ConstantStrings.Semicolon); } mailProperties[ConstantStrings.MailReceiverKey] = Convert.ToString(mailReceiver, CultureInfo.InvariantCulture); foreach (MailAddress itemCC in messageParser.CC) { string mailCCAlias = Convert.ToString(itemCC.Address, CultureInfo.InvariantCulture); string mailCCName = Convert.ToString(itemCC.DisplayName, CultureInfo.InvariantCulture); if (mailCCName.Contains(mailCCAlias)) { mailCCName = mailCCName.Replace(mailCCAlias, string.Empty).Replace(ConstantStrings.OpeningBracket + ConstantStrings.ClosingBracket, string.Empty); } mailCCAddress.Append(mailCCAlias + ConstantStrings.Semicolon + mailCCName + ConstantStrings.Semicolon); } mailProperties[ConstantStrings.MailCCAddressKey] = Convert.ToString(mailCCAddress, CultureInfo.InvariantCulture); foreach (System.Net.Mail.Attachment itemAttachment in messageParser.Attachments) { if (!string.IsNullOrWhiteSpace(itemAttachment.Name)) { attachmentName.Append(itemAttachment.Name + ConstantStrings.Semicolon); } } for (int mailEntitiesCount = 0; mailEntitiesCount < messageParser.Entities.Count; mailEntitiesCount++) { if (string.Equals(messageParser.Entities[mailEntitiesCount].MediaMainType, ConstantStrings.MailAttachmentMediaMainType, StringComparison.CurrentCultureIgnoreCase)) { attachmentName.Append(messageParser.Entities[mailEntitiesCount].ContentDescription + ConstantStrings.Semicolon); } } mailProperties[ConstantStrings.MailAttachmentKey] = Convert.ToString(attachmentName, CultureInfo.InvariantCulture); // Setting email importance mailProperties[ConstantStrings.MailImportanceKey] = (!string.IsNullOrWhiteSpace(messageParser.MailImportance) ? messageParser.MailImportance : ConstantStrings.MailDefaultImportance); // Setting email categories mailProperties[ConstantStrings.MailCategoriesKey] = (!string.IsNullOrWhiteSpace(messageParser.MailCategories) ? messageParser.MailCategories.Replace(ConstantStrings.Comma, ConstantStrings.Semicolon) : string.Empty); } } return(mailProperties); }