Skip to content

Instantly share code, notes, and snippets.

@eigengrau
Created August 16, 2018 08:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save eigengrau/5d03c2878e4e6f3bf9c5f95832358aa1 to your computer and use it in GitHub Desktop.
Save eigengrau/5d03c2878e4e6f3bf9c5f95832358aa1 to your computer and use it in GitHub Desktop.
E-mail notifications using IMAP IDLE (no polling)
#!/bin/zsh
#
# Trigger a command when new mail arrives on the server, using IMAP IDLE. Cf.
# <http://dwm.suckless.org/scripts/email_notifier_script>.
#
# Note that, theoretically, fetchmail can take a comma-separated list of
# multiple folders, but IDLE will only poll the first one, so if you need to
# monitor multiple mailboxes, you need to run multiple monitor instances.
#
# Passwords are read from the password store keyring by default.
#
# Requirements:
#
# - fetchmail
# - docopts
# - zsh
#
#
# Copyright © 2018 Sebastian Reuße
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
##############################
## Tweakables ##
##############################
get_password() {
local account=$1
pass show mail/$account
}
notify() {
local account=$1
local mail_info=$2
local mailbox=$3
local message="$account: new mail ($mail_info)"
notify-send $message
xset led named "Scroll Lock"
mbsync -V $account:$mailbox
}
##############################
## Program ##
##############################
setopt \
nounset \
pipefail \
warncreateglobal
program_name=$(basename $0)
usage_text="$program_name
Usage:
$program_name -a ACCOUNT -r HOSTNAME -l USERNAME [options]
Options:
-h, --help Show this help.
-a, --account ACCOUNT Read password from password store entry mail/ACCOUNT
-b, --mailbox MAILBOX Monitor this mailbox [default: INBOX]
-r, --host HOSTNAME Connect to HOST
-l, --login USERNAME Login as USERNAME
-p, --port PORT Connect to this port
-x, --debug Enable debug output
--caroot PATH Load TLS root certificates from here [default: /etc/ssl/cert.pem]
--imaps Connect using IMAPS instead of STARTTLS"
cleanup() {
local statuscode=$?
kill -- -$$
rm -vr $FETCHMAILHOME
exit $statuscode
}
main() {
unsetopt warncreateglobal
eval "$(docopts -h $usage_text : $*)"
setopt warncreateglobal
if [[ $debug == true ]]; then
setopt xtrace
fi
local port sslopts
if [[ $imaps == true ]]; then
port=${port:-993}
sslopts="ssl sslproto TLS1"
else
port=${port:-143}
sslopts="sslproto TLS1"
fi
local password
get_password $account | read password
# Generate a fetchmail-config on the fly. This avoids storing the password
# in the clear, since fetchmail doesn’t offer something like msmtp’s
# password_cmd.
local fetchmail_config="
poll \"$host\" port $port proto IMAP \
user \"$login\" there with password \"$password\" \
keep idle $sslopts sslcertck sslcertfile \"$caroot\"
folder \"$mailbox\""
# Since IDLE only polls one folder, we will probably want to run multiple
# instances of this script. However, fetchmail only allows one process at
# the same time. We can circumvent this by setting FETCHMAILHOME uniquely.
# Hopefully, this is okay, since we’re using fetchmail in a way that doesn’t
# touch the remote mail storage. Cf.
# <http://fnxweb.com/blog/2012/07/14/using-multiple-fetchmail-instances-for-instant-gratification/>
export FETCHMAILHOME=$(mktemp -d)
local input mail_info
fetchmail --check -f - <<<$fetchmail_config \
| while read input; do
mail_info=$(
sed 's/(//' <<<$input \
| awk '{print $1-$3}'
)
if [[ $mail_info != 0 ]]; then
notify $account $mail_info $mailbox
fi
done &
# Unset sensitive information once fetchmail is running.
unset fetchmail_config
unset password
trap cleanup INT TERM QUIT
wait
}
main $*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment