Seite 1 von 1
imap envelope parsen
Verfasst: Dienstag 8. September 2009, 13:10
von svenXY
Hallo allerseits.
meine eigentliche Herausforderung ist es, Mails aus einer IMAP-Mailbox auszulesen und in eine mbox-Datei zu schreiben.
Ich kann auch problemlos die Mail abholen, in einen String umwandeln und in eine Datei schreiben, aber...
mbox verlangt ja noch nach der "From_"- Zeile, die ich mir eigentlich aus dem Envelope bauen wollte.
Ich kann aber nirgends ein modul/Methode/... finden, welceh den Envelope, der hier als String zurückgegeben wird , parst und irgendwie nutzbar macht.
Code: Alles auswählen
import imaplib
M = imaplib.IMAP4('server')
M.login('user','pwd')
M.select('user.user.subfolder')
typ, data = M.search(None, 'ALL')
for num in data[0].split():
typ, message_parts = M.fetch(num, '(ENVELOPE)')
print message_parts[0]
M.close()
M.logout()
gibt jeweils den envelope zurück, aber als string:
1 (ENVELOPE ("Fri, 30 Jan 2009 14:54:29 +0100" "subject" (("Sender Name" NIL "user" "tld.de")) ((("Sender Name" NIL "user" "tld.de")) ((("Sender Name" NIL "user" "tld.de")) ((NIL NIL "empfaenger" "tld2.de")) NIL NIL NIL "<RFRYVkZPVihTMCRRW0U6MjIxNjA5ODQ@digassist>"))
Kann man das irgendwie (ohne regexes

) parsen?
mit Regexes sähe das in etwa so aus:
Code: Alles auswählen
for num in data[0].split():
typ, message_parts = M.fetch(num, '(RFC822 ENVELOPE)')
envelope = email.message_from_string(message_parts[0][0])
msg = email.message_from_string( message_parts[0][1])
# blöde regex, um die From_ Zeile bauen zu können
pattern=re.compile('ENVELOPE \("([^"]+)".+?\(\((".*?")\)\)')
m = pattern.search(envelope.__str__())
# baue Sender für From_ Zeile
senderfelder = m.group(2).split('"')
sender = '@'.join([senderfelder[3], senderfelder[5]])
# generiere die ASCII-Time für die From_ Zeile
timestr = time.strftime('%a %b %d %H:%M:%S %Y', email.utils.parsedate(m.group(1)))
# setze From_ Zeile
msg.set_unixfrom(" ".join(["From", sender, timestr]))
# Mail auf stdout ausgeben
print msg.as_string(True)
update: oder gibt es einen anderen - sicher tollen - Weg, um eine From_ Zeile bauen zu lassen?
Danke,
Sven
Verfasst: Mittwoch 9. September 2009, 09:08
von sma
Mit den Klammern und dem NIL sieht das eher wie Lisp, denn wie Python aus ;) Bist du sicher, dass in `message_parts[0]` überhaupt ein String drin steckt? Für mich sieht das eher wie eine spezielle Struktur aus. In der
Dokumentation sieht man, wie auf `data[0][1]` zugegriffen wird. Und es heißt "Returned data are tuples of message part envelope and data."
Stefan
Verfasst: Donnerstag 10. September 2009, 12:49
von svenXY
Hi, ja, es ist wohl ein String:
Code: Alles auswählen
server = 'imap1.domain.de'
login = 'cyrus'
password = '-----'
import imaplib, email, re, time, sys ,os
M = imaplib.IMAP4(server)
M.login(login, password)
## test mailbox
folder = 'user.user1'
## Rechte holen und Folder öffnen
M.setacl(folder, login, 'lrswipkxtecda')
M.select(folder)
# suchen
typ, data = M.search(None, '(ALL)')
for num in data[0].split():
typ, message = M.fetch(num, '(ENVELOPE)')
print type(message)
print type(message[0])
print message
print message[0]
break
M.deleteacl(folder, login)
M.close()
M.logout()
ergibt hier (anonymisiert):
<type 'list'>
<type 'str'>
['1 (ENVELOPE ("Fri, 30 Jan 2009 14:54:29 +0100" "Betreff der Mail" (("Miriam Nachname" NIL "maria.sperl" "senderdom.de")) (("Miriam Nachname" NIL "maria.sperl" "senderdom.de")) (("Miriam Nachname" NIL "maria.sperl" "senderdom.de")) ((NIL NIL "recipient" "emfaengerdom.de")) NIL NIL NIL "<RFRYVkZPVihTMCRRW0U6MjIxNjA5ODQ@digassist>"))']
1 (ENVELOPE ("Fri, 30 Jan 2009 14:54:29 +0100" "Betreff der Mail" (("Miriam Nachname" NIL "maria.sperl" "senderdom.de")) (("Miriam Nachname" NIL "maria.sperl" "senderdom.de")) (("Miriam Nachname" NIL "maria.sperl" "senderdom.de")) ((NIL NIL "recipient" "emfaengerdom.de")) NIL NIL NIL "<RFRYVkZPVihTMCRRW0U6MjIxNjA5ODQ@digassi
[/code]
Das Ergebnis ist also eine Liste mit hier nur einem Element, welches ein Strin ist und diese komische Struktur hat - nun nochmal - hat jemand das schonmal geparst?
Danke,
Sven
PS: Ich habe mittlerweile eine andere Lösung für mein Problem gefunden und hole mir die Daten nicht mehr aus dem Envelope, sondern aus den Headern des Imap-payload. Auf die kann man wesentlich einfacher zugreifen. Aber ich bin trotzdem an der "Lösung" meines beschriebenen Problems interessiert

Verfasst: Donnerstag 10. September 2009, 21:44
von sma
Ich sagte ja schon, dass sieht wie Lisp-S-Expressions aus. Die lassen sich leicht gemäß dieser Grammatik parsen:
Code: Alles auswählen
expr = atom | "(" {expr} ")"
atom = symbol | string | "NIL"
Siehe
http://www.python-forum.de/post-83870.html#83870 für einen Reader. Den würde ich aber inzwischen auch kürzer schreiben können - das war eines meiner ersten Python-Progrämmchen.
Stefan
Verfasst: Montag 14. September 2009, 10:30
von svenXY
Hi sma,
Danke für Deine Erklärung. Ich bin kein studierter Informatiker, weswegen ein zu schreibender Parser hier für mich wohl zu aufwendig wird.
Ich finde es schon seltsam, dass das ansonsten recht brauchbare imaplib-Modul hier den Benutzer mit solchen Datenstrukturen alleine lässt... Aber da ich das, was ich benötige ja mittlerweile aus den Mailheadern ziehe (s.o.) ist das auch nicht weiter problematisch
Gruss, Sven
Verfasst: Mittwoch 16. September 2009, 09:34
von sma
svenXY hat geschrieben:Ich bin kein studierter Informatiker, weswegen ein zu schreibender Parser hier für mich wohl zu aufwendig wird.
Das ist nicht so schwer. Der "Parser", also der Teil, der geklammerte Ausdrücke lesen kann, ist in Zeile 74 bis 95 in meinem Beispielcode - das sind gerade mal 20 Zeilen. Lässt man dotted-pairs weg, reicht dies:
Code: Alles auswählen
import re
def cons(a, b): return [a, b]
def read(s):
next = iter(re.findall(r'\s*([()]|"[^"]*"|[^ ()]+|)', s)).next
def _read(t):
if t == "": return None
if t == "(": return _readl(next())
if t == ")": raise SyntaxError, "unexpected )"
return t
def _readl(t):
if t == "": raise SyntaxError, "missing )"
if t == ")": return None
return cons(_read(t), _readl(next()))
return _read(next())
print read('(env foo ("bar"))')
Stefan
Verfasst: Mittwoch 16. September 2009, 15:20
von svenXY
ich werd's probieren, vielen Dank!
Re: imap envelope parsen
Verfasst: Dienstag 14. Februar 2012, 13:30
von henne.gwath
Dieser Thread ist schon alt. Ich weiß. Trotzdem ist es der einzige, der bei der Suche im großen weiten Web einige Informationen gibt.
Wegen eines kaputten IMAP-Servers muss ich einige Headerzeilen aus dem ENVELOPE wiederherstellen. Den envelope via imap bekommen ist kein Problem, das Parsen bereitet mir allerdings Probleme. Der obige Code geht in die richtige Richtung, scheint die Zeile allerdings nicht korrekt zu parsen.
Ich bekomme etwas wie
Code: Alles auswählen
['1 (ENVELOPE ("Sun, 29 Jan 2012 10:40:05 +0100" "Betreff Zeile" (("Name 1" NIL "benutzer" "domain.com")) (("Name 2" NIL "benutzer" "domain.com")) (("Name 3" NIL "benutzer" "domain.com")) (("Name 4" NIL "benutzer" "domain.com")) (("To Name 1" NIL "benutzer" "domain.com")("To Name 2" NIL "benutzer" "domain.com")("To Name 3" NIL "benutzer" "domain.com")) NIL NIL "<cryptisches34987@domain.com>"))']
Durch den Ausdruck
next = iter(re.findall(r'\s*([()]|"[^"]*"|[^ ()]+|)', s)).next
wird zusätzlich eine neue Liste angefangen, wenn ein " gefunden wird scheint mir. Der Hund liegt im mittleren Ausdruck "[^"]*" begraben, aber ich weiß nicht was ich daran ändern muss, damit es funktioniert. Ich benutze leider Python2
Viele Grüße,
Henning
Re: imap envelope parsen
Verfasst: Dienstag 14. Februar 2012, 14:51
von lunar
@henne.gwath: Ich rate Dir dazu, E-Mails mit
TurboMail zu verarbeiten, statt mit den Mitteln der Standardbibliothek selbst. Die E-Mail-Module der Standardbibliothek sind ... nun ja, eher merkwürdig.
Im Allgemeinen ist es bei derartig alten Diskussionen übrigens sinnvoller, eine neue Diskussion zu eröffnen, und dort auf die alte zu verweisen.
Re: imap envelope parsen
Verfasst: Dienstag 14. Februar 2012, 15:04
von henne.gwath
gut, machen wir das mal...
Re: imap envelope parsen
Verfasst: Dienstag 14. Februar 2012, 15:12
von lunar
@henne.gwath: Was jetzt? TurboMail verwenden, oder eine neue Diskussion eröffnen? Letzteres ist nicht unbedingt nötig, meine Bemerkung war nur allgemeiner Natur. Diese Diskussionen können wir jetzt auch hier fortsetzen, Du musst jetzt nicht mehr eigens eine neue eröffnen.
Re: imap envelope parsen
Verfasst: Dienstag 14. Februar 2012, 15:19
von henne.gwath
Also vielleicht nochmal im Ganzen:
Ich habe hier einen kaputten IMAP-Server den ich gern auf eine neue Version portieren möchte. Die Postfächer sollen mit Offlineimap kopiert werden (das war das einzige Programm, dass dazu in der Lage war). Bei gesendeten Mails fehlen dabei leider die Zeilen Date und Subject -- doof!
Ich habe herausgefunden, dass der Server diese Daten nicht im Header, sondern NUR im ENVELOPE speichert. Diese Daten können einfach via imaplib geholt werden.
Code: Alles auswählen
import imaplib
server = 'SERVER'
login = 'LOGIN'
password = 'PASSWORT'
dir = 'INBOX'
M = imaplib.IMAP4(server)
M.login(login, password)
M.select(dir)
typ, data = M.fetch('1', '(ENVELOPE)')
print(data)
M.close()
M.logout()
Offlineimap ist in Python geschrieben, daher würde ich gern eine Funktion in den Code einfügen (ich weiß noch nicht an welcher Stelle, siehe dazu auch die OfflineImap-Mailingliste), die den Envelope holt und daraus die fehlenden Header-Zeilen erstellt.
Ich weiß nicht ob man TurboMail an dieser Stelle einsetzen kann, wenn ja wäre ich über Rat dankbar.
Henning