首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

Lumisoft.NET 常見亂碼與錯誤排解:日期解析失敗、SSL 握手錯誤、MIME 標頭編碼亂碼完整修正方案

     編輯:ASP.NET 基礎

前言

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 +0800
  • 06-May-2023 19:01:44

方案 A:修改 Lumisoft 原始碼 (最徹底)

找到 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 保持不變 */ }
}

方案 B:應用層預處理 Header (不動原始碼)

在呼叫 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;

問題二:SSL/TLS 握手失敗 IOException: 由於意外的數據包格式,握手失敗。

常見原因與對應 Port 設定

郵箱類型協定伺服器PortSSL/StartTLSLumisoft 參數
GmailSMTPsmtp.gmail.com465Implicit SSLConnect(host, 465, true)
GmailSMTPsmtp.gmail.com587StartTLSConnect(host, 587, false) → 再手動 STLS (Lumisoft 需自行實作)
GmailPOP3pop.gmail.com995Implicit SSLConnect(host, 995, true)
Outlook/Office365SMTPsmtp.office365.com587StartTLS同上,需 StartTLS
OutlookPOP3outlook.office365.com995Implicit SSLConnect(host, 995, true)
QQ 郵箱SMTPsmtp.qq.com465/587SSL/StartTLS465 用 true;587 需 StartTLS
QQ 郵箱POP3pop.qq.com995Implicit SSLConnect(host, 995, true)
163/126SMTPsmtp.163.com465/587/25SSL/StartTLS/Plain465 true;587 StartTLS;25 false
163/126POP3pop.163.com995/110SSL/Plain995 true;110 false
企業自建 (Exchange/Postfix)SMTP自訂25/587/465視設定務必與管理員確認

排查清單

  1. Port 與 UseSsl 必須匹配:Implicit SSL (465, 995) → UseSsl=true;StartTLS (587) → UseSsl=false 並在連線後送 STARTTLS 指令 (Lumisoft 無內建,需自行 client.SendCommand("STARTTLS") 並升級串流)。
  2. 防火牆/殺毒軟體攔截:暫時關閉測試,或加入例外。
  3. 憑證驗證失敗:自簽憑證需在 ServicePointManager.ServerCertificateValidationCallback 忽略錯誤 (僅測試環境)。
  4. 目標伺服器不支援該 Port:用 telnet host port 或 openssl s_client -connect host:port 驗證連通性。

問題三:MIME 標頭編碼亂碼 (=?utf-8?B?5rWL6K+V?=)

現象

主旨、寄件者顯示名稱、附件檔名出現 =?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);

進階技巧:強制以 UTF-8 解析整個 Header (避開預設編碼猜測)

若專案不想逐欄位呼叫 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),舊專務可先引入本文輔助類並抽象介面,為未來遷移鋪路。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved