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

[msmtp-users] problem sending a mail & msmtpq scripts error report & alternative msmtpq script



hi everyone

however they didn't work for me!
strangely they added every mail to the queue directory...

and i got this output all over my logfiles when sending through msmtpQ
(symlinked to sendmail):
--------------------
/usr/sbin/sendmail: 39: declare: not found
[: 170: Illegal number: ==
local: 184: -i: bad variable name
PING 64.233.183.147 (64.233.183.147) 56(84) bytes of data.

--- 64.233.183.147 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 35.872/35.872/35.872/0.000 ms
[: 184: ==: unexpected operator
[: 184: ==: unexpected operator
local: 184: -i: bad variable name
[: 184: ==: unexpected operator
[: 184: -e: unexpected operator
--------------------
this might be specific because i am using the script with the version
shipped with ubuntu 8.04 LTS...
thanks for writing the msmtpq scripts anyway :-)

since i have no clue of shell scripting i was unable to fix this
script and because i need one to work now with the msmtp version
shipped with ubuntu 8.04 i wrote a new one in python that works almost
in the same way as the shell scripts included in mstmp. mostly i
combined everything into one script and when unable to send a mail it
automatically forks of as a daemon, trying to flush the queue each
minute. when the queue is empty the daemon will quit (which is very
nice if you have network outages of a few minutes once a week). like
the included scripts it determines a live internet connection through
pinging google, although i've changed it to pinging smtp.google.com...
well anyway i just thought it might be useful to someone.

claude nobs
-------------------
#!/usr/bin/env python
# -*- coding: utf8 -*-

"""
msmtp-queue <http:/mq.blunet.cc/>

Copyright (C) 2008  Claude Nobs

Based on scripts from Martin Lambers & 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 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

For more details, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
"""

import sys, os, time, pwd, signal, re
	
def _usage() :
	print "Usage: %s [COMMAND]|[MSMTP-ARGS]" % sys.argv[0]
	print "\nCommands:"
	print "   --version   show version number"
	print "   --help      show this help message"
	print "   daemon      run the queue daemon"
	print "   display     display (list) queue state"
	print "   remove      remove a single mail from the queue"
	print "   purge       purge all mail from the queue"
	print "\nNote that only one command is allowed! msmtpq does not use
the -h option in\norder to be transparent to msmtp/sendmail."
	print "\nIf you want to send an email just work as you would with
msmtp (don't specify\nany of the above commands), but note that if
there is no internet connection\nany mstmp command gets enqueued (even
wrong ones)!"
	print "\nIf you want to do anything else than sending a mail, use
msmtp/sendmail directly!"
	print "\nNOTE: You should add 'msmtpq daemon' to /etc/rc.local (or by
other means\nconfigure your system to run the msmtpq daemon on
startup) in case there is mail\nin the queue and the system crashes/is
rebooted."

def log(string) :
	sys.stdout.write(time.strftime("%Y-%m-%d--%H-%M",time.localtime()) +
'  ' + string + '\n')
	sys.stdout.flush()

def enqueue_mail(mail) :
	queueitem = os.path.join(queuedir,time.strftime("%Y-%m-%d--%H-%M-%S",time.localtime()))
	if os.path.exists(queueitem) :
		f = open(logpath,"a+")
		f.write("ERROR : queue item %s already exists! appending mail
here:\n%s\n%s\n" % (queueitem,sys.argv,mail))
		f.close()
		sys.exit(os.EX_IOERR)
	else :
		try:
			f = open(queueitem,'w')
			f.write(" ".join(sys.argv)+"\n")
			f.write(mail)
			f.close()
			print "SUCCESS : queued mail as %s" % queueitem
			# get to be server if it isn't already running # FIXME
			if not already_running(title) : # this is nearly save because
daemons last action is to evaluate listdir > 0
				daemon()				   # we might wait a second before looking if daemon
is there to make it really safe
		except (IOError,os.error), e :
			f = open(logpath,"a+")
			f.write("ERROR : failed to queue item %s! appending mail
here:\n%s\n%s\n" % (queueitem,sys.argv,mail))
			f.close()
			
#######################################################################
COMMON LIBRARY FUNCTIONS ###

def set_process_title(title):
	import ctypes
	libc = ctypes.CDLL('libc.so.6')
	libc.prctl(15,title,0,0,0)
	# FreeBSD : libc.setproctitle('rsyncbackup')

def already_running(title,uid=None,gid=None):
	"""
	check if the process with the given title already exists.
	if it does, return a tuple of [[PID,UID,GID,CMD]...]
	else return []
	on error return False
	"""
	import os, subprocess
	try :
		p = subprocess.Popen(['ps','-C',title,'--no-headers','-o',",".join(['pid','uid','gid','cmd'])],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
		output = [each.split(None,3) for each in p.stdout.read().strip().split("\n")]
		if p.wait() != 0:
			if output != [[]]:
				print "WARNING : ps execution returned unexpected result! : %s" %
str(output)
				return False
			return []
		else:
			if uid != None :
				return [each for each in output if int(each[1]) == uid]
			elif gid != None :
				return [each for each in output if int(each[2]) == uid]
			else :
				return output
	except OSError, e :
		print "ERROR : could not execute ps! ",e
		return False

def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
    '''
    This forks the current process into a daemon.
    The stdin, stdout, and stderr arguments are file names that
    will be opened and be used to replace the standard file descriptors
    in sys.stdin, sys.stdout, and sys.stderr.
    These arguments are optional and default to /dev/null.
    Note that stderr is opened unbuffered, so
    if it shares a file with stdout then interleaved output
    may not appear in the order that you expect.
    '''

    # Do first fork.
    try:
        pid = os.fork()
        if pid > 0:
            sys.exit(0)   # Exit first parent.
    except OSError, e:
        sys.stderr.write ("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror) )
        sys.exit(1)

    # Decouple from parent environment.
    os.chdir("/")
    os.umask(0)
    os.setsid()

    # Do second fork.
    try:
        pid = os.fork()
        if pid > 0:
            sys.exit(0)   # Exit second parent.
    except OSError, e:
        sys.stderr.write ("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror) )
        sys.exit(1)

    # Now I am a daemon!

    # Redirect standard file descriptors.
    si = open(stdin, 'r')
    so = open(stdout, 'a+')
    se = open(stderr, 'a+', 0)
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())

#################################################################################
MODE FUNCTIONS ###

def version() :
	print "%s v.1.0 beta" % sys.argv[0]
	sys.exit(os.EX_OK)

def usage() :
	_usage()
	sys.exit(os.EX_OK)

def daemon() :
	set_process_title(title)
	daemonize('/dev/null',logpath,logpath)
	log('%s started as a Daemon with pid %d' % (__file__, os.getpid()))

	while len(os.listdir(queuedir)) > 0 :
		time.sleep(60)
		ping = os.popen('ping -qnc 1 209.85.135.111') #we use mx.google.com
but you could use just google.com : 64.233.183.147
		if ping.close() == None :
			# notify sysadmin that there was an outage
			try:
				p = os.popen('msmtp %s' % sysemail,'w')
				p.write("Subject: NOTE : there was an email outage!")
				p.close()
			except :
				log("ERROR : could not inform sysadmin of outage!")
			# process the queue
			for each in os.listdir(queuedir) :
				try :
					f = open(os.path.join(queuedir,each),'r')
					p = os.popen(f.readline().strip(),'w')
					p.write(f.read())
					f.close()
					if p.close() :
						log("ERROR : could not send %s from queue." % each)
					else :
						log("SUCCESS : sent %s from queue." % each)
					try :
						os.remove(os.path.join(queuedir,each))
					except (IOError,os.error), e :
							log("ERROR : could not remove %s from queue." % each)
				except (IOError,os.error), e :
						log("ERROR : could not send %s from queue." % each)

def display() :
	if len(os.listdir(queuedir)) > 0 :
		for each in os.listdir(queuedir) :
			try :
				f = open (os.path.join(queuedir,each),'r')
				info = re.findall('^(msmtp(\s+-[^\s])|To:|Cc:|Bcc:)(\s*[_a-zA-Z0-9-\.]+@[_a-zA-Z0-9-\.]+)+\s*$|^Subject:(.+)$',f.read(),
re.I | re.M)
				f.close()
				print "%s   %s | %s" % (each," ".join([ i[2].strip() for i in info
if i[0] != '' ])," ".join([ i[3].strip() for i in info if i[0] == ''
]))
			except (IOError,os.error), e :
					print "ERROR : could not open %s!" % each
		if not already_running(title) :
			print "msmtpq daemon was not running! spawning daemon now..."
			daemon()
	else :
		print "queue is empty"

def remove() :
	if len(sys.argv) < 3 :
		print "you must specify an item to remove! item id is of format :
%Y-%m-%d--%H-%M-%S"
		sys.exit(os.EX_USAGE)
	if len(os.listdir(queuedir)) > 0 :
		if os.path.isfile(os.path.join(queuedir,sys.argv[2])) :
			try:
				os.remove(os.path.join(queuedir,sys.argv[2]))
				print "removed %s from queue." % sys.argv[2]
			except (IOError,os.error), e :
					print "ERROR : could not remove %s!" % sys.argv[2]
		else :
			print "ERROR : %s is not in queue!" % sys.argv[2]
	else :
		print "ERROR : queue is empty"

def purge() :
	if raw_input("Will delete all mails in queue! Type 'yes' to confirm:
") == 'yes' :
		if len(os.listdir(queuedir)) > 0 :
			for each in os.listdir(queuedir) :
				try:
					os.remove(os.path.join(queuedir,each))
					print "removed %s from queue." % each
				except (IOError,os.error), e :
						print "ERROR : could not remove %s!" % each
		else :
			print "ERROR : queue is empty"
	else :
		print "leaving queue untouched..."

def send_mail(mail='') :
	while True:
		s = sys.stdin.read(1)
		mail += s
		if not s:
			break
	
	ping = os.popen('ping -qnc 1 209.85.135.111') #we use mx.google.com
but you could use just google.com : 64.233.183.147
	if ping.close() == None :
		try :
			p = os.popen(" ".join(sys.argv),'w')
		except (IOError,os.error), e :
			print "WARNING : error executing '%s'! trying to enqueue mail..." %
" ".join(sys.argv)
			enqueue_mail(mail)
			
		p.write(mail)
		if p.close() :
			print "WARNING : sending email was unsuccessful! trying to enqueue mail..."
			enqueue_mail(mail)
	else :
		print "WARNING : no route to the internet! trying to enqueue mail..."
		enqueue_mail(mail)

###########################################################################################
MAIN ###

if __name__ == "__main__":
	"""
	set global variables
	parse commandline
	select mode function
	"""
	# set global variables
	title = 'msmtpq'
	sys.argv[0] = 'msmtp' # so we can use sys.argv as if the user had
called msmtp directly
	logpath  = os.path.join(os.path.expanduser('/var/log/'),'msmtpq.log')
	queuedir = os.path.join(os.path.expanduser('/var/tmp/'),'msmtp.queue')
	sysemail = 'root@...18...'
	
	# msmtp and we need at least one argument
	if len(sys.argv) < 2 :
		usage()
		sys.exit(os.EX_USAGE)
		
	# these commands must work even if the queue is missing
	if sys.argv[1] =='--version':
		version()
	if sys.argv[1] =='--help' :
		usage()
		
	# check if queue dir exists (or can be created)
	if not os.path.isdir(queuedir) :
		try :
			os.makedirs(queuedir)
		except (IOError,os.error), e :
			print "ERROR : can't create queue dir : %s" % queuedir
			sys.exit(os.EX_IOERR)
	elif not os.access(queuedir,os.W_OK) :
		print "ERROR : can't write to queue dir : %s" % queuedir
		sys.exit(os.EX_IOERR)
		
	# ready to run !
	if sys.argv[1] in ('daemon','display','remove','purge') :
		eval(sys.argv[1]+'()')
	else :
		send_mail()
		
	sys.exit(os.EX_OK)