piątek, 23 listopada 2007

Badmail coding adventure

Mood: Saturday party - heeeeaaaar I come !!!!!
Soundtrack: "Monkey dance" remixed (no comments)

Introduction
In one of the projects, we had a problem to ensure that the sent email, was received. As it is easy to guess there is number of reasons, why the thing may fail and one of the most obvious reasons is the wrong email address. The standard SMTP mechanisms does not provide any solution as it is assumed that is more Mail Server problem, than a protocol itself. I use the standard IIS SMTP preconfigured in well known way, but there is still couple of ways, how you can deal with a problem.

First of all you need some simple application to send the emails with use of SMTP service. To save the time you may use one of the application available on the web again, like the one. Then there are following options…

Custom headers
There is quite cool service on the web describing how the System.Net.Mail library works and at one of the paragraphs describes that

By adding custom headers, we can tag email messages with information that may not be visible to the end user, yet still might be important to us. This is especially useful in tracking bounced emails.

Well this is pretty close to what one need. Adding invisible header and reading it is not a problem with one exception… Where will we get the information that the email is bounced?

Badmail
This is a specific folder configurable in Messages>Badmail directory (by default C:\Inetpub\mailroot\Badmail), where all non-send emails are delivered after the predefined number of unsuccessful tries. You may easily test it sending the email to non existing client and reseting IIS service – the email will appear in Badmail directory immediately (usually you must to have a while).

Now, each bounced email will generate 3 files in fact:
- BAD mail which is the bounced email packed with leading message and delivery status
- BDP, which contains binary information related to the file
- BDR, which contains information about why the file was not deliverable

In fact what we need is the BAD file analysis – the plan minimum is just look for some unique name of custom header and check its content defined during sending the email as the error message ready to display.

Well… that is not elegant solution, so it is much better to parse fully the BAD file.

MIME reader
Lastly, on CodeProject there was released .Net POP3 MIME Client library, which is ready to use library – plug & play. You just need to make one small change in MIMEReader library and change one of the conditions in ParseBody:

//Parse a new child mime part.
[…]
else if (string.Equals(_entity.ContentType.MediaType, MediaTypes.MessageRfc822, StringComparison.InvariantCultureIgnoreCase)
&& ((_entity.ContentDisposition == null)
string.Equals(_entity.ContentDisposition.DispositionType, DispositionTypeNames.Attachment, StringComparison.InvariantCultureIgnoreCase)))
{
[…]


Now you are ready to create your own function to parse all bad files from bad folder…

public static ArrayList GetShortListOfBadmails(string badMailFolder)
{
ArrayList result = new ArrayList();

//Get all BAD files in Bad emails folder
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(badMailFolder);
foreach (System.IO.FileInfo f in dir.GetFiles("*.bad"))
{
System.IO.StreamReader s = f.OpenText();
string[] strArr = StreamToArray(s);

MimeReader mimeReader = new Net.Mime.MimeReader(strArr);
MimeEntity mimeEntity = mimeReader.CreateMimeEntity();
//MailMessageEx mimeEmial = mimeEntity.ToMailMessageEx();
/*
* Children[0] is the leading message (text)
* Children[1] is the "Reporting-MTA: dns;akoszlajda-lap.pwpw.pl" message (delivery status)
* Children[2] is the email which was supposed to be sent (rfc822)
*/
int i = 0;
bool rfc822Found = false;
for (i = 0; i < rfc822found =" string.Equals(mimeEntity.Children[i].ContentType.MediaType,"> 0))
{
MimeEntity nonSentEmail = mimeEntity.Children[i].Children[0];
try
{
string strErrorDate = mimeEntity.Headers.GetValues("Date")[0];
string strTo = nonSentEmail.Headers.GetValues("To")[0];
string strEmailDate = nonSentEmail.Headers.GetValues("Date")[0];
string err = strErrorDate + " - Mail wysłany do " + strTo + " " + strEmailDate + " nie został dostarczony";
result.Add(err);
}
catch
{
//if there is lack of the information do not do anything
}
}
s.Close();
}

return result;
}


Other option
Optionally the problem with bounced emails may be send to specified email address and you may parse the emails there using the .Net POP3 MIME Client library, but you must somehow filter that email is the report about bounced email. That may be a problem.

Conclusion
The solution is very narrow and it is rather the proof of concept. There are is a number of other reasons why the email is not delivered (like the target email box is full) and the solution was not tested with Microsoft Exchange, but there should not be a big difference as the system has also Badmail folder mechanism.

On request, the zipped solution may be provided.
PS.
I know that there are some significant obstacles with Badmail installation on Windows 2003 using standard SMTP! I am not quite sure if it is impossible or there is some specific workaround.

Brak komentarzy:

 
web metrics