/// <summary> /// Writes all the properties that are part of the <see cref="Email"/> object either as <see cref="CFStorage"/>'s /// or <see cref="CFStream"/>'s to the <see cref="CompoundFile.RootStorage"/> /// </summary> internal void WriteToStorage() { var rootStorage = CompoundFile.RootStorage; if (Class == MessageClass.Unknown) { Class = MessageClass.IPM_Note; } MessageSize += Recipients.WriteToStorage(rootStorage); MessageSize += Attachments.WriteToStorage(rootStorage); var recipientCount = Recipients.Count; var attachmentCount = Attachments.Count; TopLevelProperties.RecipientCount = recipientCount; TopLevelProperties.AttachmentCount = attachmentCount; TopLevelProperties.NextRecipientId = recipientCount; TopLevelProperties.NextAttachmentId = attachmentCount; TopLevelProperties.AddProperty(PropertyTags.PR_ENTRYID, Mapi.GenerateEntryId()); TopLevelProperties.AddProperty(PropertyTags.PR_INSTANCE_KEY, Mapi.GenerateInstanceKey()); TopLevelProperties.AddProperty(PropertyTags.PR_STORE_SUPPORT_MASK, StoreSupportMaskConst.StoreSupportMask, PropertyFlags.PROPATTR_READABLE); TopLevelProperties.AddProperty(PropertyTags.PR_STORE_UNICODE_MASK, StoreSupportMaskConst.StoreSupportMask, PropertyFlags.PROPATTR_READABLE); TopLevelProperties.AddProperty(PropertyTags.PR_ALTERNATE_RECIPIENT_ALLOWED, true, PropertyFlags.PROPATTR_READABLE); TopLevelProperties.AddProperty(PropertyTags.PR_HASATTACH, attachmentCount > 0); if (TransportMessageHeaders != null) { TopLevelProperties.AddProperty(PropertyTags.PR_TRANSPORT_MESSAGE_HEADERS_W, TransportMessageHeaders.ToString()); if (!string.IsNullOrWhiteSpace(TransportMessageHeaders.MessageId)) { TopLevelProperties.AddProperty(PropertyTags.PR_INTERNET_MESSAGE_ID_W, TransportMessageHeaders.MessageId); } if (TransportMessageHeaders.References.Any()) { TopLevelProperties.AddProperty(PropertyTags.PR_INTERNET_REFERENCES_W, TransportMessageHeaders.References.Last()); } if (TransportMessageHeaders.InReplyTo.Any()) { TopLevelProperties.AddProperty(PropertyTags.PR_IN_REPLY_TO_ID_W, TransportMessageHeaders.InReplyTo.Last()); } } if (!string.IsNullOrWhiteSpace(InternetMessageId)) { TopLevelProperties.AddOrReplaceProperty(PropertyTags.PR_INTERNET_MESSAGE_ID_W, InternetMessageId); } if (!string.IsNullOrWhiteSpace(InternetReferences)) { TopLevelProperties.AddOrReplaceProperty(PropertyTags.PR_INTERNET_REFERENCES_W, InternetReferences); } if (!string.IsNullOrWhiteSpace(InReplyToId)) { TopLevelProperties.AddOrReplaceProperty(PropertyTags.PR_IN_REPLY_TO_ID_W, InReplyToId); } var messageFlags = MessageFlags.MSGFLAG_UNMODIFIED; if (attachmentCount > 0) { messageFlags |= MessageFlags.MSGFLAG_HASATTACH; } TopLevelProperties.AddProperty(PropertyTags.PR_INTERNET_CPID, Encoding.UTF8.CodePage); TopLevelProperties.AddProperty(PropertyTags.PR_BODY_W, BodyText); if (!string.IsNullOrEmpty(BodyHtml) && !Draft) { TopLevelProperties.AddProperty(PropertyTags.PR_HTML, BodyHtml); TopLevelProperties.AddProperty(PropertyTags.PR_RTF_IN_SYNC, false); } else if (string.IsNullOrWhiteSpace(BodyRtf) && !string.IsNullOrWhiteSpace(BodyHtml)) { BodyRtf = Strings.GetEscapedRtf(BodyHtml); BodyRtfCompressed = true; } if (!string.IsNullOrWhiteSpace(BodyRtf)) { TopLevelProperties.AddProperty(PropertyTags.PR_RTF_COMPRESSED, new RtfCompressor().Compress(Encoding.ASCII.GetBytes(BodyRtf))); TopLevelProperties.AddProperty(PropertyTags.PR_RTF_IN_SYNC, BodyRtfCompressed); } if (MessageEditorFormat != MessageEditorFormat.EDITOR_FORMAT_DONTKNOW) { TopLevelProperties.AddProperty(PropertyTags.PR_MSG_EDITOR_FORMAT, MessageEditorFormat); } if (!SentOn.HasValue) { SentOn = DateTime.UtcNow; } if (ReceivedOn.HasValue) { TopLevelProperties.AddProperty(PropertyTags.PR_MESSAGE_DELIVERY_TIME, ReceivedOn.Value.ToUniversalTime()); } TopLevelProperties.AddProperty(PropertyTags.PR_CLIENT_SUBMIT_TIME, SentOn.Value.ToUniversalTime()); TopLevelProperties.AddProperty(PropertyTags.PR_ACCESS, MapiAccess.MAPI_ACCESS_DELETE | MapiAccess.MAPI_ACCESS_MODIFY | MapiAccess.MAPI_ACCESS_READ); TopLevelProperties.AddProperty(PropertyTags.PR_ACCESS_LEVEL, MapiAccess.MAPI_ACCESS_MODIFY); TopLevelProperties.AddProperty(PropertyTags.PR_OBJECT_TYPE, MapiObjectType.MAPI_MESSAGE); SetSubject(); TopLevelProperties.AddProperty(PropertyTags.PR_SUBJECT_W, Subject); TopLevelProperties.AddProperty(PropertyTags.PR_NORMALIZED_SUBJECT_W, SubjectNormalized); TopLevelProperties.AddProperty(PropertyTags.PR_SUBJECT_PREFIX_W, SubjectPrefix); TopLevelProperties.AddProperty(PropertyTags.PR_CONVERSATION_TOPIC_W, SubjectNormalized); // http://www.meridiandiscovery.com/how-to/e-mail-conversation-index-metadata-computer-forensics/ // http://stackoverflow.com/questions/11860540/does-outlook-embed-a-messageid-or-equivalent-in-its-email-elements //propertiesStream.AddProperty(PropertyTags.PR_CONVERSATION_INDEX, Subject); // TODO: Change modification time when this message is opened and only modified var utcNow = DateTime.UtcNow; TopLevelProperties.AddProperty(PropertyTags.PR_CREATION_TIME, utcNow); TopLevelProperties.AddProperty(PropertyTags.PR_LAST_MODIFICATION_TIME, utcNow); TopLevelProperties.AddProperty(PropertyTags.PR_PRIORITY, Priority); TopLevelProperties.AddProperty(PropertyTags.PR_IMPORTANCE, Importance); TopLevelProperties.AddProperty(PropertyTags.PR_MESSAGE_LOCALE_ID, CultureInfo.CurrentCulture.LCID); if (Draft) { messageFlags |= MessageFlags.MSGFLAG_UNSENT; IconIndex = MessageIconIndex.UnsentMail; } if (ReadRecipient) { TopLevelProperties.AddProperty(PropertyTags.PR_READ_RECEIPT_REQUESTED, true); var reportTag = new ReportTag { ANSIText = Subject }; TopLevelProperties.AddProperty(PropertyTags.PR_REPORT_TAG, reportTag.ToByteArray()); } TopLevelProperties.AddProperty(PropertyTags.PR_MESSAGE_FLAGS, messageFlags); TopLevelProperties.AddProperty(PropertyTags.PR_ICON_INDEX, IconIndex); Sender?.WriteProperties(TopLevelProperties); Receiving?.WriteProperties(TopLevelProperties); Representing?.WriteProperties(TopLevelProperties); ReceivingRepresenting?.WriteProperties(TopLevelProperties); if (recipientCount > 0) { var displayTo = new List <string>(); var displayCc = new List <string>(); var displayBcc = new List <string>(); foreach (var recipient in Recipients) { switch (recipient.RecipientType) { case RecipientType.To: if (!string.IsNullOrWhiteSpace(recipient.DisplayName)) { displayTo.Add(recipient.DisplayName); } else if (!string.IsNullOrWhiteSpace(recipient.Email)) { displayTo.Add(recipient.Email); } break; case RecipientType.Cc: if (!string.IsNullOrWhiteSpace(recipient.DisplayName)) { displayCc.Add(recipient.DisplayName); } else if (!string.IsNullOrWhiteSpace(recipient.Email)) { displayCc.Add(recipient.Email); } break; case RecipientType.Bcc: if (!string.IsNullOrWhiteSpace(recipient.DisplayName)) { displayBcc.Add(recipient.DisplayName); } else if (!string.IsNullOrWhiteSpace(recipient.Email)) { displayBcc.Add(recipient.Email); } break; default: throw new ArgumentOutOfRangeException(); } } var replyToRecipients = new List <string>(); foreach (var recipient in ReplyToRecipients) { replyToRecipients.Add(recipient.Email); } TopLevelProperties.AddProperty(PropertyTags.PR_DISPLAY_TO_W, string.Join(";", displayTo), PropertyFlags.PROPATTR_READABLE); TopLevelProperties.AddProperty(PropertyTags.PR_DISPLAY_CC_W, string.Join(";", displayCc), PropertyFlags.PROPATTR_READABLE); TopLevelProperties.AddProperty(PropertyTags.PR_DISPLAY_BCC_W, string.Join(";", displayBcc), PropertyFlags.PROPATTR_READABLE); TopLevelProperties.AddProperty(PropertyTags.PR_REPLY_RECIPIENT_NAMES_W, string.Join(";", replyToRecipients), PropertyFlags.PROPATTR_READABLE); } }
/// <summary> /// Converts an EML file to MSG format /// </summary> /// <param name="emlFileName">The EML (MIME) file</param> /// <param name="msgFileName">The MSG file</param> public static void ConvertEmlToMsg(string emlFileName, string msgFileName) { var eml = MimeMessage.Load(emlFileName); var sender = new Sender(string.Empty, string.Empty); if (eml.From.Count > 0) { var mailAddress = ((MailboxAddress)eml.From[0]); sender = new Sender(mailAddress.Address, mailAddress.Name); } var representing = new Representing(string.Empty, string.Empty); if (eml.ResentSender != null) { representing = new Representing(eml.ResentSender.Address, eml.ResentSender.Name); } var msg = new Email(sender, representing, eml.Subject) { SentOn = eml.Date.UtcDateTime, InternetMessageId = eml.MessageId }; switch (eml.Priority) { case MessagePriority.NonUrgent: msg.Priority = Enums.MessagePriority.PRIO_NONURGENT; break; case MessagePriority.Normal: msg.Priority = Enums.MessagePriority.PRIO_NORMAL; break; case MessagePriority.Urgent: msg.Priority = Enums.MessagePriority.PRIO_URGENT; break; } switch (eml.Importance) { case MessageImportance.Low: msg.Importance = Enums.MessageImportance.IMPORTANCE_LOW; break; case MessageImportance.Normal: msg.Importance = Enums.MessageImportance.IMPORTANCE_NORMAL; break; case MessageImportance.High: msg.Importance = Enums.MessageImportance.IMPORTANCE_HIGH; break; } foreach (var to in eml.To) { var mailAddress = ((MailboxAddress)to); msg.Recipients.AddTo(mailAddress.Address, mailAddress.Name); } foreach (var cc in eml.Cc) { var mailAddress = ((MailboxAddress)cc); msg.Recipients.AddCc(mailAddress.Address, mailAddress.Name); } foreach (var bcc in eml.Bcc) { var mailAddress = ((MailboxAddress)bcc); msg.Recipients.AddBcc(mailAddress.Address, mailAddress.Name); } using (var headerStream = new MemoryStream()) { eml.Headers.WriteTo(headerStream); headerStream.Position = 0; msg.TransportMessageHeaders = Encoding.ASCII.GetString(headerStream.ToArray()); } msg.BodyHtml = eml.HtmlBody; msg.BodyText = eml.TextBody; var skipFirst = true; foreach (var bodyPart in eml.BodyParts) { // We always skip the first bodypart because that is normaly the html, text or rtf body if (skipFirst) { skipFirst = false; continue; } var attachmentStream = new MemoryStream(); var fileName = bodyPart.ContentType.Name; var extension = string.Empty; if (bodyPart is MessagePart) { var part = (MessagePart)bodyPart; part.Message.WriteTo(attachmentStream); if (part.Message != null) { fileName = part.Message.Subject; } extension = ".eml"; } else if (bodyPart is MessageDispositionNotification) { var part = (MessageDispositionNotification)bodyPart; fileName = part.FileName; } else if (bodyPart is MessageDeliveryStatus) { var part = (MessageDeliveryStatus)bodyPart; fileName = "details"; extension = ".txt"; part.WriteTo(FormatOptions.Default, attachmentStream, true); } else { var part = (MimePart)bodyPart; part.ContentObject.DecodeTo(attachmentStream); fileName = part.FileName; bodyPart.WriteTo(attachmentStream); } fileName = string.IsNullOrWhiteSpace(fileName) ? "Nameless" : FileManager.RemoveInvalidFileNameChars(fileName); if (!string.IsNullOrEmpty(extension)) { fileName += extension; } var inline = bodyPart.ContentDisposition != null && bodyPart.ContentDisposition.Disposition.Equals("inline", StringComparison.InvariantCultureIgnoreCase); attachmentStream.Position = 0; msg.Attachments.Add(attachmentStream, fileName, -1, inline, bodyPart.ContentId); } msg.Save(msgFileName); }
/// <summary> /// Writes all the properties that are part of the <see cref="Email"/> object either as <see cref="CFStorage"/>'s /// or <see cref="CFStream"/>'s to the <see cref="CompoundFile.RootStorage"/> /// </summary> private void WriteToStorage() { var rootStorage = CompoundFile.RootStorage; long messageSize = 0; messageSize += Recipients.WriteToStorage(rootStorage); messageSize += Attachments.WriteToStorage(rootStorage); var recipientCount = Recipients.Count; var attachmentCount = Attachments.Count; var propertiesStream = new TopLevelProperties(recipientCount, attachmentCount, recipientCount, attachmentCount); if (!string.IsNullOrEmpty(InternetMessageId)) { propertiesStream.AddProperty(PropertyTags.PR_INTERNET_MESSAGE_ID_W, InternetMessageId); } propertiesStream.AddProperty(PropertyTags.PR_ENTRYID, Mapi.GenerateEntryId()); propertiesStream.AddProperty(PropertyTags.PR_INSTANCE_KEY, Mapi.GenerateInstanceKey()); propertiesStream.AddProperty(PropertyTags.PR_STORE_SUPPORT_MASK, StoreSupportMaskConst.StoreSupportMask, PropertyFlags.PROPATTR_READABLE); propertiesStream.AddProperty(PropertyTags.PR_STORE_UNICODE_MASK, StoreSupportMaskConst.StoreSupportMask, PropertyFlags.PROPATTR_READABLE); propertiesStream.AddProperty(PropertyTags.PR_ALTERNATE_RECIPIENT_ALLOWED, true, PropertyFlags.PROPATTR_READABLE); propertiesStream.AddProperty(PropertyTags.PR_HASATTACH, attachmentCount > 0); var messageFlags = MessageFlags.MSGFLAG_UNMODIFIED; if (Draft) { messageFlags |= MessageFlags.MSGFLAG_UNSENT | MessageFlags.MSGFLAG_FROMME; IconIndex = MessageIconIndex.UnsentMail; } if (attachmentCount > 0) { messageFlags |= MessageFlags.MSGFLAG_HASATTACH; } if (!SentOn.HasValue) { SentOn = DateTime.UtcNow; } propertiesStream.AddProperty(PropertyTags.PR_CLIENT_SUBMIT_TIME, SentOn); propertiesStream.AddProperty(PropertyTags.PR_MESSAGE_FLAGS, messageFlags); propertiesStream.AddProperty(PropertyTags.PR_ACCESS, MapiAccess.MAPI_ACCESS_DELETE | MapiAccess.MAPI_ACCESS_MODIFY | MapiAccess.MAPI_ACCESS_READ); propertiesStream.AddProperty(PropertyTags.PR_ACCESS_LEVEL, MapiAccess.MAPI_ACCESS_MODIFY); propertiesStream.AddProperty(PropertyTags.PR_OBJECT_TYPE, MapiObjectType.MAPI_MESSAGE); SetSubject(); propertiesStream.AddProperty(PropertyTags.PR_SUBJECT_W, Subject); propertiesStream.AddProperty(PropertyTags.PR_NORMALIZED_SUBJECT_W, SubjectNormalized); propertiesStream.AddProperty(PropertyTags.PR_SUBJECT_PREFIX_W, SubjectPrefix); propertiesStream.AddProperty(PropertyTags.PR_CONVERSATION_TOPIC_W, SubjectNormalized); // http://www.meridiandiscovery.com/how-to/e-mail-conversation-index-metadata-computer-forensics/ // http://stackoverflow.com/questions/11860540/does-outlook-embed-a-messageid-or-equivalent-in-its-email-elements //propertiesStream.AddProperty(PropertyTags.PR_CONVERSATION_INDEX, Subject); // TODO: Change modification time when this message is opened and only modified var utcNow = DateTime.UtcNow; propertiesStream.AddProperty(PropertyTags.PR_CREATION_TIME, utcNow); propertiesStream.AddProperty(PropertyTags.PR_LAST_MODIFICATION_TIME, utcNow); propertiesStream.AddProperty(PropertyTags.PR_MESSAGE_CLASS_W, "IPM.Note"); propertiesStream.AddProperty(PropertyTags.PR_PRIORITY, Priority); propertiesStream.AddProperty(PropertyTags.PR_IMPORTANCE, Importance); propertiesStream.AddProperty(PropertyTags.PR_MESSAGE_LOCALE_ID, CultureInfo.CurrentCulture.LCID); propertiesStream.AddProperty(PropertyTags.PR_ICON_INDEX, IconIndex); if (Sender != null) { Sender.WriteProperties(propertiesStream); } if (Receiving != null) { Receiving.WriteProperties(propertiesStream); } if (Representing != null) { Representing.WriteProperties(propertiesStream); } if (ReceivingRepresenting != null) { ReceivingRepresenting.WriteProperties(propertiesStream); } if (recipientCount > 0) { var displayTo = new List <string>(); var displayCc = new List <string>(); var displayBcc = new List <string>(); foreach (var recipient in Recipients) { switch (recipient.RecipientType) { case RecipientType.To: if (!string.IsNullOrWhiteSpace(recipient.DisplayName)) { displayTo.Add(recipient.DisplayName); } else if (!string.IsNullOrWhiteSpace(recipient.Email)) { displayTo.Add(recipient.Email); } break; case RecipientType.Cc: if (!string.IsNullOrWhiteSpace(recipient.DisplayName)) { displayCc.Add(recipient.DisplayName); } else if (!string.IsNullOrWhiteSpace(recipient.Email)) { displayCc.Add(recipient.Email); } break; case RecipientType.Bcc: if (!string.IsNullOrWhiteSpace(recipient.DisplayName)) { displayBcc.Add(recipient.DisplayName); } else if (!string.IsNullOrWhiteSpace(recipient.Email)) { displayBcc.Add(recipient.Email); } break; default: throw new ArgumentOutOfRangeException(); } } propertiesStream.AddProperty(PropertyTags.PR_DISPLAY_TO_W, string.Join(";", displayTo), PropertyFlags.PROPATTR_READABLE); propertiesStream.AddProperty(PropertyTags.PR_DISPLAY_CC_W, string.Join(";", displayCc), PropertyFlags.PROPATTR_READABLE); propertiesStream.AddProperty(PropertyTags.PR_DISPLAY_BCC_W, string.Join(";", displayBcc), PropertyFlags.PROPATTR_READABLE); } propertiesStream.AddProperty(PropertyTags.PR_INTERNET_CPID, Encoding.UTF8.CodePage); propertiesStream.AddProperty(PropertyTags.PR_BODY_W, BodyText); if (!string.IsNullOrEmpty(BodyHtml)) { propertiesStream.AddProperty(PropertyTags.PR_HTML, BodyHtml); } // This is experimental code if (string.IsNullOrWhiteSpace(BodyRtf) && !string.IsNullOrWhiteSpace(BodyHtml)) { BodyRtf = "{\\rtf1\\ansi\\ansicpg1252\\fromhtml1 " + BodyHtml + "}"; BodyRtfCompressed = true; } if (!string.IsNullOrEmpty(BodyRtf) && BodyRtfCompressed) { propertiesStream.AddProperty(PropertyTags.PR_RTF_COMPRESSED, RtfCompressor.Compress(Encoding.ASCII.GetBytes(BodyRtf))); propertiesStream.AddProperty(PropertyTags.PR_RTF_IN_SYNC, true); } propertiesStream.WriteProperties(rootStorage, messageSize); }