Aufgabe 5 - rw-v.ch

rw-v.ch
rwbu.ch
Ihre Lernplattform für Rechnungswesen
Direkt zum Seiteninhalt
import tkinter as tk from tkinter import ttk, messagebox, simpledialog import imaplib import smtplib from email.message import EmailMessage from email import policy import threading import email import email.header import os import ctypes import json import re import sys import binascii import html import time import socket # Verhindert, dass sich das Programm bei toten Verbindungen unendlich lange aufhängt (60 Sekunden Limit) socket.setdefaulttimeout(60) if os.name == 'nt': try: ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0) except Exception: pass class RealEmailClient: def __init__(self, root): self.root = root self.root.title("Echter E-Mail Client - Multi-Konto") self.root.geometry("1100x750") self.connections = {} self.active_account = None self.active_folder = None self.email_data_store = {} if getattr(sys, 'frozen', False): application_path = os.path.dirname(sys.executable) else: application_path = os.path.dirname(os.path.abspath(__file__)) self.mail_folder_path = os.path.join(application_path, "mail") if not os.path.exists(self.mail_folder_path): try: os.makedirs(self.mail_folder_path) except Exception as e: print("Konnte mail Ordner nicht erstellen:", e) self.accounts_file = os.path.join(self.mail_folder_path, "accounts.json") self.saved_accounts = self.load_accounts() self.setup_login_ui() def load_accounts(self): if os.path.exists(self.accounts_file): try: with open(self.accounts_file, "r") as f: return json.load(f) except: return {} return {} def save_account(self): email_val = self.entry_email.get() if not email_val: messagebox.showwarning("Fehler", "Bitte eine E-Mail-Adresse zum Speichern eingeben.") return self.saved_accounts[email_val] = { "password": self.entry_pass.get(), "imap": self.entry_imap.get(), "smtp": self.entry_smtp.get() } try: with open(self.accounts_file, "w") as f: json.dump(self.saved_accounts, f) self.update_account_list() messagebox.showinfo("Gespeichert", f"Das Profil für {email_val} wurde gespeichert!") self.entry_email.delete(0, tk.END) self.entry_pass.delete(0, tk.END) except Exception as e: messagebox.showerror("Fehler", f"Konnte Profil nicht speichern:\n{e}") def update_account_list(self): self.listbox_accounts.delete(0, tk.END) for acc in self.saved_accounts.keys(): self.listbox_accounts.insert(tk.END, acc) def decode_imap_utf7(self, s): r = [] parts = s.split('&') r.append(parts[0]) for part in parts[1:]: if '-' in part: b64, rest = part.split('-', 1) if b64 == '': r.append('&') else: b64 = b64.replace(',', '/') b64 += '=' * ((4 - len(b64) % 4) % 4) try: r.append(binascii.a2b_base64(b64).decode('utf-16be')) except: r.append(f"&{b64}-") r.append(rest) else: r.append('&' + part) return ''.join(r) def encode_imap_utf7(self, s): r = [] _in = [] for c in s: if 0x20 <= ord(c) <= 0x7e: if _in: b64 = binascii.b2a_base64(''.join(_in).encode('utf-16be')).decode('ascii').replace('\n', '') r.append('&' + b64.rstrip('=') + '-') _in.clear() if c == '&': r.append('&-') else: r.append(c) else: _in.append(c) if _in: b64 = binascii.b2a_base64(''.join(_in).encode('utf-16be')).decode('ascii').replace('\n', '') r.append('&' + b64.rstrip('=') + '-') return ''.join(r).replace('/', ',') def decode_header_string(self, header_val): if not header_val: return "Unbekannt" decoded_parts = email.header.decode_header(header_val) result = "" for part, encoding in decoded_parts: if isinstance(part, bytes): result += part.decode(encoding or 'utf-8', errors='ignore') else: result += str(part) return result def format_folder_name_for_ui(self, folder_name): if folder_name.upper().startswith("INBOX."): return folder_name[6:] elif folder_name.upper().startswith("INBOX/"): return folder_name[6:] elif folder_name.upper() == "INBOX": return "Posteingang" return folder_name def check_and_reconnect_imap_unlocked(self, account): # Das ist das Herzstück der "Selbstheilung". Prüft ob Verbindung lebt, wenn nicht -> Reconnect. conn_data = self.connections.get(account) if not conn_data: return None imap_conn = conn_data["imap"] acc_data = self.saved_accounts.get(account) try: status, _ = imap_conn.noop() if status != "OK": raise Exception("Status nicht OK") return imap_conn except Exception: try: new_imap = imaplib.IMAP4_SSL(acc_data["imap"], 993) new_imap.login(account, acc_data["password"]) self.connections[account]["imap"] = new_imap return new_imap except Exception as e: print(f"Automatischer Reconnect für {account} fehlgeschlagen: {e}") raise e def setup_login_ui(self): self.clear_window() frame = ttk.Frame(self.root, padding="20") frame.pack(expand=True, fill="both") ttk.Label(frame, text="Konten-Verwaltung", font=("Arial", 16, "bold")).grid(row=0, column=0, columnspan=2, pady=(0, 20)) left_frame = ttk.LabelFrame(frame, text="Meine gespeicherten Konten", padding="10") left_frame.grid(row=1, column=0, sticky="nsew", padx=(0, 20)) self.listbox_accounts = tk.Listbox(left_frame, width=35, height=10, font=("Arial", 10)) self.listbox_accounts.pack(fill="both", expand=True) self.update_account_list() right_frame = ttk.LabelFrame(frame, text="Konto hinzufügen / bearbeiten", padding="10") right_frame.grid(row=1, column=1, sticky="nsew") ttk.Label(right_frame, text="E-Mail Adresse:").grid(row=0, column=0, sticky="e", pady=5, padx=5) self.entry_email = ttk.Entry(right_frame, width=30) self.entry_email.grid(row=0, column=1, pady=5) ttk.Label(right_frame, text="Passwort:").grid(row=1, column=0, sticky="e", pady=5, padx=5) self.entry_pass = ttk.Entry(right_frame, width=30, show="*") self.entry_pass.grid(row=1, column=1, pady=5) ttk.Label(right_frame, text="IMAP Server (Empfang):").grid(row=2, column=0, sticky="e", pady=5, padx=5) self.entry_imap = ttk.Entry(right_frame, width=30) self.entry_imap.grid(row=2, column=1, pady=5) self.entry_imap.insert(0, "mail.deine-domain.de") ttk.Label(right_frame, text="SMTP Server (Versand):").grid(row=3, column=0, sticky="e", pady=5, padx=5) self.entry_smtp = ttk.Entry(right_frame, width=30) self.entry_smtp.grid(row=3, column=1, pady=5) self.entry_smtp.insert(0, "mail.deine-domain.de") ttk.Button(right_frame, text="Profil speichern", command=self.save_account).grid(row=4, column=0, columnspan=2, pady=10) self.btn_login = ttk.Button(frame, text="Alle Konten verbinden & Client starten", command=self.start_login) self.btn_login.grid(row=2, column=0, columnspan=2, pady=30, ipady=10) self.status_label = ttk.Label(frame, text="", foreground="blue") self.status_label.grid(row=3, column=0, columnspan=2) def start_login(self): if not self.saved_accounts: messagebox.showerror("Fehler", "Bitte erst mindestens ein Konto anlegen und speichern!") return self.btn_login.config(state="disabled") self.status_label.config(text="Verbinde mit allen Servern... Bitte warten.") threading.Thread(target=self.login_process, daemon=True).start() def login_process(self): errors = [] for email_addr, acc_data in self.saved_accounts.items(): try: imap_conn = imaplib.IMAP4_SSL(acc_data["imap"], 993) imap_conn.login(email_addr, acc_data["password"]) smtp_conn = smtplib.SMTP_SSL(acc_data["smtp"], 465) smtp_conn.login(email_addr, acc_data["password"]) status, folders_data = imap_conn.list() folders = [] if status == "OK": for f_bytes in folders_data: if f_bytes: f_str = f_bytes.decode('utf-8', errors='ignore') match = re.search(r'\((.*?)\)\s+("[^"]*"|NIL|\S+)\s+(.*)', f_str) if match: folder_name = match.group(3) if folder_name.startswith('"') and folder_name.endswith('"'): folder_name = folder_name[1:-1] folder_name = self.decode_imap_utf7(folder_name) folders.append(folder_name) else: raw_name = f_str.split()[-1].strip('"') folders.append(self.decode_imap_utf7(raw_name)) if not folders: folders = ["INBOX"] self.connections[email_addr] = { "imap": imap_conn, "smtp": smtp_conn, "folders": folders, "lock": threading.Lock() } except Exception as e: errors.append(f"{email_addr}: {str(e)}") if not self.connections: self.root.after(0, lambda: messagebox.showerror("Verbindungsfehler", "Kein Konto verbunden:\n" + "\n".join(errors))) self.root.after(0, lambda: self.btn_login.config(state="normal")) self.root.after(0, lambda: self.status_label.config(text="")) return if errors: self.root.after(0, lambda: messagebox.showwarning("Hinweis", "Einige Konten fehlgeschlagen:\n" + "\n".join(errors))) self.active_account = list(self.connections.keys())[0] self.root.after(0, self.setup_main_ui) def setup_main_ui(self): self.clear_window() # Keep-Alive Thread starten, falls noch nicht geschehen if not hasattr(self, "keep_alive_started"): self.keep_alive_started = True threading.Thread(target=self.keep_alive_process, daemon=True).start() toolbar = ttk.Frame(self.root, padding="5") toolbar.pack(fill="x") ttk.Button(toolbar, text="Neu laden (Empfangen)", command=self.start_fetch_emails).pack(side="left", padx=5) ttk.Button(toolbar, text="Neue E-Mail", command=self.setup_compose_ui).pack(side="left", padx=5) ttk.Button(toolbar, text="Antworten", command=self.reply_mail).pack(side="left", padx=5) ttk.Button(toolbar, text="Weiterleiten", command=self.forward_mail).pack(side="left", padx=5) ttk.Button(toolbar, text="In Ordner verschieben...", command=self.show_move_dialog).pack(side="left", padx=5) self.active_account = list(self.connections.keys())[0] self.active_folder = self.connections[self.active_account]["folders"][0] display_folder = self.format_folder_name_for_ui(self.active_folder) self.label_active = ttk.Label(toolbar, text=f"Postfach: {self.active_account} - {display_folder} (Lade...)", font=("Arial", 10, "bold")) self.label_active.pack(side="right", padx=10) main_paned = ttk.PanedWindow(self.root, orient="horizontal") main_paned.pack(fill="both", expand=True, padx=10, pady=10) sidebar_frame = ttk.Frame(main_paned) main_paned.add(sidebar_frame, weight=1) ttk.Label(sidebar_frame, text="Meine Postfächer", font=("Arial", 11, "bold")).pack(pady=(0,5), anchor="w") self.tree_accounts = ttk.Treeview(sidebar_frame, show="tree") self.tree_accounts.pack(fill="both", expand=True) for acc, data in self.connections.items(): self.tree_accounts.insert("", "end", iid=acc, text=acc, open=True) for folder in data["folders"]: folder_iid = f"{acc}||{folder}" self.tree_accounts.insert(acc, "end", iid=folder_iid, text=self.format_folder_name_for_ui(folder)) self.tree_accounts.selection_set(f"{self.active_account}||{self.active_folder}") self.tree_accounts.bind("<>", self.on_sidebar_select) right_paned = ttk.PanedWindow(main_paned, orient="vertical") main_paned.add(right_paned, weight=4) list_frame = ttk.Frame(right_paned) right_paned.add(list_frame, weight=1) columns = ("Von", "Betreff", "Datum") self.tree = ttk.Treeview(list_frame, columns=columns, show="headings") self.tree.heading("Von", text="Von / An") self.tree.heading("Betreff", text="Betreff") self.tree.heading("Datum", text="Datum") self.tree.column("Von", width=200) self.tree.column("Betreff", width=400) self.tree.column("Datum", width=150) self.tree.pack(fill="both", expand=True) self.tree.bind("<>", self.on_email_select) self.tree.bind("", self.show_context_menu) read_frame = ttk.Frame(right_paned) right_paned.add(read_frame, weight=2) self.text_read = tk.Text(read_frame, wrap="word", state="disabled", font=("Arial", 11)) self.text_read.pack(fill="both", expand=True) self.start_fetch_emails() def keep_alive_process(self): # Wird im Hintergrund ausgeführt und sendet alle 5 Minuten einen Ping an alle Server while True: time.sleep(300) for account, conn_data in list(self.connections.items()): imap_lock = conn_data.get("lock") if imap_lock: try: with imap_lock: self.check_and_reconnect_imap_unlocked(account) except: pass def on_sidebar_select(self, event): selection = self.tree_accounts.selection() if selection: item_id = selection[0] if "||" in item_id: selected_account, selected_folder = item_id.split("||", 1) if selected_account != self.active_account or selected_folder != self.active_folder: self.active_account = selected_account self.active_folder = selected_folder display_folder = self.format_folder_name_for_ui(self.active_folder) self.label_active.config(text=f"Postfach: {self.active_account} - {display_folder} (Lade...)") self.start_fetch_emails() def show_context_menu(self, event): item = self.tree.identify_row(event.y) if item: self.tree.selection_set(item) menu = tk.Menu(self.root, tearoff=0) menu.add_command(label="Antworten", command=self.reply_mail) menu.add_command(label="Weiterleiten", command=self.forward_mail) menu.add_separator() move_menu = tk.Menu(menu, tearoff=0) if self.active_account in self.connections: for f in self.connections[self.active_account]["folders"]: if f != self.active_folder: move_menu.add_command(label=self.format_folder_name_for_ui(f), command=lambda target=f: self.move_email(item, target)) menu.add_cascade(label="Verschieben nach...", menu=move_menu) menu.post(event.x_root, event.y_root) def start_fetch_emails(self): for item in self.tree.get_children(): self.tree.delete(item) self.tree.insert("", "end", values=("Lade E-Mails...", f"Öffne Ordner '{self.format_folder_name_for_ui(self.active_folder)}'...", "")) self.display_email_body("") threading.Thread(target=self.fetch_emails_process, args=(self.active_account, self.active_folder), daemon=True).start() def fetch_emails_process(self, account_to_fetch, folder_to_fetch): try: conn_data = self.connections[account_to_fetch] imap_lock = conn_data["lock"] with imap_lock: # Verbindung sichern/wiederherstellen bevor wir etwas machen! imap_conn = self.check_and_reconnect_imap_unlocked(account_to_fetch) encoded_folder = self.encode_imap_utf7(folder_to_fetch) status, _ = imap_conn.select(f'"{encoded_folder}"') if status != "OK": self.root.after(0, lambda: self.tree.delete(*self.tree.get_children())) self.root.after(0, self.tree.insert, "", "end", values=("Zugriffsfehler", "Ordner konnte nicht geöffnet werden.", "")) self.root.after(0, lambda: self.label_active.config(text=f"Postfach: {account_to_fetch} - Fehler!")) return status, messages = imap_conn.search(None, "ALL") if status != "OK": return email_ids = messages[0].split() total_emails = len(email_ids) display_folder = self.format_folder_name_for_ui(folder_to_fetch) self.root.after(0, lambda: self.label_active.config( text=f"Postfach: {account_to_fetch} - {display_folder} ({total_emails} E-Mails)" )) latest_email_ids = email_ids[-25:] self.root.after(0, lambda: self.tree.delete(*self.tree.get_children())) self.email_data_store = {} for e_id in reversed(latest_email_ids): if self.active_account != account_to_fetch or self.active_folder != folder_to_fetch: break e_id_str = e_id.decode('ascii') with imap_lock: imap_conn = self.check_and_reconnect_imap_unlocked(account_to_fetch) res, msg_data = imap_conn.fetch(e_id, "(BODY.PEEK[HEADER])") for response_part in msg_data: if isinstance(response_part, tuple): msg = email.message_from_bytes(response_part[1], policy=policy.default) subject = self.decode_header_string(str(msg.get("Subject", "Kein Betreff"))) from_ = self.decode_header_string(str(msg.get("From", "Unbekannt"))) date_ = str(msg.get("Date", "")) self.email_data_store[e_id_str] = { "body": None, "subject": subject, "from": from_, "date": date_ } if self.active_account == account_to_fetch and self.active_folder == folder_to_fetch: self.root.after(0, self.insert_tree_item, e_id_str, from_, subject, date_) except Exception as e: print(f"Fehler beim Abrufen von {account_to_fetch}: {e}") self.root.after(0, lambda: self.tree.delete(*self.tree.get_children())) self.root.after(0, self.tree.insert, "", "end", values=("Verbindungsfehler", "Server antwortet nicht mehr. Bitte 'Neu laden' klicken.", "")) def insert_tree_item(self, e_id_str, from_, subject, date_): self.tree.insert("", "end", iid=e_id_str, values=(from_[:40], subject[:80], date_[:25])) def on_email_select(self, event): selection = self.tree.selection() if not selection: return e_id_str = selection[0] if "Lade E-Mails" in str(self.tree.item(e_id_str, "values")) or "Fehler" in str(self.tree.item(e_id_str, "values")): return email_data = self.email_data_store.get(e_id_str) if not email_data: return if email_data.get("body") is not None: self.display_email_body(email_data["body"]) else: self.display_email_body("Nachricht wird vom Server heruntergeladen... Bitte warten.") threading.Thread(target=self.fetch_single_email_body, args=(self.active_account, self.active_folder, e_id_str), daemon=True).start() def fetch_single_email_body(self, account, folder, e_id_str): try: conn_data = self.connections.get(account) if not conn_data: return imap_lock = conn_data["lock"] with imap_lock: imap_conn = self.check_and_reconnect_imap_unlocked(account) encoded_folder = self.encode_imap_utf7(folder) imap_conn.select(f'"{encoded_folder}"') res, msg_data = imap_conn.fetch(e_id_str.encode('ascii'), "(BODY.PEEK[])") for response_part in msg_data: if isinstance(response_part, tuple): msg = email.message_from_bytes(response_part[1], policy=policy.default) body = self.extract_body(msg) if e_id_str in self.email_data_store: self.email_data_store[e_id_str]["body"] = body self.root.after(0, self.update_body_if_selected, e_id_str, body) except Exception as e: self.root.after(0, self.update_body_if_selected, e_id_str, f"Inhalt konnte nicht geladen werden.\nVerbindung unterbrochen. Fehler: {e}") def update_body_if_selected(self, e_id_str, body_text): selection = self.tree.selection() if selection and selection[0] == e_id_str: self.display_email_body(body_text) def display_email_body(self, text): self.text_read.config(state="normal") self.text_read.delete(1.0, tk.END) self.text_read.insert(tk.END, text) self.text_read.config(state="disabled") def extract_body(self, msg): body_text = "" body_html = "" if msg.is_multipart(): for part in msg.walk(): content_type = part.get_content_type() disposition = str(part.get("Content-Disposition")) if "attachment" not in disposition: if content_type == "text/plain": try: body_text = part.get_payload(decode=True).decode('utf-8', errors='ignore') except: pass elif content_type == "text/html": try: body_html = part.get_payload(decode=True).decode('utf-8', errors='ignore') except: pass else: content_type = msg.get_content_type() try: payload = msg.get_payload(decode=True).decode('utf-8', errors='ignore') if content_type == "text/html": body_html = payload else: body_text = payload except: body_text = str(msg.get_payload()) if body_text: return body_text elif body_html: clean_html = re.sub(r'.*?', '', body_html, flags=re.IGNORECASE | re.DOTALL) clean_html = re.sub(r'.*?', '', clean_html, flags=re.IGNORECASE | re.DOTALL) clean_html = re.sub(r'', '\n', clean_html, flags=re.IGNORECASE) clean_html = re.sub(r'

', '\n\n', clean_html, flags=re.IGNORECASE) clean_html = re.sub(r'<[^>]+>', '', clean_html) return html.unescape(clean_html).strip() return "Inhalt konnte nicht gelesen werden." def reply_mail(self): selection = self.tree.selection() if not selection: messagebox.showinfo("Hinweis", "Bitte wähle eine E-Mail zum Antworten aus.") return mail_data = self.email_data_store.get(selection[0]) if not mail_data: return if mail_data.get("body") is None: messagebox.showinfo("Hinweis", "Die E-Mail wird gerade noch geladen. Bitte einen Moment warten.") return subject = mail_data["subject"] if not subject.lower().startswith("aw:") and not subject.lower().startswith("re:"): subject = "Aw: " + subject reply_to = mail_data["from"] match = re.search(r'<(.+?)>', reply_to) if match: reply_to = match.group(1) body = f"\n\n\n--- Ursprüngliche Nachricht ---\nVon: {mail_data['from']}\nGesendet: {mail_data['date']}\nBetreff: {mail_data['subject']}\n\n{mail_data['body']}" self.setup_compose_ui(to_addr=reply_to, subject=subject, body=body) def forward_mail(self): selection = self.tree.selection() if not selection: messagebox.showinfo("Hinweis", "Bitte wähle eine E-Mail zum Weiterleiten aus.") return mail_data = self.email_data_store.get(selection[0]) if not mail_data: return if mail_data.get("body") is None: messagebox.showinfo("Hinweis", "Die E-Mail wird gerade noch geladen. Bitte einen Moment warten.") return subject = mail_data["subject"] if not subject.lower().startswith("wg:") and not subject.lower().startswith("fwd:"): subject = "Wg: " + subject body = f"\n\n\n--- Weitergeleitete Nachricht ---\nVon: {mail_data['from']}\nGesendet: {mail_data['date']}\nBetreff: {mail_data['subject']}\n\n{mail_data['body']}" self.setup_compose_ui(to_addr="", subject=subject, body=body) def show_move_dialog(self): selection = self.tree.selection() if not selection: messagebox.showinfo("Hinweis", "Bitte wähle eine E-Mail zum Verschieben aus.") return folders = self.connections[self.active_account]["folders"] display_folders = [self.format_folder_name_for_ui(f) for f in folders if f != self.active_folder] if not display_folders: messagebox.showinfo("Hinweis", "Keine anderen Ordner gefunden.") return target_display = simpledialog.askstring("Ordner wählen", f"Wohin verschieben?\nMögliche Ordner:\n{', '.join(display_folders)}", parent=self.root) if not target_display: return target_folder = None for f in folders: if self.format_folder_name_for_ui(f).lower() == target_display.lower().strip(): target_folder = f break if target_folder: self.move_email(selection[0], target_folder) else: messagebox.showerror("Fehler", "Dieser Ordner wurde nicht gefunden.") def move_email(self, email_id, target_folder): try: conn_data = self.connections[self.active_account] imap_lock = conn_data["lock"] encoded_target = self.encode_imap_utf7(target_folder) with imap_lock: imap_conn = self.check_and_reconnect_imap_unlocked(self.active_account) status, _ = imap_conn.copy(email_id.encode('ascii'), f'"{encoded_target}"') if status == "OK": imap_conn.store(email_id.encode('ascii'), '+FLAGS', '\\Deleted') imap_conn.expunge() if status == "OK": self.tree.delete(email_id) self.start_fetch_emails() else: messagebox.showerror("Fehler", "Der Server hat das Verschieben abgelehnt.") except Exception as e: messagebox.showerror("Fehler", f"Konnte nicht verschoben werden:\n{e}") def setup_compose_ui(self, to_addr="", subject="", body=""): compose_window = tk.Toplevel(self.root) compose_window.title("Neue E-Mail") compose_window.geometry("700x600") frame = ttk.Frame(compose_window, padding="10") frame.pack(fill="both", expand=True) ttk.Label(frame, text="Von:").grid(row=0, column=0, sticky="e", pady=5) combo_from = ttk.Combobox(frame, width=57, state="readonly") combo_from.grid(row=0, column=1, sticky="w", pady=5) combo_from['values'] = list(self.connections.keys()) if self.active_account in self.connections: combo_from.set(self.active_account) else: combo_from.current(0) ttk.Label(frame, text="An:").grid(row=1, column=0, sticky="e", pady=5) entry_to = ttk.Entry(frame, width=60) entry_to.grid(row=1, column=1, sticky="w", pady=5) entry_to.insert(0, to_addr) ttk.Label(frame, text="Betreff:").grid(row=2, column=0, sticky="e", pady=5) entry_subject = ttk.Entry(frame, width=60) entry_subject.grid(row=2, column=1, sticky="w", pady=5) entry_subject.insert(0, subject) ttk.Label(frame, text="Nachricht:").grid(row=3, column=0, sticky="ne", pady=5) text_body = tk.Text(frame, width=60, height=20, font=("Arial", 11)) text_body.grid(row=3, column=1, sticky="w", pady=5) text_body.insert(tk.END, body) text_body.mark_set("insert", "1.0") text_body.focus() btn_send = ttk.Button(frame, text="E-Mail Senden", command=lambda: self.send_email(combo_from.get(), entry_to.get(), entry_subject.get(), text_body.get(1.0, tk.END), compose_window)) btn_send.grid(row=4, column=1, sticky="w", pady=15) def send_email(self, from_addr, to_addr, subject, body, window): if not to_addr or not subject: messagebox.showwarning("Fehler", "Bitte Empfänger und Betreff ausfüllen!") return try: acc_data = self.saved_accounts[from_addr] smtp_conn = self.connections[from_addr]["smtp"] # Prüfen ob SMTP Server uns gekickt hat, wenn ja -> Neu verbinden try: status, _ = smtp_conn.noop() if status != 250: raise Exception("SMTP Getrennt") except Exception: smtp_conn = smtplib.SMTP_SSL(acc_data["smtp"], 465) smtp_conn.login(from_addr, acc_data["password"]) self.connections[from_addr]["smtp"] = smtp_conn msg = EmailMessage() msg.set_content(body) msg['Subject'] = subject msg['From'] = from_addr msg['To'] = to_addr smtp_conn.send_message(msg) messagebox.showinfo("Erfolg", "Die E-Mail wurde erfolgreich versendet!") window.destroy() except Exception as e: messagebox.showerror("Sende-Fehler", f"Konnte E-Mail nicht senden:\n{str(e)}") def clear_window(self): for widget in self.root.winfo_children(): widget.destroy() if __name__ == "__main__": root = tk.Tk() app = RealEmailClient(root) root.mainloop()
Zurück zum Seiteninhalt