-1

我正在解决 imap 排序扩展的问题:

我的命令如下:

  var query = "icône";
            //byte[] bytes = Encoding.Default.GetBytes(query);
            //query = Encoding.UTF8.GetString(bytes);

            var command = "SORT (REVERSE ARRIVAL) UTF-8 " + "{" + query.Length + "}";
            var imapAnswerString = client.Command(command);
            imapAnswerString = client.Command(query);

我收到以下错误: IMAP 命令 SORT 中的 BAD 错误:原子中的 8 位数据

我发现了这个: C# Imap search command with special characters like á,é

但是我看不到如何准备我的代码以成功发送此请求。

4

3 回答 3

2

如果您想坚持使用 MailSystem.NET,arnt 给出的答案是正确的。

但是,正如我在此处(以及为方便起见)指出的那样,MailSystem.NET 存在许多使其无法使用的架构设计问题。

如果您使用其他开源库,例如MailKit,您将更轻松地完成此搜索查询:

var query = SearchQuery.BodyContains ("icône");
var orderBy = new OrderBy[] { OrderBy.ReverseArrival };
var results = folder.Search (query, orderBy);

希望有帮助。

MailSystem.NET 中的架构问题包括:

MailSystem.NET 没有正确处理文字标记 - 发送它们(对于 APPEND 以外的任何内容)或接收它们(对于 FETCH 请求中的实际消息数据以外的任何内容)。作者似乎都没有注意到,服务器可能会选择对任何字符串响应使用文字。

这是什么意思?

这意味着服务器可以选择使用邮箱名称的文字来响应 LIST 命令。

这意味着 BODYSTRUCTURE 中的任何字段都可以是文字,而不必像他们都假设的那样必须是带引号的字符串。

(和更多...)

例如,MailSystem.NET 也没有正确编码或引用邮箱名称:

来自 MailSystem.NET 的示例:

public string RenameMailbox(string oldMailboxName, string newMailboxName)
{
    string response = this.Command("rename \"" + oldMailboxName + "\" \"" + newMailboxName + "\"");
    return response;
}

这值得让-卢克·皮卡德(Jean-Luc Picard)威尔·赖克( Will Riker)掌声。这段代码只是盲目地在邮箱名称周围加上双引号。这是错误的,至少有两个原因:

  1. 如果邮箱名称有任何双引号或反斜杠怎么办?它需要用 \'s 来逃避它们。
  2. 如果mailboxName 包含非ASCII 字符或& 怎么办?它需要使用 UTF-7 字符编码的修改版本对名称进行编码。

我可以找到的大多数(全部?).NET IMAP 客户端将来自服务器的整个响应读取为 1 个大字符串,然后尝试使用正则表达式、IndexOf() 和 Substring() 的某种组合来解析响应。更糟糕的是,它们中的大多数也是由不知道 unicode 字符计数(即 string.Length)和八位字节(即字节计数)之间区别的开发人员编写的,因此当他们尝试解析对 FETCH 的响应时请求消息,他们在解析响应第一行中的“{}”值后执行此操作:

int startIndex = response.IndexOf ("}") + 3;
int endIndex = startIndex + octets;

string msg = response.Substring (startIndex, endIndex - startIndex);

MailSystem.NET 开发人员显然收到了关于这不适用于国际邮件的错误报告,因此他们的“修复”是这样做:

public string Body(int messageOrdinal)
{
    this.ParentMailbox.SourceClient.SelectMailbox(this.ParentMailbox.Name);

    string response = this.ParentMailbox.SourceClient.Command("fetch "+messageOrdinal.ToString()+" body", getFetchOptions());
    return response.Substring(response.IndexOf("}")+3,response.LastIndexOf(" UID")-response.IndexOf("}")-7);
}

从本质上讲,他们假设UID 键/值对将出现消息之后,并将其用作他们无能的黑客攻击。不幸的是,在现有的无能中增加更多的无能只会增加无能,它实际上并不能解决它。

IMAP 规范明确指出,结果的顺序可能会有所不同,并且它们甚至可能不在同一个未标记的响应中。

不仅如此,他们的 FETCH 请求甚至没有向服务器请求 UID 值,所以取决于服务器是否返回它!

TL;博士

如何评估 IMAP 客户端库

  1. 在评估 IMAP 客户端库实现时,您应该做的第一件事是查看它们如何解析响应。如果他们不使用实际的分词器,您可以立即判断该库是由不知道自己在做什么的人编写的。这是远离的最可靠的警告信号。

  2. 库是否在中心位置(例如它们的命令管道)处理未标记(“*”)的响应?还是它会做一些迟钝的事情,比如尝试在每个发送命令的方法中解析它(例如 ImapClient.SelectFolder()、ImapClient.FetchMessage() 等)?如果图书馆没有在可以正确处理这些未标记响应和更新状态(并通知您诸如 EXPUNGE 之类的重要事情)的中央位置处理它,请远离。

  3. 如果库将整个响应(甚至只是“消息”)读入 System.String,请远离。

于 2014-07-02T10:48:58.117 回答
1

您快到了。您的最终命令应该类似于

x sort (reverse arrival) utf-8 subject {6+}
icône

IE。您只是缺少一个搜索词来描述 IMAP 服务器应该在哪里搜索 icône 并对结果进行排序。还有许多其他搜索键,而不仅仅是主题。请参阅 RFC3501 第 49 页和后续页面。

编辑+之后需要6,以便将其作为单个命令发送(但要求服务器支持LITERAL+扩展)。如果服务器不支持 LITERAL+,那么您需要将命令分成多个部分,如下所示:

C: a001 SORT (REVERSE ARRIVAL) UTF-8 SUBJECT {6}
S: + ok, whenever you are ready...
C: icône
S: ... <response goes here>
于 2014-07-01T12:08:16.527 回答
0

谢谢大家的回答。

基本上 MailSystem.net 发送请求的方式(命令方法)是这个问题的症结所在,实际上还有其他一些问题。

命令方法应更正如下:

首先,在向 imap 发送请求时,下面的代码比原来的代码效果更好:

 //Convert stuff to have to right encoding in a char array
            var myCommand = stamp + ((stamp.Length > 0) ? " " : "") + command + "\r\n";
            var bytesUtf8 = Encoding.UTF8.GetBytes(myCommand);
            var commandCharArray = Encoding.UTF8.GetString(bytesUtf8).ToCharArray();

#if !PocketPC
            if (this._sslStream != null)
            {


                this._sslStream.Write(System.Text.Encoding.UTF8.GetBytes(commandCharArray));
            }
            else
            {
                base.GetStream().Write(System.Text.Encoding.UTF8.GetBytes(commandCharArray), 0, commandCharArray.Length);
            }
#endif

#if PocketPC
                        base.GetStream().Write(System.Text.Encoding.UTF8.GetBytes(commandCharArray), 0, commandCharArray.Length);
#endif

然后,以同样的方法,为了避免一些死锁或错误异常,改进有效性测试如下:

if (temp.StartsWith(stamp) || temp.ToLower().StartsWith("* " + command.Split(' ')[0].ToLower()) || (temp.StartsWith("+ ") && options.IsPlusCmdAllowed) || temp.Contains("BAD Error in IMAP command"))
                    {
                        lastline = temp;
                        break;
                    }

最后,更新返回 if 如下:

if (lastline.StartsWith(stamp + " OK") || temp.ToLower().StartsWith("* " + command.Split(' ')[0].ToLower()) && !options.IsSecondCallCommand || temp.ToLower().StartsWith("* ") && options.IsSecondCallCommand && !temp.Contains("BAD") || temp.StartsWith("+ "))
                return bufferString;

通过此更改,所有命令都可以正常工作,也可以双重调用命令。与原始代码相比,副作用更少。

这解决了我的大部分问题。

于 2014-07-16T08:25:37.810 回答