private void ProcessMailMessage(IMessage msg, MailMessage outMsg) { //Copy some of the more important headers outMsg.From = MailMessage.CreateEmailAddress(MapiUtils.GetStringProperty(msg, Tags.PR_SENDER_EMAIL_ADDRESS), MapiUtils.GetStringProperty(msg, Tags.PR_SENDER_NAME)); if (MapiUtils.GetStringProperty(msg, Tags.ptagSentRepresentingName) != null) { outMsg.ReplyTo = MailMessage.CreateEmailAddress(MapiUtils.GetStringProperty(msg, Tags.ptagSentRepresentingEmailAddr), MapiUtils.GetStringProperty(msg, Tags.ptagSentRepresentingName)); } outMsg.Subject = MapiUtils.GetStringProperty(msg, Tags.PR_SUBJECT); IMAPITable recipientsTable = null; msg.GetRecipientTable(0, out recipientsTable); using (recipientsTable) { MAPI33.MapiTypes.Value[,] rows; recipientsTable.SetColumns(new Tags[] {Tags.PR_RECIPIENT_TYPE, Tags.PR_EMAIL_ADDRESS, Tags.PR_DISPLAY_NAME}, IMAPITable.FLAGS.Default); for( ;recipientsTable.QueryRows(1, 0, out rows) == Error.Success && rows.Length > 0; ) { MAPI33.WellKnownValues.PR_RECIPIENT_TYPE recipType = (MAPI33.WellKnownValues.PR_RECIPIENT_TYPE)((MapiInt32)rows[0,0]).Value; String emailAddr = null; String emailName = null; if (rows[0, 1] is MapiString) { emailAddr = ((MapiString)rows[0, 1]).Value; } if (rows[0, 2] is MapiString) { emailName = ((MapiString)rows[0, 2]).Value; } AddressType type = AddressType.To; switch (recipType) { case MAPI33.WellKnownValues.PR_RECIPIENT_TYPE.To: type = AddressType.To; break; case MAPI33.WellKnownValues.PR_RECIPIENT_TYPE.Cc: type = AddressType.Cc; break; case MAPI33.WellKnownValues.PR_RECIPIENT_TYPE.Bcc: type = AddressType.Bcc; break; } outMsg.AddRecipient(emailAddr, emailName, type); } } //Set the body. There can be a plain Body, which is the plain text version of the message, //as well as an HtmlBody, which includes rich text formatting via HTML. // //This is complicated by the fact that, in MAPI, there are three possible sources //of the body: PR_BODY (plain text body), PR_BODY_HTML (HTML body), and PR_RTF_COMPRESSED //(RTF body). Based on empirical observation, there is nearly always a PR_BODY, which is not surprising //as every HTML mail client I've ever encountered sends multi-part MIME messages with a plain text version //as well as HTML. PR_BODY_HTML *seems* to be the HTML version of the message as specified by the sender, //and may not be present in plain text messages. // //It only gets complicated with PR_RTF_COMPRESSED (or just PR_RTF for short). Often, HTML messages //don't have a PR_HTML at all, but rather a PR_RTF that has a bunch of RTF tags wrapped around the original //HTML of the message. All messages I've seen have a PR_RTF, however only messages which were originally //received as HTML and subsequently transmogrified into RTF include the \@fromhtml RTF tag. // //Thus, the existence of this \@fromhtml tag is a roundabout way of determining if a message came from //the sender as HTML or text (ignoring for now the third possiblity; an older Exchange client using RTF). // //Therefore, when setting BodyHtml, we first check if PR_RTF includes this \@fromhtml (that's what //MapiUtils.BodyRtfToHtml does, among other things). If it doesn't, BodyHtml is set to null. If it //does, PR_BODY_HTML is checked first, and used to populate BodyHtml if it's non-null. Failing that, the //unwrapped HTML from PR_RTF is used. So: // // Body: Always PR_BODY // HtmlBody: PR_BODY_HTML if PR_BODY_HTML is non-null and PR_RTF includes the \@fromhtml tag // The HTML embedded in PR_RTF if PR_BODY_HTML is null and PR_RTF includes \@fromhtml // Else null. String propVal = null; if ((propVal = MapiUtils.GetLongStringProperty(msg, Tags.PR_BODY)) != null) { outMsg.Body = propVal; } //Check for HTML embedded in PR_RTF byte[] rtf = MapiUtils.GetBodyRtf(msg); if (rtf != null) { outMsg.HtmlBody = MapiUtils.BodyRtfToHtml(rtf); if (outMsg.HtmlBody != null) { //There's HTML. use PR_HTML if it's there, else stick w/ what we have if ((propVal = MapiUtils.GetLongStringProperty(msg, Tags.PR_BODY_HTML)) != null) { outMsg.HtmlBody = propVal; } } } //Copy the attachments CopyAttachments(msg, outMsg); //Output all of the MAPI properties as extended headers, just in case they are needed later DumpMapiProperties(msg, outMsg); }