Hotmail 是我最常用的電子郵件客戶端。雖然目前容量較小、速度常讓人想問候蓋茨的母親,但不可否認其功能豐富、相對穩定,網頁設計更是我們 Web 開發人員的重要參考。
與許多 POP3 伺服器不同,Hotmail 實作了郵件傳送狀態通知(multipart/report)。我先前撰寫的郵件客戶端僅支援 multipart/related、multipart/alternative 和 multipart/mixed 等多媒體類型,對於較少見的類型便忽略了。然而客戶不買單,在未收款前滿足客戶需求是第一優先。
參見 RFC 1892,Sun 中文 JavaMail FAQ 亦有相關說明,摘錄如下:
問: 當郵件無法投遞時,會返回失敗通知。如何偵測這些「退回」郵件?
答: 雖有網際網路標準(multipart/report MIME 類型,參見 RFC 1892)用於報告此類錯誤,但尚未廣泛實作。RFC 1211 深入討論此問題並提供大量範例。
在網路郵件中,特定信箱或用戶是否存在,僅由最終傳送伺服器決定。郵件可能經過多個轉發伺服器(無法偵測錯誤),最後才到達最終伺服器。當最終伺服器偵測到錯誤,會向原寄件者返回失敗通知。雖有多項網路標準討論傳送狀態通知,但大量伺服器不支援新標準,改用特殊技術回傳錯誤郵件,導致將「退回」郵件與原始郵件關聯極為困難(注意:此問題與 JavaMail 無關)。
業界有多種技術與啟發式方法處理此問題,但皆非完美。其中一種技術是「可變信封返回路徑」,詳見 VERP 技術文件。
我修改了程式,僅做簡單處理,程式碼如下:
private void parseMultipart(Multipart multipart) throws Exception {
for (int i = 0, n = multipart.getCount(); i < n; i++) {
BodyPart part = multipart.getBodyPart(i);
String disposition = part.getDisposition();
if (part.isMimeType("multipart/*")) {
this.parseMultipart((Multipart) part.getContent());
} else if (part.isMimeType("text/html")) {
// 處理文字內容
}
// 處理附件、圖片等
else if (part.isMimeType("message/delivery-status")) {
part.setFileName("details.txt"); // 參考 Outlook,內容為傳送狀態通知的郵件頭資訊
saveAttachedFile(part);
} else if (part.isMimeType("message/rfc822")) {
MimeBodyPart mbp = new MimeBodyPart(part.getInputStream());
part.setFileName(mbp.getHeader("Subject", null) + ".eml"); // 參考 Outlook,用退件主旨作檔名
saveAttachedFile(part);
}
}
}
若要更友善,可進一步改進:根據退信找出已寄送資料夾中的原始郵件、觸發通知事件等。但客戶未要求,不再過度設計。
順帶一提,Hotmail 不使用 POP3 協定收信,而是使用 WebDAV 協定。使用 Java 收發 Hotmail 郵件請參考《透過 JavaMail 存取 Hotmail 郵箱》。