summaryrefslogtreecommitdiff
path: root/bin/mailsync
blob: 25e2e6e6e80ad9a11395ab9ddc4c3c7a4608830b (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
#!/bin/sh

# - Syncs mail for all accounts, or a single account given as an argument.
# - Displays a notification showing the number of new mails.
# - Displays a notification for each new mail with its subject displayed.
# - Runs notmuch to index new mail.
# - This script can be set up as a cron job for automated mail syncing.

# There are many arbitrary and ugly features in this script because it is
# inherently difficult to pass environmental variables to cronjobs and other
# issues. It also should at least be compatible with Linux (and maybe BSD) with
# Xorg and MacOS as well.

# Run only if not already running in other instance
pgrep mbsync >/dev/null && { echo "mbsync is already running."; exit ;}

# First, we have to get the right variables for the mbsync file, the pass
# archive, notmuch and the GPG home.  This is done by searching common profile
# files for variable assignments. This is ugly, but there are few options that
# will work on the maximum number of machines.
eval "$(grep -h -- \
	"^\s*\(export \)\?\(MBSYNCRC\|MPOPRC\|PASSWORD_STORE_DIR\|PASSWORD_STORE_GPG_OPTS\|NOTMUCH_CONFIG\|GNUPGHOME\|MAILSYNC_MUTE\|XDG_CONFIG_HOME\|XDG_DATA_HOME\)=" \
	"$HOME/.profile" "$HOME/.bash_profile" "$HOME/.zprofile"  "$HOME/.config/zsh/.zprofile" "$HOME/.zshenv" \
	"$HOME/.config/zsh/.zshenv" "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.config/zsh/.zshrc" \
	"$HOME/.pam_environment" 2>/dev/null)"

# For non-interactive shell (e.g. cron job) run only when the GPG key (in $GNUPGHOME or pass --homedir) is unlocked
tty -s || (echo "dummy" | gpg --sign --batch --pinentry-mode error -o /dev/null > /dev/null 2>&1) || exit

export GPG_TTY="$(tty)"

[ -n "$MBSYNCRC" ] && alias mbsync="mbsync -c $MBSYNCRC" || MBSYNCRC="$HOME/.mbsyncrc"
[ -n "$MPOPRC" ] || MPOPRC="$HOME/.config/mpop/config"

lastrun="${XDG_CONFIG_HOME:-$HOME/.config}/mutt/.mailsynclastrun"

# Settings are different for MacOS (Darwin) systems.
case "$(uname)" in
	Darwin) notify() { osascript -e "display notification \"$2\" with title \"$1\"" ;} ;;
	*)
	case "$(readlink -f /sbin/init)" in
		*systemd*|*openrc*) export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus ;;
	esac
	# remember if a display server is running since `ps` doesn't always contain a display
	pgrepoutput="$(pgrep -ax X\(\|org\|wayland\))"
	displays="$(echo "$pgrepoutput" | grep -wo "[0-9]*:[0-9]\+" | sort -u)"
	[ -z "$displays" ] && [ -d /tmp/.X11-unix ] && displays=$(cd /tmp/.X11-unix && for x in X*; do echo ":${x#X}"; done)

	notify() { [ -n "$pgrepoutput" ] && for x in ${displays:-:0}; do
			export DISPLAY="$x"
			notify-send --app-name="mutt-wizard" -- "$1" "$2"
		done ;}
	;;
esac

# Check account for new mail. Notify if there is new content.
syncandnotify() {
	case "$1" in
		imap) mbsync -q "$2" ;;
		pop) mpop -q "$2" ;;
	esac || return 1
	new=$(find\
		"$HOME/.local/share/mail/$2/"[Ii][Nn][Bb][Oo][Xx]/new/ \
		"$HOME/.local/share/mail/$2/"[Ii][Nn][Bb][Oo][Xx]/cur/ \
		-type f -newer "$lastrun" 2> /dev/null)
	newcount=$(echo "$new" | sed '/^\s*$/d' | wc -l)
	case 1 in
		$((newcount > 5)) )
			echo "$newcount new mails for $2." >/dev/tty
			[ -z "$MAILSYNC_MUTE" ] && notify "New Mail!" "📬 $newcount new mails in \`$2\` account."
			;;
		$((newcount > 0)) )
			echo "$newcount new mail(s) for $2." >/dev/tty
			[ -z "$MAILSYNC_MUTE" ] &&
			for file in $new; do
				# Extract and decode subject and sender from mail.
				subject=$(awk '/^Subject: / && ++n == 1,/^.*: / && ++i == 2' "$file" | head -n-1 |
					perl -CS -MEncode -ne 'print decode("MIME-Header", $_)' |
					sed 's/^Subject: //' | tr -d '\n\t')
				from="$(sed -n "/^From:/ s|From: *|| p" "$file" |
					perl -CS -MEncode -ne 'print decode("MIME-Header", $_)')"
				from="${from% *}" ; from="${from%\"}" ; from="${from#\"}"
				notify "📧$from:" "$subject"
			done
			;;
		*) echo "No new mail for $2." ;;
esac
}

allaccounts="$(grep -hs "^\(Channel\|account\)" "$MBSYNCRC" "$MPOPRC")"

# Get accounts to sync. All if no argument. Prefix with `error` if non-existent.
IFS='
'
if [ -z "$1" ]; then
	tosync="$allaccounts"
else
	tosync="$(for arg in "$@"; do for availacc in $allaccounts; do
		[ "$arg" = "${availacc##* }" ] && echo "$availacc" && break
	done || echo "error $arg"; done)"
fi

pids=      # PIDs of backgrounded subshells
count=0    # number of accounts in $tosync
failed=0   # number of failed sync attempts
for account in $tosync; do
	count=$((count+1))
	case $account in
		Channel*)
			syncandnotify imap "${account##* }" &
			pids="$pids $!"
			;;
		account*)
			syncandnotify pop "${account##* }" &
			pids="$pids $!"
			;;
		error*)
			echo "ERROR: Account ${account##* } not found."
			failed=$((failed+1))
			;;
	esac
done

IFS=' '
for pid in $pids; do
	wait $pid || failed=$((failed+1))
done

# Abort if no account sync succeeded
[ "$count" -gt "$failed" ] || exit 1

notmuch new --quiet

#Create a touch file that indicates the time of the last run of mailsync
touch "$lastrun"