三、列舉文件夾內容
得到了信箱文件夾(如收件箱)的URL之後,就可以向該文件夾的URL發送WebDAV請求列舉其內容。示例程序定義了一個托管類型MailItem,用來保存文件夾裡一項內容(即一個郵件)的信息。文件夾內容列舉從初始化一個MailItems數組開始:
// 初始化
ArrayList mailItems = new ArrayList();
為獲得郵件主題、收件人地址、發件人地址之類的郵件基本信息,我們要用到下面XML格式的WebDAV查詢:
<?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>
生成上述XML查詢字符串的C#代碼:
// 構造查詢
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>";
就象前面獲取信箱文件夾清單的方式一樣,上述請求也通過XMLHTTP用PROPFIND方法發送,這次我們把請求的正文設置成查詢字符串。由於當前會話的用戶身份已經通過驗證,所以XMLHTTP open()調用中不必再提供用戶名字和密碼:
// 獲取郵件信息
XMLHttp_.open("PROPFIND", folderUrl, false, null, null);
XMLHttp_.send(getMailQuery);
string folderInfo = XMLHttp_.responseText;
如果請求成功,服務器返回的應答XML流包含了該文件夾中各個郵件的信息:
<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>
...
觀察服務器返回的應答,我們發現每一個節點包含一組標識郵件的域,例如通過標記可提取出郵件。下面我們再次使用System.XML.XmlTextReader解析這個XML數據流,首先初始化流讀取器:
MailItem mailItem = null;
// 裝入XML
StringReader reader = new StringReader(folderInfo);
XmlTextReader xml = new XMLTextReader(reader);
四、分析郵件基本信息
為了遍歷一次就解析好整個XML文檔,我們在每次打開元素時就創建一個新的MailItem實例,一遇到標記的末尾就保存該實例,在此期間,我們提取並設置MailItem的域:
// 讀取XML數據
while(XML.Read())
{
string name = XML.Name;
XmlNodeType nodeType = XML.NodeType;
// 是一個email?
if(name == "D:response")
{
// 開始?
if(nodeType == XMLNodeType.Element)
{
// 創建一個新的MailItem
mailItem = new MailItem();
}
// 結束?
if(nodeType == XMLNodeType.EndElement)
{
// 保存email
mailItems.Add(mailItem);
// 清除變量
mailItem = null;
}
}
// 是一個元素?
if(nodeType == XMLNodeType.Element)
{
// 郵件的URL屬性
if(name == "D:href")
{
// 繼續讀取
XML.Read();
mailItem.Url = XML.Value;
}
// 郵件的“已閱讀”屬性
if(name == "hm:read")
{
// 繼續讀取
XML.Read();
mailItem.IsRead = (XML.Value == "1");
}
// 其他MailItem的屬性...
}
}
上面的代碼枚舉指定文件夾內的每一個MailItem,分別提取各個MailItem的下列屬性:
XML節點 說明
D:href 用來提取郵件的URL
hm:read 如果郵件已閱讀,則該標記被設置
m:to 收件人
m:from 發件人
m:subject 郵件主題
m:date 時間標記
D:getcontentlength 郵件的大小(字節數)
五、接收郵件
枚舉出文件夾裡面的MailItem之後,我們就可以利用MailItem的URL獲得郵件本身,只需要向Hotmail服務器發送一個HTTP/1.1 GET請求就可以了。示例代碼中的LoadMail()函數輸入一個MailItem實例作為參數,返回郵件的內容:
/// <summary>
/// 下載MailItem指定的郵件
/// </summary>
public string LoadMail(MailItem mailItem)
{
// 郵件的URL
string mailUrl = mailItem.Url;
// 打開Hotmail服務器連接
XMLHttp_.open("GET", mailUrl, false, null, null);
// 發送請求
XMLHttp_.send(null);
// 獲取應答
string mailData = XMLHttp_.responseText;
// 返回郵件數據
return mailData;
}
六、發送郵件
LoadMail()方法通過發送HTTP/1.1 GET請求獲取郵件,類似地,用Hotmail發件箱發送郵件時我們提交一個POST請求,如下面的SendMail()方法所示。
/// <summary>
/// 發送一個郵件
/// </summary>
public void SendMail(string from, string fromName,
string to, string subject, string body)
{
...
}
首先准備好後面要用到的引號字符以及郵件的時間標記:
// 引號字符
string quote = "\u0022";
// 時間標記
DateTime now = DateTime.Now;
string timeStamp = now.ToString("ddd, dd MMM yyyy hh:mm:ss");
HTTPMail協議采用與SMTP相似的通信模式。Outlook Express用MIME格式發送郵件,但為簡單計,本例我們只發送純文本的郵件:
// 構造POST請求的內容
string postBody = null;
// 郵件頭.
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";
// 郵件正文
postBody += body;
發送郵件時,我們要把Content-Type請求頭設置成message/rfc821,表示這個請求包含一個遵從RFC821的消息。最後要做的就是把郵件發送到服務器:
// 打開連接
XMLHttp_.open("POST", sendUrl_, false, null, null);
// 發送請求
XMLHttp_.setRequestHeader("Content-Type", "message/rfc821");
XMLHttp_.send(postBody);
只要目標地址正確無誤,Hotmail就會把郵件發送到目的地。
結束語:
Hotmail是世界上最大的免費Web郵件提供商。但是,Hotmail使用的HTTPMail協議是非公開的,從而為編寫直接訪問Hotmail的客戶程序帶來了困難。本文示范了如何在C#環境中利用XMLHTTP組件直接連接到Hotmail,以及如何發送和接收郵件,證明了通過HTTPMail連接Hotmail可以做到象使用POP3、IMAP4、SMTP等協議一樣簡單。