|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThe great thing about the POP mail protocol is that it is a well-documented open standard, making writing a mail client to collect mail from a POP box a relatively painless process. Armed with basic knowledge of POP or SMTP it is possible to write proxies which do a variety of useful things, such as filter out spam or junk mail, or provide an e-mail answering machine service. Unfortunately, in trying to write a standalone client for Hotmail, the world’s most popular web-based mailing system, the fact that no POP gateway exists rapidly becomes a problem. Despite the lack of POP-support, connecting to Hotmail without using a web-browser is possible. Outlook Express allows users to retrieve, delete, move and send messages, connecting directly to a standard Hotmail or MSN mailbox. By using a HTTP packet sniffer, it is possible to monitor communication between Outlook Express and Hotmail, making it possible to determine how the direct mailbox connection is made. Outlook Express uses an undocumented protocol commonly referred to as HTTPMail, allowing a client to access Hotmail using a set of HTTP/1.1 extensions. This article explains some of the features of HTTPMail, and how best to connect to Hotmail using a C# client. The sample source code accompanying this article uses COM interop to leverage XMLHTTP as the transport service. The XMLHTTP component provides a complete HTTP implementation including authentication together with ability to set custom headers before sending HTTP requests to the server. Connecting to the HTTPMail Hotmail GatewayThe 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 standard WebDAV service. As we are using C#, we could use the TCP and HTTP classes provided by the .NET framework within the // Get the namespace.
using MSXML2;
...
// Create the object.
xmlHttp_ = new XMLHTTP();
In order to connect to a secure server, the WebDAV protocol requires HTTP/1.1 authentication. The initial request sent by a HTTPMail client uses the WebDAV <?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 void open(string method, string url, bool async, string user, string password);
The first argument specifies the HTTP method used to open the connection, such as // 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 ListThe request sent to services.msn.com is typically redirected several times. After load balancing, we are finally connected to a free Hotmail server and authenticated. This redirection, with appropriate authentication, is handled by the XMLHTTP component. Once connected, the server will also ask us to set various cookies, valid for the current session (again this is all handled automatically 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, among other useful information, the URL locations of the folders within the mailbox. 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 two mailbox folders that we are interested in are the inbox and sendmsg folders, used to retrieve and send mail items respectively. There are various ways to parse XML under C#, but since we are confident of our XML structure, // 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 // 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 outbox which are valid for this session have been determined, it is possible send and retrieve and e-mail. Enumerating Folder MailItemsGiven the URL of a mailbox folder (such as the Inbox folder) we can direct a WebDAV request to the folder's URL in order to list mail items within the folder. The sample console application defines a managed type // Initiate.
ArrayList mailItems = new ArrayList();
To request mail item data, such as the mail subject, and the to and from addresses, we generate the following XML-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 the // Get the mail info.
xmlHttp_.open("PROPFIND", folderUrl, false, null, null);
xmlHttp_.send(getMailQuery);
string folderInfo = xmlHttp_.responseText;
Following a successful request, the server will respond with an XML stream containing information for each <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 find that contained within each // Holders.
MailItem mailItem = null;
// Load the Xml.
StringReader reader = new StringReader(folderInfo);
XmlTextReader xml = new XmlTextReader(reader);
Parsing Folder InformationIn order the parse the XML in a single pass, we create a new // 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 the sample console application) enumerates the
The sample code reads the XML nodes as set out above, to extract information for each mail item found within the returned Folder Info XML data stream. Retrieving Folder MailOnce /// <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 MailIn order to retrieve mail, the /// <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 the // 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. ConclusionHotmail is the world's largest provider of free, Web-based e-mail. However, the only non-web mail client with direct access to Hotmail is Outlook Express. Since the HTTPMail protocol is undocumented, other vendors are discouraged from providing a similar service. In this article we saw how to connect to a Hotmail mailbox, enumerate inbox mail items, and send and retrieve e-mail, using C# and the XMLHTTP component. Sample code accompanying this article contains a .NET assembly, demonstrating that connecting to Hotmail via HTTPMail can be as simple as working with any other mail protocol such as POP3, IMAP4 or SMTP.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||