#!/usr/bin/env python3 """ Meh mail client conf generator for other things """ import configparser import os import sys # TODO Find config file from XDG # TODO Signature file # TODO Write ~/.mail/[mailbox]/color file if required by sth? # TODO Write in .config or .cache /mel # TODO Fix IMAPS with mbsync configPath = os.path.join(os.path.expanduser("~"), ".config", "mel", "accounts.conf") config = configparser.ConfigParser() config.read(configPath) storageFull = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"])) config["GENERAL"]["storage"] = storageFull SERVER_DEFAULTS = { "imap": {"port": 143, "starttls": True}, "smtp": {"port": 587, "starttls": True}, } SERVER_ITEMS = {"host", "port", "user", "pass", "starttls"} ACCOUNT_DEFAULTS = { "color": "#FFFFFF", "color16": "0", # "colormutt": "white", "inboxfolder": "INBOX", "archivefolder": "Archive", "draftsfolder": "Drafts", "sentfolder": "Sent", "spamfolder": "Spam", "trashfolder": "Trash", } # Reading sections accounts = dict() mails = set() for name in config.sections(): if not name.islower(): continue section = config[name] data = dict() for server in SERVER_DEFAULTS.keys(): for item in SERVER_ITEMS: key = server + item try: val = ( section.get(key) or section.get(item) or SERVER_DEFAULTS[server][item] ) except KeyError: raise KeyError("{}.{}".format(name, key)) if isinstance(val, str): if val == "True": val = True elif val == "False": val = False elif val.isnumeric(): val = int(val) data[key] = val for key in section.keys(): if key in SERVER_ITEMS: continue data[key] = section[key] for k, v in config["DEFAULT"].items(): if k not in data: data[k] = v for k, v in ACCOUNT_DEFAULTS.items(): if k not in data: data[k] = v mails.add(section["from"]) if "alternatives" in section: for alt in section["alternatives"].split(";"): mails.add(alt) data["account"] = name data["storage"] = os.path.join(config["GENERAL"]["storage"], name) data["storageInbox"] = os.path.join(data["storage"], "INBOX") accounts[name] = data general = dict() section = config["GENERAL"] for key in section.keys(): general[key] = section[key] general["main"] = accounts[general["main"]] # OfflineIMAP OFFLINEIMAP_BEGIN = """[general] # List of accounts to be synced, separated by a comma. accounts = {} maxsyncaccounts = {} stocktimeout = 60 pythonfile = ~/.config/offlineimap.py [mbnames] enabled = yes filename = ~/.mutt/mailboxes header = "mailboxes " peritem = "+%(accountname)s/%(foldername)s" sep = " " footer = "\\n" """ OFFLINEIMAP_ACCOUNT = """[Account {account}] localrepository = {account}-local remoterepository = {account}-remote autorefresh = 0.5 quick = 10 utf8foldernames = yes postsynchook = ~/.mutt/postsync [Repository {account}-local] type = Maildir localfolders = {storage} [Repository {account}-remote] type = IMAP {secconf} keepalive = 60 holdconnectionopen = yes remotehost = {imaphost} remoteport = {imapport} remoteuser = {imapuser} remotepass = {imappass} """ offlineIMAPstr = OFFLINEIMAP_BEGIN.format(",".join(accounts), len(accounts)) for name, account in accounts.items(): if account["imapstarttls"]: secconf = "ssl = no" else: secconf = "sslcacertfile = /etc/ssl/certs/ca-certificates.crt" offlineIMAPstr += OFFLINEIMAP_ACCOUNT.format(**account, secconf=secconf) # TODO Write # mbsync MBSYNC_ACCOUNT = """IMAPAccount {account} Host {imaphost} Port {imapport} User {imapuser} Pass "{imappassEscaped}" {secconf} IMAPStore {account}-remote Account {account} MaildirStore {account}-local Subfolders Verbatim Path {storage}/ Inbox {storageInbox}/ Channel {account} Master :{account}-remote: Slave :{account}-local: Patterns * Create Both SyncState * """ mbsyncStr = "" for name, account in accounts.items(): if account["imapstarttls"]: secconf = "SSLType STARTTLS" else: secconf = "SSLType IMAPS" if "certificate" in account: secconf += "\nCertificateFile {certificate}".format(**account) imappassEscaped = account["imappass"].replace("\\", "\\\\") mbsyncStr += MBSYNC_ACCOUNT.format( **account, secconf=secconf, imappassEscaped=imappassEscaped ) mbsyncFilepath = os.path.join(os.path.expanduser("~"), ".config/mel/mbsyncrc") with open(mbsyncFilepath, "w") as f: f.write(mbsyncStr) # msmtp MSMTP_BEGIN = """defaults protocol smtp auth on tls_trust_file /etc/ssl/certs/ca-certificates.crt """ MSMTP_ACCOUNT = """account {account} from {from} user {smtpuser} password {smtppass} host {smtphost} port {smtpport} tls on """ msmtpStr = MSMTP_BEGIN for name, account in accounts.items(): msmtpStr += MSMTP_ACCOUNT.format(**account) mbsyncFilepath = os.path.join(os.path.expanduser("~"), ".config/msmtp/config") with open(mbsyncFilepath, "w") as f: f.write(msmtpStr) # notmuch NOTMUCH_BEGIN = """[database] path={storage} [user] name={main[name]} primary_email={main[from]} other_email={other_email} [new] tags=unprocessed;unread; ignore= [search] exclude_tags=deleted;spam; [maildir] synchronize_flags=true [crypto] gpg_path=gpg """ other_email = mails.copy() other_email.remove(general["main"]["from"]) other_email = ";".join(other_email) notmuchStr = NOTMUCH_BEGIN.format(**general, other_email=other_email) mbsyncFilepath = os.path.join(os.path.expanduser("~"), ".config/notmuch-config") with open(mbsyncFilepath, "w") as f: f.write(notmuchStr) # mutt (temp) ## mailboxes MAILBOXES_BEGIN = "mailboxes" mailboxesStr = MAILBOXES_BEGIN for name, account in accounts.items(): lines = "-" * (20 - len(name)) mailboxesStr += f' "+{name}{lines}"' for root, dirs, files in os.walk(account["storage"]): if "cur" not in dirs or "new" not in dirs or "tmp" not in dirs: continue assert root.startswith(storageFull) path = root[len(storageFull) + 1 :] mailboxesStr += f' "+{path}"' mailboxesStr += "\n" mailboxesFilepath = os.path.join(os.path.expanduser("~"), ".mutt/mailboxes") with open(mailboxesFilepath, "w") as f: f.write(mailboxesStr) ## accounts # TODO html mails MUTT_ACCOUNT = """set from = "{from}" set sendmail = "/usr/bin/msmtp -a {account}" set realname = "{name}" set spoolfile = "+{account}/{inboxfolder}" set mbox = "+{account}/{archivefolder}" set postponed = "+{account}/{draftsfolder}" set record = "+{account}/{sentfolder}" set trash = "+{account}/{trashfolder}" set signature = "~/.mutt/accounts/{account}.sig" set content_type = "text/plain" set sig_dashes = yes color status {colormutt} default macro index D \\ "N+{account}/{trashfolder}" \\ "move message to the trash" macro index S \\ "N+{account}/{spamfolder}" \\ "mark message as spam" # vim: syntax=muttrc """ for name, account in accounts.items(): muttStr = MUTT_ACCOUNT.format(**account) # Config muttFilepath = os.path.join(os.path.expanduser("~"), f".mutt/accounts/{name}") with open(muttFilepath, "w") as f: f.write(muttStr) # Signature sigStr = account.get("sig", account.get("name", "")) sigFilepath = os.path.join(os.path.expanduser("~"), f".mutt/accounts/{name}.sig") with open(sigFilepath, "w") as f: f.write(sigStr) MUTT_SELECTOR = """ set folder = "{storage}" source ~/.mutt/mailboxes source ~/.mutt/accounts/{main[account]} {hooks} source ~/.mutt/custom # vim: syntax=muttrc """ selectStr = "" hooks = "" for name, account in accounts.items(): hooks += f"folder-hook {name}/* source ~/.mutt/accounts/{name}\n" selectStr += MUTT_SELECTOR.format(**general, hooks=hooks) selectFilepath = os.path.join(os.path.expanduser("~"), ".mutt/muttrc") with open(selectFilepath, "w") as f: f.write(selectStr) ## Color for name, account in accounts.items(): # Config colorFilepath = os.path.join( os.path.expanduser("~"), f'{general["storage"]}/{name}/color' ) with open(colorFilepath, "w") as f: f.write(account["color"])