blob: 163fadafd4ac7e26f7650b0ed71f44d37bc92c6a [file] [log] [blame]
Barry Warsaw61532012006-01-17 05:58:08 +00001# Copyright (C) 2004-2006 Python Software Foundation
Barry Warsaw418101f2004-05-09 03:29:23 +00002# Authors: Baxter, Wouters and Warsaw
Barry Warsawbb113862004-10-03 03:16:19 +00003# Contact: email-sig@python.org
Anthony Baxter39a0f042004-03-22 00:33:28 +00004
Barry Warsaw418101f2004-05-09 03:29:23 +00005"""FeedParser - An email feed parser.
6
7The feed parser implements an interface for incrementally parsing an email
8message, line by line. This has advantages for certain applications, such as
9those reading email messages off a socket.
10
11FeedParser.feed() is the primary interface for pushing new data into the
12parser. It returns when there's nothing more it can do with the available
13data. When you have no more data to push into the parser, call .close().
14This completes the parsing and returns the root message object.
15
16The other advantage of this parser is that it will never throw a parsing
17exception. Instead, when it finds something unexpected, it adds a 'defect' to
18the current message. Defects are just instances that live on the message
Barry Warsawbb113862004-10-03 03:16:19 +000019object's .defects attribute.
Barry Warsaw418101f2004-05-09 03:29:23 +000020"""
21
Barry Warsaw40ef0062006-03-18 15:41:53 +000022__all__ = ['FeedParser']
23
Anthony Baxter39a0f042004-03-22 00:33:28 +000024import re
Barry Warsaw40ef0062006-03-18 15:41:53 +000025
26from email import errors
27from email import message
Anthony Baxter39a0f042004-03-22 00:33:28 +000028
29NLCRE = re.compile('\r\n|\r|\n')
Barry Warsaw418101f2004-05-09 03:29:23 +000030NLCRE_bol = re.compile('(\r\n|\r|\n)')
R. David Murray61746d52010-06-03 15:43:20 +000031NLCRE_eol = re.compile('(\r\n|\r|\n)\Z')
Barry Warsaw418101f2004-05-09 03:29:23 +000032NLCRE_crack = re.compile('(\r\n|\r|\n)')
Barry Warsawf4c7c402004-11-29 03:46:54 +000033# RFC 2822 $3.6.8 Optional fields. ftext is %d33-57 / %d59-126, Any character
34# except controls, SP, and ":".
Barry Warsaw61532012006-01-17 05:58:08 +000035headerRE = re.compile(r'^(From |[\041-\071\073-\176]{1,}:|[\t ])')
Anthony Baxter39a0f042004-03-22 00:33:28 +000036EMPTYSTRING = ''
37NL = '\n'
38
39NeedMoreData = object()
40
Anthony Baxter39a0f042004-03-22 00:33:28 +000041
Barry Warsaw418101f2004-05-09 03:29:23 +000042
43class BufferedSubFile(object):
44 """A file-ish object that can have new data loaded into it.
45
46 You can also push and pop line-matching predicates onto a stack. When the
47 current predicate matches the current line, a false EOF response
48 (i.e. empty string) is returned instead. This lets the parser adhere to a
49 simple abstraction -- it parses until EOF closes the current message.
50 """
Anthony Baxter39a0f042004-03-22 00:33:28 +000051 def __init__(self):
Barry Warsaw418101f2004-05-09 03:29:23 +000052 # The last partial line pushed into this object.
Anthony Baxter39a0f042004-03-22 00:33:28 +000053 self._partial = ''
Barry Warsaw418101f2004-05-09 03:29:23 +000054 # The list of full, pushed lines, in reverse order
55 self._lines = []
56 # The stack of false-EOF checking predicates.
57 self._eofstack = []
58 # A flag indicating whether the file has been closed or not.
59 self._closed = False
60
61 def push_eof_matcher(self, pred):
62 self._eofstack.append(pred)
63
64 def pop_eof_matcher(self):
65 return self._eofstack.pop()
66
67 def close(self):
68 # Don't forget any trailing partial line.
69 self._lines.append(self._partial)
Barry Warsaw2e8c1f12004-11-28 00:21:42 +000070 self._partial = ''
Barry Warsaw418101f2004-05-09 03:29:23 +000071 self._closed = True
Anthony Baxter39a0f042004-03-22 00:33:28 +000072
73 def readline(self):
Barry Warsaw418101f2004-05-09 03:29:23 +000074 if not self._lines:
75 if self._closed:
Anthony Baxter39a0f042004-03-22 00:33:28 +000076 return ''
77 return NeedMoreData
Barry Warsaw418101f2004-05-09 03:29:23 +000078 # Pop the line off the stack and see if it matches the current
79 # false-EOF predicate.
80 line = self._lines.pop()
Barry Warsaw4e59bc12004-05-13 20:17:51 +000081 # RFC 2046, section 5.1.2 requires us to recognize outer level
82 # boundaries at any level of inner nesting. Do this, but be sure it's
83 # in the order of most to least nested.
84 for ateof in self._eofstack[::-1]:
85 if ateof(line):
Barry Warsaw418101f2004-05-09 03:29:23 +000086 # We're at the false EOF. But push the last line back first.
87 self._lines.append(line)
88 return ''
89 return line
Anthony Baxter39a0f042004-03-22 00:33:28 +000090
91 def unreadline(self, line):
Barry Warsaw418101f2004-05-09 03:29:23 +000092 # Let the consumer push a line back into the buffer.
Barry Warsaw7cf9ce22004-12-05 03:45:42 +000093 assert line is not NeedMoreData
Barry Warsaw418101f2004-05-09 03:29:23 +000094 self._lines.append(line)
Anthony Baxter39a0f042004-03-22 00:33:28 +000095
96 def push(self, data):
Barry Warsaw418101f2004-05-09 03:29:23 +000097 """Push some new data into this object."""
Anthony Baxter39a0f042004-03-22 00:33:28 +000098 # Handle any previous leftovers
Barry Warsaw418101f2004-05-09 03:29:23 +000099 data, self._partial = self._partial + data, ''
100 # Crack into lines, but preserve the newlines on the end of each
101 parts = NLCRE_crack.split(data)
102 # The *ahem* interesting behaviour of re.split when supplied grouping
103 # parentheses is that the last element of the resulting list is the
104 # data after the final RE. In the case of a NL/CR terminated string,
105 # this is the empty string.
106 self._partial = parts.pop()
107 # parts is a list of strings, alternating between the line contents
108 # and the eol character(s). Gather up a list of lines after
109 # re-attaching the newlines.
110 lines = []
Barry Warsawbb113862004-10-03 03:16:19 +0000111 for i in range(len(parts) // 2):
Barry Warsaw418101f2004-05-09 03:29:23 +0000112 lines.append(parts[i*2] + parts[i*2+1])
113 self.pushlines(lines)
114
Anthony Baxter39a0f042004-03-22 00:33:28 +0000115 def pushlines(self, lines):
Barry Warsaw418101f2004-05-09 03:29:23 +0000116 # Reverse and insert at the front of the lines.
117 self._lines[:0] = lines[::-1]
Anthony Baxter39a0f042004-03-22 00:33:28 +0000118
Barry Warsaw418101f2004-05-09 03:29:23 +0000119 def is_closed(self):
120 return self._closed
Anthony Baxter39a0f042004-03-22 00:33:28 +0000121
122 def __iter__(self):
123 return self
124
125 def next(self):
Barry Warsaw418101f2004-05-09 03:29:23 +0000126 line = self.readline()
127 if line == '':
Anthony Baxter39a0f042004-03-22 00:33:28 +0000128 raise StopIteration
Barry Warsaw418101f2004-05-09 03:29:23 +0000129 return line
Anthony Baxter39a0f042004-03-22 00:33:28 +0000130
Barry Warsaw418101f2004-05-09 03:29:23 +0000131
132
Anthony Baxter39a0f042004-03-22 00:33:28 +0000133class FeedParser:
Barry Warsaw418101f2004-05-09 03:29:23 +0000134 """A feed-style parser of email."""
Anthony Baxter39a0f042004-03-22 00:33:28 +0000135
Barry Warsaw40ef0062006-03-18 15:41:53 +0000136 def __init__(self, _factory=message.Message):
Barry Warsaw418101f2004-05-09 03:29:23 +0000137 """_factory is called with no arguments to create a new message obj"""
138 self._factory = _factory
139 self._input = BufferedSubFile()
140 self._msgstack = []
Anthony Baxter39a0f042004-03-22 00:33:28 +0000141 self._parse = self._parsegen().next
Barry Warsaw418101f2004-05-09 03:29:23 +0000142 self._cur = None
143 self._last = None
144 self._headersonly = False
Anthony Baxter39a0f042004-03-22 00:33:28 +0000145
Barry Warsaw418101f2004-05-09 03:29:23 +0000146 # Non-public interface for supporting Parser's headersonly flag
147 def _set_headersonly(self):
148 self._headersonly = True
Anthony Baxter39a0f042004-03-22 00:33:28 +0000149
150 def feed(self, data):
Barry Warsaw418101f2004-05-09 03:29:23 +0000151 """Push more data into the parser."""
Anthony Baxter39a0f042004-03-22 00:33:28 +0000152 self._input.push(data)
153 self._call_parse()
154
155 def _call_parse(self):
156 try:
157 self._parse()
158 except StopIteration:
159 pass
160
Barry Warsaw418101f2004-05-09 03:29:23 +0000161 def close(self):
162 """Parse all remaining data and return the root message object."""
163 self._input.close()
164 self._call_parse()
165 root = self._pop_message()
166 assert not self._msgstack
Barry Warsawbb113862004-10-03 03:16:19 +0000167 # Look for final set of defects
168 if root.get_content_maintype() == 'multipart' \
169 and not root.is_multipart():
Barry Warsaw40ef0062006-03-18 15:41:53 +0000170 root.defects.append(errors.MultipartInvariantViolationDefect())
Barry Warsaw418101f2004-05-09 03:29:23 +0000171 return root
Anthony Baxter39a0f042004-03-22 00:33:28 +0000172
Barry Warsaw418101f2004-05-09 03:29:23 +0000173 def _new_message(self):
174 msg = self._factory()
175 if self._cur and self._cur.get_content_type() == 'multipart/digest':
176 msg.set_default_type('message/rfc822')
177 if self._msgstack:
178 self._msgstack[-1].attach(msg)
179 self._msgstack.append(msg)
180 self._cur = msg
Barry Warsaw418101f2004-05-09 03:29:23 +0000181 self._last = msg
182
183 def _pop_message(self):
184 retval = self._msgstack.pop()
185 if self._msgstack:
186 self._cur = self._msgstack[-1]
187 else:
188 self._cur = None
189 return retval
190
191 def _parsegen(self):
192 # Create a new message and start by parsing headers.
193 self._new_message()
194 headers = []
195 # Collect the headers, searching for a line that doesn't match the RFC
196 # 2822 header or continuation pattern (including an empty line).
197 for line in self._input:
198 if line is NeedMoreData:
199 yield NeedMoreData
200 continue
201 if not headerRE.match(line):
202 # If we saw the RFC defined header/body separator
203 # (i.e. newline), just throw it away. Otherwise the line is
204 # part of the body so push it back.
205 if not NLCRE.match(line):
206 self._input.unreadline(line)
207 break
208 headers.append(line)
209 # Done with the headers, so parse them and figure out what we're
210 # supposed to see in the body of the message.
211 self._parse_headers(headers)
212 # Headers-only parsing is a backwards compatibility hack, which was
213 # necessary in the older parser, which could throw errors. All
214 # remaining lines in the input are thrown into the message body.
215 if self._headersonly:
216 lines = []
217 while True:
218 line = self._input.readline()
219 if line is NeedMoreData:
220 yield NeedMoreData
221 continue
222 if line == '':
223 break
224 lines.append(line)
225 self._cur.set_payload(EMPTYSTRING.join(lines))
226 return
Barry Warsaw418101f2004-05-09 03:29:23 +0000227 if self._cur.get_content_type() == 'message/delivery-status':
228 # message/delivery-status contains blocks of headers separated by
229 # a blank line. We'll represent each header block as a separate
Barry Warsawd38f4482004-05-11 20:19:09 +0000230 # nested message object, but the processing is a bit different
231 # than standard message/* types because there is no body for the
232 # nested messages. A blank line separates the subparts.
Barry Warsaw418101f2004-05-09 03:29:23 +0000233 while True:
234 self._input.push_eof_matcher(NLCRE.match)
235 for retval in self._parsegen():
236 if retval is NeedMoreData:
237 yield NeedMoreData
238 continue
239 break
240 msg = self._pop_message()
241 # We need to pop the EOF matcher in order to tell if we're at
242 # the end of the current file, not the end of the last block
243 # of message headers.
244 self._input.pop_eof_matcher()
245 # The input stream must be sitting at the newline or at the
246 # EOF. We want to see if we're at the end of this subpart, so
247 # first consume the blank line, then test the next line to see
248 # if we're at this subpart's EOF.
Barry Warsaw7cf9ce22004-12-05 03:45:42 +0000249 while True:
250 line = self._input.readline()
251 if line is NeedMoreData:
252 yield NeedMoreData
253 continue
254 break
255 while True:
256 line = self._input.readline()
257 if line is NeedMoreData:
258 yield NeedMoreData
259 continue
260 break
Barry Warsaw418101f2004-05-09 03:29:23 +0000261 if line == '':
262 break
263 # Not at EOF so this is a line we're going to need.
264 self._input.unreadline(line)
265 return
Barry Warsawd38f4482004-05-11 20:19:09 +0000266 if self._cur.get_content_maintype() == 'message':
267 # The message claims to be a message/* type, then what follows is
268 # another RFC 2822 message.
269 for retval in self._parsegen():
270 if retval is NeedMoreData:
271 yield NeedMoreData
272 continue
273 break
274 self._pop_message()
275 return
Barry Warsaw418101f2004-05-09 03:29:23 +0000276 if self._cur.get_content_maintype() == 'multipart':
277 boundary = self._cur.get_boundary()
278 if boundary is None:
279 # The message /claims/ to be a multipart but it has not
280 # defined a boundary. That's a problem which we'll handle by
281 # reading everything until the EOF and marking the message as
282 # defective.
Barry Warsaw40ef0062006-03-18 15:41:53 +0000283 self._cur.defects.append(errors.NoBoundaryInMultipartDefect())
Barry Warsaw418101f2004-05-09 03:29:23 +0000284 lines = []
285 for line in self._input:
286 if line is NeedMoreData:
287 yield NeedMoreData
288 continue
289 lines.append(line)
290 self._cur.set_payload(EMPTYSTRING.join(lines))
291 return
292 # Create a line match predicate which matches the inter-part
293 # boundary as well as the end-of-multipart boundary. Don't push
294 # this onto the input stream until we've scanned past the
295 # preamble.
296 separator = '--' + boundary
297 boundaryre = re.compile(
298 '(?P<sep>' + re.escape(separator) +
Barry Warsaw2e8c1f12004-11-28 00:21:42 +0000299 r')(?P<end>--)?(?P<ws>[ \t]*)(?P<linesep>\r\n|\r|\n)?$')
Barry Warsaw418101f2004-05-09 03:29:23 +0000300 capturing_preamble = True
301 preamble = []
302 linesep = False
303 while True:
304 line = self._input.readline()
305 if line is NeedMoreData:
306 yield NeedMoreData
307 continue
308 if line == '':
309 break
310 mo = boundaryre.match(line)
311 if mo:
312 # If we're looking at the end boundary, we're done with
313 # this multipart. If there was a newline at the end of
314 # the closing boundary, then we need to initialize the
315 # epilogue with the empty string (see below).
316 if mo.group('end'):
317 linesep = mo.group('linesep')
318 break
319 # We saw an inter-part boundary. Were we in the preamble?
320 if capturing_preamble:
321 if preamble:
322 # According to RFC 2046, the last newline belongs
323 # to the boundary.
324 lastline = preamble[-1]
325 eolmo = NLCRE_eol.search(lastline)
326 if eolmo:
327 preamble[-1] = lastline[:-len(eolmo.group(0))]
328 self._cur.preamble = EMPTYSTRING.join(preamble)
329 capturing_preamble = False
330 self._input.unreadline(line)
331 continue
Barry Warsaw486cb0a2004-05-11 22:23:59 +0000332 # We saw a boundary separating two parts. Consume any
333 # multiple boundary lines that may be following. Our
334 # interpretation of RFC 2046 BNF grammar does not produce
335 # body parts within such double boundaries.
336 while True:
337 line = self._input.readline()
Barry Warsawe4aeb7d2004-05-15 16:26:28 +0000338 if line is NeedMoreData:
339 yield NeedMoreData
340 continue
Barry Warsaw486cb0a2004-05-11 22:23:59 +0000341 mo = boundaryre.match(line)
342 if not mo:
343 self._input.unreadline(line)
344 break
345 # Recurse to parse this subpart; the input stream points
346 # at the subpart's first line.
Barry Warsaw418101f2004-05-09 03:29:23 +0000347 self._input.push_eof_matcher(boundaryre.match)
348 for retval in self._parsegen():
349 if retval is NeedMoreData:
350 yield NeedMoreData
351 continue
352 break
353 # Because of RFC 2046, the newline preceding the boundary
354 # separator actually belongs to the boundary, not the
355 # previous subpart's payload (or epilogue if the previous
356 # part is a multipart).
357 if self._last.get_content_maintype() == 'multipart':
358 epilogue = self._last.epilogue
359 if epilogue == '':
360 self._last.epilogue = None
361 elif epilogue is not None:
362 mo = NLCRE_eol.search(epilogue)
363 if mo:
364 end = len(mo.group(0))
365 self._last.epilogue = epilogue[:-end]
366 else:
367 payload = self._last.get_payload()
368 if isinstance(payload, basestring):
369 mo = NLCRE_eol.search(payload)
370 if mo:
371 payload = payload[:-len(mo.group(0))]
372 self._last.set_payload(payload)
373 self._input.pop_eof_matcher()
374 self._pop_message()
375 # Set the multipart up for newline cleansing, which will
376 # happen if we're in a nested multipart.
377 self._last = self._cur
378 else:
379 # I think we must be in the preamble
380 assert capturing_preamble
381 preamble.append(line)
382 # We've seen either the EOF or the end boundary. If we're still
383 # capturing the preamble, we never saw the start boundary. Note
384 # that as a defect and store the captured text as the payload.
Barry Warsawdee0cf12004-10-09 23:00:11 +0000385 # Everything from here to the EOF is epilogue.
Barry Warsaw418101f2004-05-09 03:29:23 +0000386 if capturing_preamble:
Barry Warsaw40ef0062006-03-18 15:41:53 +0000387 self._cur.defects.append(errors.StartBoundaryNotFoundDefect())
Barry Warsaw418101f2004-05-09 03:29:23 +0000388 self._cur.set_payload(EMPTYSTRING.join(preamble))
Barry Warsawdee0cf12004-10-09 23:00:11 +0000389 epilogue = []
390 for line in self._input:
391 if line is NeedMoreData:
392 yield NeedMoreData
393 continue
394 self._cur.epilogue = EMPTYSTRING.join(epilogue)
Barry Warsaw418101f2004-05-09 03:29:23 +0000395 return
396 # If the end boundary ended in a newline, we'll need to make sure
397 # the epilogue isn't None
398 if linesep:
399 epilogue = ['']
400 else:
401 epilogue = []
402 for line in self._input:
403 if line is NeedMoreData:
404 yield NeedMoreData
405 continue
406 epilogue.append(line)
407 # Any CRLF at the front of the epilogue is not technically part of
408 # the epilogue. Also, watch out for an empty string epilogue,
409 # which means a single newline.
Barry Warsaw5b44cd62004-05-11 18:10:15 +0000410 if epilogue:
411 firstline = epilogue[0]
412 bolmo = NLCRE_bol.match(firstline)
413 if bolmo:
414 epilogue[0] = firstline[len(bolmo.group(0)):]
Barry Warsaw418101f2004-05-09 03:29:23 +0000415 self._cur.epilogue = EMPTYSTRING.join(epilogue)
416 return
417 # Otherwise, it's some non-multipart type, so the entire rest of the
418 # file contents becomes the payload.
419 lines = []
420 for line in self._input:
421 if line is NeedMoreData:
422 yield NeedMoreData
423 continue
424 lines.append(line)
425 self._cur.set_payload(EMPTYSTRING.join(lines))
426
427 def _parse_headers(self, lines):
428 # Passed a list of lines that make up the headers for the current msg
Anthony Baxter39a0f042004-03-22 00:33:28 +0000429 lastheader = ''
430 lastvalue = []
Barry Warsaw418101f2004-05-09 03:29:23 +0000431 for lineno, line in enumerate(lines):
Anthony Baxter39a0f042004-03-22 00:33:28 +0000432 # Check for continuation
433 if line[0] in ' \t':
434 if not lastheader:
Barry Warsaw418101f2004-05-09 03:29:23 +0000435 # The first line of the headers was a continuation. This
436 # is illegal, so let's note the defect, store the illegal
437 # line, and ignore it for purposes of headers.
Barry Warsaw40ef0062006-03-18 15:41:53 +0000438 defect = errors.FirstHeaderLineIsContinuationDefect(line)
Barry Warsaw418101f2004-05-09 03:29:23 +0000439 self._cur.defects.append(defect)
440 continue
Anthony Baxter39a0f042004-03-22 00:33:28 +0000441 lastvalue.append(line)
442 continue
Anthony Baxter39a0f042004-03-22 00:33:28 +0000443 if lastheader:
444 # XXX reconsider the joining of folded lines
Barry Warsaw8896bf52004-08-07 15:57:52 +0000445 lhdr = EMPTYSTRING.join(lastvalue)[:-1].rstrip('\r\n')
446 self._cur[lastheader] = lhdr
Anthony Baxter39a0f042004-03-22 00:33:28 +0000447 lastheader, lastvalue = '', []
Barry Warsaw418101f2004-05-09 03:29:23 +0000448 # Check for envelope header, i.e. unix-from
Anthony Baxter39a0f042004-03-22 00:33:28 +0000449 if line.startswith('From '):
450 if lineno == 0:
Barry Warsawc29db262004-05-10 14:48:30 +0000451 # Strip off the trailing newline
452 mo = NLCRE_eol.search(line)
453 if mo:
454 line = line[:-len(mo.group(0))]
Anthony Baxter39a0f042004-03-22 00:33:28 +0000455 self._cur.set_unixfrom(line)
456 continue
Barry Warsaw418101f2004-05-09 03:29:23 +0000457 elif lineno == len(lines) - 1:
Anthony Baxter39a0f042004-03-22 00:33:28 +0000458 # Something looking like a unix-from at the end - it's
Barry Warsaw418101f2004-05-09 03:29:23 +0000459 # probably the first line of the body, so push back the
460 # line and stop.
Anthony Baxter39a0f042004-03-22 00:33:28 +0000461 self._input.unreadline(line)
462 return
463 else:
Barry Warsaw418101f2004-05-09 03:29:23 +0000464 # Weirdly placed unix-from line. Note this as a defect
465 # and ignore it.
Barry Warsaw40ef0062006-03-18 15:41:53 +0000466 defect = errors.MisplacedEnvelopeHeaderDefect(line)
Barry Warsaw418101f2004-05-09 03:29:23 +0000467 self._cur.defects.append(defect)
Anthony Baxter39a0f042004-03-22 00:33:28 +0000468 continue
Barry Warsaw418101f2004-05-09 03:29:23 +0000469 # Split the line on the colon separating field name from value.
Anthony Baxter39a0f042004-03-22 00:33:28 +0000470 i = line.find(':')
471 if i < 0:
Barry Warsaw40ef0062006-03-18 15:41:53 +0000472 defect = errors.MalformedHeaderDefect(line)
Barry Warsaw418101f2004-05-09 03:29:23 +0000473 self._cur.defects.append(defect)
474 continue
Anthony Baxter39a0f042004-03-22 00:33:28 +0000475 lastheader = line[:i]
476 lastvalue = [line[i+1:].lstrip()]
Barry Warsaw418101f2004-05-09 03:29:23 +0000477 # Done with all the lines, so handle the last header.
Anthony Baxter39a0f042004-03-22 00:33:28 +0000478 if lastheader:
479 # XXX reconsider the joining of folded lines
Barry Warsaw8896bf52004-08-07 15:57:52 +0000480 self._cur[lastheader] = EMPTYSTRING.join(lastvalue).rstrip('\r\n')