You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

424 lines
16 KiB

  1. #!/bin/sh
  2. if [ "$#" -gt 1 ]; then
  3. echo "To many arguments. You will be asked."
  4. exit
  5. fi
  6. if [ -z "$prefix" ]; then
  7. case "$(uname)" in
  8. Linux) prefix="/usr" ;;
  9. *) prefix="/usr/local" ;;
  10. esac
  11. fi
  12. command -V gpg >/dev/null 2>&1 && GPG="gpg" || GPG="gpg2"
  13. [ -z "$PASSWORD_STORE_DIR" ] && PASSWORD_STORE_DIR="$HOME/.password-store"
  14. [ -r "$PASSWORD_STORE_DIR/.gpg-id" ] &&
  15. "$GPG" --list-secret-keys $(cat "$PASSWORD_STORE_DIR/.gpg-id") >/dev/null 2>&1 || {
  16. 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"
  17. exit
  18. }
  19. ! command -v "$prefix/bin/mbsync" >/dev/null && printf "\`mbsync\` must be installed to run mutt-wizard.\\n" && exit
  20. ! command -v "$prefix/bin/msmtp" >/dev/null && printf "\`msmtp\` must be installed.\\n" && exit
  21. ! ( command -v mutt >/dev/null || command -v neomutt >/dev/null ) && printf "\`mutt\` must be installed.\\n" && exit
  22. mwconfigdir=${XDG_CONFIG_HOME:-$HOME/.config}
  23. # Main mutt config location
  24. mwmuttdir="$mwconfigdir/mutt"
  25. # Directory for account settings
  26. mwaccrcdir="$mwmuttdir/accounts"
  27. # Location of mail storage
  28. mwmaildir="${MAILDIR:-${XDG_DATA_HOME:-$HOME/.local/share}/mail}"
  29. # Regex to confirm valid email address
  30. mwemailre=".\+@.\+\\..\+"
  31. mwshare="$prefix/share/mutt-wizard"
  32. if [ -n "$XDG_CONFIG_HOME" ]; then
  33. mwmbsyncrc="$mwconfigdir/isync/mbsyncrc"
  34. else
  35. mwmbsyncrc="$HOME/.mbsyncrc"
  36. fi
  37. mwsharerc="$mwshare/mutt-wizard.muttrc"
  38. mwcachedir="${XDG_CACHE_HOME:-$HOME/.cache}/mutt-wizard"
  39. mwmuttrc="$mwmuttdir/muttrc"
  40. mwmsmtprc="$mwconfigdir/msmtp/config"
  41. mwssltype="IMAPS"
  42. mbsyncbin="$prefix/bin/mbsync -c $mwmbsyncrc"
  43. msmtpbin="$prefix/bin/msmtp"
  44. takemwaddr(){
  45. mwacc="$mwaddr" # let the user always just deal with his email
  46. mwaccmutt="${mwaddr//[.@]/_}" # but mutt would not show it with an @ or .
  47. mwacccachedir=$mwcachedir/${mwaddr//[.@]/_} # @ cannot stay because of mutt, . could
  48. mwaccmaildir="$mwmaildir/$mwaccmutt" # folder name as shown by mutt and opens with gf in vim
  49. }
  50. 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/"
  51. do
  52. [ -f "$x" ] && sslcert="$x" && break
  53. done || { echo "CA Certificate not found. Please install one or link it to /etc/ssl/certs/ca-certificates.crt" && exit 1 ;}
  54. getaccounts() {
  55. accounts="$(find "$mwaccrcdir" -type f | grep -o "[0-9]-.*.muttrc" | sed "s/-/: /;s/\.muttrc//" | sort -n)"
  56. }
  57. mwlist() {
  58. getaccounts && [ -n "$accounts" ] && echo "$accounts"
  59. }
  60. mwadd() {
  61. asktype && askinfo && tryconnect && finalize || mwdelete
  62. }
  63. getprofiles() {
  64. unset msmtp_header msmtp_profile mutt_profile mbsync_profile
  65. printf "Creating profiles for \`%s\`..." "$mwaddr"
  66. msmtp_header="defaults
  67. auth on
  68. tls on
  69. tls_trust_file $sslcert
  70. logfile ${XDG_LOG_HOME:-$HOME}/msmtp.log
  71. "
  72. msmtp_profile="account $mwacc
  73. host $mwsmtp
  74. port $mwsport
  75. from $mwaddr
  76. user $mwlogin
  77. passwordeval \"pass $mwpass\"
  78. $starttlsoff
  79. "
  80. mbsync_profile="IMAPStore $mwacc-remote
  81. Host $mwimap
  82. Port $mwiport
  83. User $mwlogin
  84. PassCmd \"pass $mwpass\"
  85. SSLType $mwssltype
  86. CertificateFile $sslcert
  87. MaildirStore $mwacc-local
  88. Subfolders Verbatim
  89. Path $mwaccmaildir/
  90. Inbox $mwaccmaildir/INBOX
  91. Flatten .
  92. Channel $mwacc
  93. Expunge Both
  94. Master :$mwacc-remote:
  95. Slave :$mwacc-local:
  96. Patterns * !\"[Gmail]/All Mail\"
  97. Create Both
  98. SyncState *
  99. MaxMessages 0
  100. # End profile
  101. "
  102. if [ "$mwtype" = "offline" ]; then
  103. mutt_profile="# vim: filetype=neomuttrc
  104. # muttrc file for account $mwaddr
  105. set realname = \"$mwname\"
  106. set from = \"$mwaddr\"
  107. set sendmail = \"$msmtpbin -a $mwacc\"
  108. alias me $mwname <$mwaddr>
  109. set folder = \"$mwaccmaildir\"
  110. set mbox_type = Maildir
  111. macro index gm \"<shell-escape>mailsync $mwacc<enter>\" \"sync mail $mwaddr\"
  112. unmailboxes *
  113. "
  114. else
  115. mutt_profile="# vim: filetype=neomuttrc
  116. # muttrc file for account $mwaddr
  117. set realname = \"$mwname\"
  118. set from = \"$mwaddr\"
  119. set sendmail = \"$msmtpbin -a $mwacc\"
  120. alias me $mwname <$mwaddr>
  121. set folder = \"imaps://$mwaddr@$mwimap:$mwiport\"
  122. set header_cache = \"$mwacccachedir\"
  123. set message_cachedir = \$header_cache
  124. set imap_user = \"$mwlogin\"
  125. set imap_pass = \"\`pass $mwpass\`\"
  126. account-hook \$folder 'set imap_user=\"$mwlogin\" imap_pass=\"\`pass $mwpass\`\"'
  127. set mbox_type = Maildir
  128. set ssl_starttls = yes
  129. set ssl_force_tls = yes
  130. unmailboxes *
  131. "
  132. fi
  133. printf "DONE.\\n"
  134. }
  135. askinfo() {
  136. if [ -z "$mwaddr" ]; then
  137. printf "Type the \033[31memail address\033[0m\\n\t\033[36m"
  138. read -r mwaddr
  139. printf "\033[0m"
  140. while ! echo "$mwaddr" | grep "$mwemailre" >/dev/null; do
  141. printf "That is not a valid \033[31memail address\033[0m, please retype\\n\t\033[36m"
  142. read -r mwaddr
  143. printf "\033[0m"
  144. done
  145. fi
  146. mwdomain="$(echo "$mwaddr" | sed "s/.*@//")"
  147. printf "\\nSearching for \033[32m%s\033[0m in \033[34m\`domains.csv\`\033[0m..." "$mwdomain"
  148. mwserverinfo="$(grep "^$mwdomain" "$mwshare/domains.csv" 2>/dev/null)"
  149. if [ -z "$mwserverinfo" ]; then
  150. printf "Your email domain is not known to mutt-wizard.\\nType in your settings.\\nUsually you find them by an internet search.\\n"
  151. printf "Type the IMAP server (excluding the port number)\\n\033[36m\t"
  152. read -r mwimap
  153. printf "\033[0mIMAP port number (usually 993)\\n\033[36m\t"
  154. read -r mwiport
  155. printf "\033[0mSMTP server (excluding the port number)\\n\033[36m\t"
  156. read -r mwsmtp
  157. printf "\033[0mSMTP port number (usually 587 or 465)\\n\033[36m\t"
  158. read -r mwsport
  159. printf "\033[0m\\nIf you want, you can copy the line below and add it to the \`domains.csv\` file on Github, for others.\\n\\n%s,%s,%s,%s,%s\\n\\nBut be sure the setting works, first! ;-)\\n" "$mwdomain" "$mwimap" "$mwiport" "$mwsmtp" "$mwsport"
  160. else
  161. IFS=, read -r mwservice mwimap mwiport mwsmtp mwsport <<EOF
  162. $mwserverinfo
  163. EOF
  164. printf "\\n\033[3;33mCongrats!\033[0m Server info is known, so you don't need to look it 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\\n" "$mwimap" "$mwiport" "$mwsmtp" "$mwsport"
  165. case "$mwservice" in
  166. gmail.com) printf "\033[31mGmail: \"less secure\" must be enabled before you continue.\\nDo it now, if you have not done it already:\\nhttps://support.google.com/accounts/answer/6010255\\n\033[0m" ;;
  167. protonmail.ch|protonmail.com|pm.me) printf "\033[31mProtonmail: Users must install and configure Protonmail Bridge for the sync to work:\\nhttps://protonmail.com/bridge/\\n\033[0m" && ssltype="None" ;;
  168. esac
  169. [ "$mwsport" = 465 ] && starttlsoff="tls_starttls off"
  170. fi
  171. if [ -z "$mwname" ]; then
  172. printf "Name to associate to email.\\n\t"
  173. read -r mwname
  174. fi
  175. takemwaddr
  176. if [ -z "$mwlogin" ]; then
  177. printf "Type your account username if different from your email address.\\n\033[34mFor most accounts you can probably leave this blank.\033[0m\\n\tLogin(?): \033[36m"
  178. read -r mwlogin
  179. printf "\033[0m"
  180. [ -z "$mwlogin" ] && mwlogin="$mwaddr"
  181. fi
  182. # if the user has a pass entry he could provide it via mwpass
  183. if [ -z "$mwpass" ]; then
  184. mwpass=mutt-wizard-$mwaddr
  185. getpass
  186. fi
  187. getprofiles
  188. mkdir -p "$mwmuttdir" "$mwaccrcdir" "$mwconfigdir/msmtp" "${mwmbsyncrc%/*}"
  189. [ ! -f "$mwmsmtprc" ] && echo "$msmtp_header" > "$mwmsmtprc"
  190. echo "$msmtp_profile" >> "$mwmsmtprc"
  191. case "$mwservice" in
  192. protonmail.ch|protonmail.com|pm.me) protonfinger || return 1 ;;
  193. esac
  194. echo "$mbsync_profile" >> "$mwmbsyncrc"
  195. # new idnum = first one missing
  196. getaccounts
  197. for x in $(seq 1 9); do echo "$accounts" | grep "$x": >/dev/null 2>&1 || { export idnum="$x"; break ;}; done
  198. mwaccrc="$mwaccrcdir/$idnum-$mwacc.muttrc"
  199. echo "$mutt_profile" > "$mwaccrc"
  200. [ ! -f "$mwmuttrc" ] && echo "# vim: filetype=neomuttrc" > "$mwmuttrc" && echo "muttrc created."
  201. ! grep "source.*mutt-wizard.muttrc" "$mwmuttrc" >/dev/null && echo "source $mwsharerc # mw-autogenerated" >> "$mwmuttrc"
  202. if [ "$mwtype" = "offline" ]; then
  203. ! grep "^macro .* gM .*" "$mwmuttrc" >/dev/null && echo "macro index gM '<shell-escape>mailsync -Va<enter>' \"sync all mail\" # mw-autogenerated" >> "$mwmuttrc"
  204. fi
  205. ! grep "^source.*$mwaccrc" "$mwmuttrc" >/dev/null && echo "source $mwaccrc # mw-autogenerated" >> "$mwmuttrc"
  206. return 0
  207. }
  208. protonfinger() {
  209. printf "Getting Protonmail bridge fingerprint...\\n"
  210. fingerprint="$($msmtpbin --serverinfo --host=127.0.0.1 --port=1025 --tls --tls-certcheck=off)" || return 1
  211. sed -i "s/account $mwacc/&\ntls_trust_file\ntls_fingerprint $fingerprint/" "$mwmsmtprc"
  212. }
  213. getpass() {
  214. while : ; do pass rm -f "$mwpass" >/dev/null 2>&1
  215. pass insert "$mwpass" && break; done ;}
  216. formatShortcut() {
  217. while read -r data; do { echo "macro index,pager g$1 \"<change-folder>$data<enter>\" \"go to $2\" # mw-autogenerated"
  218. echo "macro index,pager M$1 \"<save-message>$data<enter>\" \"move mail to $2\" # mw-autogenerated"
  219. echo "macro index,pager C$1 \"<copy-message>$data<enter>\" \"copy mail to $2\" # mw-autogenerated"; } >> "$mwaccrc"
  220. done
  221. }
  222. tryconnect() {
  223. if [ -z "$mailboxes" ]; then
  224. if [ ! -d "$mwaccmaildir" ]; then
  225. mwaccmaildirWasThere="NO" # we need to remove again for "online"
  226. mkdir -p "$mwaccmaildir"
  227. fi
  228. mailboxes="$($mbsyncbin -l $mwacc | sed 's/\//./')" >/dev/null 2>&1
  229. fi
  230. if [ -n "$mailboxes" ]; then
  231. printf "\033[32mMailboxes detected.\033[0m\\n"
  232. echo "$mailboxes" | xargs -I {} mkdir -p "$mwaccmaildir/{}"
  233. return 0
  234. else
  235. 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"
  236. return 1
  237. fi
  238. }
  239. finalize() {
  240. sed -i "/# mw-autogenerated/d" "$mwaccrc"
  241. sed -i "/^mailboxes\|^set record\|^set postponed\|^set trash\|^set spoolfile/d" "$mwaccrc"
  242. idnum=${mwaccrc%%-*}
  243. idnum=${idnum##*/}
  244. muttsync=$(printf '<sync-mailbox><enter-command>source %s<enter><change-folder>!<enter>;<check-stats>' $mwaccrc)
  245. echo "macro index,pager i$idnum '$muttsync' \"switch to $mwaddr\" # mw-autogenerated" >> "$mwaccrc"
  246. command -V urlview >/dev/null 2>&1 && [ ! -f "$HOME/.urlview" ] && echo "COMMAND \$BROWSER" > "$HOME/.urlview"
  247. echo "folder-hook \$folder '$muttsync' # mw-autogenerated" >> "$mwaccrc"
  248. boxes="$(find "$mwaccmaildir/" -mindepth 1 -maxdepth 1 | sed "s/\ /\\\ /g;s/^.*\//=/")"
  249. if [ "$boxes" = "" ]; then
  250. 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" "$mwacc"
  251. return
  252. fi
  253. printf "Setting default mailboxes for your Inbox, Sent, Drafts and Trash in mutt...\\n"
  254. spoolfile=$(echo "$boxes" | grep -i -m 1 inbox | sed 's/=/+/g')
  255. record=$(echo "$boxes" | grep -i -m 1 sent | sed 's/=/+/g')
  256. postponed=$(echo "$boxes" | grep -i -m 1 draft | sed 's/=/+/g')
  257. trash=$(echo "$boxes" | grep -i -m 1 trash | sed 's/=/+/g')
  258. cat >> "$mwaccrc" <<EOF
  259. set spoolfile = "$spoolfile"
  260. set record = "$record"
  261. set postponed = "$postponed"
  262. set trash = "$trash"
  263. EOF
  264. echo "mailboxes =$mwaccmutt ===================== $(echo "$boxes" | sed -e "s/^\|$/\"/g" | tr "\n" " ")" >> "$mwaccrc"
  265. printf "Setting up your keyboard shortcuts for jumping between mailboxes...\\n"
  266. echo "$boxes" | grep -i inbox | head -n 1 | formatShortcut i inbox
  267. echo "$boxes" | grep -i sent | head -n 1 | formatShortcut s sent
  268. echo "$boxes" | grep -i draft | head -n 1 | formatShortcut d drafts
  269. echo "$boxes" | grep -i trash | head -n 1 | formatShortcut t trash
  270. echo "$boxes" | grep -i spam | head -n 1 | formatShortcut S spam
  271. echo "$boxes" | grep -i junk | head -n 1 | formatShortcut j junk
  272. echo "$boxes" | grep -i archive | head -n 1 | formatShortcut a archive
  273. if [ "$mwtype" = "offline" ]; then
  274. notmuchauto
  275. printf "All done.\\n\033[33mYou can now run \`\033[32mmailsync [%s]\033[33m\` to sync your mail.\033[0m\\n" "$mwacc"
  276. else
  277. mkdir -p "$mwacccachedir"
  278. sed -i "/IMAPStore $mwacc-remote$/,/# End profile/d" "$mwmbsyncrc"
  279. [ "$mwaccmaildirWasThere" = "NO" ] && rm -rf "$mwaccmaildir/"
  280. fi
  281. return 0
  282. }
  283. confirm() {
  284. printf "[y/N]: Do 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
  285. return 0 ;
  286. }
  287. mwpick() {
  288. printf "Select an accounts to %s:\\n" "$1"
  289. mwlist
  290. read -r input
  291. [ -z "$input" ] && return 1
  292. mwaddr="$(echo "$accounts" | grep "$input": | awk '{print $2}')"
  293. takemwaddr
  294. [ -z "$mwacc" ] && printf "Invalid response." && return 1
  295. return 0 ;
  296. }
  297. mwdelete() {
  298. sed -i "/IMAPStore $mwacc-remote$/,/# End profile/d" "$mwmbsyncrc"
  299. rm -rf "$mwacccachedir"
  300. rm -rf "$mwaccrcdir/"[1-9]"-$mwacc.muttrc"
  301. sed -i "/[0-9]-$mwacc.muttrc/d" "$mwmuttrc"
  302. sed -i "/account $mwacc/,/^\(\s*$\|account\)/d" "$mwmsmtprc"
  303. }
  304. mwcron() {
  305. ! pgrep cron >/dev/null && echo "No cron manager running. Install/enable one and then select this option again." && return 1
  306. if crontab -l | grep mailsync >/dev/null; then
  307. echo "Active mail sync cronjob detected. Do you want to remove it?"
  308. printf "\033[36m\t"
  309. read -r rmyn
  310. printf "\033[0m"
  311. echo "$rmyn" | grep -i "^y\(es\)*$" >/dev/null && crontab -l | sed '/mailsync/d' | crontab - >/dev/null && echo "Mail sync turned off."
  312. else
  313. echo "How many minutes between each mail sync?"
  314. printf "\033[36m\t"
  315. read -r minnum
  316. printf "\033[0m"
  317. while ! echo "$minnum" | grep "^[0-9]\+$" >/dev/null; do
  318. printf "That doesn't look like a number. How many minutes between each mail sync?\\n\033[36m\t"
  319. read -r minnum
  320. printf "\033[0m"
  321. done
  322. (crontab -l; echo "*/$minnum * * * * export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus; export DISPLAY=:0; $(type mailsync | cut -d' ' -f3)") | crontab - &&
  323. echo "Cronjob added. Mail will sync every $minnum minutes. Be sure you have your cron manager running."
  324. fi
  325. }
  326. asktype() {
  327. if [ -z "$mwtype" ]; then
  328. while : ; do
  329. printf "[yes/no]: Local mail via mbsync? No: Mutt remotes (slower)\\n\t"
  330. read -r offnot
  331. case "$offnot" in
  332. [Yy][Ee][Ss]) mwtype="offline" && break ;;
  333. [Nn][Oo]) mwtype="online" && break ;;
  334. *) echo "Write out either yes or no completely. Try again or press ctrl-c to quit." ;;
  335. esac
  336. done
  337. fi
  338. }
  339. mwpurge() {
  340. confirm "delete all account data" || exit
  341. rm -rf "$mwmbsyncrc" "$mwaccrcdir" "$mwconfigdir/msmtp" "${mwmbsyncrc%/*}" "$mwcachedir"
  342. pgrep cron >/dev/null && crontab -l | sed '/mailsync/d' | crontab - >/dev/null
  343. sed -i "/\# mw-autogenerated/d" "$mwmuttrc"
  344. echo "All configs and account settings have been purged."
  345. }
  346. notmuchauto() {
  347. [ -z "$NOTMUCH_CONFIG" ] && NOTMUCH_CONFIG="$HOME/.notmuch-config"
  348. [ -f "$NOTMUCH_CONFIG" ] && return 0
  349. nmbasic="[database]
  350. path=$mwmaildir
  351. [user]
  352. name=$mwname
  353. primary_email=$mwaddr
  354. [new]
  355. tags=unread;inbox;
  356. ignore=
  357. [search]
  358. exclude_tags=deleted;spam;
  359. [maildir]
  360. synchronize_flags=true
  361. [crypto]
  362. gpg_path=$GPG"
  363. mkdir -p "${NOTMUCH_CONFIG%/*}"
  364. echo "$nmbasic" > "$NOTMUCH_CONFIG"
  365. }
  366. trap 'echo -e "\033[0m\n"; exit' STOP INT ABRT KILL
  367. if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
  368. case "$1" in
  369. ls) mwlist ;;
  370. add) mwadd ;;
  371. pass) mwpick "change the password of" && getpass ;;
  372. delete) mwpick delete && confirm "delete the \`$mwacc\` profile" && mwdelete ;;
  373. purge) mwpurge ;;
  374. cron) mwcron ;;
  375. *) cat << EOF
  376. mw: mutt-wizard, auto-configure email accounts for mutt
  377. including downloadable mail with \`isync\`.
  378. Allowed options:
  379. add Add and autoconfigure an email address (9 max.)
  380. ls List configured accounts
  381. delete Pick an account to delete
  382. purge Delete all accounts and settings
  383. cron Enable or disable an autosync via cronjob
  384. all else Print this message
  385. NOTE: Once at least one account is added, you can run
  386. \`mailsync -a\` to begin downloading mail.
  387. EOF
  388. esac
  389. fi