2013-08-07 13 views
15

Cercando di indirizzare lo this issue, sto cercando di comprendere le varie funzioni della libreria standard Python volte a supportare RFC 2231. L'obiettivo principale di tale RFC sembra essere triplice: consentire la codifica non ASCII nei parametri di intestazione, prendere nota della lingua di un determinato valore e consentire ai parametri dell'intestazione di estendersi su più righe. Il email.util library fornisce diverse funzioni per affrontare vari aspetti di questo. Per quanto posso dire, essi funzionano come segue:Decodifica intestazioni RFC 2231

decode_rfc2231 divide solo il valore di tale parametro nelle sue parti, in questo modo:

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

decode_params si occupa di rilevare parametri RFC2231 codificato. Raccoglie le parti che appartengono insieme e decodifica anche la stringa con codifica url in una sequenza di byte. Questa sequenza di byte, tuttavia, viene quindi codificata come latin1. E tutti i valori sono racchiusi tra virgolette. Inoltre, vi è una certa gestione speciale per il primo argomento, che deve ancora essere una tupla di due elementi, ma questi due vengono passati al risultato senza modifiche.

>>> 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 può essere utilizzato per convertire questo triplice di codifica, la lingua e la sequenza di byte in una stringa unicode corretta. Ciò che mi ha confuso, tuttavia, è il fatto che se l'input fosse una tale tripla, allora le virgolette saranno riportate all'output. Se, d'altra parte, l'input era una singola stringa quotata, allora queste virgolette saranno rimosse.

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

Così sembra che, al fine di utilizzare tutto questo macchinario, avrei dovuto aggiungere ancora un altro passo per unquote il terzo elemento di una tupla che avrei incontrato. È vero o mi manca qualche punto qui? Ho dovuto capire molto di quanto sopra con l'aiuto del codice sorgente, dato che i documenti sono un po 'vaghi sui dettagli. Non riesco a immaginare quale potrebbe essere il punto dietro questo smembramento selettivo. C'è un punto in esso?

Qual è il miglior riferimento su come utilizzare queste funzioni?

Il migliore che ho trovato finora è lo email.message.Messageimplementation. Lì, il processo sembra essere approssimativamente quello descritto sopra, ma ogni campo diventa non quotato tramite _unquotevalue dopo il decode_params e solo get_filename e get_boundary comprime i loro valori, tutti gli altri restituiscono invece una tupla. Spero ci sia qualcosa di più utile.

+1

Non una risposta, ma abbiamo avuto una lunga discussione su RFC 2231 che potrebbe essere utile a voi in un altro domanda. Si trattava di campi di forma, però. - http://stackoverflow.com/questions/20591599/why-arent-post-names-with-unicode-sent-correctly-when-using-multipart-form-data/20592910#20592910 –

+0

@RobStarling: Grazie! RFC 2231 è stato [ossessionante per me da un po 'di tempo] (http://stackoverflow.com/q/13514713/1468366), soprattutto dal momento che [qualcuno ha sottolineato] (https://github.com/facebook/tornado/pull/ 869 # issuecomment-23632083) che [HTML5 richiede * non * di utilizzarlo per i nomi dei file] (http://www.w3.org/html/wg/drafts/html/master/forms.html#multipart-form-data) . Ma HTML5 non è ancora uno standard ... – MvG

+0

oh fantastico. le persone HTML5 stanno modificando HTTP? Ugh. –

risposta

4

Attualmente le funzioni da email.utils vengono utilizzate raramente oltre a email.message. La maggior parte degli utenti sembra preferire l'utilizzo di email.message.Message direttamente. C'è anche un po 'vecchio issue report per aggiungere test di unità (che sarebbero certamente utilizzabili come esempi) a Python, anche se non sono sicuro su come si rapporta a email.util.

Un breve esempio che ho trovato è this blogpost che, tuttavia, non contiene più di una frase e alcuni SLOC di informazioni sull'analisi RFC2231. L'autore rileva, tuttavia, che molti MTA utilizzano invece RFC2047. A seconda del tuo caso, quello potrebbe anche essere un problema.

A giudicare dai pochi esempi che ho trovato, suppongo che il tuo modo di analizzare usando email.util sia l'unica strada da percorrere, anche se la lunga lista di comprensione è alquanto brutta.

A causa della mancanza di esempi in qualche modo, potrebbe essere saggio scrivere un nuovo parser RFC2231 (se hai davvero bisogno di una base di codice migliore, forse più veloce o più bella). Una nuova implementazione potrebbe essere basata su implementazioni esistenti come lo Dovecot RFC2231 parser per motivi di compatibilità (si potrebbe anche usare lo Dovecot unit test. Poiché il codice C mi sembra abbastanza complesso e poiché non riesco a trovare alcuna implementazione python oltre a email.util e backports Python2 email.util compito di porting di Python non sarà facile (si noti che Dovecot è LGPL-licensed, che potrebbe essere un problema nel vostro progetto)

penso che l'API email.util RFC2231 non è stato progettato per un facile utilizzo stand-alone, ma più come un mucchio di metodi di utilità per l'uso in email.message.Message.

0

Vecchia domanda, ma non ho potuto trovare una risposta completa che funzioni su questo. questo è quello che ho finito per fare (su 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)) 

Ad esempio:

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