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