問題已經發到了開發者社區 developercommunity.visualstudio.com/content/pro…css
涉及到的Github倉庫: github.com/xiangyuecn/…html
.Net開發者社區富文本編輯器太難用了,仍是簡書的編輯器好用,而後掘金的版面好看,最後仍是喜歡cnblog裏面能夠修改版面css。git
盡瞎說大實話。 從新碼一份好看的。github
出現問題的函數:服務器
//https://source.dot.net/#System.Net.Mail/System/Net/Mail/SmtpClient.cs,966
//https://referencesource.microsoft.com/#System/net/System/Net/mail/SmtpClient.cs,892
void SendMailCallback(IAsyncResult result) {
...
//注意這個ServerSupportsEai,這個位置是allowUnicode參數
message.BeginSend(writer, DeliveryMethod != SmtpDeliveryMethod.Network,
ServerSupportsEai, new AsyncCallback(SendMessageCallback), result.AsyncState);
...
}
複製代碼
ServerSupportsEai
所在位置爲allowUnicode
參數。SmtpClient
中全部涉及到allowUnicode
參數的地方,賦值都爲IsUnicodeSupported()
返回值。但惟一這一處是例外。框架
咱們看看IsUnicodeSupported
函數:異步
//https://referencesource.microsoft.com/#System/net/System/Net/mail/SmtpClient.cs,382
private bool IsUnicodeSupported() {
if (DeliveryMethod == SmtpDeliveryMethod.Network) {
//注意看這裏的ServerSupportsEai和SmtpDeliveryFormat
return (ServerSupportsEai && (DeliveryFormat == SmtpDeliveryFormat.International));
}
else {
return (DeliveryFormat == SmtpDeliveryFormat.International);
}
}
複製代碼
DeliveryFormat
咱們能夠賦值,咱們來找找ServerSupportsEai
是在哪裏取值的:編輯器
//https://referencesource.microsoft.com/#System/net/System/Net/mail/smtpconnection.cs,280
internal void ParseExtensions(string[] extensions) {
...
//若是服務器支持SMTPUTF8,那麼ServerSupportsEai=true
else if (String.Compare(extension, 0, "SMTPUTF8", 0, 8, StringComparison.OrdinalIgnoreCase) == 0) {
((SmtpPooledStream)pooledStream).serverSupportsEai = true;
}
...
}
複製代碼
SendMailCallback
是SmtpClient.SendAsync
(SendMailAsync
會調用SendAsync
)調用的,so,異步操做已經徹底不受咱們設置的DeliveryFormat
參數控制了,UTF-8內容(如中文)轉不轉碼徹底看對方郵件服務器心情!!!函數
SmtpClient
對象DeliveryFormat
屬性賦值爲SmtpDeliveryFormat.SevenBit
,要求郵件使用 7 位 ASCII 的傳遞格式,而且用異步方法來發送;原本Subject
、附件文件名
等裏面的UTF-8內容(如中文)將會被轉碼;但若是郵件服務器EHLO
返回了SMTPUTF8
,那麼SmtpClient對象
將會將UTF-8內容不轉碼直接發送出去!致使發送出去的數據內容和預期的數據內容不一致!!!測試
同步方法Send
不受此影響。
SendMailCallback
函數中的ServerSupportsEai
應該換成統一的IsUnicodeSupported
,Bug就解決。
DKIM簽名功能寫好後測試了不少個郵箱,都能經過驗證。但隔一天測試卻發現沒有一個郵箱經過驗證,而且下載下來的郵件源碼body部分和本地額外保存的一份有很大出入,表如今郵件主題、附件文件名,本地是Base64編碼,下載下來的是中文漢字。
首先發現問題的是outlook郵箱,他們家會告訴你DKIM簽名是否正確,本地直接發送郵件沒有一個經過簽名驗證的,但經過郵箱服務器發送卻都是好的。對比直髮和服務器發的郵件源碼區別,發現郵箱服務器的沒有中文,直髮的裏面中文的地方全是中文。
看樣子中文部分有問題,而後試着把郵件裏面的中文所有換成英文,發送,又能夠了!想了一下昨天測試好像所有是英文,由於郵件內容寫了一次基本上就不會改了。
到了這時候,感受還覺得是outlook服務器進行了什麼處理,難道郵箱服務器發郵件用的協議和咱們用Smtp協議發郵件的協議有出入?但並無找到什麼相關的資料。而後測試了QQ郵箱、網易yeah.net,而且抓了一下包看了一下,發現切換SmtpClient.DeliveryFormat
參數,使用SevenBit
(此值爲默認值)(中文會被編碼)QQ郵箱沒問題,網易有問題;使用International
(中文不編碼)QQ郵箱有問題,網易反倒沒問題。
抓包發現使用SevenBit
時,中文部分給QQ郵箱發送的是Base64編碼,給網易發送的是中文內容,本地保存的是Base64編碼(和簽名時使用到的郵件內容一致);使用International
時正好相反。簽名的數據和發送的數據不一致,致使了無論怎麼改這個參數,都有一個是錯的。
爲何會這樣?查閱.Net源碼,一路看編碼部分,發現基本上每一個涉及到字符編碼、發送的地方都會傳入allowUnicode
參數,全部allowUnicode
=
SmtpClient.IsUnicodeSupported()
,但有惟一的一處例外:
[此處忽略,見上文]
SendMailCallback
是SmtpClient.SendAsync
(SendMailAsync
會調用SendAsync
)調用的,so,異步操做已經徹底不受咱們設置的DeliveryFormat
參數控制了,中文轉不轉碼徹底看對方郵件服務器心情!!!函數中的ServerSupportsEai
應該換成統一的IsUnicodeSupported
,Bug就解決。
但,咱們無法去改這個地方,那麼上Hook吧,把SmtpClient.ServerSupportsEai
Hook一下,若是是SendMailCallback
調用的就return IsUnicodeSupported()
。
但,DotNetDetour庫能夠Hook String.Length
屬性,但無法HookSmtpClient.ServerSupportsEai
屬性,不知道啥緣由。最後調試煩了放棄了。
結尾使用SmtpClient.Send
沒有這種問題,就把異步操做所有換成了同步,代碼還少了很多。Bug修理完畢,給outlook、QQ、網易發英文、中文郵件都能經過DKIM簽名驗證。