summaryrefslogtreecommitdiff
path: root/bin/mw
blob: 19befb3dee2a610ae3cad1c05a5c776597f4478e (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
365
366
367
368
369
#!/bin/sh

command -V gpg >/dev/null 2>&1 && GPG="gpg" || GPG="gpg2"
[ -z ${PASSWORD_STORE_DIR+x} ] && 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 || {
        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 \`%s --full-gen-key\` first.\\n" "$GPG"
        exit 1
    }

prefix="/usr/local"
pass_prefix="mw-"
muttdir="${XDG_CONFIG_HOME:-$HOME/.config}/mutt"	# Main mutt config location
accdir="$muttdir/accounts"				# Directory for account settings
maildir="${XDG_DATA_HOME:-$HOME/.local/share}/mail"	# Location of mail storage
emailre=".+@.+\..+" 					# Regex to confirm valid email address
muttshare="$prefix/share/mutt-wizard"
mbsyncrc="${MBSYNCRC:-$HOME/.mbsyncrc}"
mwconfig="$muttshare/mutt-wizard.muttrc"
cachedir="${XDG_CACHE_HOME:-$HOME/.cache}/mutt-wizard"
muttrc="$muttdir/muttrc"
msmtprc="${XDG_CONFIG_HOME:-$HOME/.config}/msmtp/config"
msmtplog="${XDG_CONFIG_HOME:-$HOME/.config}/msmtp/msmtp.log"
marker="# mw-autogenerated"
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 ;}

getaccounts() { accounts="$(find -L "$accdir" -type f | grep -o "[0-9]-.*.muttrc" | sed "s/-/: /;s/\.muttrc$//" | sort -n)" ;}

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

getprofiles() { \
	unset msmtp_header msmtp_profile mutt_profile mbsync_profile
	[ "$iport" = "1143" ] && imapssl="None"
msmtp_header="defaults
auth	on
tls	on
tls_trust_file	$sslcert
logfile $msmtplog
"
msmtp_profile="account $fulladdr
host $smtp
port ${sport:-587}
from $fulladdr
user $login
passwordeval \"pass $pass_prefix$fulladdr\"
$tlsline
"
mbsync_profile="IMAPStore $fulladdr-remote
Host $imap
Port ${iport:-993}
User $login
PassCmd \"pass $pass_prefix$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
Flatten .

Channel $fulladdr
Expunge Both
Master :$fulladdr-remote:
Slave :$fulladdr-local:
Patterns * !\"[Gmail]/All Mail\"
Create Both
SyncState *
MaxMessages ${maxmes:-0}
ExpireUnread no
# End profile
"

if [ -z "${online+x}" ]; then
mutt_profile="# 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 = \"$maildir/$fulladdr\"
set header_cache = $cachedir/$fulladdr/headers
set message_cachedir = $cachedir/$fulladdr/bodies
set mbox_type = Maildir

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
macro index o \"<shell-escape>mw -y $fulladdr<enter>\" \"run mbsync to sync $fulladdr\"
unmailboxes *
unalternates *
"
else
mutt_profile="# 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 = \"imaps://$login@$imap:${iport:-993}\"
set imap_user = \"$login\"
set header_cache = $cachedir/$fulladdr/headers
set message_cachedir = $cachedir/$fulladdr/bodies
set imap_pass = \"\`pass $pass_prefix$fulladdr\`\"

set mbox_type = Maildir
set ssl_starttls = yes
set ssl_force_tls = yes

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 *
"
fi
}

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 your would like to delete (by number):"
		list
		read -r input
		match="^$input:"
	else
		match=" $fulladdr$"
		getaccounts
	fi

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

	[ -z "$fulladdr" ] && echo "Invalid account name given." && return 1

	sed -ibu "/IMAPStore $fulladdr-remote$/,/# End profile/d" "$mbsyncrc" ; rm -f "$mbsyncrc"bu
	rm -rf "${cachedir:?}/${fulladdr:?}" "$accdir/"[1-9]"-$fulladdr.muttrc"
	sed -ibu "/[0-9]-$fulladdr.muttrc/d" "$muttrc" ; rm -f "$muttrc"bu
	sed -ibu "/account $fulladdr/,/^\(\s*$\|account\)/d" "$msmtprc"; rm -f "$msmtprc"bu
	pass rm -f "$pass_prefix$fulladdr" >/dev/null 2>&1

	# 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"; do
 		tr '\n' '|' < "$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 "$emailre"; do
		echo "\`$fulladdr\` is not a valid email address. Please retype the address:"
		read -r fulladdr
	done
	grep -q "Channel\s*$fulladdr$" "$mbsyncrc" &&
		{ 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/$pass_prefix$fulladdr"
	"$GPG" -qer "$(cat "$PASSWORD_STORE_DIR/.gpg-id")" "$PASSWORD_STORE_DIR/$pass_prefix$fulladdr"
	rm -f "$PASSWORD_STORE_DIR/$pass_prefix$fulladdr" ;}

writeinfo() { mkdir -p "$muttdir" "$accdir" "$cachedir/$fulladdr/bodies" "${XDG_CONFIG_HOME:-$HOME/.config}/msmtp" "$maildir/$fulladdr"

	# Get accounts and find the first missing account number (max. 9).
	getaccounts; for x in $(seq 1 9); do echo "$accounts" | grep -q "$x" || { export idnum="$x"; break ;}; done

	# Configure msmtprc for sending mail.
	[ ! -f "$msmtprc" ] && echo "$msmtp_header" > "$msmtprc"
	echo "$msmtp_profile" >> "$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

	# Create the mbsync config file if local account.
	[ -z "${online+x}" ] && mkdir -p "${mbsyncrc%/*}" && echo "$mbsync_profile" >> "$mbsyncrc"

	# Create a muttrc for viewing mail.
	echo "$mutt_profile" > "$accdir/$idnum-$fulladdr.muttrc"
	[ ! -f "$muttrc" ] && echo "# vim: filetype=neomuttrc" > "$muttrc"
	! grep -q "^source.*mutt-wizard.muttrc" "$muttrc" && echo "source $mwconfig $marker" >> "$muttrc"
	! grep "^source.*.muttrc" "$muttrc" | grep -qv "$mwconfig" && echo "source $accdir/$idnum-$fulladdr.muttrc $marker" >> "$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\" $marker" >> "$muttrc"

	notmuchauto # Create a notmuch config file if not present already.
}

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

formatShortcut() { toappend="$toappend
macro index,pager g$1 \"<change-folder>=$3<enter>\" \"go to $2\" $marker
macro index,pager M$1 \";<save-message>=$3<enter>\" \"move mail to $2\" $marker
macro index,pager C$1 \";<copy-message>=$3<enter>\" \"copy mail to $2\" $marker" >> "$accdir/$idnum-$fulladdr.muttrc" ;}

setBox() { toappend="$toappend
set $1 = \"+$2\" $marker" ;}

getboxes() { [ -n "${force+x}" ] && mailboxes="INBOX
Drafts
Junk
Trash
Sent
Archive" && return 0
	info="$(curl --location-trusted -s -m 5 --user "$login:$(pass $pass_prefix$fulladdr)" --url "${protocol:-imaps}://$imap")"
	[ -z "$info" ] && echo "Log-on not successful." && return 1
	mailboxes="$(echo "$info" | sed "s/.*\" //" | tr -d '
')"
}

finalize() { echo "$mailboxes" | xargs -I {} mkdir -p "$maildir/$fulladdr/{}/cur" "$maildir/$fulladdr/{}/tmp" "$maildir/$fulladdr/{}/new"
	sed -ibu "/$marker/d" "$accdir/$idnum-$fulladdr.muttrc" ; rm -f "$accdir/$idnum-$fulladdr.muttrcbu"
	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" ;;
		esac
	done
	echo "$toappend" >> "$accdir/$idnum-$fulladdr.muttrc"
	[ -z "${online+x}" ] && echo "$fulladdr (account #$idnum) added successfully."
	command -V urlview >/dev/null 2>&1 && [ ! -f "$HOME/.urlview" ] && echo "COMMAND \$BROWSER" > "$HOME/.urlview"
	return 0 ;}

notmuchauto() { \
	[ -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; }

while getopts "fplhodTYD: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 ; online=True ;;
	f) setact add || exit 1 ; force=True ;;
	x) setact add || exit 1 ; password="$OPTARG" ;;
	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."
		imap="127.0.0.1"
		iport="1143"
		smtp="127.0.0.1"
		sport="1025"
  		tlsline="tls_fingerprint $(msmtp --serverinfo --host=$smtp --port=$sport --tls --tls-certcheck=off | awk '/SHA256:/ {print $2}')"
		setact add || exit 1
		;;
	*) 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 server address
  -I	IMAP 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.
  -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 ${pass_prefix}your@email.com\`.
EOF
exit 1
;;
esac done

! command -v mbsync >/dev/null && [ -z "${force+x}" ] && echo "\`mbsync (isync package)\` must be installed." && exit 1

case "$action" in
	list) list ;;
	add) askinfo && getprofiles && writeinfo && getboxes && finalize || { delete ; exit 1 ;} ;;
	delete) delete ;;
	sync) mailsync $fulladdr ;;
	toggle) togglecron ;;
esac