Hotmail Using C# – An HTTPMail Client Under .NET

The great thing about the POP mail protocolis that it is a well-documented open standard, making writing a mailclient to collect mail from a POP box a relatively painless process.Armed with basic knowledge of POP, or SMTP it is possible to writeproxies which do a variety of useful things, such filter out spam orjunk mail, or provide an e-mail answering machine service.Unfortunately, in trying to write a standalone client for Hotmail, theworld?s most popular web-based mailing system, the fact that no POPgateway exists rapidly becomes a problem.

Despite the lack of POP-support, connectingto Hotmail without using a web-browser is possible. Outlook Expressallows users to retrieve, delete, move and send messages, connectingdirectly to a standard Hotmail or MSN mailbox. By using a HTTP packetsniffer, it is possible to monitor communication between OutlookExpress and Hotmail, making it possible to determine how the directmailbox connection is made.

Outlook Express uses an undocumentedprotocol commonly referred to as HTTPMail, allowing a client to accessHotmail using a set of HTTP/1.1 extensions. This article explains someof the features of HTTPMail, and how best to connect to Hotmail using aC# client. The sample source code accompanying this article uses COMinterop to leverage XMLHTTP as the transport service. The XMLHTTPcomponent provides a complete HTTP implementation, includingauthentication together with ability to set custom headers beforesending HTTP requests server-side.

Connecting to the HTTPMail Hotmail Gateway

The default HTTPMail gateway for Hotmail boxes is located at http://services.msn.com/svcs/hotmail/httpmail.asp.Although undocumented, the HTTPMail protocol is actually a standardWebDAV service. As we are using C#, we could use the TCP and HTTPclasses provided by the .NET framework within the System.Net namespace.Since we are working with WebDAV, it is simpler to use XMLHTTP toconnect to Hotmail under C#. Referencing the MSXML2 component, providesan interop assembly which may be accessed directly. Note that in thecode snippets contained within this article, variables suffixed with anunderscore refer to member fields declared elsewhere within the samplecode:

// Get the namespace.
using MSXML2;

// Create the object.
xmlHttp_ = new XMLHTTP();

In order to connect to a secure server, theWebDAV protocol requires HTTP/1.1 authentication. The initial requestsent by a HTTPMail client uses the WebDAV PROPFIND method to query fora set of properties. These include the URL of the Hotmail advertisementbar together with the location of mailbox folders:

<?xml version="1.0"?>
<D:propfind xmlns:D="DAV:" xmlns:h="http://schemas.microsoft.com/hotmail/" xmlns:hm="urn:schemas:httpmail:">
<D:prop>
<h:adbar/>
<hm:contacts/>
<hm:inbox/>
<hm:outbox/>
<hm:sendmsg/>
<hm:sentitems/>
<hm:deleteditems/>
<hm:drafts/>
<hm:msgfolderroot/>
<h:maxpoll/>
<h:sig/>
</D:prop>
</D:propfind>

Sending the initial request via XMLHTTP begins by specifying the WebDAV server URL, and generating the initial XML request:

// Specify the server URL.
string serverUrl = "http://services.msn.com/svcs/hotmail/httpmail.asp";

// Build the query.
string folderQuery = null;
folderQuery += "<?xml version='1.0'?><D:propfind xmlns:D='DAV:' ";
folderQuery += "xmlns:h='http://schemas.microsoft.com/hotmail/' ";
folderQuery += "xmlns:hm='urn:schemas:httpmail:'><D:prop><h:adbar/>";
folderQuery += "<hm:contacts/><hm:inbox/><hm:outbox/><hm:sendmsg/>";
folderQuery += "<hm:sentitems/><hm:deleteditems/><hm:drafts/>";
folderQuery += "<hm:msgfolderroot/><h:maxpoll/><h:sig/></D:prop></D:propfind>";

The HTTPXML component provides an open() method used to establish a connection to a HTTP server:

void open(string method, string url, bool async, string user, string password);

The first argument specifies the HTTPmethod used to open the connection, such as GET, POST, PUT, orPROPFIND. To connect to the Hotmail gateway, we specify the PROPFINDmethod to query the mailbox. This and other HTTP methods are used toretrieve folder information, collect mail items and send new mail. Notethat the open() method allows for the possibility of asynchronous calls(enabled by default) which is preferred for a graphical mail client.Since the sample code is a console application, we set this parameterto false. For authentication, we specify a username and password. Notethat under XMLHTTP, the component will display a login window if theseparameters are missing and the site requires authentication. To connectto the Hotmail gateway we open the connection, set the PROPFIND requestheader to our XML-based query, and then send the request with a nullbody:

// Open a connection to the Hotmail server.
xmlHttp_.open("PROPFIND", serverUrl, false, username, password);

// Send the request.
xmlHttp_.setRequestHeader("PROPFIND", folderQuery);
xmlHttp_.send(null);

Parsing the Mailbox Folder List

The request sent to services.msn.comis typically redirected several times. After load balancing, we arefinally connected to a free Hotmail server, and authenticated. Thisredirection, with appropriate authentication, is handled by the XMLHTTPcomponent. Once connected, the server will also ask us to set variouscookies, valid for the current session (again this is all handledautomatically by XMLHTTP). Upon sending the initial connection request,the server will return an XML-based response:

// Get the response.
string folderList = xmlHttp_.responseText;

The returned response will contain, amongother useful information, the URL locations of the folders within themailbox. For example:

<?xml version="1.0" encoding="Windows-1252"?>
<D:response>

<D:propstat>
<D:prop>
<h:adbar>AdPane=Off*…</h:adbar>
<hm:contacts>http://law15.oe.hotmail.com/…</hm:contacts>
<hm:inbox>http://law15.oe.hotmail.com/…</hm:inbox>
<hm:sendmsg>http://law15.oe.hotmail.com/…</hm:sendmsg>
<hm:sentitems>http://law15.oe.hotmail.com/…</hm:sentitems>
<hm:deleteditems>http://law15.oe.hotmail.com/…</hm:deleteditems>
<hm:msgfolderroot>http://law15.oe.hotmail.com/…</hm:msgfolderroot>

</D:prop>
</D:response>
</D:multistatus>

In the sample console application, the twomailbox folders that we are interested in are the inbox and sendmsgfolders, used to retrieve and send mail items respectively. There arevarious ways to
parse XM
L under C#, but since we are confident of ourXML structure, System.XML.XmlTextReader provides fast, forward-onlyaccess. We initialise the XML reader by converting the XML string datainto a string stream:

// Initiate.
inboxUrl_ = null;
sendUrl_ = null;

// Load the Xml.
StringReader reader = new StringReader(folderList);
XmlTextReader xml = new XmlTextReader(reader);

The XML is parsed by iterating through each node, picking out the hm:inbox and hm:sendmsg nodes:

// Read the Xml.
while(xml.Read())
{
// Got an element?
if(xml.NodeType == XmlNodeType.Element)
{
// Get this node.
string name = xml.Name;

// Got the inbox?
if(name == "hm:inbox")
{
// Set folder.
xml.Read();
inboxUrl_ = xml.Value;
}

// Got the send message page?
if(name == "hm:sendmsg")
{
// Set folder.
xml.Read();
sendUrl_ = xml.Value;
}
}
}

Once the URLs for the inbox and outboxwhich are valid for this session have been determined, it is possiblesend and retrieve and e-mail.

Enumerating Folder MailItems

Given the URL of a mailbox folder (such asthe Inbox folder) we can direct a WebDAV request to the folder's URL inorder to list mail items within the folder. The sample consoleapplication defines a managed type MailItem, used to store mailinformation for a folder item. Folder enumeration begins by initalisingan array of MailItems:

// Initiate.
ArrayList mailItems = new ArrayList();

To request mail item data, such as the mailsubject, and the to and from addresses, we generate the followingXML-based WebDAV query:

<?xml version="1.0"?>
<D:propfind xmlns:D="DAV:" xmlns:hm="urn:schemas:httpmail:" xmlns:m="urn:schemas:mailheader:">
<D:prop>
<D:isfolder/>
<hm:read/>
<m:hasattachment/>
<m:to/>
<m:from/>
<m:subject/>
<m:date/>
<D:getcontentlength/>
</D:prop>
</D:propfind>

The followig C# code generates the XML query string:

// Build the query.
string getMailQuery = null;
getMailQuery += "<?xml version='1.0'?><D:propfind xmlns:D='DAV:' ";
getMailQuery += "xmlns:hm='urn:schemas:httpmail:' ";
getMailQuery += "xmlns:m='urn:schemas:mailheader:'><D:prop><D:isfolder/>";
getMailQuery += "<hm:read/><m:hasattachment/><m:to/><m:from/><m:subject/>";
getMailQuery += "<m:date/><D:getcontentlength/></D:prop></D:propfind>";

This request is sent via XMLHTTP using thePROPFIND method, similar to the way in which the mailbox folder listwas requested above. This time round we set the request body to be thequery, and folder information is returned. Since we have already beenauthenticated for this session, there is no need to resupply theusername and password on the XMLHTTP open() call:

// Get the mail info.
xmlHttp_.open("PROPFIND", folderUrl, false, null, null);
xmlHttp_.send(getMailQuery);
string folderInfo = xmlHttp_.responseText;

Following a successful request, the serverwill respond with an XML stream containing information for eachMailItem contained within the folder.

<D:multistatus>
<D:response>
<D:href>
http://sea1.oe.hotmail.com/cgi-bin/hmdata/…
</D:href>
<D:propstat>
<D:prop>
<hm:read>1</hm:read>
<m:to/>
<m:from>Mark Anderson</m:from>
<m:subject>RE: New Information</m:subject>
<m:date>2002-08-06T16:38:39</m:date>
<D:getcontentlength>1238</D:getcontentlength>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>

Looking at the XML fragment above, we findthat contained within each <D:response> node are a set of fieldsidentifying the MailItem, including the <D:href> tag, which willlater allow us to retrieve the item. We can again useSystem.XML.XmlTextReader to parse this XML text stream. We firstinitalise the stream reader:

// Holders.
MailItem mailItem = null;

// Load the Xml.
StringReader reader = new StringReader(folderInfo);
XmlTextReader xml = new XmlTextReader(reader);

Parsing Folder Information

In order the parse the XML in a single pass,we create a new MailItem instance on opening the <D:response>element, and store the instance when we reach the end of the tag. Inbetween, we extract and set MailItem fields:

// Read the Xml.
while(xml.Read())
{
// Sections.
string name = xml.Name;
XmlNodeType nodeType = xml.NodeType;

// E-mail?
if(name == "D:response")
{
// Start?
if(nodeType == XmlNodeType.Element)
{
// Create a new mail item.
mailItem = new MailItem();
}

// End?
if(nodeType == XmlNodeType.EndElement)
{
// Store the last mail.
mailItems.Add(mailItem);

// Reset.
mailItem = null;
}
}

// Got an element?
if(nodeType == XmlNodeType.Element)
{
// Mail field.
if(name == "D:href")
{
// Load.
xml.Read();
mailItem.Url = xml.Value;
}

// Mail field.
if(name == "hm:read")
{
// Load.
xml.Read();
mailItem.IsRead = (xml.Value == "1");
}

// Load other MailItem fields, etc…
}
}

The above code (part of that found thesample console application) enumerates the MailItems found within thegiven folder. For each MailItem we extract the following fields:

XML Node Description
D:href The URL used to retrieve this HttpMail item.
hm:read Flag set if this e-mail is read.
m:to Who the mail was sent to.
m:from Who the mail was sent from.
m:subject The mail subject.
m:date Timestamp in the format [date]T[time]
D:getcontentlength The size of this e-mail (in bytes).

The sample code reads the XML nodes as setout above, to extract information for each mail item found within thereturned Folder Info XML data stream.

Retrieving Folder Mail

Once MailItems in the folder have beenenumerated, the mail URL (valid for the given session) for a MailItemcan be used to retrieve the actual e-mail. This is done by sending aHTTP/1.1 GET request to the Hotmail server, at the given
URL. TheLoadMail() function defined within the sample code takes a MailIteminstance, and returns the content of the mailbox e-mail:

/// <summary>
/// Loads the given mail item.
/// </summary>
public string LoadMail(MailItem mailItem)
{
// Get the Url.
string mailUrl = mailItem.Url;

// Open a connection to the Hotmail server.
xmlHttp_.open("GET", mailUrl, false, null, null);

// Send the request.
xmlHttp_.send(null);

// Get the response.
string mailData = xmlHttp_.responseText;

// Return the mail data.
return mailData;
}

Sending New Mail

In order to retrieve mail, the LoadMail()method (see above) performs a HTTP/1.1 GET request. Similarly, a POSTis sent to the sendmsg URL in order to send an e-mail from the Hotmailbox. The sample console application also contains a SendMail() methodwhich maybe invoked once connected to the mailbox:

/// <summary>
/// Sends an e-mail.
/// </summary>
public void SendMail(string from, string fromName,
string to, string subject, string body)
{

}

We begin by setting up a quote string (used later) as well generating a mail time stamp:

// Quote character.
string quote = "\u0022";

// Generate the time stamp.
DateTime now = DateTime.Now;
string timeStamp = now.ToString("ddd, dd MMM yyyy hh:mm:ss");

The HTTPMail protocol follows an SMTP-like communication scheme (See RFC 821). Outlook express sends out mail in MIME format, but for demonstrations purposes we simply send a plain text e-mail:

// Build the post body.
string postBody = null;

// Dump mail headers.
postBody += "MAIL FROM:<" + from + ">\r\n";
postBody += "RCPT TO:<" + to + ">\r\n";
postBody += "\r\n";
postBody += "From: " + quote + fromName + quote + " <" + from + ">\r\n";
postBody += "To: <" + to + ">\r\n";
postBody += "Subject: " + subject +"\r\n";
postBody += "Date: " + timeStamp + " -0000\n";
postBody += "\r\n";

// Dump mail body.
postBody += body;

To send the mail, we need to set theContent-Type request header to message/rfc821, indicating that thisrequest contains a body which follows RFC 821. We POST the request body generated above to the sendmsg URL obtained during connection time:

// Open the connection.
xmlHttp_.open("POST", sendUrl_, false, null, null);

// Send the request.
xmlHttp_.setRequestHeader("Content-Type", "message/rfc821");
xmlHttp_.send(postBody);

Given a valid destination mailbox, Hotmail will send the mail to our desired location.

Conclusion

Hotmail is the world's largest provider offree, Web-based e-mail. However, the only non-web mail client withdirect access to Hotmail is Outlook Express. Since the HTTPMailprotocol is undocumented, other vendors are discouraged from providinga similar service. In this article we saw how to connect to a Hotmailmailbox, enumerate inbox mail items, and send and retrieve e-mail,using C# and the XMLHTTP component. Sample code accompanying thisarticle contains a .NET assembly, demonstrating that connecting toHotmail via HTTPMail can be as simple as working with any other mailprotocol such as POP3, IMAP4 or SMTP.

 

About the Author

Kais Dukes is currently a softwarespecialist at a world leader in derivatives pricing, where he isconcentrating on large-scale software architecture using .NETtechnology. He is finishing a PhD in Artificial Intelligence, where heis researching methods of programming a computer in natural language.He is also completing a book on Templates and Generative Programming inC++. Kais can be reached at kaisdukes@hotmail.com.

Twitter Digg Delicious Stumbleupon Technorati Facebook Email

No comments yet... Be the first to leave a reply!