Script to read and parse error messages from mailer daemons.
Written by Jack, some minor changes by Sjoerd.
diff --git a/Tools/scripts/mailerdaemon.py b/Tools/scripts/mailerdaemon.py
new file mode 100755
index 0000000..b39760e4
--- /dev/null
+++ b/Tools/scripts/mailerdaemon.py
@@ -0,0 +1,201 @@
+"""mailerdaemon - classes to parse mailer-daemon messages"""
+
+import string
+import rfc822
+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 == 'waiting mail': return 1
+	if string.find(sub, 'warning') >= 0: return 1
+	return 0
+
+    def get_errors(self):
+	for p in EMPARSERS:
+	    self.rewindbody()
+	    try:
+		return p(self.fp)
+	    except Unparseable:
+		pass
+	raise Unparseable
+
+sendmail_pattern = regex.compile('[0-9][0-9][0-9] ')
+def emparse_sendmail(fp):
+    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
+	found_a_line = 1
+	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
+    # 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):
+    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
+	errors.append(line)
+	if line[:2] == '|-':
+	    break
+    return errors
+
+def emparse_aol(fp):
+    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
+
+
+
+EMPARSERS = [emparse_sendmail, emparse_aol, emparse_cts]
+
+def parsedir(dir, modify):
+    os.chdir(dir)
+    files = os.listdir('.')
+    pat = regex.compile('^[0-9]*$')
+    errordict = {}
+    errorlast = {}
+    nok = nwarn = nbad = 0
+    
+    for fn in files:
+	if pat.match(fn) > 0:
+	    # Ok, so it's a numeric filename. Lets try to parse it.
+	    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:
+		if not errordict.has_key(e):
+		    errordict[e] = 1
+		else:
+		    errordict[e] = errordict[e] + 1
+		errorlast[e] = fn
+
+	    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], '\t', 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()