summaryrefslogtreecommitdiff
path: root/bin/mw
blob: 2baa5f73b48dd48600a88ca774d579efef07177b (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
#!/bin/sh

set -a

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_CACHE_HOME:-$HOME/.cache}/msmtp/msmtp.log"
mbsyncrc="${MBSYNCRC:-$HOME/.mbsyncrc}"
mpoprc="${XDG_CONFIG_HOME:-$HOME/.config}/mpop/config"
mpoptemp="$muttshare/mpop-temp"
mbsynctemp="$muttshare/mbsync-temp"
mutttemp="$muttshare/mutt-temp"
msmtptemp="$muttshare/msmtp-temp"
onlinetemp="$muttshare/online-temp"
notmuchtemp="$muttshare/notmuch-temp"
# With the use of templates, it's impossible to use parameter substitution.
# Therefore, some default variables that might be otherwise overwritten are set
# here.
iport="993"
sport="465"
imapssl="IMAPS"
tlsline="tls_starttls off"
maxmes="0"

alias mbsync='mbsync -c "$mbsyncrc"'

# mbsync now requires "Far/Near" rather than "Master/Slave", but Ubuntu/Debian
# have the older version.
if command -V apt-get >/dev/null 2>&1; then
	master="Master"
	slave="Slave"
else
	master="Far"
	slave="Near"
fi

for x in "/etc/ssl/certs/ca-certificates.crt" \
	"/etc/pki/tls/certs/ca-bundle.crt" "/etc/ssl/cert.pem" \
	"/etc/ssl/ca-bundle.pem" "/etc/pki/tls/cacert.pem" \
	"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.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" ] || {
	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 "\S*.muttrc" | sed "s|.*/\([0-9]-\)*||;s/\.muttrc$//" | nl)" ;}

list() { getaccounts && [ -n "$accounts" ] && echo "$accounts" || exit 1 ;}

prepmsmtp() { mkdir -p "${msmtprc%/*}" "${msmtplog%/*}" ; ln -s "$msmtprc" "$HOME/.msmtprc" 2>/dev/null ; envsubst < "$msmtptemp"  >> "$msmtprc" ;}

prepmbsync() { mkdir -p "${mbsyncrc%/*}" ; [ -f "$mbsyncrc" ] && echo >> "$mbsyncrc" ; envsubst < "$mbsynctemp" >> "$mbsyncrc" ;}

prepmpop() { mkdir -p "${mpoprc%/*}" ; envsubst < "$mpoptemp" >> "$mpoprc" ;}

prepmutt() { mkdir -p "${muttrc%/*}" "$accdir"
	envsubst < "$mutttemp" > "$accdir/$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/$fulladdr.muttrc" >> "$muttrc"
	echo "macro index,pager i$idnum '<sync-mailbox><enter-command>source $accdir/$fulladdr.muttrc<enter><change-folder>!<enter>;<check-stats>' \"switch to $fulladdr\"" >> "$muttrc"
}

getprofiles() { \
	safename="$(echo $fulladdr | sed 's/@/_/g')"
	case "$type" in
		online)
			folder="imaps://$login@$imap:$iport"
			extra="$(envsubst < "$onlinetemp")"
			;;
		pop) prepmpop ;;
		*)
		case "$iport" in
			1143) imapssl=None ;;
			143) imapssl=STARTTLS ;;
		esac
		prepmbsync ;;
	esac
	prepmsmtp
	prepmutt
	prepnotmuch
}

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="^\s*$input\s\+"
	else
		match="\s\+$fulladdr$"
		getaccounts
	fi

	fulladdr="$(echo "$accounts" | grep "$match" | grep -o "\S*@\S*")"

	[ -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/$fulladdr.muttrc"  "$accdir/"[0-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 "$passprefix$fulladdr" >/dev/null 2>&1
	[ -n "${purge+x}" ] && safename="$(echo $fulladdr | sed 's/@/_/g')" && rm -rf "${cachedir:?}/${safename:?}" "${maildir:?}/${fulladdr:?}"
}

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
	folder="$maildir/$fulladdr"
	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
	case $sport in
		587) tlsline="# tls_starttls" ;;
	esac
	[ -z "$realname" ] && realname="${fulladdr%%@*}"
	[ -z "$passprefix" ] && passprefix=""
	hostname="${fulladdr#*@}"
	login="${login:-$fulladdr}"
	if [ -n "${password+x}" ]; then
		createpass
	else
		getpass
	fi
}

createpass() { echo "$password" > "$PASSWORD_STORE_DIR/$passprefix$fulladdr"
  "$GPG" -qe $(printf -- " -r %s" $(cat "$PASSWORD_STORE_DIR/.gpg-id")) "$PASSWORD_STORE_DIR/$passprefix$fulladdr"
	case "$(uname)" in
		Darwin|*BSD) rm -P "$PASSWORD_STORE_DIR/$passprefix$fulladdr" ;;
		*) shred -u "$PASSWORD_STORE_DIR/$passprefix$fulladdr" ;;
	esac
	rm -f "$PASSWORD_STORE_DIR/$passprefix$fulladdr" ;}

errorexit() {
	echo "Log-on not successful."
	case "$imap" in
		imap.gmail.com)
			echo "This account with $service is using Google's Gmail servers, which disable all third-party applications without an application-specific password.
Please be sure you are using OAUTH with your Gmail account, or better yet, stop using Gmail."
			;;
		imap.mail.me.com)
			echo "This account with $service is using Apple's iCloud servers, which disable all non-Apple applications by default.
Please be sure you either enable third-party applications, or create an app-specific password, or best of all, stop using Apple."
			;;
	esac
	exit 1
	}

getpass() { while : ; do pass rm -f "$passprefix$fulladdr" >/dev/null 2>&1
		pass insert -f "$passprefix$fulladdr" && break; done ;}

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 "$passprefix$fulladdr")" --url "${protocol:-imaps}://$imap:${iport:-993}")"
		[ -z "$info" ] && errorexit
		mailboxes="$(echo "$info" | grep -v HasChildren | sed "s/.*\" //;s/\"//g" | tr -d '\r')"
	fi
	[ "$type" = "pop" ] && mailboxes="INBOX"
	for x in $(sed -n "/^macro.* i[0-9] / s/\(^macro.* i\| .*\)//gp " "$muttrc" 2>/dev/null | sort -u; echo 0); do
		idnum=$((idnum + 1))
		[ "$idnum" -eq "$x" ] || break
	done
	toappend="mailboxes $(echo "$mailboxes" | sed "s/^/\"=/;s/$/\"/;s/'/\\\'/g" | paste -sd ' ' - )"
}

finalize() { echo "$toappend" >> "$accdir/$fulladdr.muttrc"
	[ "$type" != "online" ] && echo "$mailboxes" | xargs -I {} mkdir -p "$maildir/$fulladdr/{}/cur" "$maildir/$fulladdr/{}/tmp" "$maildir/$fulladdr/{}/new"
	mkdir -p "$cachedir/$safename/bodies"
	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
	envsubst < "$notmuchtemp" > "$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."
		exit 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
  -t number		Toggle automatic mailsync every <number> minutes
  -T			Toggle automatic mailsync
  -r			Reorder account numbers

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	Add for a POP server instead of IMAP.
  -P	Pass Prefix (prefix of the file where password is stored)
  -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 '$passprefix'your@email.com\`.
EOF
}

reorder() {
	tempfile="$(mktemp -u)"
	trap 'rm -f $tempfile' HUP INT QUIT TERM PWR EXIT
  echo "# Carefully reorder these accounts with the desired numbers in the first column.
# DO NOT reorder rows or rename the accounts in the second column." > "$tempfile"
	sed -n "
	/ i[0-9] / s?\(.* i\|'<sync.*/\|\.muttrc.*\)??g p
	" "$muttrc" >> "$tempfile"
	${EDITOR:-vim} "$tempfile" || exit 1
  sed -i -e 's/#.*//' -e '/^$/d' "$tempfile"
	default="$(sort -n "$tempfile" | head -n 1)"
	default="${default#* }"
	sed -ibu "
  /.* i[0-9] .*.muttrc/d
  /^source.*accounts.*.muttrc/d
  " "$muttrc" 2>/dev/null; rm -f "$muttrc"bu
	awk -v a="$accdir" -v d="$default" ' BEGIN { print "source "a"/"d".muttrc" }
		{
		print "macro index,pager i"$1" '\''<sync-mailbox><enter-command>source "a"/"$2".muttrc<enter><change-folder>!<enter>;<check-stats>'\'' \"switch to "$2"\""
		}
	' "$tempfile" >> "$muttrc"
}

while getopts "rfpXlhodTYD:y:i:I:s:S:u:a:n:P:x:m:t:" o; do case "${o}" in
	l) setact list ;;
	r) setact reorder1 ;;
	d) setact delete ;;
	D) setact delete ; fulladdr="$OPTARG" ;;
	y) setact sync ; fulladdr="$OPTARG" ;;
	Y) setact sync ;;
	a) setact add ; fulladdr="$OPTARG" ;;
	i) setact add ; imap="$OPTARG" ;;
	I) setact add ; iport="$OPTARG" ;;
	s) setact add ; smtp="$OPTARG" ;;
	S) setact add ; sport="$OPTARG" ;;
	u) setact add ; login="$OPTARG" ;;
	n) setact add ; realname="$OPTARG" ;;
	P) setact add ; passprefix="$OPTARG" ;;
	m) setact add ; maxmes="$OPTARG" ;;
	o) setact add ; type="online" ;;
	p) setact add ; type="pop"; protocol="pop3s" ; iport="${iport:-995}" ;;
	f) setact add ; force=True ;;
	x) setact add ; password="$OPTARG" ;;
	X) setact delete ; purge=True ;;
	t) setact toggle ; cronmin="$OPTARG" ;;
	T) setact toggle ;;
	h) setact info ;;
	\?) echo "See \`$(basename $0) -h\` for possible options and help."; exit 1 ;;
esac done

[ -z "$action" ] && action="info"

case "$action" in
	list) list ;;
	add) checkbasics && askinfo && getboxes && getprofiles && finalize ;;
	delete) delete ;;
	sync)
		echo "\`mw -y\` and \`mw -Y\` are now deprecated and will be removed in a future update. Please switch to using \`mailsync\`."
		mailsync $fulladdr
		;;
	toggle) togglecron ;;
	reorder) reorder ;;
	info) mwinfo; exit 1 ;;
esac