解码RFC 2231标头

时间:2013-08-07 03:47:26

标签: python http mime multipartform-data

试图解决this issue,我试图围绕Python标准库中旨在支持RFC 2231的各种函数。该RFC的主要目的似乎是三方面:允许在头参数中进行非ASCII编码,注意给定值的语言,并允许头参数跨越多行。 email.util library提供了几个函数来处理这方面的各个方面。据我所知,它们的工作原理如下:

decode_rfc2231仅将此类参数的值拆分为其部分,如下所示:

>>> email.utils.decode_rfc2231("utf-8''T%C3%A4st.txt")
['utf-8', '', 'T%C3%A4st.txt']

decode_params负责检测RFC2231编码的参数。它收集属于一起的部分,并将url编码的字符串解码为字节序列。然而,该字节序列被编码为latin1。并且所有值都用引号括起来。此外,第一个参数有一些特殊的处理,它仍然必须是两个元素的元组,但是这两个元素在没有修改的情况下传递给结果。

>>> email.utils.decode_params([
...   (1,2),
...   ("foo","bar"),
...   ("name*","utf-8''T%C3%A4st.txt"),
...   ("baz*0","two"),("baz*1","-part")])
[(1, 2), ('foo', '"bar"'), ('baz', '"two-part"'), ('name', ('utf-8', '', '"Täst.txt"'))]

collapse_rfc2231_value可用于将此三重编码,语言和字节序列转换为正确的unicode字符串。然而,令我困惑的是,如果输入是这样的三倍,那么引号将被转移到输出。另一方面,如果输入是单引号字符串,则将删除这些引号。

>>> [(k, email.utils.collapse_rfc2231_value(v)) for k, v in
...  email.utils.decode_params([
...   (1,2),
...   ("foo","bar"),
...   ("name*","utf-8''T%C3%A4st.txt"),
...   ("baz*0","two"),("baz*1","-part")])[1:]]
[('foo', 'bar'), ('baz', 'two-part'), ('name', '"Täst.txt"')]

所以似乎为了使用所有这些机器,我必须再添加一个步骤来取消我遇到的任何元组的第三个元素。这是真的吗,还是我在这里错过了一些观点?我必须在源代码的帮助下弄清楚上面的很多内容,因为文档在细节上有点模糊。我无法想象这有选择性的不引用的背后可能是什么。有没有意义呢?

如何使用这些功能的最佳参考是什么?

到目前为止,我发现的最好的是email.message.Message implementation。在那里,过程似乎大致与上面概述的过程相同,但是decode_params之后_unquotevalue的每个字段都会被取消引用,只有get_filenameget_boundary会折叠它们的值其他人则返回一个元组。我希望有更多有用的东西。

2 个答案:

答案 0 :(得分:4)

目前email.utils内的功能很少在email.message内使用。大多数用户似乎更喜欢直接使用email.message.Message。即使我不确定它与email.util的关系如何,在向Python添加单元测试(当然可以用作示例)时甚至有点旧issue report

我发现的一个简短示例是this blogpost,但是,它不包含多于一次的句子和一些关于RFC2231解析的SLOC信息。但是,作者指出,许多MTA使用RFC2047代替。根据您的使用情况,这可能也是一个问题。

从我能找到的几个例子来看,我假设你使用email.util进行解析的方式是唯一的方法,即使长列表理解有点难看。

由于在某些方面缺乏示例,因此编写新的RFC2231解析器(如果您确实需要更好,更快或更漂亮的代码库)可能是明智的。出于兼容性原因,新的实现可以基于Dovecot RFC2231 parser之类的现有实现(你甚至可以使用Dovecot unit test。因为C代码对我来说似乎很复杂,因为我找不到任何python实现除了email.utilemail.util的Python2反向移植之外,移植到Python的任务并不容易(注意Dovecot是LGPL-licensed,这可能是你项目中的一个问题)

我认为email.util RFC2231 API并非设计用于简单的独立使用,而是更多作为email.message.Message中使用的一堆实用程序方法。

答案 1 :(得分:0)

老问题,但我找不到一个完整的答案。所以这就是我最终做的(在Python 2.7上):

def decode_rfc2231_header(header):
    """Decode a RFC 2231 header"""
    # Remove any quotes
    header = email.utils.unquote(header)
    encoding, language, value = email.utils.decode_rfc2231(header)
    value = urllib.unquote(value)
    return email.utils.collapse_rfc2231_value((encoding, language, value))

例如:

>>> name = u'èéêëēėęûüùúūàáâäæãåāāîïíīįì test ôöòóœøōõssśšłžźżçćčñń'
>>> encoded_header = email.utils.encode_rfc2231(name.encode("utf8"), 'utf8', 'en')
>>> print encoded_header 
utf8'en'%C3%A8%C3%A9%C3%AA%C3%AB%C4%93%C4%97%C4%99%C3%BB%C3%BC%C3%B9%C3%BA%C5%AB%C3%A0%C3%A1%C3%A2%C3%A4%C3%A6%C3%A3%C3%A5%C4%81%C4%81%C3%AE%C3%AF%C3%AD%C4%AB%C4%AF%C3%AC%20test%20%C3%B4%C3%B6%C3%B2%C3%B3%C5%93%C3%B8%C5%8D%C3%B5ss%C5%9B%C5%A1%C5%82%C5%BE%C5%BA%C5%BC%C3%A7%C4%87%C4%8D%C3%B1%C5%84
>>> print decode_rfc2231_header(encoded_header)
èéêëēėęûüùúūàáâäæãåāāîïíīįì test ôöòóœøōõssśšłžźżçćčñń
相关问题