#!/bin/bash ################### # a simple mail delivery and transmission system # Copyright Andrew Tridgell 1997-2002 # released under the GNU General Public License version 2 or later VERSION="1.2" # setup the default configuration MAIL_SSH="ssh" MAIL_DIR="Mail" MAIL_INBOX="$HOME/InBox" MAIL_RUNNER="mail.runner" SENDMAIL="/usr/lib/sendmail" SENDMAIL_OPTS="-t -i" RSYNC_OPTS="-Pavz" MAIL_TIMEOUT=300 PROCMAIL_COMMAND="formail -s procmail" MAIL_VERBOSE=2 MAIL_NO_PIDOF=0 # sensible paths are often not setup when invoking commands remotely export PATH=$PATH:$HOME/bin:/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin # define an empty sendmail hook - replace this in .mail.runner for # interesting multi-host tricks mail_sendmail_hook() { msg="$1" } # load the user specific config if [ -f $HOME/.mail.runner ]; then HAVE_MAIL_RUNNER_CONFIG=1 . $HOME/.mail.runner else echo "YOU MUST HAVE A .mail.runner config!" 1>&2 HAVE_MAIL_RUNNER_CONFIG=0 fi if [ ! -z "$MAIL_RUNNER_CONF" -a -r "$MAIL_RUNNER_CONF" ]; then . $MAIL_RUNNER_CONF fi if [ -z "$MAIL_OUT_DIR" ]; then MAIL_OUT_DIR="$MAIL_DIR" fi # processing now continues in MAIN SCRIPT section at bottom of file ############################################################## # print a message if the level <= $MAIL_VERBOSE debug_msg() { level=$1; if [ $level -le $MAIL_VERBOSE ]; then message="$2"; echo "$message"; fi } ################################## # print an error message to stderr error_msg() { echo "$1" 1>&2; } ############################################################## # try to make sure two copies of this script don't run at once exit_if_running() { if [ "$MAIL_NO_PIDOF" == 1 ]; then return; fi pids=`pidof -x -o $$ mail.runner`; myid=`id -u`; for p in $pids; do pid_owner=`stat -c"%u" /proc/$p/stat` if [ x$pid_owner = x$myid ]; then echo "mail.runner is already running - pid $p" exit 1 fi done } ############################################################################### # send all pending mail, invoking mail.deliver at the other end mail_send() { exit_if_running list=`echo $HOME/$MAIL_OUT_DIR/mail.out.*.*[0-9]` debug_msg 2 ""; if [ "$list" = "" ]; then debug_msg 2 "No messages to send"; return 0; fi debug_msg 1 "Sending `echo $list | wc -w` messages"; if rsync --rsync-path="$MAIL_RUNNER remote_deliver" \ --timeout=$MAIL_TIMEOUT $RSYNC_OPTS -e "$MAIL_SSH" \ $list $MAIL_HOST:$MAIL_DIR/; then rm -f $list fi return 0; } ############################################################################### # fetch mail from the remote host and deliver it via procmail mail_fetch() { exit_if_running if ! rsync --timeout=$MAIL_TIMEOUT $RSYNC_OPTS -e "$MAIL_SSH" \ --rsync-path="$MAIL_RUNNER remote_send" \ $MAIL_HOST:$MAIL_DIR/EMPTY_FILE $HOME/$MAIL_DIR/; then error_msg "transfer failed"; return 1; fi if [ "`echo $HOME/$MAIL_DIR/mail.in.*.*`" = "" ]; then debug_msg 1 ""; debug_msg 1 "No mail to retrieve"; return 1 fi for f in $HOME/$MAIL_DIR/mail.in.*.*; do if cat $f | $PROCMAIL_COMMAND; then # it is useful seeing what mail is being processed if [ "$MAIL_FRM" = "1" ]; then frm $f fi rm -f $f fi done # this is useful for seeing where procmail has put things ... if [ "$MAIL_PROCMAIL_TAIL" = "1" ]; then debug_msg 2 ""; debug_msg 2 ""; debug_msg 2 `tail $HOME/$MAIL_DIR/procmail.log`; fi return 0; } ############################################################################### # a sendmail-like call. This just places the mail in a known location for # mail_send to find mail_sendmail() { msg=$HOME/$MAIL_DIR/mail.out.$RANDOM.$$ tmp=$msg.tmp cat > $tmp /bin/mv $tmp $msg mail_sendmail_hook $msg exit 0 } ############################################################################## # a rsync wrapper function that delivers mail on the remote host mail_remote_deliver() { exit_if_running rsync $* status=$? if [ $status != 0 ]; then return $status; fi for f in $HOME/$MAIL_DIR/mail.out.*.*[0-9]; do if $SENDMAIL $SENDMAIL_OPTS < $f; then rm -f $f else error_msg "Mail of $f failed $status"; fi done return 0 } ############################################################################### # a rsync wrapper function for fetching mail from the remote host mail_remote_send() { exit_if_running # atomically move mail from InBox to a temporary file if test -s $MAIL_INBOX; then movemail $MAIL_INBOX $HOME/$MAIL_DIR/mail.in.$RANDOM.$$ > /dev/null fi # by using an empty file we avoid error messages from rsync about not # having any files to transfer if [ ! -f $HOME/$MAIL_DIR/EMPTY_FILE ]; then touch $HOME/$MAIL_DIR/EMPTY_FILE fi rsync $* $HOME/$MAIL_DIR/mail.in.*.* status=$? if [ $status != 0 ]; then return $status; fi rm -f $HOME/$MAIL_DIR/mail.in.*.* return 0 } ############################################################################### # display help text mail_help() { cat < EOF } ############################################################################### # START OF MAIN SCRIPT ############################################################################### # See if user is just after help, if so display and exit if [ "$1" = "-h" -o "$1" = "--help" -o $HAVE_MAIL_RUNNER_CONFIG == 0 ]; then mail_help exit 0 fi # we rely on expansion to a null list shopt -s nullglob # make sure the necessary variables are defined if [ "$MAIL_HOST" = "" ]; then error_msg "You must define a MAIL_HOST in $HOME/.mail.runner"; error_msg "Use -h for more help"; exit 1 fi # when an argument is passed then call the corresponding mail_* function # with the remaining arguments. This is used to invoke the right function # remotely, but can also be used to do things like "mail.runner send" from # the command line if [ $# != 0 ]; then cmd=$1 shift mail_$cmd $* exit $? fi # the default is to fetch then send mail_fetch mail_send