blob: f2e8ac46415ac3ef494f304d4f86fab74f7001fa [file] [log] [blame]
Sjoerd Mullender89686241995-10-19 09:35:31 +00001"""mailerdaemon - classes to parse mailer-daemon messages"""
2
3import string
4import rfc822
Guido van Rossum8e7a54f1996-07-21 02:50:30 +00005import calendar
Sjoerd Mullender89686241995-10-19 09:35:31 +00006import regex
7import os
8import sys
9
10Unparseable = 'mailerdaemon.Unparseable'
11
12class ErrorMessage(rfc822.Message):
13 def __init__(self, fp):
14 rfc822.Message.__init__(self, fp)
15
16 def is_warning(self):
17 sub = self.getheader('Subject')
18 if not sub:
19 return 0
20 sub = string.lower(sub)
Guido van Rossum8e7a54f1996-07-21 02:50:30 +000021 if sub[:12] == 'waiting mail': return 1
Sjoerd Mullender89686241995-10-19 09:35:31 +000022 if string.find(sub, 'warning') >= 0: return 1
Guido van Rossum8e7a54f1996-07-21 02:50:30 +000023 self.sub = sub
Sjoerd Mullender89686241995-10-19 09:35:31 +000024 return 0
25
26 def get_errors(self):
27 for p in EMPARSERS:
28 self.rewindbody()
29 try:
Guido van Rossum8e7a54f1996-07-21 02:50:30 +000030 return p(self.fp, self.sub)
Sjoerd Mullender89686241995-10-19 09:35:31 +000031 except Unparseable:
32 pass
33 raise Unparseable
34
35sendmail_pattern = regex.compile('[0-9][0-9][0-9] ')
Guido van Rossum8e7a54f1996-07-21 02:50:30 +000036def emparse_sendmail(fp, sub):
Sjoerd Mullender89686241995-10-19 09:35:31 +000037 while 1:
38 line = fp.readline()
39 if not line:
40 raise Unparseable
41 line = line[:-1]
42
43 # Check that we're not in the returned message yet
44 if string.lower(line)[:5] == 'from:':
45 raise Unparseable
46 line = string.split(line)
47 if len(line) > 3 and \
48 ((line[0] == '-----' and line[1] == 'Transcript') or
49 (line[0] == '---' and line[1] == 'The' and
50 line[2] == 'transcript') or
51 (line[0] == 'While' and line[1] == 'talking' and line[2] == 'to')):
52 # Yes, found it!
53 break
54
55 errors = []
56 found_a_line = 0
57 warnings = 0
58 while 1:
59 line = fp.readline()
60 if not line:
61 break
62 line = line[:-1]
63 if not line:
64 continue
Sjoerd Mullender89686241995-10-19 09:35:31 +000065 if sendmail_pattern.match(line) == 4:
66 # Yes, an error/warning line. Ignore 4, remember 5, stop on rest
67 if line[0] == '5':
68 errors.append(line)
69 elif line[0] == '4':
70 warnings = 1
71 else:
72 raise Unparseable
73 line = string.split(line)
74 if line and line[0][:3] == '---':
75 break
Guido van Rossum8e7a54f1996-07-21 02:50:30 +000076 found_a_line = 1
77 # special case for CWI sendmail
78 if len(line) > 1 and line[1] == 'Undelivered':
79 while 1:
80 line = fp.readline()
81 if not line:
82 break
83 line = string.strip(line)
84 if not line:
85 break
86 errors.append(line + ': ' + sub)
Sjoerd Mullender89686241995-10-19 09:35:31 +000087 # Empty transcripts are ok, others without an error are not.
88 if found_a_line and not (errors or warnings):
89 raise Unparseable
90 return errors
91
Guido van Rossum8e7a54f1996-07-21 02:50:30 +000092def emparse_cts(fp, sub):
Sjoerd Mullender89686241995-10-19 09:35:31 +000093 while 1:
94 line = fp.readline()
95 if not line:
96 raise Unparseable
97 line = line[:-1]
98
99 # Check that we're not in the returned message yet
100 if string.lower(line)[:5] == 'from:':
101 raise Unparseable
102 line = string.split(line)
103 if len(line) > 3 and line[0][:2] == '|-' and line[1] == 'Failed' \
104 and line[2] == 'addresses':
105 # Yes, found it!
106 break
107
108 errors = []
109 while 1:
110 line = fp.readline()
111 if not line:
112 break
113 line = line[:-1]
114 if not line:
115 continue
Sjoerd Mullender89686241995-10-19 09:35:31 +0000116 if line[:2] == '|-':
117 break
Jack Jansene48aa961995-11-10 14:56:16 +0000118 errors.append(line)
Sjoerd Mullender89686241995-10-19 09:35:31 +0000119 return errors
120
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000121def emparse_aol(fp, sub):
Sjoerd Mullender89686241995-10-19 09:35:31 +0000122 while 1:
123 line = fp.readline()
124 if not line:
125 raise Unparseable
126 line = line[:-1]
127 if line:
128 break
129 exp = 'The mail you sent could not be delivered to:'
130 if line[:len(exp)] != exp:
131 raise Unparseable
132 errors = []
133 while 1:
134 line = fp.readline()
135 if sendmail_pattern.match(line) == 4:
136 # Yes, an error/warning line. Ignore 4, remember 5, stop on rest
137 if line[0] == '5':
138 errors.append(line)
139 elif line[0] != '4':
140 raise Unparseable
141 elif line == '\n':
142 break
143 else:
144 raise Unparseable
145 return errors
Jack Jansen4ec940a1995-10-30 10:10:19 +0000146
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000147def emparse_compuserve(fp, sub):
Jack Jansen4ec940a1995-10-30 10:10:19 +0000148 while 1:
149 line = fp.readline()
150 if not line:
151 raise Unparseable
152 line = line[:-1]
153 if line:
154 break
155 exp = 'Your message could not be delivered for the following reason:'
156 if line[:len(exp)] != exp:
157 raise Unparseable
158 errors = []
159 while 1:
160 line = fp.readline()
161 if not line: break
162 if line[:3] == '---': break
163 line = line[:-1]
164 if not line: continue
165 if line == 'Please resend your message at a later time.':
166 continue
167 line = 'Compuserve: ' + line
168 errors.append(line)
169 return errors
Sjoerd Mullender89686241995-10-19 09:35:31 +0000170
Jack Jansen81299f11995-10-30 10:23:10 +0000171prov_pattern = regex.compile('.* | \(.*\)')
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000172def emparse_providence(fp, sub):
Jack Jansen81299f11995-10-30 10:23:10 +0000173 while 1:
174 line = fp.readline()
175 if not line:
176 raise Unparseable
177 line = line[:-1]
Sjoerd Mullender89686241995-10-19 09:35:31 +0000178
Jack Jansen81299f11995-10-30 10:23:10 +0000179 # Check that we're not in the returned message yet
180 if string.lower(line)[:5] == 'from:':
181 raise Unparseable
182 exp = 'The following errors occurred'
183 if line[:len(exp)] == exp:
184 break
185
186 errors = []
187 while 1:
188 line = fp.readline()
189 if not line:
190 break
191 line = line[:-1]
192 if not line:
193 continue
194 if line[:4] == '----':
195 break
196 if prov_pattern.match(line) > 0:
197 errors.append(prov_pattern.group(1))
198
199 if not errors:
200 raise Unparseable
201 return errors
202
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000203def emparse_x400(fp, sub):
204 exp = 'This report relates to your message:'
205 while 1:
206 line = fp.readline()
207 if not line:
208 raise Unparseable
209 line = line[:-1]
210
211 # Check that we're not in the returned message yet
212 if string.lower(line)[:5] == 'from:':
213 raise Unparseable
214 if line[:len(exp)] == exp:
215 break
216
217 errors = []
218 exp = 'Your message was not delivered to'
219 while 1:
220 line = fp.readline()
221 if not line:
222 break
223 line = line[:-1]
224 if not line:
225 continue
226 if line[:len(exp)] == exp:
227 error = string.strip(line[len(exp):])
228 sep = ': '
229 while 1:
230 line = fp.readline()
231 if not line:
232 break
233 line = line[:-1]
234 if not line:
235 break
236 if line[0] == ' ' and line[-1] != ':':
237 error = error + sep + string.strip(line)
238 sep = '; '
239 errors.append(error)
240 return errors
241 raise Unparseable
242
243def emparse_passau(fp, sub):
244 exp = 'Unable to deliver message because'
245 while 1:
246 line = fp.readline()
247 if not line:
248 raise Unparseable
249 if string.lower(line)[:5] == 'from:':
250 raise Unparseable
251 if line[:len(exp)] == exp:
252 break
253
254 errors = []
255 exp = 'Returned Text follows'
256 while 1:
257 line = fp.readline()
258 if not line:
259 raise Unparseable
260 line = line[:-1]
261 # Check that we're not in the returned message yet
262 if string.lower(line)[:5] == 'from:':
263 raise Unparseable
264 if not line:
265 continue
266 if line[:len(exp)] == exp:
267 return errors
268 errors.append(string.strip(line))
269
Jack Jansen81299f11995-10-30 10:23:10 +0000270EMPARSERS = [emparse_sendmail, emparse_aol, emparse_cts, emparse_compuserve,
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000271 emparse_providence, emparse_x400, emparse_passau]
272
273def sort_numeric(a, b):
274 a = string.atoi(a)
275 b = string.atoi(b)
276 if a < b: return -1
277 elif a > b: return 1
278 else: return 0
Sjoerd Mullender89686241995-10-19 09:35:31 +0000279
280def parsedir(dir, modify):
281 os.chdir(dir)
Sjoerd Mullender89686241995-10-19 09:35:31 +0000282 pat = regex.compile('^[0-9]*$')
283 errordict = {}
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000284 errorfirst = {}
Sjoerd Mullender89686241995-10-19 09:35:31 +0000285 errorlast = {}
286 nok = nwarn = nbad = 0
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000287
288 # find all numeric file names and sort them
289 files = filter(lambda fn, pat=pat: pat.match(fn) > 0, os.listdir('.'))
290 files.sort(sort_numeric)
Sjoerd Mullender89686241995-10-19 09:35:31 +0000291
292 for fn in files:
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000293 # Lets try to parse the file.
294 fp = open(fn)
295 m = ErrorMessage(fp)
296 sender = m.getaddr('From')
297 print '%s\t%-40s\t'%(fn, sender[1]),
Sjoerd Mullender89686241995-10-19 09:35:31 +0000298
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000299 if m.is_warning():
300 print 'warning only'
301 nwarn = nwarn + 1
Sjoerd Mullender89686241995-10-19 09:35:31 +0000302 if modify:
303 os.unlink(fn)
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000304 continue
305
306 try:
307 errors = m.get_errors()
308 except Unparseable:
309 print '** Not parseable'
310 nbad = nbad + 1
311 continue
312 print len(errors), 'errors'
313
314 # Remember them
315 for e in errors:
316 try:
317 mm, dd = m.getdate('date')[1:1+2]
318 date = '%s %02d' % (calendar.month_abbr[mm], dd)
319 except:
320 date = '??????'
321 if not errordict.has_key(e):
322 errordict[e] = 1
323 errorfirst[e] = '%s (%s)' % (fn, date)
324 else:
325 errordict[e] = errordict[e] + 1
326 errorlast[e] = '%s (%s)' % (fn, date)
327
328 nok = nok + 1
329 if modify:
330 os.unlink(fn)
Sjoerd Mullender89686241995-10-19 09:35:31 +0000331
332 print '--------------'
333 print nok, 'files parsed,',nwarn,'files warning-only,',
334 print nbad,'files unparseable'
335 print '--------------'
336 for e in errordict.keys():
Guido van Rossum8e7a54f1996-07-21 02:50:30 +0000337 print errordict[e], errorfirst[e], '-', errorlast[e], '\t', e
Sjoerd Mullender89686241995-10-19 09:35:31 +0000338
339def main():
340 modify = 0
341 if len(sys.argv) > 1 and sys.argv[1] == '-d':
342 modify = 1
343 del sys.argv[1]
344 if len(sys.argv) > 1:
345 for folder in sys.argv[1:]:
346 parsedir(folder, modify)
347 else:
348 parsedir('/ufs/jack/Mail/errorsinbox', modify)
349
350if __name__ == '__main__' or sys.argv[0] == __name__:
351 main()