带有特殊字符的c# Imap排序命令

本文关键字:Imap 排序 命令 特殊字符 | 更新日期: 2023-09-27 18:11:09

我正在研究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: atom中的8bit数据

我发现了这个:c# Imap搜索命令,特殊字符如á,é

但是我不知道如何准备我的代码来成功地发送这个请求。

带有特殊字符的c# Imap排序命令

如果你想坚持使用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不能正确地处理文字令牌——无论是发送它们(除了APPEND)还是接收它们(除了FETCH请求中的实际消息数据)。作者似乎都没有注意到的是,服务器可以选择对任何字符串响应使用文字。

这是什么意思?

这意味着服务器可以选择使用一个字面值作为邮箱名来响应LIST命令。

这意味着BODYSTRUCTURE中的任何字段都可以是字面量,而不必像它们所假设的那样是引号字符串。

(和更多…)

MailSystem。例如,NET也不能正确编码或引用邮箱名:

示例来自MailSystem。净:

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

这值得让-吕克·皮卡德将瑞克face-palm。这段代码只是盲目地在邮箱名周围加上双引号。这是错误的,至少有两个原因:

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

我能找到的大多数(全部?). net IMAP客户端从服务器读取整个响应到一个大字符串,然后尝试用regex, IndexOf()和Substring()的一些组合来解析响应。更糟糕的是,它们中的大多数也是由不知道unicode字符计数(即string.Length)和octet(即字节计数)之间区别的开发人员编写的,所以当他们试图解析对消息的FETCH请求的响应时,他们会在解析响应的第一行中的"{}"值后执行此操作:

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

MailSystem。NET开发人员显然收到了关于国际邮件不工作的bug报告,所以他们的"修复"是这样做的:

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. 如果库将整个响应(甚至只是"消息")读入系统。

你就快成功了。最后的命令应该类似于

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

ie。您只是缺少一个搜索词来描述IMAP服务器应该在哪里搜索icône并对结果进行排序。除了主题,还有很多其他搜索关键字。参见RFC3501第49页及以下各页。

Edit: +需要在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>

谢谢大家的回答。

基本上,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;

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

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