| Request | MaildropHost -- feature request -- by Maik Jablonski |
| Posted on | Sep 22, 2004 5:07 pm |
| Subscribe |
| Resolve by Jens Vagelpohl on Sep 24, 2004 4:10 am | |
|
Thanks a lot Maik - the patch is applied and version 1.8 has just been released. jens |
|
|
|
| Comment by Maik Jablonski on Sep 24, 2004 3:52 am | |
|
Hi, anbei der "unified" diff gegen die aktuelle Version aus dem CVS und zur Sicherheit nochmal die geänderte maildrop.py, damit Du evtl. selbst nochmal einen diff machen kannst. Vielleicht lerne ich das ja nochmal eines Tages mit den diffs..;) Die Variante läuft bei mir jetzt seit ein paar Tagen ohne Probleme auf meinem Server (mit Sendmail). Ob andere MTAs da genauso reibungsfrei mit arbeiten, würde ich jetzt mal schätzen, weil eigentlich Fehlercodes aus dem 500-Raum einheitlich als "unauflösbar problematisch" in den RFCs definiert sind. Auf jeden Fall bin ich jetzt haufenweise Spam und Unsinn aus den Logs los, der sonst alle 2 Minuten wieder und wieder versucht wurde...:) Grüße, Maik -- Maik Jablonski - Universität Bielefeld - Zentrum für Lehrerbildung http://www.zfl.uni-bielefeld.de/personal/mjablonski/ phone://+49.(0)521.106.4234 --- /mnt/d/downloads/maildrop.py 2004-09-22 23:02:55.197974400 +0200 +++ maildrop.py 2004-09-22 23:00:39.252494400 +0200 @@ -8,7 +8,7 @@ # LICENSE.txt for the terms of this license. # ##################################################################### -__version__ = "$Revision: 1.9 $"[11:-2] +__version__ = "$Revision: 1.8 $"[11:-2] usage = """ Maildrop service startup file @@ -50,6 +50,8 @@ MAILDROP_BATCH = 0 MaildropError = 'Maildrop Error' +FATAL_ERROR_CODES = ["500","501","502","503","504","550","551","553"] + try: if sys.version.split()[0] < '2.1': raise MaildropError, 'Invalid python version %s' % sys.version.split[0] @@ -229,6 +231,12 @@ smtp_server.sendmail( h_from, h_to_list, h_body ) stat = 'OK' os.remove( mail_dict.get( 'file_path' ) ) + except smtplib.SMTPRecipientsRefused, e: + for (addr, error) in e.recipients.items(): + stat = 'FATAL: ', str(e) + if str(error[0]) in FATAL_ERROR_CODES: + os.remove( mail_dict.get( 'file_path' ) ) + break except smtplib.SMTPException, e: stat = 'BAD: ', str(e) #!/usr/bin/env python2.1 ##################################################################### # # maildrop A daemon to handle mail delivery for the MaildropHost # # This software is governed by a license. See # LICENSE.txt for the terms of this license. # ##################################################################### __version__ = "$Revision: 1.8 $"[11:-2] usage = """ Maildrop service startup file Usage: maildrop.py [options] Options: - d Debug mode: All output will be written to the terminal - h Maildrop home (Must be specified!) - s SMTP host to be used (Must be specified!) - p SMTP port to be used (defaults to 25) - i Polling interval in seconds - b Drop only x mails in one smtp-connection (defaults to 0 = all mails at once) """ import getopt, smtplib, os, sys, time, rfc822 DEBUG = 0 MAILDROP_INTERVAL = 120 SMTP_PORT = 25 MAILDROP_BATCH = 0 MaildropError = 'Maildrop Error' FATAL_ERROR_CODES = ["500","501","502","503","504","550","551","553"] try: if sys.version.split()[0] < '2.1': raise MaildropError, 'Invalid python version %s' % sys.version.split[0] if len( sys.argv ) < 3: print usage opts, args = getopt.getopt( sys.argv[1:], 'h:i:s:d:b:p:' ) for o_key, o_val in opts: if o_key == '-h': MAILDROP_HOME = o_val if not os.path.isdir( MAILDROP_HOME ): raise MaildropError, 'Invalid maildrop home "%s"' % o_val MAILDROP_SPOOL = os.path.join( MAILDROP_HOME, 'spool' ) if not os.path.isdir( MAILDROP_SPOOL ): os.mkdir( MAILDROP_SPOOL ) log_home = os.path.join( MAILDROP_HOME, 'var' ) if not os.path.isdir( log_home ): os.mkdir( log_home ) LOG_FILE = os.path.join( log_home, 'maildrop.log' ) if o_key == '-s': SMTP_HOST = o_val if o_key == '-p': try: SMTP_PORT = int(o_val) except ValueError: SMTP_PORT = 25 if o_key == '-i': try: MAILDROP_INTERVAL = int( o_val ) except: msg = 'Invalid Maildrop interval "%s"' % str( o_val ) raise MaildropError, msg if o_key == '-b': try: MAILDROP_BATCH = int(o_val) except ValueError: MAILDROP_BATCH = 0 if o_key == '-d': if o_val not in (0, '0', 'False'): DEBUG = 1 try: mail_server = smtplib.SMTP( SMTP_HOST, SMTP_PORT ) mail_server.quit() except: msg = 'Invalid SMTP server "%s:%d"' % (SMTP_HOST, SMTP_PORT) raise MaildropError, msg except SystemExit: sys.exit( 0 ) except: print usage print "%s: %s" % ( sys.exc_type, sys.exc_value ) sys.exit( 1 ) try: ppid, pid = os.getppid(), os.getpid() if ppid == 1: ppid = pid except: pass # getpid not supported else: pid_home = os.path.join( MAILDROP_HOME, 'var' ) if not os.path.isdir( pid_home ): os.mkdir( pid_home ) pidfile_path = os.path.join( pid_home, 'maildrop.pid' ) pid_file = open( pidfile_path, 'w' ) pid_file.write( '%s %s' % ( pid, ppid ) ) pid_file.close() if DEBUG: # PID still might not exist so it's inside a try try: print 'Maildrop started with PID %s' % str( pid ) except: print 'Maildrop started with unknown PID' while 1: # Are there any files in the spool directory? to_be_sent = [] all_files = os.listdir( MAILDROP_SPOOL ) if len( all_files ) > 0: for file_name in all_files: f_name, f_ext = os.path.splitext( file_name ) # Exclude lock files if f_ext == '.lck': continue # Exclude files that have locked files if f_name + '.lck' in all_files: continue # Exclude directories file_path = os.path.join( MAILDROP_SPOOL, file_name ) if os.path.isdir( file_path ): continue # Read in file file_handle = open( file_path, 'r' ) file_contents = file_handle.read() file_handle.close() # Is this a real mail turd? if not file_contents.startswith( '##To:' ): continue # Parse and handle content (mail it out) mail_dict = {} mail_dict['file_path'] = file_path file_lines = file_contents.split( '\n' ) for i in range( len( file_lines ) ): if file_lines[i].startswith( '##' ): header_line = file_lines[i][2:] header_key, header_val = header_line.split( ':', 1 ) mail_dict[header_key] = header_val else: mail_dict['body'] = '\n'.join( file_lines[i:] ) break to_be_sent.append( mail_dict ) if len( to_be_sent ) > 0: # Open the log file time_stamp = time.strftime( '%Y/%m/%d %H:%M:%S' ) log_file = open( LOG_FILE, 'a' ) msg = '\n### Started at %s...' % time_stamp log_file.write( msg ) if DEBUG: print msg while len( to_be_sent ) > 0: if (MAILDROP_BATCH == 0) or (MAILDROP_BATCH > len(to_be_sent)): batch = len(to_be_sent) else: batch = MAILDROP_BATCH # Send mail try: smtp_server = smtplib.SMTP( SMTP_HOST, SMTP_PORT ) except smtplib.SMTPConnectError: # SMTP server did not respond. Log it and stop processing. time_stamp = time.strftime( '%Y/%m/%d %H:%M:%S' ) err_msg = '!!!!! Connection error at %s' % time_stamp finish_msg = '### Finished at %s' % time_stamp log_file.write( err_msg ) if DEBUG: print err_msg log_file.write( finish_msg ) if DEBUG: print finish_msg log_file.close() break for mail_dict in to_be_sent[0:batch]: # Create mail and send it off h_from = mail_dict.get( 'From' ) h_to = mail_dict.get( 'To' ) h_to_list = [] for item in rfc822.AddressList(h_to): h_to_list.append(item[1]) h_body = mail_dict.get( 'body' ) try: smtp_server.sendmail( h_from, h_to_list, h_body ) stat = 'OK' os.remove( mail_dict.get( 'file_path' ) ) except smtplib.SMTPRecipientsRefused, e: for (addr, error) in e.recipients.items(): stat = 'FATAL: ', str(e) if str(error[0]) in FATAL_ERROR_CODES: os.remove( mail_dict.get( 'file_path' ) ) break except smtplib.SMTPException, e: stat = 'BAD: ', str(e) mail_msg = '\n%s\t %s' % ( stat, h_to ) log_file.write( mail_msg ) if DEBUG: print mail_msg log_file.flush() to_be_sent = to_be_sent[batch:] smtp_server.quit() time_stamp = time.strftime( '%Y/%m/%d %H:%M:%S' ) finish_msg = '\n### Finished at %s\n' % time_stamp log_file.write( finish_msg ) if DEBUG: print finish_msg log_file.close() time.sleep( MAILDROP_INTERVAL ) |
|
|
|
| Accept by Jens Vagelpohl on Sep 24, 2004 3:39 am | |
|
Hi Maik, Could you send me a complete unified diff (do "cvs diff -u maildrop.py") that is usable as input for the patch utility? This diff cannot be used for that and if I type this stuff in manually I *will* mistype somehow. Since there are no real "tests" in the package and I want to release it as a new version with this feature please make sure I get a precise diff against CVS HEAD, preferably from a working installation that has been tested by you. jens |
|
|
|
| Initial Request by Maik Jablonski on Sep 22, 2004 5:07 pm | |
|
Sometimes mails with unresolvable recipients make it into the maildrop-queue. If the mail cannot be delivered because the mail-server can't handle the adress (because it's syntactically incorrect like horst.köhler@domain.com), the mail will be requeued and resent again and again. The following patch to maildrop/maildrop.py fixes this for fatal error codes from the smtp-server. 52a53,54 > FATAL_ERROR_CODES = ["500","501","502","503","504","550","551","553"] > 231a234,239 > except smtplib.SMTPRecipientsRefused, e: > for (addr, error) in e.recipients.items(): > stat = 'FATAL: ', str(e) > if str(error[0]) in FATAL_ERROR_CODES: > os.remove( mail_dict.get( 'file_path' ) ) > break |