473,320 Members | 2,124 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

C# A

Access Hotmail Using C#
By Wouter van Vugt
Introduction
Hotmail is probably the most popular e-mailing facility found on the web
today. Millions of people around the world use it for everyday communication
with friends and relatives. The main reason for it's popularity is the fact
that you can use the service absolutely free of charge. Users are able to
read their emails daily using a web interface and there is a client program
available, called Outlook Express, which most people will only use at home.
Only these two methods are officially available to read mail from your
Hotmail account. This is strange because Outlook Express is not the safest
client to use for reading emails, especially the spam-sensitive Hotmail
accounts. Secondly, web-mail is generally considered to be an annoyance,
that is only there to offer access to your account from locations such as
schools and public internet access points.

No longer! This document will enable you to build your own client, using a
sure and solid way to communicate with Hotmail in the same way as Outlook
does. It will be shown how the protocol can be used to your own advantage,
it isn't at all hard to understand either.

To build a working client we will need to know about the way Outlook
communicates with Hotmail. The protocol that is being used is called
Httpmail. Even when completely unfamiliar with this protocol, it is not hard
to understand. It looks like an XML structure in which some elements are
placed that you want to find. The server will respond with the same kind of
structure with the queried elements filled with information. I will not
start explaining how the protocol looks, if you want to find out it's
available from SourceForge.

Now that the manner of communication is clear, there are some other things
that need clarification. For instance; how do we authenticate ourselves, and
how the heck are we going to implement such authentication? Usually this
will mean calculating hashes and keeping passwords in a safe place. At first
sight this could seem like a enormous task to implement. It turned out to be
not so difficult at all.

After a review of the SourceForge article a few things are noticeably
interesting. The authentication is done via Http headers and is described in
an RFC (2617). It's even called HTTP authentication, there have got to be
some classes in the .NET Framework that are able to do that! To parse the
response in a safe manner, we will make use of XPath, it should be up to the
job and there are classes available for that too!

Building the client
To build the client two components need to be created:

a.. A proxy that is able to 'speak' Httpmail and is able to do HTTP
authentication as described in the RFC.
b.. The actual client that accesses Hotmail via the proxy and uses XPath
for parsing of the responses.

Instead of building a custom proxy another method could be used. Microsoft
ships a component called XmlHttp that is able to make XML based HTTP
requests. The use of this class presented several problems:

1.. A request for a limited amount of properties, such as the
msgfolderroot, returns all the possible responses for that request. So a
query to obtain only the msgfolderroot would return information about all
the mailboxes. This isn't fatal of course but it does show that the class
could use some work. The proxy class that is built in this document does not
show this problem.
2.. The component generates weird looking HTTP packets. For instance, the
query is placed in the HTTP headers, it should be sent as a data section.
This will probably result in erroneous server responses, and may be causing
problem 1.
3.. Most importantly; the component will not work for some email
addresses, while the only configuration that can be made is the username and
password. This problem is the biggest show-stopper when using XmlHttp.

Before reading the rest of this document; I assume that you have basic
knowledge of the following subjects:

a.. Httpmail
b.. XPath
c.. Basic C#
d.. Cookies

Please note that the code is fully documented in the source files.
Hotmail Proxy
The proxy will be responsible for making HTTP requests to the Hotmail
servers. This will require various things to be implemented:

a.. Make requests using the PROPFIND method, instead of the usual GET and
POST.
b.. Handle redirection.
c.. Handle cookies.
d.. Handle HTTP authentication.
e.. Send XML data to the remote host, which is basically the same as
sending a string.

To send the requests and receive the responses the class will make use of
the HttpWebRequest and HttpWebResponse classes. Cookies will be stored in a
CookieContainer. And most importantly authentication can be done by adding a
NetworkCredential object to the HttpWebRequest. This will fully implement
HTTP authentication, how easy is that! All classes reside in the System.Net
namespace and are part of the .NET Framework, this means; easy use and
documented code!

Let's start with building the class framework.

public class HotmailProxy
{
private CookieContainer ccContainer;

public HotmailProxy ()
{
ccContainer = new CookieContainer();
}
}

Ok, not to interesting. Now let's get on with building the really important
part, building the method that actually sends the client's request to a
remote host. It will implement all the proxy's requirements that are stated
above!

The method will need to accommodate the request as a byte array, the
destination, and the necessary credentials for authentication(which can be
null if cookies are set after logging in):

private string SendRequestTo(byte[] requestBytes, Uri destination,
NetworkCredential credential)The first thing that needs to be done is build
the HttpWebRequest object. The object will need to have a specific UserAgent
header, otherwise Hotmail will decline the request (pfff, how conceited).
Also it will have a few other headers initialized.

Implementation of the authentication process is done by adding the
NetworkCredential object to the request, that object was quite difficult to
build, read about it further on.

{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(destination);
webRequest.Method = "PROPFIND";
webRequest.Accept = "*/*";
webRequest.AllowAutoRedirect = false;
webRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0;
..NET CLR 1.0.3705)";
webRequest.CookieContainer = new CookieContainer();
webRequest.ContentLength = requestBytes.Length;
webRequest.ContentType = "text/xml";
webRequest.Credentials = credential;
webRequest.CookieContainer.Add(ccContainer.GetCook ies(destination));Now the
request has been build, we will write the request to the requested host and
try to get a response:

try
{
Stream reqStream = webRequest.GetRequestStream();
reqStream.Write(requestBytes,0,requestBytes.Length );
reqStream.Close();
HttpWebResponse webResponse =
(HttpWebResponse)webRequest.GetResponse();After verification that a response
was received, cookies and redirection can be handled. Otherwise an exception
will be thrown (build your own MailException of some sort for this). Cookies
should be compared to the stored cookies by matching names. Redirection is
done by checking the HTTP status code, and recursively calling this method.

if (webRequest.HaveResponse)
{
// First handle cookies
foreach(Cookie retCookie in webResponse.Cookies)
{
bool cookieFound = false;
foreach(Cookie oldCookie in ccContainer.GetCookies(destination))
{
if (retCookie.Name.Equals(oldCookie.Name))
{
oldCookie.Value = retCookie.Value;
cookieFound = true;
}
}
if (!cookieFound)
ccContainer.Add(retCookie);
}
// Next is redirection
if ((webResponse.StatusCode == HttpStatusCode.Found) ||
(webResponse.StatusCode == HttpStatusCode.Redirect) ||
(webResponse.StatusCode == HttpStatusCode.Moved) ||
(webResponse.StatusCode == HttpStatusCode.MovedPerm..y))
{
// Get new location and call recursively
WebHeaderCollection headers = webResponse.Headers;
return SendRequestTo(requestBytes,new
Uri(headers["location"]),credential);
}Now that redirection has been handled and all cookies are set, the
response stream can be read to receive the final server's response. This
finishes the method.

// Read response
StreamReader stream = new StreamReader(webResponse.GetResponseStream());
string responseString = stream.ReadToEnd();
stream.Close();
return responseString;
}
throw new Exception("No response received from host.");
}
catch(WebException e)
{
throw new Exception("Exception occured while sending request.",e);
}
}To complete the class a public interface will need to be provided, that
calls the SendRequestTo method. The request is an XML string, so the method
will need to translate that string into a byte array. The following code is
pretty basic. Check the input, build the byte array and send away!

public string SendRequest(string request, Uri destination, NetworkCredential
credential)
{
if(request == null || request.Trim().Length == 0)
throw new ArgumentNullException("request");
else if (destination == null)
throw new ArgumentNullException("destination");
else
{
byte[] xmlBytes = Encoding.ASCII.GetBytes(request);
return SendRequestTo(xmlBytes,destination, credential);
}
}Because authentication isn't done in all the requests, the following method
has been made available to make further requests from a selected host after
logging in.

public string SendRequest(string request, Uri destination)
{
return SendRequest(request,destination,null);
}Hoera! This completes our proxy, it is now able to do all the things we
started out to do! It is able to send equests to Hotmail servers across the
world.

Hotmail Client
Now for implementing a class that provides access to Hotmail. The following
example will show how to connect to Hotmail using the proxy, get the
msgfolderroot and request some information about mailboxes. The
msgfolderroot can be queried for information such as mailbox names, message
count and unread count. This will allow clients to determine how many
messages are new, without having to download all the messages. (Which would
be a stupid method to implement such behavior.) Using this example and the
webpage provided at the top of this document, it will be easy to implement
the rest of an email client (it was for me!).

First let's begin again with the class framework:

public class HotmailClient
{
private HotmailProxy hHttp = null;

public HotmailClient()
{}
}Connect()
Now for the only public method, Connect(). This method will connect to
Hotmail using HTTP authentication, it will parse the response to obtain the
Url of the msgfolderroot. Next this Url will be used to determine some
mailbox information.

First of all the proxy class en credentials needed for authentication are
build:

public void Connect(string username, string password)
{
hHttp = new HotmailHttp();
NetworkCredential credentials = new
NetworkCredential(username,password,null);Pffew, that was hard, one complete
line of code. Easy enough as you can see! The next job will be to build the
XML query for the msgfolderroot. (XmlHttp will return all kinds of nonsense
about the inbox, advertisements and others while as you can see only the
msgfolderroot is requested).

string query = "<?xml version='1.0'?>" +
"<D:propfind xmlns:D='DAV:' " +
"xmlns:h='http://schemas.microsoft.com/hotmail/' " +
"xmlns:hm='urn:schemas:httpmail:'>" +
"<D:prop>" +
"<hm:msgfolderroot/>" +
"</D:prop>" +
"</D:propfind>";The query and the credentials can be used to get a response
from the Hotmail gateway located at
http://services.msn.com/svcs/hotmail/httpmail.asp. This will cause several
redirections and cause the HTTP authentication to take place. All can be
done with just a single line of code.

try
{
string hotmailEntryPoint =
"http://services.msn.com/svcs/hotmail/httpmail.asp";
string response = hHttp.SendRequest(query, new
Uri(hotmailEntryPoint),credentials);
// Verify response
if (response == null || response.Trim().Length == 0)
throw new Exception();The Hotmail server will respond with the Url of the
msgfolderroot. This Url is placed in the XML based response, so it can be
found using Xpath. Another method has been build to do just that.

// Parse the response, further verifying it.
Uri folderRootUri = ParseConnectResponse(response);With the now obtained
Url, information about all the mailboxes on the server can be retrieved.

// Obtain available folders using folderRootUrl
RetrieveMailboxes(folderRootUri);
}
catch(Exception e)
{
// Something went wrong.
throw new MailException("Exception occured while connecting to remote
host.",e);
}
}This completes our first method. As you can see it calls two other methods
which we will now construct, these two contain the interesting parts,
parsing the response! But first an important helper method needs to be
build, without it all XPath queries fail.

CreateNamespaceManager (XmlNameTable table)
To obtain the Url of the folder root we will use XPath. The problem lies in
the fact that XPath will not return results for nodes that are declared in a
namespace. It is able to do so, but information about namespaces need to be
stated with the XPath query. This can be done by constructing an
XmlNamespaceManager that knows about the namespaces in the Hotmail
responses. As these namespaces appear to be really constant, a special
method can be build to construct the necessary object.

private XmlNamespaceManager CreateNamespaceManager(XmlNameTable table)
{
XmlNamespaceManager m = new XmlNamespaceManager(table);
m.AddNamespace("hm","urn:schemas:httpmail:");
m.AddNamespace("D","DAV:");
m.AddNamespace("m","urn:schemas:mailheader:");
m.AddNamespace("c","urn:schemas:contacts:");
m.AddNamespace("h","http://schemas.microsoft.com/hotmail/");
return m;
}ParseConnectResponse(string response)
To parse the response there are a two things that need to be done; place the
returned response in an XmlDocument and parse the document using XPath to
obtain the Url of the msgfolderroot.

private Uri ParseConnectResponse(string response)
{
try
{
// Load response into XmlDocument
XmlDocument dom = new XmlDocument();
dom.LoadXml(response);
// Query XmlDocument for msgfolderroot node.
string xpath = "//hm:msgfolderroot";
XmlNamespaceManager context = CreateNamespaceManager(dom.NameTable);
XmlNode folderRoot = dom.SelectSingleNode(xpath,context);
// Verify node
if (folderRoot == null)
throw new Exception("Node '" + xpath + "' not found.");
// Get node text and verify,
string folderRootUrl = folderRoot.InnerText;
if ((folderRootUrl == null) || (folderRootUrl.Trim().Length == 0))
throw new Exception("No url found in node '" + xpath + "'.");
try
{
// Return the uri, this may result in a
// UriFormatException
return new Uri(folderRootUrl);
}
catch
{
throw new MailException("Url found in node '" + xpath + "' is invalid:" +
folderRootUrl);
}
}
catch(Exception e)
{
// Something went wrong.
throw new Exception("Error occured while parsing connect response.",e);
}
}The way this method works is pretty basic now. The correct query to make
can be determined using the webpage stated in the introduction, it's also
possible to use a network analyzer for this purpose. Probably you will need
to use both. To parse the response, the XmlNamespaceManager is build, and
XPath is used.

RetrieveMailboxes(Uri folderRootUrl)
The last method in this example will retrieve information about all
mailboxes on the server. Basically it does the same thing as the first two
methods combined, so you should already be able to build it yourself. The
method is shown here for completion sake. It will build a query and send it
to the Url of the msgfolderroot now determined.

private void RetrieveMailboxes(Uri folderRootUrl)
{
try
{
// Build the needed query
string query = "<?xml version='1.0'?>" +
"<D:propfind xmlns:D='DAV:' " +
"xmlns:hm='urn:schemas:httpmail:'>" +
"<D:prop>" +
"<D:displayname/>" +
"<hm:special/>" +
"<hm:unreadcount/>" +
"<D:visiblecount/>" +
"</D:prop>" +
"</D:propfind>";
// Get a response from server No Credentials are used!
string response = hHttp.SendRequest(query,folderRootUrl);
// Verify response
if (response == null || response.Trim().Length == 0)
throw new ApplicationException("No response received from host.");
// Load response into XmlDocument
XmlDocument dom = new XmlDocument();
dom.LoadXml(response);
// Query XmlDocument for all mailboxes using XPath
string xpath = "//D:response";
XmlNamespaceManager context = CreateNamespaceManager(dom.NameTable);
XmlNodeList mailBoxNodes = dom.SelectNodes(xpath,context);
// Parse each node found
foreach(XmlNode mailBoxNode in mailBoxNodes)
{
try
{
// Direct mapping using XPath, should not
// result in any errors
// as long as Hotmail keeps it protocol
// the same.
string type =
mailBoxNode.SelectSingleNode("descendant::hm:speci al",context).InnerText;
string nameUrl =
mailBoxNode.SelectSingleNode("descendant::D:href", context).InnerText;
int visibleCount =
Int32.Parse(mailBoxNode.SelectSingleNode("descenda nt::D:visiblecount",contex
t).InnerText);
int unreadCount =
Int32.Parse(mailBoxNode.SelectSingleNode("descenda nt::hm:unreadcount",contex
t).InnerText);

Console.WriteLine("MailBox found: " + type + "\r\n" + "\turl: " +
nameUrl + "\r\n" + "\tVisible: " + visibleCount + "\r\n" + "\tUnread: "
+
unreadCount + "\r\n");
}
catch(Exception e)
{
Console.WriteLine("Exception occured while obtaining mailbox info: " +
e.Message);
}
}
}
catch(Exception e)
{
// Something went wrong.
throw new ApplicationException("Error occured while retrieving available
mailboxes.",e);
}
}Conclusion
As you can see accessing Hotmail from C# can be as easy as any other type of
mail server. The code works without any strange loops or use of classes that
function only partly. With this code it should be easy to build your own
email client that is able to access Hotmail with the same type of interface
as you would build for IMAP or POP. Happy coding!

About the author
I am a 23 year old student, currently looking for a project so I can finish
my bachelor study of Computer Science. I first came in contact with CSharp
because of my work as a developer for various software development projects
done at my college. Recent activities include assisting in the development
of a large web-based learning environment to be used at my college's site.
The Hotmail client was a part of that environment. For that project I've
also built a comprehensive web spider based on XML, XPath and XSLT.

What I like to do is creating things, writing code is a very suitable way to
do just that! Currently my main interest lies in writing reliable code and
programming in various languages and with various technologies. I am
researching Sun's Jini technology, am very interested in the field of
Artificial Intelligence and will build my own computer brain one day! Or I
may shift my attention to the field of management, but hey; Life is long,
Time is short. much to do, much to see I will definitely continue coding,
although it might not always be for my profession. But right now you could
call me a complete coding junkie.
Nov 15 '05 #1
0 1282

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: William C. White | last post by:
Does anyone know of a way to use PHP /w Authorize.net AIM without using cURL? Our website is hosted on a shared drive and the webhost company doesn't installed additional software (such as cURL)...
2
by: Albert Ahtenberg | last post by:
Hello, I don't know if it is only me but I was sure that header("Location:url") redirects the browser instantly to URL, or at least stops the execution of the code. But appearantely it continues...
3
by: James | last post by:
Hi, I have a form with 2 fields. 'A' 'B' The user completes one of the fields and the form is submitted. On the results page I want to run a query, but this will change subject to which...
0
by: Ollivier Robert | last post by:
Hello, I'm trying to link PHP with Oracle 9.2.0/OCI8 with gcc 3.2.3 on a Solaris9 system. The link succeeds but everytime I try to run php, I get a SEGV from inside the libcnltsh.so library. ...
1
by: Richard Galli | last post by:
I want viewers to compare state laws on a single subject. Imagine a three-column table with a drop-down box on the top. A viewer selects a state from the list, and that state's text fills the...
4
by: Albert Ahtenberg | last post by:
Hello, I have two questions. 1. When the user presses the back button and returns to a form he filled the form is reseted. How do I leave there the values he inserted? 2. When the...
1
by: inderjit S Gabrie | last post by:
Hi all Here is the scenerio ...is it possibly to do this... i am getting valid course dates output on to a web which i have designed ....all is okay so far , look at the following web url ...
2
by: Jack | last post by:
Hi All, What is the PHP equivilent of Oracle bind variables in a SQL statement, e.g. select x from y where z=:parameter Which in asp/jsp would be followed by some statements to bind a value...
3
by: Sandwick | last post by:
I am trying to change the size of a drawing so they are all 3x3. the script below is what i was trying to use to cut it in half ... I get errors. I can display the normal picture but not the...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
0
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.