home *** CD-ROM | disk | FTP | other *** search
- """mailerdaemon - classes to parse mailer-daemon messages"""
-
- import string
- import rfc822
- import calendar
- import regex
- import os
- import sys
-
- Unparseable = 'mailerdaemon.Unparseable'
-
- class ErrorMessage(rfc822.Message):
- def __init__(self, fp):
- rfc822.Message.__init__(self, fp)
-
- def is_warning(self):
- sub = self.getheader('Subject')
- if not sub:
- return 0
- sub = string.lower(sub)
- if sub[:12] == 'waiting mail': return 1
- if string.find(sub, 'warning') >= 0: return 1
- self.sub = sub
- return 0
-
- def get_errors(self):
- for p in EMPARSERS:
- self.rewindbody()
- try:
- return p(self.fp, self.sub)
- except Unparseable:
- pass
- raise Unparseable
-
- sendmail_pattern = regex.compile('[0-9][0-9][0-9] ')
- def emparse_sendmail(fp, sub):
- while 1:
- line = fp.readline()
- if not line:
- raise Unparseable
- line = line[:-1]
-
- # Check that we're not in the returned message yet
- if string.lower(line)[:5] == 'from:':
- raise Unparseable
- line = string.split(line)
- if len(line) > 3 and \
- ((line[0] == '-----' and line[1] == 'Transcript') or
- (line[0] == '---' and line[1] == 'The' and
- line[2] == 'transcript') or
- (line[0] == 'While' and line[1] == 'talking' and line[2] == 'to')):
- # Yes, found it!
- break
-
- errors = []
- found_a_line = 0
- warnings = 0
- while 1:
- line = fp.readline()
- if not line:
- break
- line = line[:-1]
- if not line:
- continue
- if sendmail_pattern.match(line) == 4:
- # Yes, an error/warning line. Ignore 4, remember 5, stop on rest
- if line[0] == '5':
- errors.append(line)
- elif line[0] == '4':
- warnings = 1
- else:
- raise Unparseable
- line = string.split(line)
- if line and line[0][:3] == '---':
- break
- found_a_line = 1
- # special case for CWI sendmail
- if len(line) > 1 and line[1] == 'Undelivered':
- while 1:
- line = fp.readline()
- if not line:
- break
- line = string.strip(line)
- if not line:
- break
- errors.append(line + ': ' + sub)
- # Empty transcripts are ok, others without an error are not.
- if found_a_line and not (errors or warnings):
- raise Unparseable
- return errors
-
- def emparse_cts(fp, sub):
- while 1:
- line = fp.readline()
- if not line:
- raise Unparseable
- line = line[:-1]
-
- # Check that we're not in the returned message yet
- if string.lower(line)[:5] == 'from:':
- raise Unparseable
- line = string.split(line)
- if len(line) > 3 and line[0][:2] == '|-' and line[1] == 'Failed' \
- and line[2] == 'addresses':
- # Yes, found it!
- break
-
- errors = []
- while 1:
- line = fp.readline()
- if not line:
- break
- line = line[:-1]
- if not line:
- continue
- if line[:2] == '|-':
- break
- errors.append(line)
- return errors
-
- def emparse_aol(fp, sub):
- while 1:
- line = fp.readline()
- if not line:
- raise Unparseable
- line = line[:-1]
- if line:
- break
- exp = 'The mail you sent could not be delivered to:'
- if line[:len(exp)] != exp:
- raise Unparseable
- errors = []
- while 1:
- line = fp.readline()
- if sendmail_pattern.match(line) == 4:
- # Yes, an error/warning line. Ignore 4, remember 5, stop on rest
- if line[0] == '5':
- errors.append(line)
- elif line[0] != '4':
- raise Unparseable
- elif line == '\n':
- break
- else:
- raise Unparseable
- return errors
-
- def emparse_compuserve(fp, sub):
- while 1:
- line = fp.readline()
- if not line:
- raise Unparseable
- line = line[:-1]
- if line:
- break
- exp = 'Your message could not be delivered for the following reason:'
- if line[:len(exp)] != exp:
- raise Unparseable
- errors = []
- while 1:
- line = fp.readline()
- if not line: break
- if line[:3] == '---': break
- line = line[:-1]
- if not line: continue
- if line == 'Please resend your message at a later time.':
- continue
- line = 'Compuserve: ' + line
- errors.append(line)
- return errors
-
- prov_pattern = regex.compile('.* | \(.*\)')
- def emparse_providence(fp, sub):
- while 1:
- line = fp.readline()
- if not line:
- raise Unparseable
- line = line[:-1]
-
- # Check that we're not in the returned message yet
- if string.lower(line)[:5] == 'from:':
- raise Unparseable
- exp = 'The following errors occurred'
- if line[:len(exp)] == exp:
- break
-
- errors = []
- while 1:
- line = fp.readline()
- if not line:
- break
- line = line[:-1]
- if not line:
- continue
- if line[:4] == '----':
- break
- if prov_pattern.match(line) > 0:
- errors.append(prov_pattern.group(1))
-
- if not errors:
- raise Unparseable
- return errors
-
- def emparse_x400(fp, sub):
- exp = 'This report relates to your message:'
- while 1:
- line = fp.readline()
- if not line:
- raise Unparseable
- line = line[:-1]
-
- # Check that we're not in the returned message yet
- if string.lower(line)[:5] == 'from:':
- raise Unparseable
- if line[:len(exp)] == exp:
- break
-
- errors = []
- exp = 'Your message was not delivered to'
- while 1:
- line = fp.readline()
- if not line:
- break
- line = line[:-1]
- if not line:
- continue
- if line[:len(exp)] == exp:
- error = string.strip(line[len(exp):])
- sep = ': '
- while 1:
- line = fp.readline()
- if not line:
- break
- line = line[:-1]
- if not line:
- break
- if line[0] == ' ' and line[-1] != ':':
- error = error + sep + string.strip(line)
- sep = '; '
- errors.append(error)
- return errors
- raise Unparseable
-
- def emparse_passau(fp, sub):
- exp = 'Unable to deliver message because'
- while 1:
- line = fp.readline()
- if not line:
- raise Unparseable
- if string.lower(line)[:5] == 'from:':
- raise Unparseable
- if line[:len(exp)] == exp:
- break
-
- errors = []
- exp = 'Returned Text follows'
- while 1:
- line = fp.readline()
- if not line:
- raise Unparseable
- line = line[:-1]
- # Check that we're not in the returned message yet
- if string.lower(line)[:5] == 'from:':
- raise Unparseable
- if not line:
- continue
- if line[:len(exp)] == exp:
- return errors
- errors.append(string.strip(line))
-
- EMPARSERS = [emparse_sendmail, emparse_aol, emparse_cts, emparse_compuserve,
- emparse_providence, emparse_x400, emparse_passau]
-
- def sort_numeric(a, b):
- a = string.atoi(a)
- b = string.atoi(b)
- if a < b: return -1
- elif a > b: return 1
- else: return 0
-
- def parsedir(dir, modify):
- os.chdir(dir)
- pat = regex.compile('^[0-9]*$')
- errordict = {}
- errorfirst = {}
- errorlast = {}
- nok = nwarn = nbad = 0
-
- # find all numeric file names and sort them
- files = filter(lambda fn, pat=pat: pat.match(fn) > 0, os.listdir('.'))
- files.sort(sort_numeric)
-
- for fn in files:
- # Lets try to parse the file.
- fp = open(fn)
- m = ErrorMessage(fp)
- sender = m.getaddr('From')
- print '%s\t%-40s\t'%(fn, sender[1]),
-
- if m.is_warning():
- print 'warning only'
- nwarn = nwarn + 1
- if modify:
- os.unlink(fn)
- continue
-
- try:
- errors = m.get_errors()
- except Unparseable:
- print '** Not parseable'
- nbad = nbad + 1
- continue
- print len(errors), 'errors'
-
- # Remember them
- for e in errors:
- try:
- mm, dd = m.getdate('date')[1:1+2]
- date = '%s %02d' % (calendar.month_abbr[mm], dd)
- except:
- date = '??????'
- if not errordict.has_key(e):
- errordict[e] = 1
- errorfirst[e] = '%s (%s)' % (fn, date)
- else:
- errordict[e] = errordict[e] + 1
- errorlast[e] = '%s (%s)' % (fn, date)
-
- nok = nok + 1
- if modify:
- os.unlink(fn)
-
- print '--------------'
- print nok, 'files parsed,',nwarn,'files warning-only,',
- print nbad,'files unparseable'
- print '--------------'
- for e in errordict.keys():
- print errordict[e], errorfirst[e], '-', errorlast[e], '\t', e
-
- def main():
- modify = 0
- if len(sys.argv) > 1 and sys.argv[1] == '-d':
- modify = 1
- del sys.argv[1]
- if len(sys.argv) > 1:
- for folder in sys.argv[1:]:
- parsedir(folder, modify)
- else:
- parsedir('/ufs/jack/Mail/errorsinbox', modify)
-
- if __name__ == '__main__' or sys.argv[0] == __name__:
- main()
-