2016年6月4日

[.Net] System.Net.Mail 寄信到 Zimbra 主旨亂碼的問題


如下圖,主旨太長就被截斷
根據 RFC2047 上講的
An 'encoded-word' may not be more than 75 characters long
看樣子要嘛就是 Zimbra 處理的時候有問題,要嘛就是 System.Net.Mail 發信的時候處理有問題。

顯示信件原始內容來看,直接拿 subject 裡最後一行去解 Base64 會發現有亂碼!
解回來的內容開頭是亂碼:

問題追到這邊大概有答案了,就是 System.Net.Mail 雖然會幫我們處理不能超過 75 個字元的問題,但是沒有考慮到雙位元的文字處理,而 Zimbra 分段去處理主旨的編碼,就會造成中文字被分割成兩個半的問題。

所以追一下 System.Net.Mail 的 Source Code 會發現,如果自己處理掉編碼換行這段,System.Net.Mail 就會以我們處理過的原封不動丟出去。

雖然這邊註解是說會 re-encode before sending ,但是再往 DecodeHeaderValue 裡面看,會發現,他只處理 Base64 編碼過的,如果是又做過 75 個字元切段又做 Base64 的話,他就會直接放棄 re-encode 。

用 ? 去切字串,如果切出來不是  5 個,就放棄。( MimeBasePart.cs )

所以,如果我們自己處理有考慮中文字的 75 個字元切段後再 Base64 編碼的話,應該就可以解決這個問題。

private string folding(string input)
        {
            string tempInput = input;
            string result = "";
            const int f = 37; // 一個字一律算兩個 bytes 最長只能 75 所以 75/2 https://www.ietf.org/rfc/rfc2047.txt
            for (int i = 0, len = tempInput.Length; i <= len; i += f)
            {
                string x = tempInput.Substring(i, (i + f >= len ? len - i : f));
                result += string.Format(@"=?UTF-8?B?{0}?=", Convert.ToBase64String(Encoding.UTF8.GetBytes(x)));
            }
            if (result.Split("?".ToCharArray()).Length == 5) result = result + "=?UTF-8?B??="; // 只切到一次還會會被解回去,所以補一個空的
            return result;
        }