feat: MIME email composer with .ovpn attachment via msmtp/sendmail
Add send_email() function that composes multipart MIME messages with template-based body and .ovpn file attachment, piped to mail binary via subprocess.Popen. Supports Cryptgeon URL insertion and sender metadata. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -309,6 +309,50 @@ def create_note(content: str, base_url: str) -> str:
|
||||
return f"{base_url.rstrip('/')}/#/note/{note_id}/{key_fragment}"
|
||||
|
||||
|
||||
# ============================================================
|
||||
# === MAILER ===
|
||||
# ============================================================
|
||||
|
||||
|
||||
def send_email(
|
||||
to_address: str,
|
||||
cn: str,
|
||||
one_time_url: str,
|
||||
ovpn_path: str,
|
||||
mail_from: str,
|
||||
subject: str,
|
||||
template_path: str,
|
||||
mail_binary: str,
|
||||
) -> None:
|
||||
"""Compose and send email with .ovpn attachment via msmtp/sendmail."""
|
||||
config_name = os.path.basename(ovpn_path)
|
||||
body = Path(template_path).read_text().format(
|
||||
cn=cn, url=one_time_url, config_name=config_name,
|
||||
)
|
||||
msg = email.mime.multipart.MIMEMultipart()
|
||||
msg["From"] = mail_from
|
||||
msg["To"] = to_address
|
||||
msg["Subject"] = subject
|
||||
msg.attach(email.mime.text.MIMEText(body, "plain"))
|
||||
|
||||
with open(ovpn_path, "rb") as fh:
|
||||
part = email.mime.base.MIMEBase("application", "octet-stream")
|
||||
part.set_payload(fh.read())
|
||||
email.encoders.encode_base64(part)
|
||||
part.add_header("Content-Disposition", f'attachment; filename="{config_name}"')
|
||||
msg.attach(part)
|
||||
|
||||
proc = subprocess.Popen(
|
||||
[mail_binary, "-t"],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
)
|
||||
_, err = proc.communicate(input=msg.as_bytes())
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(
|
||||
f"mail delivery failed (exit {proc.returncode}): {err.decode().strip()}"
|
||||
)
|
||||
|
||||
|
||||
# (remaining sections added in later tasks)
|
||||
# ============================================================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user