"""Upload-Ziele: lokaler Ordner, Nextcloud (WebDAV), SFTP. Plus E-Mail-Notify.""" from __future__ import annotations import logging import smtplib import ssl from email.message import EmailMessage from pathlib import Path from urllib.parse import quote import paramiko import requests from .config import EmailNotify, FolderUpload, NextcloudUpload, SftpUpload log = logging.getLogger(__name__) def upload_folder(pdf: Path, cfg: FolderUpload, default_target: Path) -> bool: if not cfg.enabled: return True target = Path(cfg.target) if cfg.target else default_target target.mkdir(parents=True, exist_ok=True) dest = target / pdf.name try: if pdf.resolve() == dest.resolve(): return True dest.write_bytes(pdf.read_bytes()) log.info("Folder upload OK: %s", dest) return True except OSError as e: log.error("Folder upload failed: %s", e) return False def upload_nextcloud(pdf: Path, cfg: NextcloudUpload) -> bool: if not cfg.enabled: return True base = cfg.url.rstrip("/") remote = "/".join(quote(part) for part in cfg.remote_path.strip("/").split("/") if part) url = f"{base}/remote.php/dav/files/{quote(cfg.username)}/{remote}/{quote(pdf.name)}" try: with pdf.open("rb") as f: r = requests.put(url, data=f, auth=(cfg.username, cfg.password), verify=cfg.verify_ssl, timeout=300) if r.status_code in (200, 201, 204): log.info("Nextcloud upload OK: %s", pdf.name) return True log.error("Nextcloud upload HTTP %s: %s", r.status_code, r.text[:200]) return False except requests.RequestException as e: log.error("Nextcloud upload failed: %s", e) return False def upload_sftp(pdf: Path, cfg: SftpUpload) -> bool: if not cfg.enabled: return True try: client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) connect_kwargs: dict = { "hostname": cfg.host, "port": cfg.port, "username": cfg.username, "timeout": 30, } if cfg.key_file: connect_kwargs["key_filename"] = cfg.key_file if cfg.password: connect_kwargs["password"] = cfg.password client.connect(**connect_kwargs) sftp = client.open_sftp() try: remote = f"{cfg.remote_path.rstrip('/')}/{pdf.name}" sftp.put(str(pdf), remote) log.info("SFTP upload OK: %s", remote) return True finally: sftp.close() client.close() except (paramiko.SSHException, OSError) as e: log.error("SFTP upload failed: %s", e) return False def notify_email(cfg: EmailNotify, subject: str, body: str, success: bool) -> None: if not cfg.enabled or cfg.on == "never": return if cfg.on == "errors" and success: return msg = EmailMessage() msg["Subject"] = subject msg["From"] = cfg.from_addr msg["To"] = ", ".join(cfg.to_addrs) msg.set_content(body) try: with smtplib.SMTP(cfg.smtp_host, cfg.smtp_port, timeout=30) as s: if cfg.use_starttls: s.starttls(context=ssl.create_default_context()) if cfg.smtp_user: s.login(cfg.smtp_user, cfg.smtp_password) s.send_message(msg) log.info("E-Mail-Notify gesendet: %s", subject) except (smtplib.SMTPException, OSError) as e: log.error("E-Mail-Notify fehlgeschlagen: %s", e)