2013-08-07 13 views
15

Ich versuche, this issue zu adressieren, ich versuche, den Kopf um die verschiedenen Funktionen in der Python-Standard-Bibliothek zur Unterstützung RFC 2231. Das Hauptziel dieses RFC scheint dreifach zu sein: Nicht-ASCII-Codierung in Header-Parametern zu erlauben, die Sprache eines gegebenen Wertes zu notieren und Header-Parameter zu erlauben, sich über mehrere Zeilen zu erstrecken. Die email.util library bietet mehrere Funktionen, um mit verschiedenen Aspekten davon umzugehen. Soweit ich sagen kann, sie funktionieren wie folgt:Decoding RFC 2231 Header

decode_rfc2231 spaltet nur den Wert eines solchen Parameters in seine Teile, wie folgt aus:

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

decode_params nimmt RFC2231-kodierten Parameter zu erfassen. Es sammelt Teile zusammen, die zusammen gehören, und dekodiert auch die url-codierte Zeichenfolge in eine Byte-Sequenz. Diese Bytefolge wird jedoch dann als latin1 codiert. Und alle Werte sind in Anführungszeichen eingeschlossen. Außerdem gibt es eine spezielle Behandlung für das erste Argument, das immer noch ein Tupel aus zwei Elementen sein muss, aber diese beiden werden ohne Änderung an das Ergebnis übergeben.

>>> 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 kann diese dreifache der Codierung, Sprache und Bytefolge in eine geeignete Unicode-Zeichenfolge zu konvertieren verwendet werden. Was mich jedoch verwirrt hat, ist die Tatsache, dass, wenn die Eingabe so ein Tripel war, die Anführungszeichen auf die Ausgabe übertragen werden. Wenn die Eingabe andererseits eine einzelne Zeichenfolge in Anführungszeichen ist, werden diese Anführungszeichen entfernt.

>>> [(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"')] 

So scheint es, dass, um alle diese Maschinen zu verwenden, würde ich noch einen weiteren Schritt hinzufügen müssen, um das dritte Element jedes Tupel unquote begegne ich hatte. Ist das wahr, oder fehlt mir hier ein Punkt? Ich musste viel von dem oben genannten mit Hilfe des Quellcodes herausfinden, da die Dokumente auf den Details ein wenig vage sind. Ich kann mir nicht vorstellen, was dieser selektive Verzicht sein könnte. Gibt es einen Sinn?

Was ist die beste Referenz zur Verwendung dieser Funktionen?

Die beste, die ich bis jetzt gefunden habe, ist die email.message.Messageimplementation. Es scheint der Prozess in etwa die oben beschriebenen zu sein, aber jedes Feld über _unquotevalue nach dem decode_params unquoted wird, und nur get_filename und get_boundary Zusammenbruch ihre Werte, alle anderen zurückgeben anstelle eines Tupels. Ich hoffe, es gibt etwas Nützlicheres.

+1

keine Antwort, aber wir hatten eine lange Diskussion über RFC 2231, die in einem anderen zu Ihnen sein könnte nützlich Frage. Es ging jedoch um Formularfelder. - http://stackoverflow.com/questions/20591599/why-arent-post-names-withunicode-sent-correctly-when-using-multipart-form-data/20592910#20592910 –

+0

@RobStarling: Danke! RFC 2231 [quält mich schon seit einiger Zeit] (http://stackoverflow.com/q/13514713/1468366), insbesondere seit [jemand darauf hingewiesen hat] (https://github.com/facebook/tornado/pull/ 869 # issuecomment-23632083), dass [HTML5 verlangt, dass * es * nicht für Dateinamen verwendet wird] (http://www.w3.org/html/wg/drafts/html/master/forms.html#multipart-form-data) . Aber HTML5 ist noch kein Standard ... – MvG

+0

oh toll. die HTML5-Leute optimieren HTTP? Pfui. –

Antwort

4

Momentan werden die Funktionen von email.utils selten innerhalb von email.message genutzt. Die meisten Benutzer bevorzugen die direkte Verwendung von email.message.Message. Es gibt sogar eine etwas alte issue report über das Hinzufügen von Unit-Tests (das sicherlich brauchbar als Beispiele wäre) zu Python, auch wenn ich nicht sicher bin, wie es bezieht sich auf email.util.

Ein kurzes Beispiel, das ich fand, ist this blogpost, die jedoch nicht mehr als einmal Satz und ein paar SLOCs Informationen über RFC2231 Parsing enthält. Der Autor stellt jedoch fest, dass viele MTAs verwenden RFC2047 statt. Abhängig von Ihrem Anwendungsfall könnte dies ebenfalls ein Problem darstellen.

Aus den wenigen Beispielen, die ich finden konnte, gehe ich davon aus, dass Ihre Art der Analyse mit email.util der einzige Weg ist, selbst wenn das Verständnis der langen Liste etwas hässlich ist.

Aufgrund des Fehlens von Beispielen in gewisser Hinsicht könnte es klug sein, einen neuen RFC2231-Parser zu schreiben (wenn Sie wirklich eine bessere, vielleicht schnellere oder schönere Codebasis benötigen). Eine neue Implementierung könnte aus Kompatibilitätsgründen auf existierenden Implementierungen wie der Dovecot RFC2231 parser basieren (Sie könnten sogar die Dovecot unit test verwenden. Da mir der C-Code sehr komplex erscheint und ich neben email.util und Python2 Backports von email.util keine Python-Implementierung finden kann Aufgabe Python Portierung wird nicht einfach sein (beachten Sie, dass Dovecot LGPL-licensed ist, die ein Problem in Ihrem Projekt auch sein mag)

ich denke, die email.util RFC2231 API für einfache Standalone-Nutzung hat sich entwickelt, aber mehr als ein Haufen von nicht Hilfsprozeduren zur Verwendung in email.message.Message

0

Alte Frage, aber ich konnte keine vollständige Antwort finden, die auf diese funktioniert das ist, was ich (auf Python 2.7) am Ende tun:

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)) 

Zum Beispiel:

>>> 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śšłžźżçćčñń