The ability to rewrite the from address of emails can be exceedingly useful if you are working with consolidating or migrating email systems especially if you have two (or more) companies merging or you're trying to consolidate disparate email system across orgs. Exchange 2007 has a great address rewriting agent that runs on a Edge server which is explained in http://technet.microsoft.com/en-us/library/aa996806.aspx. If you aren't implementing an Edge server as part of your Exchange org then unfortunately you cant use this agent on a Hub server. One solution to this is that you write your own agent to do this which is what this post is about. Now that link I just pointed to has some very important information about the fields you should and shouldn't rewrite in an address rewriting Agent and the reason you shouldn't rewrite particular fields. The Return-Path is probably the one of most note and maybe the one people will tend to want to rewrite because of the visibility of the original address in the header and possible SPAM detection issues where the return-path will be different to the From address etc. But it is probably best to leave it as someone has probably done a lot more testing then you at this and it best not to rewrite it the longer story I'm sure is a lot more complicated.
Envelope From (MAIL FROM)
This field can be known more commonly as one of the P1 headers and is available in a Transport event as the mailitem.FromAddress property. To rewrite this address you need to use the RoutingAddress type e.g
e.MailItem.FromAddress = new RoutingAddress("localPart", "newFromDomain");
Body From and Sender
These fields are part of the P2 headers and are exposed via the EmailMessageClass accessable via the MailItem.Message property. Eg to rewrite these properties
e.MailItem.Message.From.SmtpAddress = localPart + "@" + newFromDomain;
e.MailItem.Message.Sender.SmtpAddress = localPart + "@" + newFromDomain;
Body Reply-To
Because there can be more then one address within the Reply-To property you need some code that will loop through the recipients collection and re-map address's as nessasary. e.g
foreach (EmailRecipient rpReplyTo in e.MailItem.Message.ReplyTo) {
if (rpReplyTo.SmtpAddress != null)
{
String rtSourceAddress = rpReplyTo.SmtpAddress.ToString();
String[] rtSourceArray = rtSourceAddress.Split('@');
if (rtSourceArray[1].ToString().ToLower() == oldFromDomain) {
rpReplyTo.SmtpAddress = rtSourceArray[0].ToString() + "@" + newFromDomain;
}
}
Disposition-Notification-To and Return-Receipt-To
These two properties represent Read Recipient address's the reason there are two is a little complicated and has a little to do with history but the one that relates to the RFC and is the standard property is Disposition-Notification-To which is what is exposed by the EmailMessageClass and can be rewritten with something like.
EmailRecipient dnDispNotice = e.MailItem.Message.DispositionNotificationTo;
String dnSourceAddress = dnDispNotice.SmtpAddress.ToString();
String[] dnSourceArray = dnSourceAddress.Split('@');
if (dnSourceArray[1].ToString().ToLower() == oldFromDomain)
{
dnDispNotice.SmtpAddress = dnSourceArray[0].ToString() + "@" + newFromDomain;
}
The second header Return-Receipt-To which is non standard is a little trickier this isn't exposed as a property by the EmailMessageClass but can be accessed as an AddressHeader when looping through the MIME headers. Rewriting this field is difficult in a Transport agent and the only method I've found that works is to use the MIME writer. To simplify my transport agent i took what might prove to be the wrong approach of just deleting this header. I took the view point that its a redundant header anyway and any compliant client should support the standard DispositionNotificationTo header. To delete the header you can use code like.
HeaderList hlHeaderlist = e.MailItem.Message.RootPart.Headers;
AddressHeader mhRrecp = (AddressHeader)hlHeaderlist.FindFirst("Return-Receipt-To");
hlHeaderlist.RemoveChild(mhRrecp);
If you are looking for some code to read an AddressHeader Email value which you wont be able to do by just looping through the headerlist like normal Text headers you need to use some code that looks like.
Stream messageStream = e.MailItem.GetMimeReadStream();
MimeReader mrMimeReader = new MimeReader(e.MailItem.GetMimeReadStream());
mrMimeReader.ReadFirstChildPart();
mrMimeReader.EnableReadingUnparsedHeaders();
while (mrMimeReader.HeaderReader.ReadNextHeader()) {
if (mrMimeReader.HeaderReader.Name.ToString() == "Return-Receipt-To") {
MimeAddressReader reRecp = mrMimeReader.HeaderReader.AddressReader;
};
}
Im nore sure if this is the best method but the documentation for the MIMEReader class is very limited.
Thats it while its not 100% hopefully it will help if your trying to write your own address rewriting Transport Agent.
Envelope From (MAIL FROM)
This field can be known more commonly as one of the P1 headers and is available in a Transport event as the mailitem.FromAddress property. To rewrite this address you need to use the RoutingAddress type e.g
e.MailItem.FromAddress = new RoutingAddress("localPart", "newFromDomain");
Body From and Sender
These fields are part of the P2 headers and are exposed via the EmailMessageClass accessable via the MailItem.Message property. Eg to rewrite these properties
e.MailItem.Message.From.SmtpAddress = localPart + "@" + newFromDomain;
e.MailItem.Message.Sender.SmtpAddress = localPart + "@" + newFromDomain;
Body Reply-To
Because there can be more then one address within the Reply-To property you need some code that will loop through the recipients collection and re-map address's as nessasary. e.g
foreach (EmailRecipient rpReplyTo in e.MailItem.Message.ReplyTo) {
if (rpReplyTo.SmtpAddress != null)
{
String rtSourceAddress = rpReplyTo.SmtpAddress.ToString();
String[] rtSourceArray = rtSourceAddress.Split('@');
if (rtSourceArray[1].ToString().ToLower() == oldFromDomain) {
rpReplyTo.SmtpAddress = rtSourceArray[0].ToString() + "@" + newFromDomain;
}
}
Disposition-Notification-To and Return-Receipt-To
These two properties represent Read Recipient address's the reason there are two is a little complicated and has a little to do with history but the one that relates to the RFC and is the standard property is Disposition-Notification-To which is what is exposed by the EmailMessageClass and can be rewritten with something like.
EmailRecipient dnDispNotice = e.MailItem.Message.DispositionNotificationTo;
String dnSourceAddress = dnDispNotice.SmtpAddress.ToString();
String[] dnSourceArray = dnSourceAddress.Split('@');
if (dnSourceArray[1].ToString().ToLower() == oldFromDomain)
{
dnDispNotice.SmtpAddress = dnSourceArray[0].ToString() + "@" + newFromDomain;
}
The second header Return-Receipt-To which is non standard is a little trickier this isn't exposed as a property by the EmailMessageClass but can be accessed as an AddressHeader when looping through the MIME headers. Rewriting this field is difficult in a Transport agent and the only method I've found that works is to use the MIME writer. To simplify my transport agent i took what might prove to be the wrong approach of just deleting this header. I took the view point that its a redundant header anyway and any compliant client should support the standard DispositionNotificationTo header. To delete the header you can use code like.
HeaderList hlHeaderlist = e.MailItem.Message.RootPart.Headers;
AddressHeader mhRrecp = (AddressHeader)hlHeaderlist.FindFirst("Return-Receipt-To");
hlHeaderlist.RemoveChild(mhRrecp);
If you are looking for some code to read an AddressHeader Email value which you wont be able to do by just looping through the headerlist like normal Text headers you need to use some code that looks like.
Stream messageStream = e.MailItem.GetMimeReadStream();
MimeReader mrMimeReader = new MimeReader(e.MailItem.GetMimeReadStream());
mrMimeReader.ReadFirstChildPart();
mrMimeReader.EnableReadingUnparsedHeaders();
while (mrMimeReader.HeaderReader.ReadNextHeader()) {
if (mrMimeReader.HeaderReader.Name.ToString() == "Return-Receipt-To") {
MimeAddressReader reRecp = mrMimeReader.HeaderReader.AddressReader;
};
}
Im nore sure if this is the best method but the documentation for the MIMEReader class is very limited.
Thats it while its not 100% hopefully it will help if your trying to write your own address rewriting Transport Agent.