#!/bin/sh prefix="/usr/local" maildir="${XDG_DATA_HOME:-$HOME/.local/share}/mail" muttshare="$prefix/share/mutt-wizard" cachedir="${XDG_CACHE_HOME:-$HOME/.cache}/mutt-wizard" muttrc="${XDG_CONFIG_HOME:-$HOME/.config}/mutt/muttrc" accdir="${XDG_CONFIG_HOME:-$HOME/.config}/mutt/accounts" msmtprc="${XDG_CONFIG_HOME:-$HOME/.config}/msmtp/config" msmtplog="${XDG_CONFIG_HOME:-$HOME/.config}/msmtp/msmtp.log" mbsyncrc="${MBSYNCRC:-$HOME/.mbsyncrc}" mpoprc="${XDG_CONFIG_HOME:-$HOME/.config}/mpop/config" alias mbsync='mbsync -c "$mbsyncrc"' for x in "/etc/ssl/certs/ca-certificates.crt" "/etc/pki/tls/certs/ca-bundle.crt" "/etc/ssl/ca-bundle.pem" "/etc/pki/tls/cacert.pem" "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" "/etc/ssl/cert.pem" "/usr/local/share/ca-certificates/"; do [ -f "$x" ] && sslcert="$x" && break done || { echo "CA Certificate not found. Please install one or link it to /etc/ssl/certs/ca-certificates.crt" && exit 1 ;} checkbasics() { command -V gpg >/dev/null 2>&1 && GPG="gpg" || GPG="gpg2" PASSWORD_STORE_DIR="${PASSWORD_STORE_DIR:-$HOME/.password-store}" [ -r "$PASSWORD_STORE_DIR/.gpg-id" ] && "$GPG" --list-secret-keys "$(cat "$PASSWORD_STORE_DIR/.gpg-id")" >/dev/null 2>&1 || { echo "First run \`pass init \` to set up a password archive." echo "(If you don't already have a GPG key pair, first run \`$GPG --full-generate-key\`.)" exit 1 ;} ;} getaccounts() { accounts="$(find -L "$accdir" -type f 2>/dev/null | grep -o "[0-9]-.*.muttrc" | sed "s/-/: /;s/\.muttrc$//" | sort -n)" ;} list() { getaccounts && [ -n "$accounts" ] && echo "$accounts" || exit 1 ;} prepmsmtp() { echo "account $fulladdr host $smtp port ${sport:-587} from $fulladdr user $login passwordeval \"pass $fulladdr\" auth ${auth:-on} ${tlsline:-tls on} tls_trust_file $sslcert logfile $msmtplog " >> "$msmtprc" # On Ubuntu/Debian, a link is needed since they use an older version. command -V apt-get >/dev/null 2>&1 && ln -s "$msmtprc" "$HOME/.msmtprc" 2>/dev/null } prepmbsync() { mkdir -p "${mbsyncrc%/*}" echo " IMAPStore $fulladdr-remote Host $imap Port ${iport:-993} User $login PassCmd \"pass $fulladdr\" AuthMechs LOGIN SSLType ${imapssl:-IMAPS} CertificateFile $sslcert MaildirStore $fulladdr-local Subfolders Verbatim Path ${XDG_DATA_HOME:-$HOME/.local/share}/mail/$fulladdr/ Inbox ${XDG_DATA_HOME:-$HOME/.local/share}/mail/$fulladdr/${inbox:-INBOX} Channel $fulladdr Expunge Both Master :$fulladdr-remote: Slave :$fulladdr-local: Patterns * !\"[Gmail]/All Mail\" Create Both SyncState * MaxMessages ${maxmes:-0} ExpireUnread no # End profile " >> "$mbsyncrc" ;} prepmpop() { mkdir -p "${mpoprc%/*}" echo " account $fulladdr tls on user $login host $imap delivery maildir ${XDG_DATA_HOME:-$HOME/.local/share}/mail/$fulladdr/${inbox:-INBOX} passwordeval pass $fulladdr " >> "$mpoprc" ;} prepmutt() { echo "# vim: filetype=neomuttrc # muttrc file for account $fulladdr set realname = \"$realname\" set from = \"$fulladdr\" set sendmail = \"msmtp -a $fulladdr\" alias me $realname <$fulladdr> set folder = \"$folder\" set header_cache = $cachedir/$fulladdr/headers set message_cachedir = $cachedir/$fulladdr/bodies set mbox_type = Maildir $extra bind index,pager gi noop bind index,pager gs noop bind index,pager gd noop bind index,pager ga noop bind index,pager gS noop bind index,pager gj noop bind index,pager gt noop bind index,pager Mi noop bind index,pager Ms noop bind index,pager Md noop bind index,pager Ma noop bind index,pager MS noop bind index,pager Mj noop bind index,pager Mt noop bind index,pager Ci noop bind index,pager Cs noop bind index,pager Cd noop bind index,pager Ca noop bind index,pager CS noop bind index,pager Cj noop bind index,pager Ct noop bind index,pager gg noop bind index,pager g noop bind index,pager M noop bind index,pager C noop bind index gg first-entry unmailboxes * unalternates * unset signature unset hostname unmy_hdr Organization unmacro index o $synccmd " > "$accdir/$idnum-$fulladdr.muttrc" [ ! -f "$muttrc" ] && echo "# vim: filetype=neomuttrc" > "$muttrc" ! grep -q "^source.*mutt-wizard.muttrc" "$muttrc" && echo "source $muttshare/mutt-wizard.muttrc" >> "$muttrc" ! grep "^source.*.muttrc" "$muttrc" | grep -qv "$muttshare/mutt-wizard.muttrc" && echo "source $accdir/$idnum-$fulladdr.muttrc" >> "$muttrc" echo "macro index,pager i$idnum 'source $accdir/$idnum-$fulladdr.muttrc!;' \"switch to $fulladdr\"" >> "$muttrc" } getprofiles() { \ mkdir -p "${muttrc%/*}" "$accdir" "$cachedir/$fulladdr/bodies" "${XDG_CONFIG_HOME:-$HOME/.config}/msmtp" unset msmtp_header msmtp_profile mutt_profile mbsync_profile case "$iport" in 1143) imapssl=None ;; 143) imapssl=STARTTLS ;; esac case "$type" in online) folder="imaps://$login@$imap:${iport:-993}" extra="set imap_user = \"$login\" set imap_pass = \"\`pass $fulladdr\`\" set ssl_starttls = yes set ssl_force_tls = yes" ;; pop) synccmd="macro index o \"mpop $fulladdr\" \"run mpop to get $fulladdr's mail\"" folder="$maildir/$fulladdr" prepmpop ;; *) synccmd="macro index o \"mw -y $fulladdr\" \"run mbsync to sync $fulladdr\"" folder="$maildir/$fulladdr" prepmbsync ;; esac prepmsmtp prepmutt prepnotmuch # Create a notmuch config file if not present already. } parsedomains(){ serverinfo="$(grep "^${fulladdr#*@}" "$muttshare/domains.csv" 2>/dev/null)" [ -z "$serverinfo" ] && serverinfo="$(grep "$(echo "${fulladdr#*@}" | sed "s/\.[^\.]*$/\.\\\*/")" "$muttshare/domains.csv" 2>/dev/null)" IFS=, read -r service imapsugg iportsugg smtpsugg sportsugg </dev/null ; rm -f "$mbsyncrc"bu rm -rf "${cachedir:?}/${fulladdr:?}" "$accdir/"[1-9]"-$fulladdr.muttrc" sed -ibu "/[0-9]-$fulladdr.muttrc/d" "$muttrc" 2>/dev/null; rm -f "$muttrc"bu sed -ibu "/account $fulladdr$/,/^\(\s*$\|account\)/d" "$msmtprc" 2>/dev/null; rm -f "$msmtprc"bu sed -ibu "/account $fulladdr$/,/^\(\s*$\|account\)/d" "$mpoprc" 2>/dev/null; rm -f "$mpoprc"bu pass rm -f "$fulladdr" >/dev/null 2>&1 [ -n "${purge+x}" ] && rm -rf "${maildir:?}/${fulladdr:?}" # Get rid of those multiple newlines because I don't know awk well enough to do it by default lol. for file in "$msmtprc" "$mbsyncrc" "$mpoprc"; do tr '\n' '|' 2>/dev/null < "$file" | sed "s/||\+/||/g" | tr '|' '\n' >> "$file"bu mv -f "$file"bu "$file" done } askinfo() { \ [ -z "$fulladdr" ] && echo "Give the full email address to add:" && read -r fulladdr while ! echo "$fulladdr" | grep -qE "^.+@.+\.[A-Za-z]+$"; do echo "\`$fulladdr\` is not a valid email address. Please retype the address:" read -r fulladdr done getaccounts; echo "$accounts" | grep -q "\s$fulladdr$" 2>/dev/null && { echo "$fulladdr has already been added" && exit 1 ;} { [ -z "$imap" ] || [ -z "$smtp" ] ;} && parsedomains [ -z "$imap" ] && echo "Give your email server's IMAP address (excluding the port number):" && read -r imap [ -z "$smtp" ] && echo "Give your email server's SMTP address (excluding the port number):" && read -r smtp [ "$sport" = 465 ] && tlsline="tls_starttls off" [ -z "$realname" ] && realname="${fulladdr%%@*}" login="${login:-$fulladdr}" if [ -n "${password+x}" ]; then createpass else getpass fi } createpass() { echo "$password" > "$PASSWORD_STORE_DIR/$fulladdr" "$GPG" -qer "$(cat "$PASSWORD_STORE_DIR/.gpg-id")" "$PASSWORD_STORE_DIR/$fulladdr" rm -f "$PASSWORD_STORE_DIR/$fulladdr" ;} getpass() { while : ; do pass rm -f "$fulladdr" >/dev/null 2>&1 pass insert -f "$fulladdr" && break; done ;} formatShortcut() { toappend="$toappend macro index,pager g$1 \"=$3\" \"go to $2\" macro index,pager M$1 \";=$3\" \"move mail to $2\" macro index,pager C$1 \";=$3\" \"copy mail to $2\"" ;} setBox() { toappend="$toappend set $1 = \"+$2\"" ;} getboxes() { if [ -n "${force+x}" ] ; then mailboxes="$(printf "INBOX\\nDrafts\\nJunk\\nTrash\\nSent\\nArchive")" else info="$(curl --location-trusted -s -m 5 --user "$login:$(pass "$fulladdr")" --url "${protocol:-imaps}://$imap:${iport:-993}")" [ -z "$info" ] && echo "Log-on not successful." && return 1 mailboxes="$(echo "$info" | grep -v HasChildren | sed "s/.*\" //;s/\"//g" | tr -d ' ')" fi [ "$type" = "pop" ] && mailboxes="INBOX" getaccounts; for x in $(seq 1 9); do echo "$accounts" | grep -q "^$x:" || { export idnum="$x"; break ;}; done toappend="mailboxes $(echo "$mailboxes" | sed "s/^/\"=/;s/$/\"/" | paste -sd ' ' - )" for x in $mailboxes; do case "$x" in *[Ss][Ee][Nn][Tt]*) setBox record "$x"; formatShortcut s sent "$x" ;; *[Dd][Rr][Aa][Ff][Tt][Ss]*) setBox postponed "$x"; formatShortcut d drafts "$x" ;; *[Tt][Rr][Aa][Ss][Hh]*) formatShortcut t trash "$x"; setBox trash "$x" ;; *[Jj][Uu][Nn][Kk]*) formatShortcut j junk "$x" ;; *[Aa][Rr][Cc][Hh][Ii][Vv][Ee]*) formatShortcut a archive "$x" ;; *[Ss][Pp][Aa][Mm]*) formatShortcut S spam "$x" ;; *[Ii][Nn][Bb][Oo][Xx]*) formatShortcut i inbox "$x"; setBox spoolfile "$x" inbox="$x" ;; esac done } finalize() { echo "$toappend" >> "$accdir/$idnum-$fulladdr.muttrc" [ "$type" != "online" ] && echo "$mailboxes" | xargs -I {} mkdir -p "$maildir/$fulladdr/{}/cur" "$maildir/$fulladdr/{}/tmp" "$maildir/$fulladdr/{}/new" echo "$fulladdr (account #$idnum) added successfully." command -V urlview >/dev/null 2>&1 && [ ! -f "$HOME/.urlview" ] && echo "COMMAND \$BROWSER" > "$HOME/.urlview" return 0 ;} prepnotmuch() { \ [ -z "$NOTMUCH_CONFIG" ] && NOTMUCH_CONFIG="$HOME/.notmuch-config" [ -f "$NOTMUCH_CONFIG" ] && return 0 nmbasic="[database] path=$maildir [user] name=$realname primary_email=$fulladdr [new] tags=unread;inbox; ignore=.mbsyncstate;.uidvalidity [search] exclude_tags=deleted;spam; [maildir] synchronize_flags=true [crypto] gpg_path=$GPG" echo "$nmbasic" > "$NOTMUCH_CONFIG" ;} togglecron() { cron="$(mktemp)" crontab -l > "$cron" if grep -q mailsync "$cron"; then echo "Removing automatic mailsync..." sed -ibu /mailsync/d "$cron"; rm -f "$cron"bu else echo "Adding automatic mailsync every ${cronmin:-10} minutes..." echo "*/${cronmin-10} * * * * $prefix/bin/mailsync" >> "$cron" fi && crontab "$cron"; rm -f "$cron" ;} setact() { if [ -n "${action+x}" ] && [ "$action" != "$1" ]; then echo "Running $1 with $action..." echo "Incompatible options given. Only one action may be specified per run." return 1 else action="$1" fi; } mwinfo() { cat << EOF mw: mutt-wizard, auto-configure email accounts for mutt including downloadable mail with \`isync\`. Main actions: -a your@email.com Add an email address -l List email addresses configured -d Remove an already added address -D your@email.com Force remove account without confirmation -y your@email.com Sync mail for account by name -Y Sync mail for all accounts -t number Toggle automatic mailsync every minutes -T Toggle automatic mailsync Options allowed with -a: -u Account login name if not full address -n "Real name" to be on the email account -i IMAP/POP server address -I IMAP/POP server port -s SMTP server address -S SMTP server port -x Password for account (recommended to be in double quotes) -p Install for a Protonmail account. -P Add for a POP server instead of IMAP. -X Delete an account's local email too when deleting. -o Configure address, but keep mail online. -f Assume typical English mailboxes without attempting log-on. NOTE: Once at least one account is added, you can run \`mbsync -a\` to begin downloading mail. To change an account's password, run \`pass edit your@email.com\`. EOF } while getopts "fpPXlhodTYD:y:i:I:s:S:u:a:n:x:m:t:" o; do case "${o}" in l) setact list || exit 1 ;; d) setact delete || exit 1 ;; D) setact delete || exit 1 ; fulladdr="$OPTARG" ;; y) setact sync || exit 1 ; fulladdr="$OPTARG" ;; Y) setact sync || exit 1 ;; a) setact add || exit 1 ; fulladdr="$OPTARG" ;; i) setact add || exit 1 ; imap="$OPTARG" ;; I) setact add || exit 1 ; iport="$OPTARG" ;; s) setact add || exit 1 ; smtp="$OPTARG" ;; S) setact add || exit 1 ; sport="$OPTARG" ;; u) setact add || exit 1 ; login="$OPTARG" ;; n) setact add || exit 1 ; realname="$OPTARG" ;; m) setact add || exit 1 ; maxmes="$OPTARG" ;; o) setact add || exit 1 ; type="online" ;; P) setact add || exit 1 ; type="pop"; protocol="pop3s" ; iport="${iport:-995}" ;; f) setact add || exit 1 ; force=True ;; x) setact add || exit 1 ; password="$OPTARG" ;; X) setact delete || exit 1 ; purge=True ;; t) setact toggle || exit 1 ; cronmin="$OPTARG" ;; T) setact toggle || exit 1 ;; p) echo "NOTE: Protonmail users must install and configure Protonmail Bridge first for the first sync to work." protocol="imap" imap="127.0.0.1" iport="1143" smtp="127.0.0.1" sport="1025" auth="login" tlsline="tls_fingerprint $(msmtp --serverinfo --host=$smtp --port=$sport --tls --tls-certcheck=off | awk '/SHA256:/ {print $2}')" setact add || exit 1 ;; *) mwinfo; exit 1 ;; esac done case "$action" in list) list ;; add) checkbasics && askinfo && getboxes && getprofiles && finalize ;; delete) delete ;; sync) mailsync $fulladdr ;; toggle) togglecron ;; *) mwinfo; exit 1 ;; esac