summaryrefslogtreecommitdiff
path: root/bin/mw
blob: 3cc3d7813697323538a101e36746d46e1e0ac402 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
#!/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 <yourgpgemail>\` 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 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
$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 '<sync-mailbox><enter-command>source $accdir/$idnum-$fulladdr.muttrc<enter><change-folder>!<enter>;<check-stats>' \"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 \"<shell-escape>mpop $fulladdr<enter>\" \"run mpop to get $fulladdr's mail\""
			folder="$maildir/$fulladdr"
			prepmpop ;;
		*) synccmd="macro index o \"<shell-escape>mbsync $fulladdr<enter>\" \"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 <<EOF
$serverinfo
EOF
	imap="${imap:-$imapsugg}"
	smtp="${smtp:-$smtpsugg}"
	sport="${sport:-$sportsugg}"
	iport="${iport:-$iportsugg}"
}

delete() { if [ -z "${fulladdr+x}" ]; then
		echo "Select the account you would like to delete (by number):"
		list || exit 1
		read -r input
		match="^$input:"
	else
		match=" $fulladdr$"
		getaccounts
	fi

	fulladdr="$(echo "$accounts" | grep "$match" | cut -f2 -d' ')"

	[ -z "$fulladdr" ] && echo "$fulladdr is not a valid account name." && return 1

	sed -ibu "/IMAPStore $fulladdr-remote$/,/# End profile/d" "$mbsyncrc" 2>/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 \"<change-folder>=$3<enter>\" \"go to $2\"
macro index,pager M$1 \";<save-message>=$3<enter>\" \"move mail to $2\"
macro index,pager C$1 \";<copy-message>=$3<enter>\" \"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 \`mdir=$maildir/$fulladdr/; find \$mdir -mindepth 1 -type d -name cur | sed -e 's:/cur\$:\":' -e \"s:\$mdir:\\\"=:\" | sort | tr '\\\n' ' '\`"
	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 <number> 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