C# 在.net Framework 4.6.1中使用mailkit支持多内容编码
我正在使用.NETFramework4.6.1构建一个电子邮件客户端,它将从邮箱获取电子邮件并显示在电子邮件客户端上。目前我正在使用s22.Imap的C# 在.net Framework 4.6.1中使用mailkit支持多内容编码,c#,.net,mailkit,C#,.net,Mailkit,我正在使用.NETFramework4.6.1构建一个电子邮件客户端,它将从邮箱获取电子邮件并显示在电子邮件客户端上。目前我正在使用s22.Imap的ImapClient.GetMessage()方法来检索电子邮件内容。它适用于附件和大多数默认编码的bodyContent 但是我的一些邮件类型是CodePage=932和EncodingName=“Japanese(Shift-JIS)”。我无法获取这些电子邮件,因为它们对大多数BodyEncoding属性/属性抛出了System.NotSupp
ImapClient.GetMessage()
方法来检索电子邮件内容。它适用于附件和大多数默认编码的bodyContent
但是我的一些邮件类型是CodePage=932
和EncodingName=“Japanese(Shift-JIS)”
。我无法获取这些电子邮件,因为它们对大多数BodyEncoding属性/属性抛出了System.NotSupportedException
在对s22.Imap的github问题进行搜索时,有人建议使用mailkit而不是s22.Imap。我想了解更多关于mailkit中如何处理此编码部分的信息。还想知道是否有一种默认方法来处理未知代码页类型的编码。您可以阅读这篇博客文章,其中解释了大多数C#MIME解析器出错的原因以及为什么MimeKit可以处理多个字符集编码 是时候对mime解析器大发雷霆了。。。 警告:建议观众自行决定 我应该从哪里开始 我想我应该先说我痴迷于MIME,尤其是MIME解析器。不,真的。我被迷住了。不相信我?在这一点上,我已经编写和/或使用了几个MIME解析器。它开始于我大学时代在Spruce上的工作,它有一个糟糕得可怕的MIME解析器,所以当你进一步阅读我关于糟糕的MIME解析器的咆哮时,请记住:我曾经在那里工作过,我写过一个糟糕的MIME解析器 正如少数人所知,我最近开始实现一个名为的C#MIME解析器。在我进行这项工作的过程中,我一直在GitHub和Google上搜索其他MIME解析器,以了解它们提供的API类型。我想也许我会找到一个提供了设计良好的API的API,它会激励我。也许,由于某种奇迹,我找到了一个实际上相当不错的,我可以贡献自己的作品,而不是从头开始写自己的作品(是的,一厢情愿)。相反,我所发现的都是设计和实现得很差的MIME解析器,其中许多可能出现在本期的头版 我想我先来点垒球 首先,它们中的每一个都被写成
System.String
解析器。不要被那些自称是“流解析器”的人所愚弄,因为他们所做的一切只是在字节流上加上一个TextReader
,然后开始使用reader.ReadLine()
。你会问,这有什么不好?对于那些不熟悉MIME的人,我想让你们看看收件箱中原始的电子邮件来源,特别是当你们和美国以外的任何人有通信的时候。希望你的大多数朋友和同事都或多或少地使用了MIME兼容的电子邮件客户端,但我保证你至少会找到一些8比特原始文本的电子邮件
现在,如果他们使用的语言是C或C++,他们可能会逃脱,因为它们在技术上是在字节数组上运行的,但是用java和C语言,一个“string”是Unicode字符串。告诉我:如何从原始字节数组中获取unicode字符串
宾果。在将这些字节转换为unicode字符之前,您需要了解字符集 平心而论,确实没有处理消息头中原始8位文本的好方法,但是通过使用TextReader
方法,您实际上限制了这种可能性
接下来是ReadLine()
方法。中的两个早期解析器之一(在0.7天版本中)使用了ReadLine()
方法,因此我理解其背后的思想。事实上,就正确性而言,这种方法没有错,它更多的是一种“这永远不会是快的”抱怨。在GMime中的两个早期解析器中,pan mime parser.c后端比内存中的解析器慢得可怕。当然,这并不奇怪。当时更让我惊讶的是,当我编写GMime的当前一代解析器(介于v0.7和v1.0之间)时,它的速度与内存中的解析器一样快,并且在任何给定的时间,读取缓冲区中最多只有4k。我的观点是,如果您希望解析器的性能合理,那么有比ReadLine()
更好的方法。。。你为什么不想要呢?你的用户肯定希望这样
好的,现在是我在发现的几乎所有mime解析器库中遇到的更严重的问题
我认为到目前为止,我发现的每个mime解析器都使用“String.Split()”方法来解析地址头和/或解析头上的参数列表,如内容类型和内容处置
下面是一个C#MIME解析器的示例:
string[] emails = addressHeader.Split(',');
下面是同一个解析器如何解码编码的单词标记:
private static void DecodeHeaders(NameValueCollection headers)
{
ArrayList tmpKeys = new ArrayList(headers.Keys);
foreach (string key in headers.AllKeys)
{
//strip qp encoding information from the header if present
headers[key] = Regex.Replace(headers[key].ToString(), @"=\?.*?\?Q\?(.*?)\?=",
new MatchEvaluator(MyMatchEvaluator), RegexOptions.IgnoreCase | RegexOptions.Multiline);
headers[key] = Regex.Replace(headers[key].ToString(), @"=\?.*?\?B\?(.*?)\?=",
new MatchEvaluator(MyMatchEvaluatorBase64), RegexOptions.IgnoreCase | RegexOptions.Multiline);
}
}
private static string MyMatchEvaluator(Match m)
{
return DecodeQP(m.Groups[1].Value);
}
private static string MyMatchEvaluatorBase64(Match m)
{
System.Text.Encoding enc = System.Text.Encoding.UTF7;
return enc.GetString(Convert.FromBase64String(m.Groups[1].Value));
}
原谅我的语言,但他妈的怎么了?它完全丢弃了每个编码单词标记中的字符集。在引用的可打印标记的情况下,它假设它们都是ASCII(实际上,latin1也可以工作?),而在base64编码字标记的情况下,它假设它们都是UTF-7!?!?他到底是从哪里得到这个主意的?我无法想象他的代码在现实世界中使用任何base64编码的单词标记。如果有什么东西值得一个双面手掌,这就是它
我只想指出,这是本项目的描述所述:
一个用c#编写的小型、高效、可运行的mime解析器库。
...
我以前使用过几个开源mime解析器,但它们都不是
在一种或另一种编码上失败,或者错过一些关键的编码
信息。这就是我最终决定尝试解决这个问题的原因
我自己
我承认他的MIME解析器很小,但我不得不对“高效”和“wo”提出异议
public static StringDictionary parseHeaderFieldBody ( String field, String fieldbody ) {
if ( fieldbody==null )
return null;
// FIXME: rewrite parseHeaderFieldBody to being regexp based.
fieldbody = SharpMimeTools.uncommentString (fieldbody);
StringDictionary fieldbodycol = new StringDictionary ();
String[] words = fieldbody.Split(new Char[]{';'});
if ( words.Length>0 ) {
fieldbodycol.Add (field.ToLower(), words[0].ToLower().Trim());
for (int i=1; i<words.Length; i++ ) {
String[] param = words[i].Trim(new Char[]{' ', '\t'}).Split(new Char[]{'='}, 2);
if ( param.Length==2 ) {
param[0] = param[0].Trim(new Char[]{' ', '\t'});
param[1] = param[1].Trim(new Char[]{' ', '\t'});
if ( param[1].StartsWith("\"") && !param[1].EndsWith("\"")) {
do {
param[1] += ";" + words[++i];
} while ( !words[i].EndsWith("\"") && i<words.Length);
}
fieldbodycol.Add ( param[0], SharpMimeTools.parserfc2047Header (param[1].TrimEnd(';').Trim('\"', ' ')) );
}
}
}
return fieldbodycol;
}
if (m_EncodedWordPattern.RegularExpression.IsMatch(field.Body))
{
string charset = m_CharsetPattern.RegularExpression.Match(field.Body).Value;
string text = m_EncodedTextPattern.RegularExpression.Match(field.Body).Value;
string encoding = m_EncodingPattern.RegularExpression.Match(field.Body).Value;
Encoding enc = Encoding.GetEncoding(charset);
byte[] bar;
if (encoding.ToLower().Equals("q"))
{
bar = m_QPDecoder.Decode(ref text);
}
else
{
bar = m_B64decoder.Decode(ref text);
}
text = enc.GetString(bar);
field.Body = Regex.Replace(field.Body,
m_EncodedWordPattern.TextPattern, text);
field.Body = field.Body.Replace('_', ' ');
}