10a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Copyright (C) 2001-2010 Python Software Foundation 20a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Contact: email-sig@python.org 30a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 40a8c90248264a8b26970b4473770bcc3df8515fJosh Gao"""Classes to generate plain text from a message object tree.""" 50a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 60a8c90248264a8b26970b4473770bcc3df8515fJosh Gao__all__ = ['Generator', 'DecodedGenerator'] 70a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 80a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport re 90a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport sys 100a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport time 110a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport random 120a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport warnings 130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 140a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom cStringIO import StringIO 150a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom email.header import Header 160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 170a8c90248264a8b26970b4473770bcc3df8515fJosh GaoUNDERSCORE = '_' 180a8c90248264a8b26970b4473770bcc3df8515fJosh GaoNL = '\n' 190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 200a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofcre = re.compile(r'^From ', re.MULTILINE) 210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 220a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef _is8bitstring(s): 230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if isinstance(s, str): 240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao unicode(s, 'us-ascii') 260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except UnicodeError: 270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return True 280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return False 290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 320a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass Generator: 330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Generates output from a Message object tree. 340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This basic generator writes the message to the given file object as plain 360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text. 370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Public interface 400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, outfp, mangle_from_=True, maxheaderlen=78): 430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Create the generator for message flattening. 440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao outfp is the output file-like object for writing the message to. It 460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao must have a write() method. 470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Optional mangle_from_ is a flag that, when True (the default), escapes 490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao From_ lines in the body of the message by putting a `>' in front of 500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao them. 510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Optional maxheaderlen specifies the longest length for a non-continued 530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao header. When a header line is longer (in characters, with tabs 540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao expanded to 8 spaces) than maxheaderlen, the header will split as 550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao defined in the Header class. Set maxheaderlen to zero to disable 560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao header wrapping. The default is 78, as recommended (but not required) 570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao by RFC 2822. 580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp = outfp 600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._mangle_from_ = mangle_from_ 610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._maxheaderlen = maxheaderlen 620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def write(self, s): 640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Just delegate to the file object 650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp.write(s) 660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def flatten(self, msg, unixfrom=False): 680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Print the message object tree rooted at msg to the output file 690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao specified when the Generator instance was created. 700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao unixfrom is a flag that forces the printing of a Unix From_ delimiter 720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao before the first object in the message tree. If the original message 730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao has no From_ delimiter, a `standard' one is crafted. By default, this 740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao is False to inhibit the printing of any From_ delimiter. 750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Note that for subobjects, no From_ line is printed. 770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if unixfrom: 790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ufrom = msg.get_unixfrom() 800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not ufrom: 810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ufrom = 'From nobody ' + time.ctime(time.time()) 820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp, ufrom 830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._write(msg) 840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def clone(self, fp): 860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Clone this generator with the exact same options.""" 870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.__class__(fp, self._mangle_from_, self._maxheaderlen) 880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Protected interface - undocumented ;/ 910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _write(self, msg): 940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # We can't write the headers yet because of the following scenario: 950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # say a multipart message includes the boundary string somewhere in 960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # its body. We'd have to calculate the new boundary /before/ we write 970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # the headers so that we can write the correct Content-Type: 980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # parameter. 990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 1000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # The way we do this, so as to make the _handle_*() methods simpler, 1010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # is to cache any subpart writes into a StringIO. The we write the 1020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # headers and the StringIO contents. That way, subpart handlers can 1030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Do The Right Thing, and can still modify the Content-Type: header if 1040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # necessary. 1050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao oldfp = self._fp 1060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 1070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp = sfp = StringIO() 1080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._dispatch(msg) 1090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao finally: 1100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp = oldfp 1110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Write the headers. First we see if the message object wants to 1120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # handle that itself. If not, we'll do it generically. 1130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao meth = getattr(msg, '_write_headers', None) 1140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if meth is None: 1150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._write_headers(msg) 1160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 1170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao meth(self) 1180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp.write(sfp.getvalue()) 1190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _dispatch(self, msg): 1210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Get the Content-Type: for the message, then try to dispatch to 1220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # self._handle_<maintype>_<subtype>(). If there's no handler for the 1230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # full MIME type, then dispatch to self._handle_<maintype>(). If 1240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # that's missing too, then dispatch to self._writeBody(). 1250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao main = msg.get_content_maintype() 1260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao sub = msg.get_content_subtype() 1270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao specific = UNDERSCORE.join((main, sub)).replace('-', '_') 1280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao meth = getattr(self, '_handle_' + specific, None) 1290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if meth is None: 1300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao generic = main.replace('-', '_') 1310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao meth = getattr(self, '_handle_' + generic, None) 1320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if meth is None: 1330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao meth = self._writeBody 1340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao meth(msg) 1350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 1370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Default handlers 1380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 1390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _write_headers(self, msg): 1410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for h, v in msg.items(): 1420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp, '%s:' % h, 1430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self._maxheaderlen == 0: 1440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Explicit no-wrapping 1450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp, v 1460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif isinstance(v, Header): 1470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Header instances know what to do 1480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp, v.encode() 1490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif _is8bitstring(v): 1500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # If we have raw 8bit data in a byte string, we have no idea 1510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # what the encoding is. There is no safe way to split this 1520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # string. If it's ascii-subset, then we could do a normal 1530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # ascii split, but if it's multibyte then we could break the 1540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # string. There's no way to know so the least harm seems to 1550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # be to not split the string and risk it being too long. 1560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp, v 1570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 1580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Header's got lots of smarts, so use it. Note that this is 1590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # fundamentally broken though because we lose idempotency when 1600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # the header string is continued with tabs. It will now be 1610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # continued with spaces. This was reversedly broken before we 1620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # fixed bug 1974. Either way, we lose. 1630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp, Header( 1640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao v, maxlinelen=self._maxheaderlen, header_name=h).encode() 1650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # A blank line always separates headers from body 1660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp 1670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 1690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Handlers for writing types and subtypes 1700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 1710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _handle_text(self, msg): 1730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao payload = msg.get_payload() 1740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if payload is None: 1750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return 1760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not isinstance(payload, basestring): 1770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise TypeError('string payload expected: %s' % type(payload)) 1780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self._mangle_from_: 1790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao payload = fcre.sub('>From ', payload) 1800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp.write(payload) 1810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Default body handler 1830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _writeBody = _handle_text 1840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _handle_multipart(self, msg): 1860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # The trick here is to write out each part separately, merge them all 1870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # together, and then make sure that the boundary we've chosen isn't 1880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # present in the payload. 1890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao msgtexts = [] 1900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao subparts = msg.get_payload() 1910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if subparts is None: 1920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao subparts = [] 1930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif isinstance(subparts, basestring): 1940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # e.g. a non-strict parse of a message with no starting boundary. 1950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp.write(subparts) 1960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return 1970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif not isinstance(subparts, list): 1980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Scalar payload 1990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao subparts = [subparts] 2000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for part in subparts: 2010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao s = StringIO() 2020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao g = self.clone(s) 2030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao g.flatten(part, unixfrom=False) 2040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao msgtexts.append(s.getvalue()) 2050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # BAW: What about boundaries that are wrapped in double-quotes? 2060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao boundary = msg.get_boundary() 2070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not boundary: 2080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Create a boundary that doesn't appear in any of the 2090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # message texts. 2100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao alltext = NL.join(msgtexts) 2110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao boundary = _make_boundary(alltext) 2120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao msg.set_boundary(boundary) 2130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # If there's a preamble, write it out, with a trailing CRLF 2140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if msg.preamble is not None: 2150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self._mangle_from_: 2160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao preamble = fcre.sub('>From ', msg.preamble) 2170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao preamble = msg.preamble 2190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp, preamble 2200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # dash-boundary transport-padding CRLF 2210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp, '--' + boundary 2220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # body-part 2230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if msgtexts: 2240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp.write(msgtexts.pop(0)) 2250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # *encapsulation 2260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # --> delimiter transport-padding 2270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # --> CRLF body-part 2280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for body_part in msgtexts: 2290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # delimiter transport-padding CRLF 2300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp, '\n--' + boundary 2310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # body-part 2320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp.write(body_part) 2330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # close-delimiter transport-padding 2340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp.write('\n--' + boundary + '--') 2350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if msg.epilogue is not None: 2360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self._fp 2370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self._mangle_from_: 2380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao epilogue = fcre.sub('>From ', msg.epilogue) 2390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao epilogue = msg.epilogue 2410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp.write(epilogue) 2420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _handle_multipart_signed(self, msg): 2440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # The contents of signed parts has to stay unmodified in order to keep 2450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # the signature intact per RFC1847 2.1, so we disable header wrapping. 2460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # RDM: This isn't enough to completely preserve the part, but it helps. 2470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao old_maxheaderlen = self._maxheaderlen 2480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 2490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._maxheaderlen = 0 2500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._handle_multipart(msg) 2510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao finally: 2520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._maxheaderlen = old_maxheaderlen 2530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _handle_message_delivery_status(self, msg): 2550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # We can't just write the headers directly to self's file object 2560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # because this will leave an extra newline between the last header 2570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # block and the boundary. Sigh. 2580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao blocks = [] 2590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for part in msg.get_payload(): 2600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao s = StringIO() 2610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao g = self.clone(s) 2620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao g.flatten(part, unixfrom=False) 2630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text = s.getvalue() 2640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao lines = text.split('\n') 2650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Strip off the unnecessary trailing empty line 2660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if lines and lines[-1] == '': 2670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao blocks.append(NL.join(lines[:-1])) 2680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao blocks.append(text) 2700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Now join all the blocks with an empty line. This has the lovely 2710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # effect of separating each block with an empty line, but not adding 2720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # an extra one after the last one. 2730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp.write(NL.join(blocks)) 2740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _handle_message(self, msg): 2760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao s = StringIO() 2770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao g = self.clone(s) 2780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # The payload of a message/rfc822 part should be a multipart sequence 2790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # of length 1. The zeroth element of the list should be the Message 2800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # object for the subpart. Extract that object, stringify it, and 2810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # write it out. 2820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Except, it turns out, when it's a string instead, which happens when 2830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # and only when HeaderParser is used on a message of mime type 2840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # message/rfc822. Such messages are generated by, for example, 2850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Groupwise when forwarding unadorned messages. (Issue 7970.) So 2860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # in that case we just emit the string body. 2870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao payload = msg.get_payload() 2880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if isinstance(payload, list): 2890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao g.flatten(msg.get_payload(0), unixfrom=False) 2900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao payload = s.getvalue() 2910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fp.write(payload) 2920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_FMT = '[Non-text (%(type)s) part of message omitted, filename %(filename)s]' 2960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2970a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass DecodedGenerator(Generator): 2980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Generates a text representation of a message. 2990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Like the Generator base class, except that non-text parts are substituted 3010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao with a format string representing the part. 3020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 3030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, outfp, mangle_from_=True, maxheaderlen=78, fmt=None): 3040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Like Generator.__init__() except that an additional optional 3050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao argument is allowed. 3060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Walks through all subparts of a message. If the subpart is of main 3080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao type `text', then it prints the decoded payload of the subpart. 3090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Otherwise, fmt is a format string that is used instead of the message 3110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao payload. fmt is expanded with the following keywords (in 3120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao %(keyword)s format): 3130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao type : Full MIME type of the non-text part 3150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao maintype : Main MIME type of the non-text part 3160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao subtype : Sub-MIME type of the non-text part 3170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao filename : Filename of the non-text part 3180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao description: Description associated with the non-text part 3190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao encoding : Content transfer encoding of the non-text part 3200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao The default value for fmt is None, meaning 3220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao [Non-text (%(type)s) part of message omitted, filename %(filename)s] 3240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 3250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Generator.__init__(self, outfp, mangle_from_, maxheaderlen) 3260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if fmt is None: 3270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fmt = _FMT 3280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._fmt = fmt 3300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _dispatch(self, msg): 3320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for part in msg.walk(): 3330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao maintype = part.get_content_maintype() 3340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if maintype == 'text': 3350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self, part.get_payload(decode=True) 3360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif maintype == 'multipart': 3370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Just skip this 3380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 3390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print >> self, self._fmt % { 3410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'type' : part.get_content_type(), 3420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'maintype' : part.get_content_maintype(), 3430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'subtype' : part.get_content_subtype(), 3440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'filename' : part.get_filename('[no filename]'), 3450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'description': part.get('Content-Description', 3460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao '[no description]'), 3470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'encoding' : part.get('Content-Transfer-Encoding', 3480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao '[no encoding]'), 3490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao } 3500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Helper 3540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_width = len(repr(sys.maxint-1)) 3550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_fmt = '%%0%dd' % _width 3560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3570a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef _make_boundary(text=None): 3580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Craft a random boundary. If text is given, ensure that the chosen 3590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # boundary doesn't appear in the text. 3600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao token = random.randrange(sys.maxint) 3610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao boundary = ('=' * 15) + (_fmt % token) + '==' 3620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if text is None: 3630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return boundary 3640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao b = boundary 3650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao counter = 0 3660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao while True: 3670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cre = re.compile('^--' + re.escape(b) + '(--)?$', re.MULTILINE) 3680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not cre.search(text): 3690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao break 3700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao b = boundary + '.' + str(counter) 3710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao counter += 1 3720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return b 373