mutt-wizard

A wizard that autocreates an offline email setup for (neo)mutt with isync/mbsync
Log | Files | Refs | README | LICENSE

mw (16262B)


      1 #!/bin/sh
      2 command -V gpg >/dev/null 2>&1 && GPG="gpg" || GPG="gpg2"
      3 [ -r "$HOME/.password-store/.gpg-id" ] &&
      4     "$GPG" --list-secret-keys $(cat "$HOME/.password-store/.gpg-id") >/dev/null 2>&1 || {
      5         printf "\`pass\` must be installed and initialized to encrypt passwords.\\nBe sure it is installed and run \`pass init <yourgpgemail>\`.\\nIf you don't have a GPG public private key pair, run \`$GPG --full-gen-key\` first.\\n"
      6         exit
      7     }
      8 ! command -v mbsync >/dev/null && printf "\`mbsync\` must be installed to run mutt-wizard.\\n" && exit
      9 
     10 case "$(uname)" in
     11 	Linux) prefix="/usr" ;;
     12 	*)  prefix="/usr/local" ;;
     13 esac
     14 
     15 muttdir="$HOME/.config/mutt"		# Main mutt config location
     16 accdir="$muttdir/accounts"		# Directory for account settings
     17 maildir="$HOME/.local/share/mail"	# Location of mail storage
     18 namere="^[a-z_][a-z0-9_-]*$"		# Regex to ensure viable username
     19 emailre=".\+@.\+\\..\+" 		# Regex to confirm valid email address
     20 muttshare="$prefix/share/mutt-wizard"
     21 mbsyncrc="$HOME/.mbsyncrc"
     22 mwconfig="$muttshare/mutt-wizard.muttrc"
     23 cachedir="$HOME/.cache/mutt-wizard"
     24 muttrc="$muttdir/muttrc"
     25 msmtprc="$HOME/.config/msmtp/config"
     26 ssltype="IMAPS"				# This is later changed to `None` later in the script if using Protonmail
     27 
     28 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"
     29 do
     30 	[ -f "$x" ] && sslcert="$x" && break
     31 done || { echo "CA Certificate not found. Please install one or link it to /etc/ssl/certs/ca-certificates.crt" && exit 1 ;}
     32 
     33 getaccounts() { accounts="$(find "$accdir" -type f | grep -o "[0-9]-.*.muttrc" | sed "s/-/: /;s/\..*//" | sort -n)" ;}
     34 list() { getaccounts && [ -n "$accounts" ] && echo "$accounts" ;}
     35 
     36 getprofiles() { \
     37 	unset msmtp_header msmtp_profile mutt_profile mbsync_profile
     38 	printf "Creating profiles for \`%s\`..." "$title"
     39 msmtp_header="defaults
     40 auth	on
     41 tls	on
     42 tls_trust_file	$sslcert
     43 logfile	~/.config/msmtp/msmtp.log
     44 "
     45 msmtp_profile="account $title
     46 host $smtp
     47 port $sport
     48 from $fulladdr
     49 user $login
     50 passwordeval \"pass mutt-wizard-$title\"
     51 "
     52 mbsync_profile="IMAPStore $title-remote
     53 Host $imap
     54 Port  $iport
     55 User $login
     56 PassCmd \"pass mutt-wizard-$title\"
     57 SSLType $ssltype
     58 CertificateFile $sslcert
     59 
     60 MaildirStore $title-local
     61 Subfolders Verbatim
     62 Path ~/.local/share/mail/$title/
     63 Inbox ~/.local/share/mail/$title/INBOX
     64 Flatten .
     65 
     66 Channel $title
     67 Expunge Both
     68 Master :$title-remote:
     69 Slave :$title-local:
     70 Patterns * !\"[Gmail]/All Mail\"
     71 Create Both
     72 SyncState *
     73 MaxMessages $maxmes
     74 # End profile
     75 "
     76 
     77 if [ "$accounttype" = "offline" ]; then
     78 mutt_profile="# vim: filetype=neomuttrc
     79 # muttrc file for account $title
     80 set realname = \"$realname\"
     81 set from = \"$fulladdr\"
     82 set sendmail = \"msmtp -a $title\"
     83 alias me $realname <$fulladdr>
     84 set folder = \"$maildir/$title\"
     85 set header_cache = $cachedir/$title/headers
     86 set message_cachedir = $cachedir/$title/bodies
     87 set mbox_type = Maildir
     88 
     89 bind index,pager gg noop
     90 bind index,pager g noop
     91 bind index,pager M noop
     92 bind index,pager C noop
     93 bind index gg first-entry
     94 macro index o \"<shell-escape>mailsync -V $title<enter>\" \"run mbsync to sync $title\"
     95 unmailboxes *
     96 "
     97 else
     98 mutt_profile="# vim: filetype=neomuttrc
     99 # muttrc file for account $title
    100 set realname = \"$realname\"
    101 set from = \"$fulladdr\"
    102 set sendmail = \"$prefix/bin/msmtp -a $title\"
    103 alias me $realname <$fulladdr>
    104 set folder = \"imaps://$fulladdr@$imap:$iport\"
    105 set imap_user = \"$login\"
    106 set header_cache = $cachedir/$title/headers
    107 set message_cachedir = $cachedir/$title/bodies
    108 set imap_pass = \`pass mutt-wizard-$title\`
    109 
    110 set mbox_type = Maildir
    111 set ssl_starttls = yes
    112 set ssl_force_tls = yes
    113 
    114 bind index,pager gg noop
    115 bind index,pager g noop
    116 bind index,pager M noop
    117 bind index,pager C noop
    118 bind index gg first-entry
    119 unmailboxes *
    120 "
    121 fi
    122 	printf "DONE.\\n"
    123 }
    124 
    125 askinfo() { \
    126 	printf "Insert the \033[31memail address\033[0m that you want to autoconfigure for mutt/mbsync\\n\tEmail: \033[36m"
    127 	read -r fulladdr
    128 	printf "\033[0m"
    129 	while ! echo "$fulladdr" | grep "$emailre" >/dev/null; do
    130 		printf "That is not a valid \033[31memail address\033[0m, please retype the desired email.\\n\\nEmail: \033[36m\t"
    131 		read -r fulladdr
    132 		printf "\033[0m"
    133 	done
    134 	domain="$(echo "$fulladdr" | sed "s/.*@//")"
    135 	printf "\\nSearching for \033[32m%s\033[0m in \033[34m\`domains.csv\`\033[0m..." "$domain"
    136 	serverinfo="$(grep "^$domain" "$muttshare/domains.csv" 2>/dev/null)"
    137 	if [ -z "$serverinfo" ]; then
    138 		printf "Your email domain is not in mutt-wizard's database yet.\\nmutt-wizard will still autoconfigure everything, but you will have to manually type in your service's IMAP and SMTP server information.\\nYou can usually quickly find this by internet searching for it.\\n"
    139 		printf "Insert the IMAP server for your email provider (excluding the port number)\\n\033[36m\t"
    140 		read -r imap
    141 		printf "\033[0mWhat is your server's IMAP port number? (Usually something like 993)\\n\033[36m\t"
    142 		read -r iport
    143 		printf "\033[0mInsert the SMTP server for your email provider (excluding the port number)\\n\033[36m\t"
    144 		read -r smtp
    145 		printf "\033[0mWhat is your server's SMTP port number? (Usually 587 or 465)\\n\033[36m\t"
    146 		read -r sport
    147 		printf "\033[0m\\nGreat! If you want to be helpful, copy the line below and you can add it to the \`domains.csv\` file on Github.\\nThis will make things easier for others who use your email provider.\\n\\n%s,%s,%s,%s,%s\\n\\nAlthough be sure to test to see if these settings work first! ;-)\\n" "$domain" "$imap" "$iport" "$smtp" "$sport"
    148 	else
    149 		IFS=, read -r service imap iport smtp sport <<EOF
    150 $serverinfo
    151 EOF
    152 	printf "\\n\033[3;33mCongrats!\033[0m Server info has automatically been found, so you won't have to look anything up!\\n\t\033[1mIMAP server\033[0m: %s\\n\t\033[1mIMAP port\033[0m: %s\\n\t\033[1mSMTP server\033[0m: %s\\n\t\033[1mSMTP port\033[0m: %s\\nThis data will be used by the wizard.\\n" "$imap" "$iport" "$smtp" "$sport"
    153 	case "$service" in
    154 		gmail.com) printf "\033[31mREMEMBER: Gmail users must enable \"less secure\" (third-party) applications first for the sync to work:\\nhttps://support.google.com/accounts/answer/6010255\\n\033[0m" ;;
    155 		protonmail.ch|protonmail.com|pm.me) printf "\033[31mREMEMBER: Protonmail users must install and configure Protonmail Bridge first for the sync to work:\\nhttps://protonmail.com/bridge/\\n\033[0m" && ssltype="None" ;;
    156 	esac
    157 	fi
    158 	printf "Enter the \033[35mfull name\033[0m you want to be identified by on this account.\\n\tReal name: "
    159 	read -r realname
    160 	printf "Enter a short, \033[36mone-word identifier\033[0m for this email account that will distinguish them from any other accounts you add.\\n\tAccount name: "
    161 	read -r title
    162 	while ! echo "$title" | grep "$namere" >/dev/null || ls "$accdir"/[0-9]"-$title.muttrc" >/dev/null 2>&1; do
    163 		printf "\033[31mTry again\033[0m. Pick a nickname that is one word only including lowercase letters and _ or - and that you have \033[1mnot\033[0m used before.\\n\tAccount name: \033[36m\t"
    164 		read -r title
    165 		printf "\033[0m"
    166 	done
    167 	printf "If your account has a special username different from your address, insert it now. Otherwise leave this prompt totally blank.\\n\033[34mMost accounts will not have a separate login, so you should probably leave this blank.\033[0m\\n\tLogin(?): \033[36m"
    168 	read -r login
    169 	printf "\033[0m"
    170 	[ -z "$login" ] && login="$fulladdr"
    171 	[ "$accounttype" = "offline" ] && printf "If you want to limit the number of messages kept offline to a number, enter that number below. If you do not want to limit your mail and would like \`mbsync\` to sync all mail, press enter without typing a number.\\n\t" && read -r maxmes
    172 	echo "$maxmes" | grep "[1-9]" >/dev/null || maxmes="0"
    173 	getpass
    174 	getprofiles
    175 	mkdir -p "$muttdir" "$accdir" "$cachedir/$title/bodies" "$HOME/.config/msmtp"
    176 	getaccounts
    177 	for x in $(seq 1 9); do echo "$accounts" | grep "$x" >/dev/null 2>&1 || { export idnum="$x"; break ;}; done
    178 	[ ! -f "$msmtprc" ] && echo "$msmtp_header" > "$msmtprc"
    179 	echo "$msmtp_profile" >> "$msmtprc"
    180 	command -V apt-get >/dev/null 2>&1 && ln -s "$msmtprc" "$HOME/.msmtprc" 2>/dev/null
    181 	case "$service" in
    182 		protonmail.ch|protonmail.com|pm.me) protonfinger || return 1 ;;
    183 	esac
    184 	echo "$mutt_profile" > "$accdir/$idnum-$title.muttrc"
    185 	echo "$mbsync_profile" >> "$mbsyncrc"
    186 	notmuchauto
    187 	[ ! -f "$muttrc" ] && echo "# vim: filetype=neomuttrc" > "$muttrc" && echo "muttrc created."
    188 	! grep "^source.*mutt-wizard.muttrc" "$muttrc" >/dev/null && echo "source $mwconfig # mw-autogenerated" >> "$muttrc"
    189 	! grep "^source.*.muttrc" "$muttrc" | grep -v "$mwconfig" >/dev/null && echo "source $accdir/$idnum-$title.muttrc # mw-autogenerated" >> "$muttrc"
    190 	echo "macro index,pager i$idnum '<sync-mailbox><enter-command>source $accdir/$idnum-$title.muttrc<enter><change-folder>!<enter>;<check-stats>' \"switch to $fulladdr\" # mw-autogenerated" >> "$muttrc"
    191 }
    192 
    193 protonfinger() { printf "Getting Protonmail bridge fingerprint...\\n"
    194 	fingerprint="$(msmtp --serverinfo --tls --tls-certcheck=off -a "$title")" || return 1
    195 	sed -ibu "s/account $title/&\ntls_trust_file\ntls_fingerprint $fingerprint/" "$msmtprc" ; rm -f "$msmtprc"bu
    196 }
    197 
    198 getpass() { while : ; do pass rm -f "mutt-wizard-$title" >/dev/null 2>&1
    199 		pass insert "mutt-wizard-$title" && break; done ;}
    200 
    201 formatShortcut() { \
    202 	while read -r data; do { echo "macro index,pager g$1 \"<change-folder>$data<enter>\" \"go to $2\" # mw-autogenerated"
    203 	echo "macro index,pager M$1 \"<save-message>$data<enter>\" \"move mail to $2\" # mw-autogenerated"
    204 	echo "macro index,pager C$1 \"<copy-message>$data<enter>\" \"copy mail to $2\" # mw-autogenerated"; } >> "$accdir/$idnum-$title.muttrc"
    205 	done ;}
    206 
    207 tryconnect() { mkdir -p "$maildir/$title"
    208 	if mailboxes="$(mbsync -l "$title" | sed 's/\//./')" >/dev/null 2>&1 && [ -n "$mailboxes" ]; then
    209 		[ "$accounttype" = "online" ] && sed -ibu "/IMAPStore $title-remote$/,/# End profile/d" "$mbsyncrc" ; rm -f "$mbsyncrc"bu
    210 		printf "\033[32mMailboxes detected.\033[0m\\n"
    211 		echo "$mailboxes" | xargs -I {} mkdir -p "$maildir/$title/{}"
    212 		return 0
    213 	else
    214 		printf "\033[31m\033[31mLog-on not successful.\033[0m\\nIt seems that either you inputted the wrong password or server settings, or there are other requirements for your account out of the control of mutt-wizard.\\n"
    215 		return 1
    216 	fi ;}
    217 
    218 finalize() { \
    219 	boxes="$(find "$maildir/$title/" -mindepth 1 -maxdepth 1 | sed "s/\ /\\\ /g;s/^.*\//=/")"
    220 	[ -z "$boxes" ] && printf "\033[31mNo local mailboxes have been detected for %s.\033[0m\\nThis means that mbsync has not been successfully run.\\nRun mbsync, and if it has an error, be sure to check your password and server settings manually if needbe.\\n" "$title" && return
    221 	printf "Setting default mailboxes for your Inbox, Sent, Drafts and Trash in mutt...\\n"
    222 	spoolfile=$(echo "$boxes" | grep -i -m 1 inbox | sed 's/=/+/g')
    223 	record=$(echo "$boxes" | grep -i -m 1 sent | sed 's/=/+/g')
    224 	postponed=$(echo "$boxes" | grep -i -m 1 draft | sed 's/=/+/g')
    225 	trash=$(echo "$boxes" | grep -i -m 1 trash | sed 's/=/+/g')
    226 	sed -ibu "/^mailboxes\|^set record\|^set postponed\|^set trash\|^set spoolfile/d" "$accdir/$idnum-$title.muttrc" ; rm -f "$accdir/$idnum-$title.muttrcbu"
    227 	{ echo "set spoolfile = \"$spoolfile\""; echo "set record = \"$record\""; echo "set postponed = \"$postponed\""; echo "set trash = \"$trash\""; } >> "$accdir/$idnum-$title.muttrc"
    228 	echo "mailboxes =$title ===================== $(echo "$boxes" | sed -e "s/^\|$/\"/g" | tr "\n" " ")" >> "$accdir/$idnum-$title.muttrc"
    229 	printf "Setting up your keyboard shortcuts for jumping between mailboxes...\\n"
    230 	sed -ibu "/# mw-autogenerated/d" "$accdir/$idnum-$title.muttrc" ; rm -f "$accdir/$idnum-$title.muttrcbu"
    231 	echo "$boxes" | grep -i inbox | head -n 1 | formatShortcut i inbox
    232 	echo "$boxes" | grep -i sent | head -n 1 | formatShortcut s sent
    233 	echo "$boxes" | grep -i draft | head -n 1 | formatShortcut d drafts
    234 	echo "$boxes" | grep -i trash | head -n 1 | formatShortcut t trash
    235 	echo "$boxes" | grep -i spam | head -n 1 | formatShortcut S spam
    236 	echo "$boxes" | grep -i junk | head -n 1 | formatShortcut j junk
    237 	echo "$boxes" | grep -i archive | head -n 1 | formatShortcut a archive
    238 	[ "$accounttype" = "offline" ] && printf "All done.\\n\033[33mYou should now be able to run \`\033[32mmbsync %s\033[33m\` to begin to download your mail.\033[0m\\n" "$title"
    239 	command -V urlview >/dev/null 2>&1 && [ ! -f "$HOME/.urlview" ] && echo "COMMAND \$BROWSER" > "$HOME/.urlview"
    240 	return 0
    241 }
    242 
    243 confirm() { printf "Do you want to %s? [y/N]\\n\t" "$@" && read -r input && ! echo "$input" | grep -i "^y$\|^yes$" >/dev/null && printf "That doesn't seem like a yes to me.\\n\\n" && return 1
    244 	printf "Are you really, really sure you want to %s?\\n\t" "$@" && read -r input && ! echo "$input" | grep -i "^y$\|^yes$" >/dev/null && printf "That doesn't seem like a yes to me.\\n\\n" && return 1
    245 	return 0 ;}
    246 
    247 pick() { printf "Select an accounts to %s:\\n" "$1"
    248 	list
    249 	read -r input
    250 	[ -z "$input" ] && return 1
    251 	title="$(echo "$accounts" | grep "$input" | awk '{print $2}')"
    252 	[ -z "$title" ] && printf "Invalid response." && return 1
    253 	return 0 ;}
    254 
    255 delete() { sed -ibu "/IMAPStore $title-remote$/,/# End profile/d" "$mbsyncrc" ; rm -rf "$mbsyncrc"bu
    256 	rm -rf "${cachedir:?}/${title:?}" "$accdir/"[1-9]"-$title.muttrc"
    257 	sed -ibu "/[0-9]-$title.muttrc/d" "$muttrc" ; rm -f "$muttrc"bu
    258 	sed -ibu "/account $title/,/^\(\s*$\|account\)/d" "$msmtprc"; rm -f "$msmtprc"bu
    259 	}
    260 
    261 choosecron() { ! pgrep cron >/dev/null && echo "No cron manager running. Install/enable one and then select this option again." && return 1
    262 	if crontab -l | grep mailsync >/dev/null; then
    263 		echo "Active mail sync cronjob detected. Do you want to remove it?"
    264 		printf "\033[36m\t"
    265 		read -r rmyn
    266 		printf "\033[0m"
    267 		echo "$rmyn" | grep -i "^y\(es\)*$" >/dev/null && crontab -l | sed '/mailsync/d' | crontab - >/dev/null && echo "Mail sync turned off."
    268 	else
    269 		echo "How many minutes between each mail sync?"
    270 		printf "\033[36m\t"
    271 		read -r minnum
    272 		printf "\033[0m"
    273 		while ! echo "$minnum" | grep "^[0-9]\+$" >/dev/null; do
    274 			printf "That doesn't look like a number. How many minutes between each mail sync?\\n\033[36m\t"
    275 			read -r minnum
    276 			printf "\033[0m"
    277 		done
    278 		(crontab -l; echo "*/$minnum * * * * export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus; export DISPLAY=:0; . \$HOME/.profile; $(type mailsync | cut -d' ' -f3)") | crontab - &&
    279 			echo "Cronjob added. Mail will sync every $minnum minutes. Be sure you have your cron manager running."
    280 	fi ;}
    281 
    282 asktype() { while : ; do
    283 		printf "Do you want to keep your mail for this account offline with mbsync? [yes/no]\\n\t"
    284 		read -r offnot
    285 		case "$offnot" in
    286 			[Yy][Ee][Ss]) accounttype="offline" && break ;;
    287 			[Nn][Oo]) accounttype="online" && break ;;
    288 			*) echo "Write out either yes or no completely. Try again or press ctrl-c to quit." ;;
    289 		esac; done ;}
    290 
    291 purge() { confirm "delete all account data" || exit
    292 	rm -rf "$mbsyncrc" "$accdir" "$HOME/.config/msmtp" "$cachedir"
    293 	echo "All configs and account settings have been purged."
    294 	sed -ibu "/\# mw-autogenerated/d" "$muttrc" ; rm -f "$muttrc"bu
    295 }
    296 
    297 notmuchauto() { \
    298 	[ -z "$NOTMUCH_CONFIG" ] && NOTMUCH_CONFIG="$HOME/.notmuch-config"
    299 	[ -f "$NOTMUCH_CONFIG" ] && return 0
    300 	nmbasic="[database]
    301 path=$maildir
    302 [user]
    303 name=$realname
    304 primary_email=$fulladdr
    305 [new]
    306 tags=unread;inbox;
    307 ignore=
    308 [search]
    309 exclude_tags=deleted;spam;
    310 [maildir]
    311 synchronize_flags=true
    312 [crypto]
    313 gpg_path=$GPG"
    314 	echo "$nmbasic" > "$NOTMUCH_CONFIG" ;}
    315 
    316 trap 'echo -e "\033[0m\n"' INT
    317 
    318 case "$1" in
    319 	ls) list ;;
    320 	add) asktype && askinfo && tryconnect && finalize || delete ;;
    321 	pass) pick "change the password of" && getpass ;;
    322 	delete) pick delete && confirm "delete the \`$title\` profile" && delete ;;
    323 	purge) purge ;;
    324 	cron) choosecron ;;
    325 	*) cat << EOF
    326 mw: mutt-wizard, auto-configure email accounts for mutt
    327 including downloadable mail with \`isync\`.
    328 
    329 Allowed options:
    330   add		Add and autoconfigure an email address (9 max.)
    331   ls		List configured accounts
    332   delete	Pick an account to delete
    333   purge		Delete all accounts and settings
    334   cron		Enable or disable an autosync via cronjob
    335   all else	Print this message
    336 
    337 NOTE: Once at least one account is added, you can run
    338 \`mbsync -a\` to begin downloading mail.
    339 EOF
    340 esac