[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[msmtp-users] [denis.golovan@...20...: msmtp queue script]



Hi, all!

If somebody uses msmtpq script for queue management. Please give feedback on my patch.
Thanks.

----- Forwarded message from Denis Golovan <denis.golovan@...20...> -----

From: Denis Golovan <denis.golovan@...20...>
Subject: msmtp queue script
To: marlam@...23...
Date: Tue, 26 Feb 2008 15:40:49 +0200
User-Agent: Mutt/1.5.16 (2007-06-09)
Message-ID: <20080226134049.GY8748@...134...>

Hi, Martin.

I've read your post in msmtp mailing list on queue script.
I found your work useful, thanks.

But, I found some bugs.

Here is patched version of send_mail function (I am not bash guru :) ). 
** quotes mean my comments. Patch is also in attach.

Using previous version of send_mail function lead to enqueueing successfully sent messages in mail folder. All *.mail files were empty though (cat 
patch part).
Also I don't quite understand & in msmtp invocation - it seems to me, that result code could be wrong??

My app versions:
GNU bash, version 3.2.17(1)-release (i686-pc-linux-gnu)
msmtp version 1.4.7

Some feature request, if you please :)
1. Form some kind of options area with constants. Such as google ip, log file and etc
2. Write dsp function output to log
3. Insert script version :). BTW what is the current (msmtp mailing list 2008-01-27) version number?

If you are not against, I'd like to make those feature request changes myself and send you for "publishing".

Best regards,
Denis Golovan

--- msmtpq	2008-02-26 15:28:03.000000000 +0200
+++ msmtpq.1	2008-02-26 15:32:28.000000000 +0200
@@ -25,9 +25,10 @@
 #--------------------------------------------------------------
 
 #Q=$HOME/.msmtp.queue                 # the queue
-Q=~/.msmtp.queue                     # the queue
+Q=~/mail/mqueue                      # the queue
 umask 077                            # set secure permissions on created directories and files
 declare -i CNT                       # a count of mail(s) in the queue
+declare TFILE
 
 usage() {
   echo
@@ -86,7 +87,7 @@
 
   for M ; do
     if [ -n "$M" ] ; then
-      echo "  $M"
+      echo "  $M" >> ~/mail/logs/msmtpq.log
     else
       echo
     fi
@@ -100,9 +101,11 @@
   local -i RC                        # msmtp return code
 
   if /bin/ping -qnc 1 -w 2 64.233.183.147 &> /dev/null ; then  # ip addr of www.google.com
-    /bin/cat | /usr/bin/msmtp "$@" > /dev/null &   # send mail immediately
+    #/bin/cat | /usr/bin/msmtp "$@" > /dev/null &   # **seems pretty strange - return without waiting for result**  send mail immediately  
+    /bin/cat "$TFILE" | /usr/bin/msmtp "$@" > /dev/null   # send mail immediately
     RC=$?
-    if $RC ; then                    # send was successful
+    #if $RC; then   #**by some reason 0 result leads to "mail send was unsuccessful"**
+    if [ $RC = 0 ]; then     # send was successful, **works as expected**
       dsp 'mail sent'
     else
       dsp 'mail send was unsuccessful' \
@@ -130,7 +133,7 @@
 
   echo "$@" > "${BASE}.msmtp" || dsp -e 'writing msmtp cmd line' \
            "to ${BASE}.msmtp failed" # write command line to .msmtp file
-  /bin/cat  > "${BASE}.mail" || dsp -e 'writing mail' \
+  /bin/cat "$TFILE" > "${BASE}.mail" || dsp -e 'writing mail' \
            "to ${BASE}.mail failed"  # write mail to .mail file
   dsp 'mail queued'
 }
@@ -252,10 +255,22 @@
   done
 }
 
+cleanup()
+{
+  #remove temporary message file
+  rm -f $TFILE
+}
+
 #
 ## -- entry point
 #
 
+trap cleanup 0 1 2 3 6 9 14 15
+#message from standart input
+#because /bin/cat does not repeat its output on subsequent runs :)
+TFILE="/tmp/$(basename $0).$$.tmp"
+/bin/cat > $TFILE
+
 # check what we're being called as - 'msmtpq' or 'msmtpQ'
 if [ "$(/usr/bin/basename $0)" == 'msmtpQ' ] ; then  # called as msmtpQ
   if [ -n "$1" ] ; then              # if arg(s)

#!/bin/sh

#--------------------------------------------------------------
#
#  msmtpq : queue funtions to use and manage the msmtp queue,
#    as it was defined by Martin Lambers
#  Copyright (C) 2008 Chris Gianniotis
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or (at
#  your option) any later version.
#
#--------------------------------------------------------------

#--------------------------------------------------------------
# the msmtp queue contains unique filenames of the following form :
#   two for each mail in the queue
#
# create new unique filenames of the form :
#   MLF: ccyy-mm-dd-hh.mm.ss[-x].mail   -- mail file
#   MSF: ccyy-mm-dd-hh.mm.ss[-x].msmtp  -- msmtp command line file
# where x is a consecutive number only appended for uniqueness
#   if you send more than one mail per second
#--------------------------------------------------------------

#Q=$HOME/.msmtp.queue                 # the queue
Q=~/mail/mqueue                      # the queue
umask 077                            # set secure permissions on created directories and files
declare -i CNT                       # a count of mail(s) in the queue
declare TFILE

usage() {
  echo
  echo '  usage : msmtpq functions'
  echo
  echo '          msmtpq <op> --'
  echo '          ops : -s   send a mail'
  echo '                -sr  send a mail, then run queue'
  echo '                -e   enqueue a mail'
  echo '                -er  enqueue a mail, then run queue'
  echo
  echo '                -r   run (flush) mail queue'
  echo '                -d   display (list) queue'
  echo '                -m   purge a single mail from queue'
  echo '                -a   purge all mail in queue'
  echo '                -h   this helpful blurt'
  echo
  echo '  -s   an outgoing mail will normally be sent directly out,'
  echo '         if connected, otherwise it is queued'
  echo '  -e   causes the mail to be enqueued'
  echo '  -r   flushes the mail queue, if anything is there'
  echo '  -sr  if connected sends out the mail then flushes the queue'
  echo '         otherwise enqueues it'
  echo '  -er  enqueues the mail then flushes the queue'
  echo '         (but why would anyone want to do this ?)'
  echo
  echo '       - note that only one op per invocation is allowed'
  echo '       - the only combined ops are the ones listed above'
  echo '       - if more than one op is specified, the first one'
  echo '           only is executed'
  echo '       - for -s, -e, -sr or -er the msmtpq op MUST be followed'
  echo "           by '--' to separate it from any msmtp options"
  echo
}

# verify that msmtp queue is present
  # the first version can be used if you'd like to create the queue dir
  # if it's not found ; I'd rather just be warned if it's not there
check_queue() {                      # make certain queue dir is present
  #if [ ! -d "$Q" ] ; then            # queue dir present or
	#  /bin/mkdir -p "$Q" || dsp -e 'could not create queue dir'
  #fi                                 # create it
  if [ ! -d "$Q" ] ; then            # queue dir not present
    dsp -e "  can't find $Q"         # complain
  fi
}

# display a message, possibly an error
dsp() {
  local M ERR

  if [ "$1" == '-e' ] ; then
    ERR='t'
    shift
  fi

  for M ; do
    if [ -n "$M" ] ; then
      echo "  $M" >> ~/mail/logs/msmtpq.log
    else
      echo
    fi
  done

  [ -n "$ERR" ] && exit 1
}

# send a mail (otherwise queue it)
send_mail() {    # <-- all mail args # send or enqueue a mail
  local -i RC                        # msmtp return code

  if /bin/ping -qnc 1 -w 2 64.233.183.147 &> /dev/null ; then  # ip addr of www.google.com
    #/bin/cat | /usr/bin/msmtp "$@" > /dev/null &   # **seems pretty strange - return without waiting for result**  send mail immediately  
    /bin/cat "$TFILE" | /usr/bin/msmtp "$@" > /dev/null   # send mail immediately
    RC=$?
    #if $RC; then   #**by some reason 0 result leads to "mail send was unsuccessful"**
    if [ $RC = 0 ]; then     # send was successful, **works as expected**
      dsp 'mail sent'
    else
      dsp 'mail send was unsuccessful' \
      dsp "msmtp rc = $RC ; enqueuing mail"
      enqueue_mail "$@"
    fi
  else                               # not connected
    enqueue_mail "$@"                # enqueue mail
  fi
}

# enqueue a mail
enqueue_mail() { # <-- all mail args # enqueue a mail
  local BASE                         # base name
  local -i INC                       # increment counter for basename collision

  BASE="$Q/`/bin/date +%Y-%m-%d-%H.%M.%S`"  # make unique base filename
  if [ -f "$BASE.*" ] ; then         # base filename exists
	  INC=1                            # initial increment to 1
	  while [ -f "${BASE}-${INC}.*" ] ; do
      INC="(( ++INC ))"              # filename exists ; bump increment
	  done
	  BASE="${BASE}-${INC}"            # unique ; set base filename
  fi

  echo "$@" > "${BASE}.msmtp" || dsp -e 'writing msmtp cmd line' \
           "to ${BASE}.msmtp failed" # write command line to .msmtp file
  /bin/cat "$TFILE" > "${BASE}.mail" || dsp -e 'writing mail' \
           "to ${BASE}.mail failed"  # write mail to .mail file
  dsp 'mail queued'
}

# run (flush) queue
run_queue() {                        # run queue
  local LOC="$Q/.lock" MLF MSF       # lock file name ; queued mail filename pairs
  local -i MAX=120 SEC=0 RC          # max seconds to gain a lock ; seconds waiting

  if (( $(/bin/ls -1 *.mail 2> /dev/null | /usr/bin/wc -l) > 0 )) ; then # if any mail in Q
                                                     # attempt to lock queue
    while [ -e "$LOC" ] && (( SEC < MAX )) ; do      # if a lock file there
	    /bin/sleep 1                                   # wait a second
	    (( ++SEC ))                                    # accumulate seconds
    done                                             # try again while locked for MAX secs
    if [ -e "$LOC" ] ; then                          # lock file still there, give up
	    dsp -e '' "cannot use $Q : waited $MAX seconds for" \
	              "  lockfile $LOC to vanish, giving up" \
	              'if you are sure that no other instance of this script' \
	              '  is running, then delete the lock file' ''
    fi

    /bin/touch "$LOC" || exit 1      # lock queue

    for MLF in *.mail ; do           # process all mails
	    dsp "sending $MLF ..."         # scratch user's itch
	    MSF="${MLF%.*}.msmtp"
	    if [ ! -f "$MSF" ] ; then      # no corresponding MSF file found
		    dsp "corresponding file $MSF not found" \
		        'check this ...'         # give user the bad news
		    continue                     # crank on
	    fi
	    /usr/bin/msmtp $(/bin/cat "$MSF") < "$MLF"  # the mail goes out the door
      RC=$?
	    if $RC ; then                  # send was successful
		    /bin/rm "$MLF" "$MSF"        # nuke the files
		    dsp "$MLF sent successfully" # good news to user
	    else                           # send unsuccessful
		    dsp "$MLF send failed" \
            "msmtp rc = $RC" \
            'check this out ...'     # bad news ...
	    fi
    done
  else                               # no mails in queue
    dsp '' 'mail queue is empty' ''  # inform user
  fi

  /bin/rm -f "$LOC"                  # remove the lock
}

# display queue
display_queue() {
  local LST M                        # list of mails in queue ; each mail

  echo
  LST="$(/bin/ls $Q/*.mail 2>/dev/null)"
  (( CNT = 0 ))
  if [ -n "$LST" ] ; then
    for M in $LST ; do
      echo "  mail id = [$(/usr/bin/basename $M .mail)]"       # show mail id
      /bin/egrep -s --colour -h '(^From:|^To:|^Subject:)' "$M" # show mail info
      (( CNT++ ))
      echo
    done
  else
    dsp 'no mail in queue' ''
  fi
}

# delete all mail in queue
purge_all_mail() { # <-- question txt, positive ack txt
  local YN I C                       # confirmation response ; question text ; ack text

  if [ "$1" == '-1' ] ; then         # queue contains single mail
    I="remove the only mail from the queue"
    C="single mail purged from queue"
  else                               # queue contains multiple mails
    I="remove (purge) all mail from the queue"
    C="msmtp queue purged"
    display_queue
  fi

  echo -n "  $I [y/N] ? ...: " ; read YN
  case $YN in
    y|Y) /bin/rm -f "$Q"/*.*         # msmtp - nukes all mail in queue (dir)
         dsp '' "$C ..." ''
         ;;
    *)   dsp '' 'nothing done ; queue is untouched ...' ''
         ;;
  esac
}

purge_a_mail() {
  local ID                           # id of mail being deleted

  while true ; do                    # purge an individual mail from queue
    display_queue                    # show queue contents, if any
    if (( CNT > 0 )) ; then          # something there
      if (( CNT == 1 )) ; then       # only one mail
        purge_all_mail -1            # purge it
      else                           # more than one mail
        echo '  remove (purge) a mail from the queue ; enter its id'
        echo -n '    ( <cr> to exit ) ...: ' ; read ID
        if [ -n "$ID" ] ; then       # <-- file name (only, no suff)
          if [ -n "$(/bin/ls "$Q"/"$ID".* 2>/dev/null)" ] ; then
            /bin/rm "$Q"/"$ID".*     # msmtp - nukes a single mail in queue (dir)
            dsp '' "mail [$ID] purged ..."
          else                       # invalid id entered
            dsp '' "mail [$ID] not found ; bad id ..."
          fi
        else                         # nothing entered
          dsp '' 'nothing done ; queue is untouched ...' ''
          break
        fi
      fi
    else
      break
    fi
  done
}

cleanup()
{
  #remove temporary message file
  rm -f $TFILE
}

#
## -- entry point
#

trap cleanup 0 1 2 3 6 9 14 15
#message from standart input
#because /bin/cat does not repeat its output on subsequent runs :)
TFILE="/tmp/$(basename $0).$$.tmp"
/bin/cat > $TFILE

# check what we're being called as - 'msmtpq' or 'msmtpQ'
if [ "$(/usr/bin/basename $0)" == 'msmtpQ' ] ; then  # called as msmtpQ
  if [ -n "$1" ] ; then              # if arg(s)
    check_queue                      # verify that queue directory is present ...
    send_mail "$@"                   # send mail (or queue it if not connected)
    exit 0
  else
    dsp -e 'msmtpQ requires input of a mail'
  fi
fi

if [ -z "$1" ] ; then
  dsp 'msmtpq requires an instruction &/or a mail'
  usage
  exit 1
fi

for A ; do                           # sort opts, args, etc.
  case "$A" in
    --) shift                        # end of msmtp opts - shift off '--'
        break                        # anything further belongs to msmtp
        ;;
    -h) usage                        # show help
        exit 0
        ;;
    -s|-sr|-e|-er|-r|-d|-m|-a)       # all other valid ops
        [ -z "$OP" ] && OP=${A:1}    # op not already spec'd ; take all but first char (hyphen)
        shift
        ;;
    *)  dsp '' "[$A] is an unknown option"
        usage
        exit 1
        ;;
  esac
done

if [ -z "$OP" ] ; then               # check for an empty op
  dsp -e 'must specify an operation for msmtpq'
fi

check_queue                          # check that queue directory is present ...
while [ -n "$OP" ] ; do              # while op contains a spec
  case "$OP" in                      # sort ops ; run according to spec
    s)  send_mail "@"     ;;         # send the mail
    e)  enqueue_mail "$@" ;;         # enqueue the mail
    r)  run_queue         ;;         # run (flush) the queue
    d)  display_queue     ;;         # display (list) all mail in queue
    m)  purge_a_mail      ;;         # purge an individual mail from queue
    a)  purge_all_mail    ;;         # purge all mail in queue
  esac
  OP=${OP:1}                         # trim off first char of OP
done

exit 0


----- End forwarded message -----

-- 
Best regards,
Denis Golovan aka MageSlayer