C#Imap具有特殊字符的排序命令

时间:2014-07-01 10:10:29

标签: c# sorting imap mailsystem.net

我正在研究imap sort extention的一个问题:

我的命令如下:

  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命令中的BAD错误SORT:原子中的8位数据

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

但我不知道如何准备我的代码以成功发送此请求。

3 个答案:

答案 0 :(得分:2)

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

但是,正如我指出here(以下为方便起见),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. 如果邮箱名称有双引号或反斜杠怎么办?它需要用\“#来逃避它们。
  2. 如果mailboxName包含非ASCII字符或&amp ;?,该怎么办?它需要使用UTF-7字符编码的修改版本对名称进行编码。
  3. 我能找到的大多数(全部?).NET IMAP客户端将服务器的整个响应读入1个大字符串,然后尝试使用正则表达式,IndexOf()和Substring()的某种组合来解析响应。更糟糕的是,它们中的大多数也是由开发人员编写的,他们不知道unicode字符计数(即string.Length)和八位字节(即 byte 计数)之间的区别,所以当他们尝试解析对消息的FETCH请求的响应,他们在解析" {}"之后执行此操作。响应的第一行中的值:

    int startIndex = response.IndexOf ("}") + 3;
    int endIndex = startIndex + octets;
    
    string msg = response.Substring (startIndex, endIndex - startIndex);
    

    MailSystem.NET开发人员显然得到了关于这不适用于国际邮件的错误报告,所以他们的#34;修复"是这样做的:

    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; DR

    如何评估IMAP客户端库

    1. 在评估IMAP客户端库实现时,您应该做的第一件事是查看它们如何解析响应。如果他们不使用实际的标记器,你可以直接告诉他们图书馆是由那些不知道他们在做什么的人写的。这是STAY AWAY最明确的警告标志。

    2. 库是否在中心位置(例如命令管道)处理未标记的(&#34; *&#34;)响应?或者它是否会像尝试一样做一些延迟,并在每个发送命令的方法中解析它(例如ImapClient.SelectFolder(),ImapClient.FetchMessage()等)?如果图书馆没有在中心位置处理它,可以正确处理这些未标记的响应并更新状态(并通知您重要的事情,如EXPUNGE&#39; s),请保留。

    3. 如果库将整个响应(或者甚至只是&#34;消息&#34;)读入System.String,请保持打开状态。

答案 1 :(得分:1)

你快到了。你的最终命令应该是

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

即。您只是缺少一个搜索词来描述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>

答案 2 :(得分: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 (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;

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

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