#!/usr/bin/env python3 """SMTP/Mailpit client tool for ScadaLink test infrastructure.""" import argparse import email.mime.text import json import smtplib import sys import urllib.request DEFAULT_SMTP_HOST = "localhost" DEFAULT_SMTP_PORT = 1025 DEFAULT_API_URL = "http://localhost:8025/api" def cmd_check(args): """Test SMTP connectivity and report Mailpit status.""" # Test SMTP connection try: server = smtplib.SMTP(args.host, args.port, timeout=5) server.ehlo() smtp_ok = True server.quit() except Exception as e: smtp_ok = False smtp_err = str(e) # Test API/web UI try: req = urllib.request.Request(f"{args.api}/v1/info") with urllib.request.urlopen(req, timeout=5) as resp: info = json.loads(resp.read()) api_ok = True except Exception as e: api_ok = False api_err = str(e) print(f"SMTP ({args.host}:{args.port}): {'OK' if smtp_ok else 'FAILED - ' + smtp_err}") print(f"Web UI/API ({args.api}): {'OK' if api_ok else 'FAILED - ' + api_err}") if api_ok: print(f"\nMailpit version: {info.get('Version', 'unknown')}") print(f"Database path: {info.get('DatabasePath', 'unknown')}") messages = info.get('Messages', 0) print(f"Stored messages: {messages}") if smtp_ok and api_ok: print("\nSMTP server is healthy.") else: sys.exit(1) def cmd_send(args): """Send a test email via SMTP.""" msg = email.mime.text.MIMEText(args.body, "plain") msg["Subject"] = args.subject msg["From"] = args.sender msg["To"] = args.to if args.bcc: recipients = [args.to] + [b.strip() for b in args.bcc.split(",")] else: recipients = [args.to] try: server = smtplib.SMTP(args.host, args.port, timeout=5) server.ehlo() server.sendmail(args.sender, recipients, msg.as_string()) server.quit() bcc_note = f" (BCC: {args.bcc})" if args.bcc else "" print(f"Sent: {args.sender} -> {args.to}{bcc_note}") print(f"Subject: {args.subject}") except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) def cmd_list(args): """List messages in Mailpit inbox.""" try: req = urllib.request.Request(f"{args.api}/v1/messages?limit={args.limit}") with urllib.request.urlopen(req, timeout=5) as resp: data = json.loads(resp.read()) except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) total = data.get("messages_count", 0) messages = data.get("messages", []) print(f"Messages: {len(messages)} shown / {total} total\n") if not messages: print("(inbox empty)") return print(f"{'ID':<24} {'Date':<22} {'From':<30} {'To':<30} {'Subject'}") print("-" * 130) for msg in messages: msg_id = msg["ID"] date = msg.get("Date", "")[:21] from_addr = msg.get("From", {}).get("Address", "")[:28] to_list = msg.get("To", []) to_addr = (to_list[0].get("Address", "") if to_list else "")[:28] subject = msg.get("Subject", "")[:40] print(f"{msg_id:<24} {date:<22} {from_addr:<30} {to_addr:<30} {subject}") def cmd_read(args): """Read a specific message by ID.""" try: req = urllib.request.Request(f"{args.api}/v1/message/{args.id}") with urllib.request.urlopen(req, timeout=5) as resp: msg = json.loads(resp.read()) except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) print(f"ID: {msg['ID']}") print(f"Date: {msg.get('Date', '')}") from_info = msg.get("From", {}) print(f"From: {from_info.get('Name', '')} <{from_info.get('Address', '')}>") for field in ["To", "Cc", "Bcc"]: addrs = msg.get(field, []) if addrs: formatted = ", ".join(f"{a.get('Name', '')} <{a.get('Address', '')}>" for a in addrs) print(f"{field}: {formatted}") print(f"Subject: {msg.get('Subject', '')}") print() print(msg.get("Text", "(no text body)")) def cmd_clear(args): """Delete all messages in Mailpit.""" try: req = urllib.request.Request(f"{args.api}/v1/messages", method="DELETE") with urllib.request.urlopen(req, timeout=5) as resp: resp.read() print("All messages deleted.") except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) def main(): parser = argparse.ArgumentParser(description="SMTP/Mailpit client tool for ScadaLink test infrastructure") parser.add_argument("--host", default=DEFAULT_SMTP_HOST, help=f"SMTP host (default: {DEFAULT_SMTP_HOST})") parser.add_argument("--port", type=int, default=DEFAULT_SMTP_PORT, help=f"SMTP port (default: {DEFAULT_SMTP_PORT})") parser.add_argument("--api", default=DEFAULT_API_URL, help=f"Mailpit API URL (default: {DEFAULT_API_URL})") sub = parser.add_subparsers(dest="command", required=True) sub.add_parser("check", help="Test SMTP connectivity and Mailpit status") send_p = sub.add_parser("send", help="Send a test email") send_p.add_argument("--sender", default="scada-notifications@company.com", help="From address (default: scada-notifications@company.com)") send_p.add_argument("--to", required=True, help="Recipient address") send_p.add_argument("--bcc", help="Comma-separated BCC addresses") send_p.add_argument("--subject", default="Test notification", help="Subject line") send_p.add_argument("--body", default="This is a test notification from ScadaLink.", help="Message body") list_p = sub.add_parser("list", help="List messages in Mailpit inbox") list_p.add_argument("--limit", type=int, default=20, help="Number of messages to show (default: 20)") read_p = sub.add_parser("read", help="Read a specific message by ID") read_p.add_argument("--id", required=True, help="Message ID (from list command)") sub.add_parser("clear", help="Delete all messages") args = parser.parse_args() commands = { "check": cmd_check, "send": cmd_send, "list": cmd_list, "read": cmd_read, "clear": cmd_clear, } commands[args.command](args) if __name__ == "__main__": main()