2014-10-22 14 views
-1

У меня есть скрипт python 2.7, работающий на окнах. Он входит в Gmail, проверяет наличие новых сообщений электронной почты и вложений:Неверное кодирование вложения электронной почты

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 

file_types = ["pdf", "doc", "docx"] # download attachments with these extentions 

login = "login" 
passw = "password" 

imap_server = "imap.gmail.com" 
smtp_server = "smtp.gmail.com" 
smtp_port = 587 

from smtplib import SMTP 
from email.parser import HeaderParser 
from email.MIMEText import MIMEText 
import sys 
import imaplib 
import getpass 
import email 
import datetime 
import os 
import time 

if __name__ == "__main__": 
    try: 
     while True: 
      session = imaplib.IMAP4_SSL(imap_server) 
      try: 
       rv, data = session.login(login, passw) 
       print "Logged in: ", rv 
      except imaplib.IMAP4.error: 
       print "Login failed!" 
       sys.exit(1) 

      rv, mailboxes = session.list() 
      rv, data = session.select(foldr) 
      rv, data = session.search(None, "(UNSEEN)") 
      for num in data[ 0 ].split(): 
       rv, data = session.fetch(num, "(RFC822)") 
       for rpart in data: 
        if isinstance(rpart, tuple): 
         msg = email.message_from_string(rpart[ 1 ]) 
         to = email.utils.parseaddr(msg[ "From" ])[ 1 ] 
       text = data[ 0 ][ 1 ] 
       msg = email.message_from_string(text) 
       got = [] 
       for part in msg.walk(): 
        if part.get_content_maintype() == 'multipart': 
         continue 
        if part.get('Content-Disposition') is None: 
         continue 
        filename = part.get_filename() 
        print "file: ", filename 
        print "Extention: ", filename.split(".")[ -1 ] 
        if filename.split(".")[ -1 ] not in file_types: 
         continue 
        data = part.get_payload(decode = True) 
        if not data: 
         continue 
        date = datetime.datetime.now().strftime("%Y-%m-%d") 
        if not os.path.isdir("CONTENT"): 
         os.mkdir("CONTENT") 
        if not os.path.isdir("CONTENT/" + date): 
         os.mkdir("CONTENT/" + date) 
        ftime = datetime.datetime.now().strftime("%H-%M-%S") 
        new_file = "CONTENT/" + date + "/" + ftime + "_" + filename 
        f = open(new_file, 'wb') 
        print "Got new file %s from %s" % (new_file, to) 
        got.append(filename.encode("utf-8")) 
        f.write(data) 
        f.close() 
      session.close() 
      session.logout() 
      time.sleep(60) 
    except: 
     print "TARFUN!" 

И проблема в том, что последняя печать читает мусор:
=?UTF-8?B?0YfQsNGB0YLRjCAxINGC0LXQutGB0YIg0LzQtdGC0L7QtNC40YfQutC4LmRv?=
, например так позже чеки не работают. На linux все работает отлично. На данный момент я попытался использовать d/e [n] имя файла кода для utf-8. Но он ничего не сделал. Заранее спасибо.

+0

Можете ли вы дать нам [минимальный, полный пример] (http://stackoverflow.com/help/mcve), с достаточным кодом для запуска чего-либо (и постороннего кода, например, выборки IMAP, удаление) и входных данных запустить его (например, возможно, как строковый литерал в источнике)? – abarnert

+0

@abarnert отредактировал сообщение –

+1

Нет, это не минимальный, полный пример.Никто не может запускать этот код или ничего тестировать (если вы не дадите нам свое имя пользователя и пароль Gmail и не обещаете больше не получать почту или каким-либо иным образом изменять что-либо о вашем почтовом ящике, что не является разумным). Пожалуйста, посмотрите на ссылку помощи, которую я дал, и сделайте то, что я предложил: удалите посторонний код и дайте нам входные данные для соответствующего кода. – abarnert

ответ

0

Если вы читали спецификацию, которая определяет поле имени файла, RFC 2183, раздел 2.3, он говорит:

Текущая [RFC 2045] грамматика ограничивает значения параметров (и, следовательно, Content-Disposition имена файлов) в US-ASCII. Мы признаем большую целесообразность разрешать произвольные наборы символов в именах файлов, но не входит в объем этого документа для определения необходимых механизмов . Мы ожидаем, что базовая спецификация [RFC 1521] 'value' будет впоследствии изменена, чтобы разрешить использование символов, отличных от US-ASCII , и в этот момент тот же самый механизм должен использоваться в параметре имени файла-объявления .

Предлагаются RFC для обработки этого. В частности, было предложено, чтобы имена файлов обрабатывались как encoded-word с, как определено RFC 5987, RFC 2047 и RFC 2231. Вкратце это означает, что либо RFC 2047 формат:

"=?" charset "?" encoding "?" encoded-text "?=" 

... или RFC формат 2231:

"=?" charset ["*" language] "?" encoded-text "?=" 

Некоторые почтовые агенты уже используют эту функцию, другие не знают, что делать с ним. Пакет email в Python 2.x относится к тем, кто не знает, что с ним делать. (Возможно, что более поздняя версия в Python 3.x делает или может измениться в будущем, но это не поможет вам, если вы хотите придерживаться 2.x.) Итак, если вы хотите разобрать это, вы должны сделать это сами.

В вашем примере, у вас есть имя файла в формате RFC 2047, с кодировкой UTF-8 (который может использоваться непосредственно как имя кодировки Python), кодирование B, что означает Base-64, и содержание 0YfQsNGB0YLRjCAxINGC0LXQutGB0YIg0LzQtdGC0L7QtNC40YfQutC4LmRv. Итак, вы должны декодировать base-64, а затем UTF-8-декодировать это, и вы получите u'часть 1 текст методички.do'.

Если вы хотите сделать это в более общем плане, вам придется писать код, который пытается интерпретировать каждое имя файла в формате RFC 2231, если это возможно, в формате RFC 2047 в противном случае и выполняет соответствующие шаги декодирования. Этот код не достаточно велик, чтобы писать в ответе StackOverflow, но основная идея довольно проста, как было показано выше, чтобы вы могли написать ее самостоятельно. Вы также можете искать PyPI для существующих реализаций.

+0

Этот трюк с декодированием b64, а затем декодирование utf-8! Но строка пропускает последний символ: это документ .doc. Как так? 0.0 –

+0

Ваша характеристика RFC2231 неверна; он не имеет префикса '=?' и '? =' суффикса RFC2047, а скорее формата типа Content-Type: application/x-stuff; имя * 0 * = мы-ascii'en'This% 20is% 20even% 20more% 20; имя * 1 * =% 2A% 2A% 2Afun% 2A% 2A% 2A% 20; имя * 2 = «не так ли!» (адаптировано из одного из сложных примеров в RFC). То, что вы описываете, является расширением базового формата RFC2047, который действительно определен в RFC2231, но формат основного заголовка для определения имен файлов и т. Д. Совсем другой. – tripleee

+0

Кроме того, RFC5987 применяется к HTTP, а не по электронной почте. – tripleee

Смежные вопросы