#!/bin/zsh # # Trigger a command when new mail arrives on the server, using IMAP IDLE. Cf. # . # # 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. # 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 $*