Hotmail 使用 HTTPMail 協議(基於 WebDAV),非標準 POP3/IMAP。本文為系列第二部分,涵蓋:列舉資料夾內容、XML 解析郵件屬性、接收郵件內容、發送郵件完整流程。
<?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>
using MSXML2; // 需參考 Microsoft XML, v6.0 (MSXML2)
XMLHTTP xmlHttp = new XMLHTTP();
string folderUrl = "https://xxx.oe.hotmail.com/.../Inbox/"; // 從第一步取得
string query = "..."; // 上方 XML 字串
xmlHttp.open("PROPFIND", folderUrl, false, null, null); // 同步,已驗證會話
xmlHttp.send(query);
string responseXml = xmlHttp.responseText;
<D:multistatus>
<D:response>
<D:href>https://xxx/Inbox/msg123.eml</D:href>
<D:propstat>
<D:prop>
<hm:read>1</hm:read>
<m:from>John Doe</m:from>
<m:subject>Hello</m:subject>
<m:date>2024-01-15T10:30:00</m:date>
<D:getcontentlength>2048</D:getcontentlength>
</D:prop>
</D:propstat>
</D:response>
...
</D:multistatus>
public class MailItem
{
public string Url { get; set; }
public bool IsRead { get; set; }
public string From { get; set; }
public string To { get; set; }
public string Subject { get; set; }
public DateTime Date { get; set; }
public long Size { get; set; }
public bool HasAttachment { get; set; }
}
var mailItems = new List();
MailItem current = null;
using (var reader = new StringReader(responseXml))
using (var xml = new XmlTextReader(reader))
{
while (xml.Read())
{
string name = xml.Name;
XmlNodeType nodeType = xml.NodeType;
// 遇到 開始新郵件
if (name == "D:response" && nodeType == XmlNodeType.Element)
current = new MailItem();
// 遇到 完成一筆
if (name == "D:response" && nodeType == XmlNodeType.EndElement)
{
if (current != null)
{
mailItems.Add(current);
current = null;
}
}
// 元素內容處理
if (nodeType == XmlNodeType.Element && current != null)
{
if (xml.Read()) // 移到內容
{
switch (name)
{
case "D:href": current.Url = xml.Value; break;
case "hm:read": current.IsRead = xml.Value == "1"; break;
case "m:from": current.From = xml.Value; break;
case "m:to": current.To = xml.Value; break;
case "m:subject": current.Subject = xml.Value; break;
case "m:date": DateTime.TryParse(xml.Value, out current.Date); break;
case "D:getcontentlength": long.TryParse(xml.Value, out current.Size); break;
case "m:hasattachment": current.HasAttachment = xml.Value == "1"; break;
}
}
}
}
}
public string LoadMail(MailItem mail)
{
xmlHttp.open("GET", mail.Url, false, null, null);
xmlHttp.send(null);
return xmlHttp.responseText; // 完整郵件原始內容 (MIME 格式)
}
public void SendMail(string from, string fromName, string to, string subject, string body)
{
string quote = "\""; // \u0022
string timeStamp = DateTime.Now.ToString("ddd, dd MMM yyyy HH:mm:ss");
string postBody = "";
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\r\n";
postBody += "\r\n";
postBody += body; // 純文字內容
// 發送
xmlHttp.open("POST", _sendUrl, false, null, null); // _sendUrl 從第一步取得
xmlHttp.setRequestHeader("Content-Type", "message/rfc821");
xmlHttp.send(postBody);
// 檢查結果
if (xmlHttp.status != 200)
throw new Exception($"發送失敗: {xmlHttp.status} {xmlHttp.statusText}");
}
| 操作 | HTTP 方法 | 關鍵 Header | 資料格式 |
|---|---|---|---|
| 列舉資料夾 | PROPFIND | Content-Type: text/xml | WebDAV propfind XML |
| 接收郵件 | GET | - | MIME (RFC822) |
| 發送郵件 | POST | Content-Type: message/rfc821 | RFC821 郵件格式 |