Lumisoft.NET 是功能強大的郵件元件,但因 MIME 標準複雜、郵件客戶端實作不一,實務上常遇到日期解析拋出例外、SSL/TLS 握手失敗、中文主旨/寄件者/附件檔名亂碼 等問題。本文針對這三大類高頻錯誤,提供原始碼層級修正與應用層繞過方案,並附上常用郵箱伺服器設定對照表。
ParseException: Header field 'Date' parsing failed.LumiSoft.Net.ParseException: Header field 'Date' parsing failed.
at LumiSoft.Net.Mail.Mail_Message.get_Date()
Mail_Message.Date 屬性內部呼叫 MIME_Utils.ParseRfc2822DateTime,僅支援 RFC 2822/5322 格式 (如 Mon, 26 Apr 2023 08:56:52 GMT)。部分郵件系統 (特別是國內企業郵箱、舊版 Exchange、某些自動發信程式) 產出非標準格式,例如:
2023-05-06 19:01:44 (ISO-like,無星期、時區)2023/05/06 19:01:44 +080006-May-2023 19:01:44找到 Mail_Message.cs 中 Date 屬性的 get 區塊,在 catch 區塊加入 DateTime.TryParse 容錯:
public DateTime Date
{
get
{
if (this.IsDisposed) throw new ObjectDisposedException(this.GetType().Name);
MIME_h h = this.Header.GetFirst("Date");
if (h != null)
{
try
{
return MIME_Utils.ParseRfc2822DateTime(((MIME_h_Unstructured)h).Value);
}
catch
{
// === 新增:嘗試通用日期解析 ===
string dateString = ((MIME_h_Unstructured)h).Value;
if (DateTime.TryParse(dateString, out DateTime dt))
return dt;
// 仍失敗則拋出原例外 (或回傳 MinValue 視專案策略)
throw new ParseException("Header field 'Date' parsing failed.");
}
}
return DateTime.MinValue;
}
set { /* 原有 setter 保持不變 */ }
}
在呼叫 Mail_Message.ParseFromByte 前,將原始 Header 字串以 UTF-8 重新編碼,強制 Lumisoft 以 UTF-8 解析標頭,間接減少編碼導致的日期字串變形:
// 取得原始 Header 字串 (已含 Date 標頭)
string rawHeader = pop3Message.HeaderToString(); // 或 smtpMessage.HeaderToString()
// 強制 UTF-8 位元組陣列
byte[] utf8Bytes = Encoding.UTF8.GetBytes(rawHeader);
// 解析
var mime = Mail_Message.ParseFromByte(utf8Bytes);
// 此時 mime.Date 解析成功機率大增
DateTime dt = mime.Date;
IOException: 由於意外的數據包格式,握手失敗。| 郵箱類型 | 協定 | 伺服器 | Port | SSL/StartTLS | Lumisoft 參數 |
|---|---|---|---|---|---|
| Gmail | SMTP | smtp.gmail.com | 465 | Implicit SSL | Connect(host, 465, true) |
| Gmail | SMTP | smtp.gmail.com | 587 | StartTLS | Connect(host, 587, false) → 再手動 STLS (Lumisoft 需自行實作) |
| Gmail | POP3 | pop.gmail.com | 995 | Implicit SSL | Connect(host, 995, true) |
| Outlook/Office365 | SMTP | smtp.office365.com | 587 | StartTLS | 同上,需 StartTLS |
| Outlook | POP3 | outlook.office365.com | 995 | Implicit SSL | Connect(host, 995, true) |
| QQ 郵箱 | SMTP | smtp.qq.com | 465/587 | SSL/StartTLS | 465 用 true;587 需 StartTLS |
| QQ 郵箱 | POP3 | pop.qq.com | 995 | Implicit SSL | Connect(host, 995, true) |
| 163/126 | SMTP | smtp.163.com | 465/587/25 | SSL/StartTLS/Plain | 465 true;587 StartTLS;25 false |
| 163/126 | POP3 | pop.163.com | 995/110 | SSL/Plain | 995 true;110 false |
| 企業自建 (Exchange/Postfix) | SMTP | 自訂 | 25/587/465 | 視設定 | 務必與管理員確認 |
UseSsl=true;StartTLS (587) → UseSsl=false 並在連線後送 STARTTLS 指令 (Lumisoft 無內建,需自行 client.SendCommand("STARTTLS") 並升級串流)。ServicePointManager.ServerCertificateValidationCallback 忽略錯誤 (僅測試環境)。telnet host port 或 openssl s_client -connect host:port 驗證連通性。主旨、寄件者顯示名稱、附件檔名出現 =?charset?B?base64data?= 或 =?charset?Q?quoted-printable?= 形式,未正確解碼。
Lumisoft Mail_Message.ParseFromByte 預設以 UTF-8 解碼整個原始位元組流。若郵件標頭實際為 Big5/GB2312,會導致中文變成亂碼或解碼失敗。
public static class MimeHeaderDecoder
{
private static readonly Regex EncodedWordRegex = new Regex(
@"=\?(?<charset>[^?]+)\?(?<encoding>[BQ])\?(?<data>[^?]+)\?=",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static string Decode(string input)
{
if (string.IsNullOrEmpty(input)) return input;
return EncodedWordRegex.Replace(input, m =>
{
string charset = m.Groups["charset"].Value.Trim().ToLower();
string encoding = m.Groups["encoding"].Value.ToUpper();
string data = m.Groups["data"].Value;
// 正規化字集別名
if (charset.Contains("gb2312") || charset.Contains("gbk") || charset.Contains("euc-cn") || charset.Contains("euccn"))
charset = "gb2312";
else if (charset.Contains("utf8") || charset.Contains("utf-8"))
charset = "utf-8";
else if (charset.Contains("big5"))
charset = "big5";
else if (charset.Contains("iso-8859-1") || charset.Contains("latin1"))
charset = "iso-8859-1";
try
{
byte[] bytes = encoding == "B"
? Convert.FromBase64String(data)
: DecodeQuotedPrintable(data);
return Encoding.GetEncoding(charset).GetString(bytes);
}
catch
{
return m.Value; // 解碼失敗保留原文
}
});
}
private static byte[] DecodeQuotedPrintable(string input)
{
using (var ms = new MemoryStream())
{
for (int i = 0; i < input.Length; i++)
{
if (input[i] == '=' && i + 2 < input.Length)
{
string hex = input.Substring(i + 1, 2);
if (byte.TryParse(hex, System.Globalization.NumberStyles.HexNumber, null, out byte b))
{
ms.WriteByte(b);
i += 2;
continue;
}
}
ms.WriteByte((byte)input[i]);
}
return ms.ToArray();
}
}
}
// 主旨
string subject = MimeHeaderDecoder.Decode(mimeHeader.Subject);
// 寄件者顯示名稱
string fromDisplay = MimeHeaderDecoder.Decode(mimeHeader.From[0].DisplayName);
// 附件檔名
string fileName = MimeHeaderDecoder.Decode(entity.ContentDisposition.Param_FileName);
若專案不想逐欄位呼叫 Decode,可在 ParseFromByte 前預處理:
// 1. 取得原始 Header 字串 (Lumisoft 提供 HeaderToString)
string rawHeader = message.HeaderToString();
// 2. 轉為 UTF-8 位元組
byte[] utf8Bytes = Encoding.UTF8.GetBytes(rawHeader);
// 3. 解析
var mime = Mail_Message.ParseFromByte(utf8Bytes);
// 4. 此時 Subject、From.DisplayName、To.DisplayName 等皆已正確解碼
// (前提是原始 Header 確實為 UTF-8 編碼的 encoded-word)
Console.WriteLine(mime.Subject); // 正常顯示中文
| 問題 | 核心原因 | 最快修正 | 徹底解決 |
|---|---|---|---|
| 日期解析失敗 | 非 RFC2822 日期格式 | 應用層 DateTime.TryParse 容錯 |
修改 Mail_Message.Date getter 加入通用解析 |
| SSL 握手失敗 | Port/SSL 模式不匹配、防火牆、憑證 | 對照上表確認 Port 與 UseSsl | 改用 MailKit 內建 StartTLS、憑證驗證回呼 |
| 標頭/檔名亂碼 | 字集非 UTF-8、encoded-word 未解碼 | 套用 MimeHeaderDecoder.Decode |
預處理 Header 強制 UTF-8、或遷移 MailKit |
Lumisoft.NET 在處理真實世界郵件時,上述三類問題幾乎必遇。本文提供的原始碼修正與應用層輔助類可即時缓解痛點。但長期來看,Lumisoft 更新緩慢、缺乏現代協定支援、同步 API 難以整合進非同步架構,建議新專務直接採用 MailKit (MimeKit),舊專務可先引入本文輔助類並抽象介面,為未來遷移鋪路。