#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ ================================================================================ Nazev: mailstore_read_v1.0.py Verze: 1.0 Datum: 2026-06-11 Autor: Vladimir Buzalka (asistovano Claude) Popis: Precte JEDNU konkretni zpravu z MailStore slozky a vypise jeji plny obsah - hlavicky, telo (text), seznam priloh. Volitelne ulozi prilohy na disk. Posledni dilek rucniho prohlizece archivu. Argumenty: slozka = plna cesta (fullName z mapy / vystupu mailstore_folder) cislo = poradove cislo zpravy (# z mailstore_folder), nebo UID s --uid Zdroj: MailStore IMAP, port 143, STARTTLS, auth Prosty text (LOGIN). FETCH (RFC822) = cely syrovy EML, naparsovan emailem. Spusteni: python mailstore_read_v1.0.py "...slozka..." 63627 python mailstore_read_v1.0.py "...slozka..." 12345 --uid # cislo je UID python mailstore_read_v1.0.py "...slozka..." 63627 --save .\att # ulozi prilohy python mailstore_read_v1.0.py "...slozka..." 63627 --raw # vypise cely EML ================================================================================ """ from __future__ import annotations import argparse import email import imaplib import os import ssl import sys from email.header import decode_header from email.utils import parsedate_to_datetime # --- konfigurace ------------------------------------------------------------ HOST = "192.168.1.53" PORT = 143 USER = "admin" PASS = "*$N(B)vMUym!%" BODY_PREVIEW_CHARS = 4000 # kolik znaku tela vypsat na obrazovku def connect() -> imaplib.IMAP4: ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE M = imaplib.IMAP4(HOST, PORT) M.starttls(ssl_context=ctx) M.login(USER, PASS) return M def encode_mutf7(s: str) -> str: """Nazev IMAP slozky -> modified UTF-7 (RFC 3501) kvuli diakritice.""" import base64 as _b64 res = [] i, n = 0, len(s) while i < n: ch = s[i]; o = ord(ch) if 0x20 <= o <= 0x7e: res.append("&-" if ch == "&" else ch); i += 1 else: j = i while j < n and not (0x20 <= ord(s[j]) <= 0x7e): j += 1 enc = _b64.b64encode(s[i:j].encode("utf-16-be")).decode("ascii").rstrip("=").replace("/", ",") res.append("&" + enc + "-"); i = j return "".join(res) def dec(s: str | None) -> str: if not s: return "" out = [] for txt, enc in decode_header(s): if isinstance(txt, bytes): out.append(txt.decode(enc or "utf-8", errors="replace")) else: out.append(txt) return "".join(out).replace("\r", " ").replace("\n", " ").strip() def html_to_text(html: str) -> str: """HTML -> text. Zkusi bs4 (je v projektu), jinak hrubsi fallback.""" try: from bs4 import BeautifulSoup try: soup = BeautifulSoup(html, "lxml") except Exception: soup = BeautifulSoup(html, "html.parser") for t in soup(["script", "style", "head"]): t.decompose() text = soup.get_text(separator="\n") except Exception: import re text = re.sub(r"<[^>]+>", "", html) lines = [ln.strip() for ln in text.splitlines()] return "\n".join(ln for ln in lines if ln) def main() -> int: ap = argparse.ArgumentParser(description="Precist jednu zpravu z MailStore") ap.add_argument("folder", help="Plna cesta slozky") ap.add_argument("number", help="Poradove cislo zpravy (nebo UID s --uid)") ap.add_argument("--uid", action="store_true", help="Cislo je IMAP UID, ne poradi") ap.add_argument("--save", metavar="DIR", help="Ulozit prilohy do adresare") ap.add_argument("--raw", action="store_true", help="Vypsat cely syrovy EML a skoncit") args = ap.parse_args() M = connect() typ, data = M.select(f'"{encode_mutf7(args.folder)}"', readonly=True) if typ != "OK": print(f"Slozku nelze otevrit: {data}", file=sys.stderr) return 1 # FETCH cele zpravy (RFC822). UID FETCH kdyz --uid. if args.uid: typ, msg_data = M.uid("FETCH", args.number, "(RFC822)") else: typ, msg_data = M.fetch(args.number, "(RFC822)") if typ != "OK" or not msg_data or not isinstance(msg_data[0], tuple): print(f"Zpravu #{args.number} nelze nacist (typ={typ})", file=sys.stderr) M.logout() return 1 raw = msg_data[0][1] M.logout() if args.raw: sys.stdout.buffer.write(raw) return 0 msg = email.message_from_bytes(raw) # --- hlavicky --- print("=" * 80) print(f"Slozka : {args.folder}") print(f"{'UID' if args.uid else 'Cislo'} : {args.number}") print("-" * 80) print(f"Datum : {msg.get('Date')}") print(f"Od : {dec(msg.get('From'))}") print(f"Komu : {dec(msg.get('To'))}") if msg.get("Cc"): print(f"Kopie : {dec(msg.get('Cc'))}") print(f"Predmet : {dec(msg.get('Subject'))}") print(f"Msg-ID : {msg.get('Message-ID')}") print(f"EML velikost: {len(raw):,} bytu") # --- telo + prilohy --- body_text = body_html = "" attachments = [] # (filename, size, payload) for part in msg.walk(): if part.is_multipart(): continue ct = part.get_content_type() disp = str(part.get("Content-Disposition") or "") payload = part.get_payload(decode=True) if "attachment" in disp or (part.get_filename() and ct not in ("text/plain", "text/html")): attachments.append((dec(part.get_filename()) or "(bez nazvu)", len(payload or b""), payload or b"")) elif ct == "text/plain" and not body_text: body_text = (payload or b"").decode(part.get_content_charset() or "utf-8", errors="replace") elif ct == "text/html" and not body_html: body_html = (payload or b"").decode(part.get_content_charset() or "utf-8", errors="replace") print("-" * 80) if attachments: print(f"Prilohy ({len(attachments)}):") for name, size, _ in attachments: print(f" - {name} ({size:,} B)") else: print("Prilohy: zadne") # telo: preferuj plain, jinak html->text text = body_text or (html_to_text(body_html) if body_html else "") src = "text/plain" if body_text else ("text/html->text" if body_html else "(zadne)") print("-" * 80) print(f"TELO ({src}, {len(text):,} znaku):") print("-" * 80) if text: print(text[:BODY_PREVIEW_CHARS]) if len(text) > BODY_PREVIEW_CHARS: print(f"\n... [zkraceno, celkem {len(text):,} znaku] ...") else: print("(prazdne telo)") # --- ulozeni priloh --- if args.save and attachments: os.makedirs(args.save, exist_ok=True) print("-" * 80) for name, size, payload in attachments: safe = name.replace("/", "_").replace("\\", "_") or "att.bin" path = os.path.join(args.save, safe) with open(path, "wb") as f: f.write(payload) print(f"Ulozeno: {path} ({size:,} B)") print("=" * 80) return 0 if __name__ == "__main__": try: sys.exit(main()) except KeyboardInterrupt: print("\nPreruseno", file=sys.stderr) sys.exit(1)