blob: 245f670217a86da38df4fdef66cc68a2ee91af97 [file] [log] [blame]
Sjoerd Mullender89686241995-10-19 09:35:31 +00001"""mailerdaemon - classes to parse mailer-daemon messages"""
2
3import string
4import rfc822
5import regex
6import os
7import sys
8
9Unparseable = 'mailerdaemon.Unparseable'
10
11class ErrorMessage(rfc822.Message):
12 def __init__(self, fp):
13 rfc822.Message.__init__(self, fp)
14
15 def is_warning(self):
16 sub = self.getheader('Subject')
17 if not sub:
18 return 0
19 sub = string.lower(sub)
20 if sub == 'waiting mail': return 1
21 if string.find(sub, 'warning') >= 0: return 1
22 return 0
23
24 def get_errors(self):
25 for p in EMPARSERS:
26 self.rewindbody()
27 try:
28 return p(self.fp)
29 except Unparseable:
30 pass
31 raise Unparseable
32
33sendmail_pattern = regex.compile('[0-9][0-9][0-9] ')
34def emparse_sendmail(fp):
35 while 1:
36 line = fp.readline()
37 if not line:
38 raise Unparseable
39 line = line[:-1]
40
41 # Check that we're not in the returned message yet
42 if string.lower(line)[:5] == 'from:':
43 raise Unparseable
44 line = string.split(line)
45 if len(line) > 3 and \
46 ((line[0] == '-----' and line[1] == 'Transcript') or
47 (line[0] == '---' and line[1] == 'The' and
48 line[2] == 'transcript') or
49 (line[0] == 'While' and line[1] == 'talking' and line[2] == 'to')):
50 # Yes, found it!
51 break
52
53 errors = []
54 found_a_line = 0
55 warnings = 0
56 while 1:
57 line = fp.readline()
58 if not line:
59 break
60 line = line[:-1]
61 if not line:
62 continue
63 found_a_line = 1
64 if sendmail_pattern.match(line) == 4:
65 # Yes, an error/warning line. Ignore 4, remember 5, stop on rest
66 if line[0] == '5':
67 errors.append(line)
68 elif line[0] == '4':
69 warnings = 1
70 else:
71 raise Unparseable
72 line = string.split(line)
73 if line and line[0][:3] == '---':
74 break
75 # Empty transcripts are ok, others without an error are not.
76 if found_a_line and not (errors or warnings):
77 raise Unparseable
78 return errors
79
80def emparse_cts(fp):
81 while 1:
82 line = fp.readline()
83 if not line:
84 raise Unparseable
85 line = line[:-1]
86
87 # Check that we're not in the returned message yet
88 if string.lower(line)[:5] == 'from:':
89 raise Unparseable
90 line = string.split(line)
91 if len(line) > 3 and line[0][:2] == '|-' and line[1] == 'Failed' \
92 and line[2] == 'addresses':
93 # Yes, found it!
94 break
95
96 errors = []
97 while 1:
98 line = fp.readline()
99 if not line:
100 break
101 line = line[:-1]
102 if not line:
103 continue
Sjoerd Mullender89686241995-10-19 09:35:31 +0000104 if line[:2] == '|-':
105 break
Jack Jansene48aa961995-11-10 14:56:16 +0000106 errors.append(line)
Sjoerd Mullender89686241995-10-19 09:35:31 +0000107 return errors
108
109def emparse_aol(fp):
110 while 1:
111 line = fp.readline()
112 if not line:
113 raise Unparseable
114 line = line[:-1]
115 if line:
116 break
117 exp = 'The mail you sent could not be delivered to:'
118 if line[:len(exp)] != exp:
119 raise Unparseable
120 errors = []
121 while 1:
122 line = fp.readline()
123 if sendmail_pattern.match(line) == 4:
124 # Yes, an error/warning line. Ignore 4, remember 5, stop on rest
125 if line[0] == '5':
126 errors.append(line)
127 elif line[0] != '4':
128 raise Unparseable
129 elif line == '\n':
130 break
131 else:
132 raise Unparseable
133 return errors
Jack Jansen4ec940a1995-10-30 10:10:19 +0000134
135def emparse_compuserve(fp):
136 while 1:
137 line = fp.readline()
138 if not line:
139 raise Unparseable
140 line = line[:-1]
141 if line:
142 break
143 exp = 'Your message could not be delivered for the following reason:'
144 if line[:len(exp)] != exp:
145 raise Unparseable
146 errors = []
147 while 1:
148 line = fp.readline()
149 if not line: break
150 if line[:3] == '---': break
151 line = line[:-1]
152 if not line: continue
153 if line == 'Please resend your message at a later time.':
154 continue
155 line = 'Compuserve: ' + line
156 errors.append(line)
157 return errors
Sjoerd Mullender89686241995-10-19 09:35:31 +0000158
Jack Jansen81299f11995-10-30 10:23:10 +0000159prov_pattern = regex.compile('.* | \(.*\)')
Sjoerd Mullender89686241995-10-19 09:35:31 +0000160
Jack Jansen81299f11995-10-30 10:23:10 +0000161def emparse_providence(fp):
162 while 1:
163 line = fp.readline()
164 if not line:
165 raise Unparseable
166 line = line[:-1]
Sjoerd Mullender89686241995-10-19 09:35:31 +0000167
Jack Jansen81299f11995-10-30 10:23:10 +0000168 # Check that we're not in the returned message yet
169 if string.lower(line)[:5] == 'from:':
170 raise Unparseable
171 exp = 'The following errors occurred'
172 if line[:len(exp)] == exp:
173 break
174
175 errors = []
176 while 1:
177 line = fp.readline()
178 if not line:
179 break
180 line = line[:-1]
181 if not line:
182 continue
183 if line[:4] == '----':
184 break
185 if prov_pattern.match(line) > 0:
186 errors.append(prov_pattern.group(1))
187
188 if not errors:
189 raise Unparseable
190 return errors
191
192EMPARSERS = [emparse_sendmail, emparse_aol, emparse_cts, emparse_compuserve,
193 emparse_providence]
Sjoerd Mullender89686241995-10-19 09:35:31 +0000194
195def parsedir(dir, modify):
196 os.chdir(dir)
197 files = os.listdir('.')
198 pat = regex.compile('^[0-9]*$')
199 errordict = {}
200 errorlast = {}
201 nok = nwarn = nbad = 0
202
203 for fn in files:
204 if pat.match(fn) > 0:
205 # Ok, so it's a numeric filename. Lets try to parse it.
206 fp = open(fn)
207 m = ErrorMessage(fp)
208 sender = m.getaddr('From')
209 print '%s\t%-40s\t'%(fn, sender[1]),
210
211 if m.is_warning():
212 print 'warning only'
213 nwarn = nwarn + 1
214 if modify:
215 os.unlink(fn)
216 continue
217
218 try:
219 errors = m.get_errors()
220 except Unparseable:
221 print '** Not parseable'
222 nbad = nbad + 1
223 continue
224 print len(errors), 'errors'
225
226 # Remember them
227 for e in errors:
228 if not errordict.has_key(e):
229 errordict[e] = 1
230 else:
231 errordict[e] = errordict[e] + 1
232 errorlast[e] = fn
233
234 nok = nok + 1
235 if modify:
236 os.unlink(fn)
237
238 print '--------------'
239 print nok, 'files parsed,',nwarn,'files warning-only,',
240 print nbad,'files unparseable'
241 print '--------------'
242 for e in errordict.keys():
243 print errordict[e], '\t', errorlast[e], '\t', e
244
245def main():
246 modify = 0
247 if len(sys.argv) > 1 and sys.argv[1] == '-d':
248 modify = 1
249 del sys.argv[1]
250 if len(sys.argv) > 1:
251 for folder in sys.argv[1:]:
252 parsedir(folder, modify)
253 else:
254 parsedir('/ufs/jack/Mail/errorsinbox', modify)
255
256if __name__ == '__main__' or sys.argv[0] == __name__:
257 main()