-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmailsync.py
More file actions
156 lines (131 loc) · 4.98 KB
/
Copy pathmailsync.py
File metadata and controls
156 lines (131 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import imaplib
import email
from email.header import decode_header
import os
import json
import logging
import sys
def load_synced_uids(mailbox):
"""Lädt die UIDs bereits synchronisierter E-Mails für eine Mailbox."""
if mailbox in LAST_UID:
return LAST_UID[mailbox]
else:
return 0
def save_synced_uid(mailbox, uid):
"""Speichert eine neue UID, nachdem eine E-Mail erfolgreich heruntergeladen wurde."""
with open(CONFFILE, 'r+') as f:
data = json.load(f)
data['last_uid'].update({mailbox: uid})
f.seek(0)
json.dump(data, f, indent=4)
f.truncate()
def connect_to_mailbox(mailbox):
"""Verbindet sich mit einem Mailbox-Ordner."""
mail = imaplib.IMAP4_SSL(IMAP_SERVER)
mail.login(IMAP_USER, IMAP_PASSWORD)
# mail.select(mailbox, readonly=True)
status, _ = mail.select(mailbox, readonly=True)
if status != "OK":
logging.error(f"Fehler beim Öffnen der Mailbox: {mailbox}")
mail.logout()
return None # Fehlerbehandlung
return mail
def save_attachment(part, output_dir, uid):
"""Speichert einen E-Mail-Anhang."""
filename = part.get_filename()
if filename:
filepath = os.path.join(output_dir, f"{uid}_{filename}")
with open(filepath, "wb") as f:
f.write(part.get_payload(decode=True))
logging.debug(f"Anhang gespeichert: {filepath}")
def save_email(msg, uid, output_dir):
try:
"""Speichert die E-Mail und ihre Anhänge."""
# Betreff verarbeiten
if msg["Subject"] is None:
subject = "no_subject" # Standardwert für E-Mails ohne Betreff
else:
subject, encoding = decode_header(msg["Subject"])[0]
if isinstance(subject, bytes):
subject = subject.decode(encoding or "utf-8")
safe_subject = "".join(c for c in subject if c.isalnum() or c in " _-").strip()
filename = f"{uid}_{safe_subject or 'no_subject'}.eml"
# E-Mail speichern
filepath = os.path.join(output_dir, filename)
with open(filepath, "wb") as f:
f.write(msg.as_bytes())
logging.debug(f"E-Mail gespeichert: {filepath}")
# Anhänge speichern
for part in msg.walk():
if part.get_content_maintype() == "multipart":
continue
if part.get("Content-Disposition") and "attachment" in part.get("Content-Disposition"):
save_attachment(part, output_dir, uid)
except:
logging.error(f"E-Mail konnte nicht verarbeitet werden: {uid}")
def fetch_emails(mailbox):
"""E-Mails aus einem Postfach abrufen und speichern."""
# Lokalen Speicherordner sicherstellen
mailbox_folder = os.path.join(LOCAL_FOLDER, mailbox.replace("/", "_"))
os.makedirs(mailbox_folder, exist_ok=True)
mail = connect_to_mailbox(mailbox)
if mail is None:
logging.warning(f"Überspringe {mailbox} wegen Verbindungsfehler.")
return
# Alle E-Mail-UIDs abrufen
status, messages = mail.search(None, "ALL")
if status != "OK":
logging.error(f"Fehler beim Abrufen von E-Mails aus {mailbox}")
mail.close()
mail.logout()
return
uids = messages[0].split()
for uid in uids:
uid_str = uid.decode()
if int(uid_str) <= load_synced_uids(mailbox):
continue # Überspringe bereits gespeicherte E-Mail
status, msg_data = mail.fetch(uid, "(RFC822)")
if status != "OK":
logging.error(f"Fehler beim Abrufen von UID {uid.decode()}")
continue
for response_part in msg_data:
if isinstance(response_part, tuple):
msg = email.message_from_bytes(response_part[1])
save_email(msg, uid_str, mailbox_folder)
if len(uids) == 0:
latest_uid = 0
else:
latest_uid = uids[-1]
latest_uid= int(latest_uid.decode())
save_synced_uid(mailbox, latest_uid)
mail.close()
mail.logout()
def init_sync(conf):
# Konfiguration
global CONFFILE, IMAP_SERVER, IMAP_USER, IMAP_PASSWORD, MAILBOXES, LOCAL_FOLDER, LAST_UID, LOGFILE
CONFFILE = conf
with open(CONFFILE) as config_file:
config = json.load(config_file)
IMAP_SERVER = config["imap_server"]
IMAP_USER = config["imap_user"]
IMAP_PASSWORD = config["imap_password"]
MAILBOXES = config["mailboxes"]
LOCAL_FOLDER = config["local_folder"]
LAST_UID = config["last_uid"]
LOGFILE = config["logfile"]
# logging
FORMAT = '%(asctime)s %(levelname)s [%(funcName)s] %(message)s'
logging.basicConfig(filename=LOGFILE, encoding='utf-8', level=logging.INFO, format=FORMAT)
# log name of conffile
logging.debug(f"using {CONFFILE}")
# start syncing
for mailbox in MAILBOXES:
logging.debug(f"Synchronisiere Ordner: {mailbox}")
fetch_emails(mailbox)
if __name__ == "__main__":
# Konfiguration
if len(sys.argv) == 2:
CONFFILE = sys.argv[1]
else:
CONFFILE = "config.json"
init_sync(CONFFILE)