ulthar.xyz > Repos

dotfiles

Seven years worth of accumulated configuration cruft
About Files Commits git clone https://ulthar.xyz/repos/dotfiles/dotfiles.git

dotfiles/scripts/.local/bin/mailaddr

Download raw file: scripts/.local/bin/mailaddr

#!/usr/bin/env python3
# Add a new email address alias to my domain name using migadu.

import re
import sys
import requests
import subprocess
from bs4 import BeautifulSoup

class MigaduError(Exception):
	pass;

def get_email_and_passwd():
	print("Getting username and password from keyring...")
	o = subprocess.run(["pass", "migadu.com"],
			   stdout=subprocess.PIPE).stdout.decode("utf-8").split("\n")
	return (o[1].split(" ")[1], o[0])

def get_otp_code():
	print("Getting OTP code from keyring...")
	return subprocess.run(["pass", "otp", "migadu.com"],
			      stdout=subprocess.PIPE).stdout.decode("utf-8").split("\n")[0]

def fetch_csrf_token_and_session_cookie():
	print("Fetching signin CSRF token and initial session cookie...")
	r = requests.get("https://admin.migadu.com/public/login")
	r.raise_for_status()
	soup = BeautifulSoup(r.text, "html.parser")
	return (soup.find(id="signin_form").input.get("value"), r.cookies)

def signin_passwd(email, passwd, cookies, signin_csrf_token):
	print("Signing in with username and password...")
	r = requests.post("https://admin.migadu.com/public/login",
			  cookies=cookies,
			  data={"user[email]": email,
				"user[password]": passwd,
				"_csrf_token": signin_csrf_token})
	r.raise_for_status()
	soup = BeautifulSoup(r.text, "html.parser")
	otp_csrf_token = soup.find(id="otp_form").input.get("value")
	org_id = soup.find(id="otp_form_organization_id").get("value")
	return (otp_csrf_token, r.cookies, org_id)

def signin_otp(cookies, otp_csrf_token, otp_code, org_id):
	print("Signing in with OTP code...")
	r = requests.post("https://admin.migadu.com/public/login",
			  cookies=cookies,
			  data={"user[otp_token]": otp_code,
				"user[organization_id]": org_id,
				"user[temporary_disable_second_factor]": "false",
				"_csrf_token": otp_csrf_token})
	r.raise_for_status()
	return r.cookies

def get_login_cookies():
	print("Logging you in...")
	(email, passwd) = get_email_and_passwd()
	(csrf_token, cookies) = fetch_csrf_token_and_session_cookie()
	(csrf_token, cookies, org_id) = signin_passwd(email, passwd, cookies, csrf_token)
	cookies = signin_otp(cookies, csrf_token, get_otp_code(), org_id)
	print("Successfully logged in.")
	return cookies

def get_alias_csrf_token(auth):
	print("Getting CSRF token for alias form...")
	r = requests.get("https://admin.migadu.com/domains/104297/aliases/new",
			 cookies=auth)
	r.raise_for_status()
	soup = BeautifulSoup(r.text, "html.parser")
	return soup.find(action="/domains/104297/aliases").input.get("value")

def create_mail_alias(auth, addr):
	alias_csrf_token = get_alias_csrf_token(auth)
	print("Creating alias " + addr + "@ulthar.xyz...")
	r = requests.post("https://admin.migadu.com/domains/104297/aliases",
			  cookies=auth,
			  data={"_csrf_token": alias_csrf_token,
				"alias[tags]": "",
				"alias[local_part]": addr,
				"alias[destinations]": "ymir"})
	r.raise_for_status()
	error = BeautifulSoup(r.text, "html.parser").find(class_="alert alert-error")
	if error:
		print("Something's wrong, writing response to migadu_debug.html",
		      file=sys.stderr)
		with open("migadu_debug.html", "w") as f:
			f.write(r.text)
		raise MigaduError(error.span.text)
	else:
		print("Created alias " + addr + "@ulthar.xyz")
		pass
	pass

def main(argv):
	if len(argv) == 1:
		print("Generate email aliases: mailaddr [aliases...]",
		      file=sys.stderr)
		return 1
	auth = get_login_cookies()
	for arg in argv[1:]:
		has_domain = re.compile("^.*@ulthar.xyz$")
		addr = arg.replace("@ulthar.xyz", "", 1) if has_domain.match(arg) else arg
		try:
			create_mail_alias(auth, addr)
		except MigaduError as err:
			print("Migadu Error: " + str(err), file=sys.stderr);
			return -1
	return 0

if __name__ == '__main__':
	sys.exit(main(sys.argv))
	pass
Generated 2025-03-07 15:24:27 -0700 by RepoRat