1*cda5da8dSAndroid Build Coastguard Worker# Copyright (C) 2001-2010 Python Software Foundation 2*cda5da8dSAndroid Build Coastguard Worker# Author: Barry Warsaw 3*cda5da8dSAndroid Build Coastguard Worker# Contact: [email protected] 4*cda5da8dSAndroid Build Coastguard Worker 5*cda5da8dSAndroid Build Coastguard Worker"""Classes to generate plain text from a message object tree.""" 6*cda5da8dSAndroid Build Coastguard Worker 7*cda5da8dSAndroid Build Coastguard Worker__all__ = ['Generator', 'DecodedGenerator', 'BytesGenerator'] 8*cda5da8dSAndroid Build Coastguard Worker 9*cda5da8dSAndroid Build Coastguard Workerimport re 10*cda5da8dSAndroid Build Coastguard Workerimport sys 11*cda5da8dSAndroid Build Coastguard Workerimport time 12*cda5da8dSAndroid Build Coastguard Workerimport random 13*cda5da8dSAndroid Build Coastguard Worker 14*cda5da8dSAndroid Build Coastguard Workerfrom copy import deepcopy 15*cda5da8dSAndroid Build Coastguard Workerfrom io import StringIO, BytesIO 16*cda5da8dSAndroid Build Coastguard Workerfrom email.utils import _has_surrogates 17*cda5da8dSAndroid Build Coastguard Worker 18*cda5da8dSAndroid Build Coastguard WorkerUNDERSCORE = '_' 19*cda5da8dSAndroid Build Coastguard WorkerNL = '\n' # XXX: no longer used by the code below. 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard WorkerNLCRE = re.compile(r'\r\n|\r|\n') 22*cda5da8dSAndroid Build Coastguard Workerfcre = re.compile(r'^From ', re.MULTILINE) 23*cda5da8dSAndroid Build Coastguard Worker 24*cda5da8dSAndroid Build Coastguard Worker 25*cda5da8dSAndroid Build Coastguard Worker 26*cda5da8dSAndroid Build Coastguard Workerclass Generator: 27*cda5da8dSAndroid Build Coastguard Worker """Generates output from a Message object tree. 28*cda5da8dSAndroid Build Coastguard Worker 29*cda5da8dSAndroid Build Coastguard Worker This basic generator writes the message to the given file object as plain 30*cda5da8dSAndroid Build Coastguard Worker text. 31*cda5da8dSAndroid Build Coastguard Worker """ 32*cda5da8dSAndroid Build Coastguard Worker # 33*cda5da8dSAndroid Build Coastguard Worker # Public interface 34*cda5da8dSAndroid Build Coastguard Worker # 35*cda5da8dSAndroid Build Coastguard Worker 36*cda5da8dSAndroid Build Coastguard Worker def __init__(self, outfp, mangle_from_=None, maxheaderlen=None, *, 37*cda5da8dSAndroid Build Coastguard Worker policy=None): 38*cda5da8dSAndroid Build Coastguard Worker """Create the generator for message flattening. 39*cda5da8dSAndroid Build Coastguard Worker 40*cda5da8dSAndroid Build Coastguard Worker outfp is the output file-like object for writing the message to. It 41*cda5da8dSAndroid Build Coastguard Worker must have a write() method. 42*cda5da8dSAndroid Build Coastguard Worker 43*cda5da8dSAndroid Build Coastguard Worker Optional mangle_from_ is a flag that, when True (the default if policy 44*cda5da8dSAndroid Build Coastguard Worker is not set), escapes From_ lines in the body of the message by putting 45*cda5da8dSAndroid Build Coastguard Worker a `>' in front of them. 46*cda5da8dSAndroid Build Coastguard Worker 47*cda5da8dSAndroid Build Coastguard Worker Optional maxheaderlen specifies the longest length for a non-continued 48*cda5da8dSAndroid Build Coastguard Worker header. When a header line is longer (in characters, with tabs 49*cda5da8dSAndroid Build Coastguard Worker expanded to 8 spaces) than maxheaderlen, the header will split as 50*cda5da8dSAndroid Build Coastguard Worker defined in the Header class. Set maxheaderlen to zero to disable 51*cda5da8dSAndroid Build Coastguard Worker header wrapping. The default is 78, as recommended (but not required) 52*cda5da8dSAndroid Build Coastguard Worker by RFC 2822. 53*cda5da8dSAndroid Build Coastguard Worker 54*cda5da8dSAndroid Build Coastguard Worker The policy keyword specifies a policy object that controls a number of 55*cda5da8dSAndroid Build Coastguard Worker aspects of the generator's operation. If no policy is specified, 56*cda5da8dSAndroid Build Coastguard Worker the policy associated with the Message object passed to the 57*cda5da8dSAndroid Build Coastguard Worker flatten method is used. 58*cda5da8dSAndroid Build Coastguard Worker 59*cda5da8dSAndroid Build Coastguard Worker """ 60*cda5da8dSAndroid Build Coastguard Worker 61*cda5da8dSAndroid Build Coastguard Worker if mangle_from_ is None: 62*cda5da8dSAndroid Build Coastguard Worker mangle_from_ = True if policy is None else policy.mangle_from_ 63*cda5da8dSAndroid Build Coastguard Worker self._fp = outfp 64*cda5da8dSAndroid Build Coastguard Worker self._mangle_from_ = mangle_from_ 65*cda5da8dSAndroid Build Coastguard Worker self.maxheaderlen = maxheaderlen 66*cda5da8dSAndroid Build Coastguard Worker self.policy = policy 67*cda5da8dSAndroid Build Coastguard Worker 68*cda5da8dSAndroid Build Coastguard Worker def write(self, s): 69*cda5da8dSAndroid Build Coastguard Worker # Just delegate to the file object 70*cda5da8dSAndroid Build Coastguard Worker self._fp.write(s) 71*cda5da8dSAndroid Build Coastguard Worker 72*cda5da8dSAndroid Build Coastguard Worker def flatten(self, msg, unixfrom=False, linesep=None): 73*cda5da8dSAndroid Build Coastguard Worker r"""Print the message object tree rooted at msg to the output file 74*cda5da8dSAndroid Build Coastguard Worker specified when the Generator instance was created. 75*cda5da8dSAndroid Build Coastguard Worker 76*cda5da8dSAndroid Build Coastguard Worker unixfrom is a flag that forces the printing of a Unix From_ delimiter 77*cda5da8dSAndroid Build Coastguard Worker before the first object in the message tree. If the original message 78*cda5da8dSAndroid Build Coastguard Worker has no From_ delimiter, a `standard' one is crafted. By default, this 79*cda5da8dSAndroid Build Coastguard Worker is False to inhibit the printing of any From_ delimiter. 80*cda5da8dSAndroid Build Coastguard Worker 81*cda5da8dSAndroid Build Coastguard Worker Note that for subobjects, no From_ line is printed. 82*cda5da8dSAndroid Build Coastguard Worker 83*cda5da8dSAndroid Build Coastguard Worker linesep specifies the characters used to indicate a new line in 84*cda5da8dSAndroid Build Coastguard Worker the output. The default value is determined by the policy specified 85*cda5da8dSAndroid Build Coastguard Worker when the Generator instance was created or, if none was specified, 86*cda5da8dSAndroid Build Coastguard Worker from the policy associated with the msg. 87*cda5da8dSAndroid Build Coastguard Worker 88*cda5da8dSAndroid Build Coastguard Worker """ 89*cda5da8dSAndroid Build Coastguard Worker # We use the _XXX constants for operating on data that comes directly 90*cda5da8dSAndroid Build Coastguard Worker # from the msg, and _encoded_XXX constants for operating on data that 91*cda5da8dSAndroid Build Coastguard Worker # has already been converted (to bytes in the BytesGenerator) and 92*cda5da8dSAndroid Build Coastguard Worker # inserted into a temporary buffer. 93*cda5da8dSAndroid Build Coastguard Worker policy = msg.policy if self.policy is None else self.policy 94*cda5da8dSAndroid Build Coastguard Worker if linesep is not None: 95*cda5da8dSAndroid Build Coastguard Worker policy = policy.clone(linesep=linesep) 96*cda5da8dSAndroid Build Coastguard Worker if self.maxheaderlen is not None: 97*cda5da8dSAndroid Build Coastguard Worker policy = policy.clone(max_line_length=self.maxheaderlen) 98*cda5da8dSAndroid Build Coastguard Worker self._NL = policy.linesep 99*cda5da8dSAndroid Build Coastguard Worker self._encoded_NL = self._encode(self._NL) 100*cda5da8dSAndroid Build Coastguard Worker self._EMPTY = '' 101*cda5da8dSAndroid Build Coastguard Worker self._encoded_EMPTY = self._encode(self._EMPTY) 102*cda5da8dSAndroid Build Coastguard Worker # Because we use clone (below) when we recursively process message 103*cda5da8dSAndroid Build Coastguard Worker # subparts, and because clone uses the computed policy (not None), 104*cda5da8dSAndroid Build Coastguard Worker # submessages will automatically get set to the computed policy when 105*cda5da8dSAndroid Build Coastguard Worker # they are processed by this code. 106*cda5da8dSAndroid Build Coastguard Worker old_gen_policy = self.policy 107*cda5da8dSAndroid Build Coastguard Worker old_msg_policy = msg.policy 108*cda5da8dSAndroid Build Coastguard Worker try: 109*cda5da8dSAndroid Build Coastguard Worker self.policy = policy 110*cda5da8dSAndroid Build Coastguard Worker msg.policy = policy 111*cda5da8dSAndroid Build Coastguard Worker if unixfrom: 112*cda5da8dSAndroid Build Coastguard Worker ufrom = msg.get_unixfrom() 113*cda5da8dSAndroid Build Coastguard Worker if not ufrom: 114*cda5da8dSAndroid Build Coastguard Worker ufrom = 'From nobody ' + time.ctime(time.time()) 115*cda5da8dSAndroid Build Coastguard Worker self.write(ufrom + self._NL) 116*cda5da8dSAndroid Build Coastguard Worker self._write(msg) 117*cda5da8dSAndroid Build Coastguard Worker finally: 118*cda5da8dSAndroid Build Coastguard Worker self.policy = old_gen_policy 119*cda5da8dSAndroid Build Coastguard Worker msg.policy = old_msg_policy 120*cda5da8dSAndroid Build Coastguard Worker 121*cda5da8dSAndroid Build Coastguard Worker def clone(self, fp): 122*cda5da8dSAndroid Build Coastguard Worker """Clone this generator with the exact same options.""" 123*cda5da8dSAndroid Build Coastguard Worker return self.__class__(fp, 124*cda5da8dSAndroid Build Coastguard Worker self._mangle_from_, 125*cda5da8dSAndroid Build Coastguard Worker None, # Use policy setting, which we've adjusted 126*cda5da8dSAndroid Build Coastguard Worker policy=self.policy) 127*cda5da8dSAndroid Build Coastguard Worker 128*cda5da8dSAndroid Build Coastguard Worker # 129*cda5da8dSAndroid Build Coastguard Worker # Protected interface - undocumented ;/ 130*cda5da8dSAndroid Build Coastguard Worker # 131*cda5da8dSAndroid Build Coastguard Worker 132*cda5da8dSAndroid Build Coastguard Worker # Note that we use 'self.write' when what we are writing is coming from 133*cda5da8dSAndroid Build Coastguard Worker # the source, and self._fp.write when what we are writing is coming from a 134*cda5da8dSAndroid Build Coastguard Worker # buffer (because the Bytes subclass has already had a chance to transform 135*cda5da8dSAndroid Build Coastguard Worker # the data in its write method in that case). This is an entirely 136*cda5da8dSAndroid Build Coastguard Worker # pragmatic split determined by experiment; we could be more general by 137*cda5da8dSAndroid Build Coastguard Worker # always using write and having the Bytes subclass write method detect when 138*cda5da8dSAndroid Build Coastguard Worker # it has already transformed the input; but, since this whole thing is a 139*cda5da8dSAndroid Build Coastguard Worker # hack anyway this seems good enough. 140*cda5da8dSAndroid Build Coastguard Worker 141*cda5da8dSAndroid Build Coastguard Worker def _new_buffer(self): 142*cda5da8dSAndroid Build Coastguard Worker # BytesGenerator overrides this to return BytesIO. 143*cda5da8dSAndroid Build Coastguard Worker return StringIO() 144*cda5da8dSAndroid Build Coastguard Worker 145*cda5da8dSAndroid Build Coastguard Worker def _encode(self, s): 146*cda5da8dSAndroid Build Coastguard Worker # BytesGenerator overrides this to encode strings to bytes. 147*cda5da8dSAndroid Build Coastguard Worker return s 148*cda5da8dSAndroid Build Coastguard Worker 149*cda5da8dSAndroid Build Coastguard Worker def _write_lines(self, lines): 150*cda5da8dSAndroid Build Coastguard Worker # We have to transform the line endings. 151*cda5da8dSAndroid Build Coastguard Worker if not lines: 152*cda5da8dSAndroid Build Coastguard Worker return 153*cda5da8dSAndroid Build Coastguard Worker lines = NLCRE.split(lines) 154*cda5da8dSAndroid Build Coastguard Worker for line in lines[:-1]: 155*cda5da8dSAndroid Build Coastguard Worker self.write(line) 156*cda5da8dSAndroid Build Coastguard Worker self.write(self._NL) 157*cda5da8dSAndroid Build Coastguard Worker if lines[-1]: 158*cda5da8dSAndroid Build Coastguard Worker self.write(lines[-1]) 159*cda5da8dSAndroid Build Coastguard Worker # XXX logic tells me this else should be needed, but the tests fail 160*cda5da8dSAndroid Build Coastguard Worker # with it and pass without it. (NLCRE.split ends with a blank element 161*cda5da8dSAndroid Build Coastguard Worker # if and only if there was a trailing newline.) 162*cda5da8dSAndroid Build Coastguard Worker #else: 163*cda5da8dSAndroid Build Coastguard Worker # self.write(self._NL) 164*cda5da8dSAndroid Build Coastguard Worker 165*cda5da8dSAndroid Build Coastguard Worker def _write(self, msg): 166*cda5da8dSAndroid Build Coastguard Worker # We can't write the headers yet because of the following scenario: 167*cda5da8dSAndroid Build Coastguard Worker # say a multipart message includes the boundary string somewhere in 168*cda5da8dSAndroid Build Coastguard Worker # its body. We'd have to calculate the new boundary /before/ we write 169*cda5da8dSAndroid Build Coastguard Worker # the headers so that we can write the correct Content-Type: 170*cda5da8dSAndroid Build Coastguard Worker # parameter. 171*cda5da8dSAndroid Build Coastguard Worker # 172*cda5da8dSAndroid Build Coastguard Worker # The way we do this, so as to make the _handle_*() methods simpler, 173*cda5da8dSAndroid Build Coastguard Worker # is to cache any subpart writes into a buffer. The we write the 174*cda5da8dSAndroid Build Coastguard Worker # headers and the buffer contents. That way, subpart handlers can 175*cda5da8dSAndroid Build Coastguard Worker # Do The Right Thing, and can still modify the Content-Type: header if 176*cda5da8dSAndroid Build Coastguard Worker # necessary. 177*cda5da8dSAndroid Build Coastguard Worker oldfp = self._fp 178*cda5da8dSAndroid Build Coastguard Worker try: 179*cda5da8dSAndroid Build Coastguard Worker self._munge_cte = None 180*cda5da8dSAndroid Build Coastguard Worker self._fp = sfp = self._new_buffer() 181*cda5da8dSAndroid Build Coastguard Worker self._dispatch(msg) 182*cda5da8dSAndroid Build Coastguard Worker finally: 183*cda5da8dSAndroid Build Coastguard Worker self._fp = oldfp 184*cda5da8dSAndroid Build Coastguard Worker munge_cte = self._munge_cte 185*cda5da8dSAndroid Build Coastguard Worker del self._munge_cte 186*cda5da8dSAndroid Build Coastguard Worker # If we munged the cte, copy the message again and re-fix the CTE. 187*cda5da8dSAndroid Build Coastguard Worker if munge_cte: 188*cda5da8dSAndroid Build Coastguard Worker msg = deepcopy(msg) 189*cda5da8dSAndroid Build Coastguard Worker # Preserve the header order if the CTE header already exists. 190*cda5da8dSAndroid Build Coastguard Worker if msg.get('content-transfer-encoding') is None: 191*cda5da8dSAndroid Build Coastguard Worker msg['Content-Transfer-Encoding'] = munge_cte[0] 192*cda5da8dSAndroid Build Coastguard Worker else: 193*cda5da8dSAndroid Build Coastguard Worker msg.replace_header('content-transfer-encoding', munge_cte[0]) 194*cda5da8dSAndroid Build Coastguard Worker msg.replace_header('content-type', munge_cte[1]) 195*cda5da8dSAndroid Build Coastguard Worker # Write the headers. First we see if the message object wants to 196*cda5da8dSAndroid Build Coastguard Worker # handle that itself. If not, we'll do it generically. 197*cda5da8dSAndroid Build Coastguard Worker meth = getattr(msg, '_write_headers', None) 198*cda5da8dSAndroid Build Coastguard Worker if meth is None: 199*cda5da8dSAndroid Build Coastguard Worker self._write_headers(msg) 200*cda5da8dSAndroid Build Coastguard Worker else: 201*cda5da8dSAndroid Build Coastguard Worker meth(self) 202*cda5da8dSAndroid Build Coastguard Worker self._fp.write(sfp.getvalue()) 203*cda5da8dSAndroid Build Coastguard Worker 204*cda5da8dSAndroid Build Coastguard Worker def _dispatch(self, msg): 205*cda5da8dSAndroid Build Coastguard Worker # Get the Content-Type: for the message, then try to dispatch to 206*cda5da8dSAndroid Build Coastguard Worker # self._handle_<maintype>_<subtype>(). If there's no handler for the 207*cda5da8dSAndroid Build Coastguard Worker # full MIME type, then dispatch to self._handle_<maintype>(). If 208*cda5da8dSAndroid Build Coastguard Worker # that's missing too, then dispatch to self._writeBody(). 209*cda5da8dSAndroid Build Coastguard Worker main = msg.get_content_maintype() 210*cda5da8dSAndroid Build Coastguard Worker sub = msg.get_content_subtype() 211*cda5da8dSAndroid Build Coastguard Worker specific = UNDERSCORE.join((main, sub)).replace('-', '_') 212*cda5da8dSAndroid Build Coastguard Worker meth = getattr(self, '_handle_' + specific, None) 213*cda5da8dSAndroid Build Coastguard Worker if meth is None: 214*cda5da8dSAndroid Build Coastguard Worker generic = main.replace('-', '_') 215*cda5da8dSAndroid Build Coastguard Worker meth = getattr(self, '_handle_' + generic, None) 216*cda5da8dSAndroid Build Coastguard Worker if meth is None: 217*cda5da8dSAndroid Build Coastguard Worker meth = self._writeBody 218*cda5da8dSAndroid Build Coastguard Worker meth(msg) 219*cda5da8dSAndroid Build Coastguard Worker 220*cda5da8dSAndroid Build Coastguard Worker # 221*cda5da8dSAndroid Build Coastguard Worker # Default handlers 222*cda5da8dSAndroid Build Coastguard Worker # 223*cda5da8dSAndroid Build Coastguard Worker 224*cda5da8dSAndroid Build Coastguard Worker def _write_headers(self, msg): 225*cda5da8dSAndroid Build Coastguard Worker for h, v in msg.raw_items(): 226*cda5da8dSAndroid Build Coastguard Worker self.write(self.policy.fold(h, v)) 227*cda5da8dSAndroid Build Coastguard Worker # A blank line always separates headers from body 228*cda5da8dSAndroid Build Coastguard Worker self.write(self._NL) 229*cda5da8dSAndroid Build Coastguard Worker 230*cda5da8dSAndroid Build Coastguard Worker # 231*cda5da8dSAndroid Build Coastguard Worker # Handlers for writing types and subtypes 232*cda5da8dSAndroid Build Coastguard Worker # 233*cda5da8dSAndroid Build Coastguard Worker 234*cda5da8dSAndroid Build Coastguard Worker def _handle_text(self, msg): 235*cda5da8dSAndroid Build Coastguard Worker payload = msg.get_payload() 236*cda5da8dSAndroid Build Coastguard Worker if payload is None: 237*cda5da8dSAndroid Build Coastguard Worker return 238*cda5da8dSAndroid Build Coastguard Worker if not isinstance(payload, str): 239*cda5da8dSAndroid Build Coastguard Worker raise TypeError('string payload expected: %s' % type(payload)) 240*cda5da8dSAndroid Build Coastguard Worker if _has_surrogates(msg._payload): 241*cda5da8dSAndroid Build Coastguard Worker charset = msg.get_param('charset') 242*cda5da8dSAndroid Build Coastguard Worker if charset is not None: 243*cda5da8dSAndroid Build Coastguard Worker # XXX: This copy stuff is an ugly hack to avoid modifying the 244*cda5da8dSAndroid Build Coastguard Worker # existing message. 245*cda5da8dSAndroid Build Coastguard Worker msg = deepcopy(msg) 246*cda5da8dSAndroid Build Coastguard Worker del msg['content-transfer-encoding'] 247*cda5da8dSAndroid Build Coastguard Worker msg.set_payload(payload, charset) 248*cda5da8dSAndroid Build Coastguard Worker payload = msg.get_payload() 249*cda5da8dSAndroid Build Coastguard Worker self._munge_cte = (msg['content-transfer-encoding'], 250*cda5da8dSAndroid Build Coastguard Worker msg['content-type']) 251*cda5da8dSAndroid Build Coastguard Worker if self._mangle_from_: 252*cda5da8dSAndroid Build Coastguard Worker payload = fcre.sub('>From ', payload) 253*cda5da8dSAndroid Build Coastguard Worker self._write_lines(payload) 254*cda5da8dSAndroid Build Coastguard Worker 255*cda5da8dSAndroid Build Coastguard Worker # Default body handler 256*cda5da8dSAndroid Build Coastguard Worker _writeBody = _handle_text 257*cda5da8dSAndroid Build Coastguard Worker 258*cda5da8dSAndroid Build Coastguard Worker def _handle_multipart(self, msg): 259*cda5da8dSAndroid Build Coastguard Worker # The trick here is to write out each part separately, merge them all 260*cda5da8dSAndroid Build Coastguard Worker # together, and then make sure that the boundary we've chosen isn't 261*cda5da8dSAndroid Build Coastguard Worker # present in the payload. 262*cda5da8dSAndroid Build Coastguard Worker msgtexts = [] 263*cda5da8dSAndroid Build Coastguard Worker subparts = msg.get_payload() 264*cda5da8dSAndroid Build Coastguard Worker if subparts is None: 265*cda5da8dSAndroid Build Coastguard Worker subparts = [] 266*cda5da8dSAndroid Build Coastguard Worker elif isinstance(subparts, str): 267*cda5da8dSAndroid Build Coastguard Worker # e.g. a non-strict parse of a message with no starting boundary. 268*cda5da8dSAndroid Build Coastguard Worker self.write(subparts) 269*cda5da8dSAndroid Build Coastguard Worker return 270*cda5da8dSAndroid Build Coastguard Worker elif not isinstance(subparts, list): 271*cda5da8dSAndroid Build Coastguard Worker # Scalar payload 272*cda5da8dSAndroid Build Coastguard Worker subparts = [subparts] 273*cda5da8dSAndroid Build Coastguard Worker for part in subparts: 274*cda5da8dSAndroid Build Coastguard Worker s = self._new_buffer() 275*cda5da8dSAndroid Build Coastguard Worker g = self.clone(s) 276*cda5da8dSAndroid Build Coastguard Worker g.flatten(part, unixfrom=False, linesep=self._NL) 277*cda5da8dSAndroid Build Coastguard Worker msgtexts.append(s.getvalue()) 278*cda5da8dSAndroid Build Coastguard Worker # BAW: What about boundaries that are wrapped in double-quotes? 279*cda5da8dSAndroid Build Coastguard Worker boundary = msg.get_boundary() 280*cda5da8dSAndroid Build Coastguard Worker if not boundary: 281*cda5da8dSAndroid Build Coastguard Worker # Create a boundary that doesn't appear in any of the 282*cda5da8dSAndroid Build Coastguard Worker # message texts. 283*cda5da8dSAndroid Build Coastguard Worker alltext = self._encoded_NL.join(msgtexts) 284*cda5da8dSAndroid Build Coastguard Worker boundary = self._make_boundary(alltext) 285*cda5da8dSAndroid Build Coastguard Worker msg.set_boundary(boundary) 286*cda5da8dSAndroid Build Coastguard Worker # If there's a preamble, write it out, with a trailing CRLF 287*cda5da8dSAndroid Build Coastguard Worker if msg.preamble is not None: 288*cda5da8dSAndroid Build Coastguard Worker if self._mangle_from_: 289*cda5da8dSAndroid Build Coastguard Worker preamble = fcre.sub('>From ', msg.preamble) 290*cda5da8dSAndroid Build Coastguard Worker else: 291*cda5da8dSAndroid Build Coastguard Worker preamble = msg.preamble 292*cda5da8dSAndroid Build Coastguard Worker self._write_lines(preamble) 293*cda5da8dSAndroid Build Coastguard Worker self.write(self._NL) 294*cda5da8dSAndroid Build Coastguard Worker # dash-boundary transport-padding CRLF 295*cda5da8dSAndroid Build Coastguard Worker self.write('--' + boundary + self._NL) 296*cda5da8dSAndroid Build Coastguard Worker # body-part 297*cda5da8dSAndroid Build Coastguard Worker if msgtexts: 298*cda5da8dSAndroid Build Coastguard Worker self._fp.write(msgtexts.pop(0)) 299*cda5da8dSAndroid Build Coastguard Worker # *encapsulation 300*cda5da8dSAndroid Build Coastguard Worker # --> delimiter transport-padding 301*cda5da8dSAndroid Build Coastguard Worker # --> CRLF body-part 302*cda5da8dSAndroid Build Coastguard Worker for body_part in msgtexts: 303*cda5da8dSAndroid Build Coastguard Worker # delimiter transport-padding CRLF 304*cda5da8dSAndroid Build Coastguard Worker self.write(self._NL + '--' + boundary + self._NL) 305*cda5da8dSAndroid Build Coastguard Worker # body-part 306*cda5da8dSAndroid Build Coastguard Worker self._fp.write(body_part) 307*cda5da8dSAndroid Build Coastguard Worker # close-delimiter transport-padding 308*cda5da8dSAndroid Build Coastguard Worker self.write(self._NL + '--' + boundary + '--' + self._NL) 309*cda5da8dSAndroid Build Coastguard Worker if msg.epilogue is not None: 310*cda5da8dSAndroid Build Coastguard Worker if self._mangle_from_: 311*cda5da8dSAndroid Build Coastguard Worker epilogue = fcre.sub('>From ', msg.epilogue) 312*cda5da8dSAndroid Build Coastguard Worker else: 313*cda5da8dSAndroid Build Coastguard Worker epilogue = msg.epilogue 314*cda5da8dSAndroid Build Coastguard Worker self._write_lines(epilogue) 315*cda5da8dSAndroid Build Coastguard Worker 316*cda5da8dSAndroid Build Coastguard Worker def _handle_multipart_signed(self, msg): 317*cda5da8dSAndroid Build Coastguard Worker # The contents of signed parts has to stay unmodified in order to keep 318*cda5da8dSAndroid Build Coastguard Worker # the signature intact per RFC1847 2.1, so we disable header wrapping. 319*cda5da8dSAndroid Build Coastguard Worker # RDM: This isn't enough to completely preserve the part, but it helps. 320*cda5da8dSAndroid Build Coastguard Worker p = self.policy 321*cda5da8dSAndroid Build Coastguard Worker self.policy = p.clone(max_line_length=0) 322*cda5da8dSAndroid Build Coastguard Worker try: 323*cda5da8dSAndroid Build Coastguard Worker self._handle_multipart(msg) 324*cda5da8dSAndroid Build Coastguard Worker finally: 325*cda5da8dSAndroid Build Coastguard Worker self.policy = p 326*cda5da8dSAndroid Build Coastguard Worker 327*cda5da8dSAndroid Build Coastguard Worker def _handle_message_delivery_status(self, msg): 328*cda5da8dSAndroid Build Coastguard Worker # We can't just write the headers directly to self's file object 329*cda5da8dSAndroid Build Coastguard Worker # because this will leave an extra newline between the last header 330*cda5da8dSAndroid Build Coastguard Worker # block and the boundary. Sigh. 331*cda5da8dSAndroid Build Coastguard Worker blocks = [] 332*cda5da8dSAndroid Build Coastguard Worker for part in msg.get_payload(): 333*cda5da8dSAndroid Build Coastguard Worker s = self._new_buffer() 334*cda5da8dSAndroid Build Coastguard Worker g = self.clone(s) 335*cda5da8dSAndroid Build Coastguard Worker g.flatten(part, unixfrom=False, linesep=self._NL) 336*cda5da8dSAndroid Build Coastguard Worker text = s.getvalue() 337*cda5da8dSAndroid Build Coastguard Worker lines = text.split(self._encoded_NL) 338*cda5da8dSAndroid Build Coastguard Worker # Strip off the unnecessary trailing empty line 339*cda5da8dSAndroid Build Coastguard Worker if lines and lines[-1] == self._encoded_EMPTY: 340*cda5da8dSAndroid Build Coastguard Worker blocks.append(self._encoded_NL.join(lines[:-1])) 341*cda5da8dSAndroid Build Coastguard Worker else: 342*cda5da8dSAndroid Build Coastguard Worker blocks.append(text) 343*cda5da8dSAndroid Build Coastguard Worker # Now join all the blocks with an empty line. This has the lovely 344*cda5da8dSAndroid Build Coastguard Worker # effect of separating each block with an empty line, but not adding 345*cda5da8dSAndroid Build Coastguard Worker # an extra one after the last one. 346*cda5da8dSAndroid Build Coastguard Worker self._fp.write(self._encoded_NL.join(blocks)) 347*cda5da8dSAndroid Build Coastguard Worker 348*cda5da8dSAndroid Build Coastguard Worker def _handle_message(self, msg): 349*cda5da8dSAndroid Build Coastguard Worker s = self._new_buffer() 350*cda5da8dSAndroid Build Coastguard Worker g = self.clone(s) 351*cda5da8dSAndroid Build Coastguard Worker # The payload of a message/rfc822 part should be a multipart sequence 352*cda5da8dSAndroid Build Coastguard Worker # of length 1. The zeroth element of the list should be the Message 353*cda5da8dSAndroid Build Coastguard Worker # object for the subpart. Extract that object, stringify it, and 354*cda5da8dSAndroid Build Coastguard Worker # write it out. 355*cda5da8dSAndroid Build Coastguard Worker # Except, it turns out, when it's a string instead, which happens when 356*cda5da8dSAndroid Build Coastguard Worker # and only when HeaderParser is used on a message of mime type 357*cda5da8dSAndroid Build Coastguard Worker # message/rfc822. Such messages are generated by, for example, 358*cda5da8dSAndroid Build Coastguard Worker # Groupwise when forwarding unadorned messages. (Issue 7970.) So 359*cda5da8dSAndroid Build Coastguard Worker # in that case we just emit the string body. 360*cda5da8dSAndroid Build Coastguard Worker payload = msg._payload 361*cda5da8dSAndroid Build Coastguard Worker if isinstance(payload, list): 362*cda5da8dSAndroid Build Coastguard Worker g.flatten(msg.get_payload(0), unixfrom=False, linesep=self._NL) 363*cda5da8dSAndroid Build Coastguard Worker payload = s.getvalue() 364*cda5da8dSAndroid Build Coastguard Worker else: 365*cda5da8dSAndroid Build Coastguard Worker payload = self._encode(payload) 366*cda5da8dSAndroid Build Coastguard Worker self._fp.write(payload) 367*cda5da8dSAndroid Build Coastguard Worker 368*cda5da8dSAndroid Build Coastguard Worker # This used to be a module level function; we use a classmethod for this 369*cda5da8dSAndroid Build Coastguard Worker # and _compile_re so we can continue to provide the module level function 370*cda5da8dSAndroid Build Coastguard Worker # for backward compatibility by doing 371*cda5da8dSAndroid Build Coastguard Worker # _make_boundary = Generator._make_boundary 372*cda5da8dSAndroid Build Coastguard Worker # at the end of the module. It *is* internal, so we could drop that... 373*cda5da8dSAndroid Build Coastguard Worker @classmethod 374*cda5da8dSAndroid Build Coastguard Worker def _make_boundary(cls, text=None): 375*cda5da8dSAndroid Build Coastguard Worker # Craft a random boundary. If text is given, ensure that the chosen 376*cda5da8dSAndroid Build Coastguard Worker # boundary doesn't appear in the text. 377*cda5da8dSAndroid Build Coastguard Worker token = random.randrange(sys.maxsize) 378*cda5da8dSAndroid Build Coastguard Worker boundary = ('=' * 15) + (_fmt % token) + '==' 379*cda5da8dSAndroid Build Coastguard Worker if text is None: 380*cda5da8dSAndroid Build Coastguard Worker return boundary 381*cda5da8dSAndroid Build Coastguard Worker b = boundary 382*cda5da8dSAndroid Build Coastguard Worker counter = 0 383*cda5da8dSAndroid Build Coastguard Worker while True: 384*cda5da8dSAndroid Build Coastguard Worker cre = cls._compile_re('^--' + re.escape(b) + '(--)?$', re.MULTILINE) 385*cda5da8dSAndroid Build Coastguard Worker if not cre.search(text): 386*cda5da8dSAndroid Build Coastguard Worker break 387*cda5da8dSAndroid Build Coastguard Worker b = boundary + '.' + str(counter) 388*cda5da8dSAndroid Build Coastguard Worker counter += 1 389*cda5da8dSAndroid Build Coastguard Worker return b 390*cda5da8dSAndroid Build Coastguard Worker 391*cda5da8dSAndroid Build Coastguard Worker @classmethod 392*cda5da8dSAndroid Build Coastguard Worker def _compile_re(cls, s, flags): 393*cda5da8dSAndroid Build Coastguard Worker return re.compile(s, flags) 394*cda5da8dSAndroid Build Coastguard Worker 395*cda5da8dSAndroid Build Coastguard Worker 396*cda5da8dSAndroid Build Coastguard Workerclass BytesGenerator(Generator): 397*cda5da8dSAndroid Build Coastguard Worker """Generates a bytes version of a Message object tree. 398*cda5da8dSAndroid Build Coastguard Worker 399*cda5da8dSAndroid Build Coastguard Worker Functionally identical to the base Generator except that the output is 400*cda5da8dSAndroid Build Coastguard Worker bytes and not string. When surrogates were used in the input to encode 401*cda5da8dSAndroid Build Coastguard Worker bytes, these are decoded back to bytes for output. If the policy has 402*cda5da8dSAndroid Build Coastguard Worker cte_type set to 7bit, then the message is transformed such that the 403*cda5da8dSAndroid Build Coastguard Worker non-ASCII bytes are properly content transfer encoded, using the charset 404*cda5da8dSAndroid Build Coastguard Worker unknown-8bit. 405*cda5da8dSAndroid Build Coastguard Worker 406*cda5da8dSAndroid Build Coastguard Worker The outfp object must accept bytes in its write method. 407*cda5da8dSAndroid Build Coastguard Worker """ 408*cda5da8dSAndroid Build Coastguard Worker 409*cda5da8dSAndroid Build Coastguard Worker def write(self, s): 410*cda5da8dSAndroid Build Coastguard Worker self._fp.write(s.encode('ascii', 'surrogateescape')) 411*cda5da8dSAndroid Build Coastguard Worker 412*cda5da8dSAndroid Build Coastguard Worker def _new_buffer(self): 413*cda5da8dSAndroid Build Coastguard Worker return BytesIO() 414*cda5da8dSAndroid Build Coastguard Worker 415*cda5da8dSAndroid Build Coastguard Worker def _encode(self, s): 416*cda5da8dSAndroid Build Coastguard Worker return s.encode('ascii') 417*cda5da8dSAndroid Build Coastguard Worker 418*cda5da8dSAndroid Build Coastguard Worker def _write_headers(self, msg): 419*cda5da8dSAndroid Build Coastguard Worker # This is almost the same as the string version, except for handling 420*cda5da8dSAndroid Build Coastguard Worker # strings with 8bit bytes. 421*cda5da8dSAndroid Build Coastguard Worker for h, v in msg.raw_items(): 422*cda5da8dSAndroid Build Coastguard Worker self._fp.write(self.policy.fold_binary(h, v)) 423*cda5da8dSAndroid Build Coastguard Worker # A blank line always separates headers from body 424*cda5da8dSAndroid Build Coastguard Worker self.write(self._NL) 425*cda5da8dSAndroid Build Coastguard Worker 426*cda5da8dSAndroid Build Coastguard Worker def _handle_text(self, msg): 427*cda5da8dSAndroid Build Coastguard Worker # If the string has surrogates the original source was bytes, so 428*cda5da8dSAndroid Build Coastguard Worker # just write it back out. 429*cda5da8dSAndroid Build Coastguard Worker if msg._payload is None: 430*cda5da8dSAndroid Build Coastguard Worker return 431*cda5da8dSAndroid Build Coastguard Worker if _has_surrogates(msg._payload) and not self.policy.cte_type=='7bit': 432*cda5da8dSAndroid Build Coastguard Worker if self._mangle_from_: 433*cda5da8dSAndroid Build Coastguard Worker msg._payload = fcre.sub(">From ", msg._payload) 434*cda5da8dSAndroid Build Coastguard Worker self._write_lines(msg._payload) 435*cda5da8dSAndroid Build Coastguard Worker else: 436*cda5da8dSAndroid Build Coastguard Worker super(BytesGenerator,self)._handle_text(msg) 437*cda5da8dSAndroid Build Coastguard Worker 438*cda5da8dSAndroid Build Coastguard Worker # Default body handler 439*cda5da8dSAndroid Build Coastguard Worker _writeBody = _handle_text 440*cda5da8dSAndroid Build Coastguard Worker 441*cda5da8dSAndroid Build Coastguard Worker @classmethod 442*cda5da8dSAndroid Build Coastguard Worker def _compile_re(cls, s, flags): 443*cda5da8dSAndroid Build Coastguard Worker return re.compile(s.encode('ascii'), flags) 444*cda5da8dSAndroid Build Coastguard Worker 445*cda5da8dSAndroid Build Coastguard Worker 446*cda5da8dSAndroid Build Coastguard Worker 447*cda5da8dSAndroid Build Coastguard Worker_FMT = '[Non-text (%(type)s) part of message omitted, filename %(filename)s]' 448*cda5da8dSAndroid Build Coastguard Worker 449*cda5da8dSAndroid Build Coastguard Workerclass DecodedGenerator(Generator): 450*cda5da8dSAndroid Build Coastguard Worker """Generates a text representation of a message. 451*cda5da8dSAndroid Build Coastguard Worker 452*cda5da8dSAndroid Build Coastguard Worker Like the Generator base class, except that non-text parts are substituted 453*cda5da8dSAndroid Build Coastguard Worker with a format string representing the part. 454*cda5da8dSAndroid Build Coastguard Worker """ 455*cda5da8dSAndroid Build Coastguard Worker def __init__(self, outfp, mangle_from_=None, maxheaderlen=None, fmt=None, *, 456*cda5da8dSAndroid Build Coastguard Worker policy=None): 457*cda5da8dSAndroid Build Coastguard Worker """Like Generator.__init__() except that an additional optional 458*cda5da8dSAndroid Build Coastguard Worker argument is allowed. 459*cda5da8dSAndroid Build Coastguard Worker 460*cda5da8dSAndroid Build Coastguard Worker Walks through all subparts of a message. If the subpart is of main 461*cda5da8dSAndroid Build Coastguard Worker type `text', then it prints the decoded payload of the subpart. 462*cda5da8dSAndroid Build Coastguard Worker 463*cda5da8dSAndroid Build Coastguard Worker Otherwise, fmt is a format string that is used instead of the message 464*cda5da8dSAndroid Build Coastguard Worker payload. fmt is expanded with the following keywords (in 465*cda5da8dSAndroid Build Coastguard Worker %(keyword)s format): 466*cda5da8dSAndroid Build Coastguard Worker 467*cda5da8dSAndroid Build Coastguard Worker type : Full MIME type of the non-text part 468*cda5da8dSAndroid Build Coastguard Worker maintype : Main MIME type of the non-text part 469*cda5da8dSAndroid Build Coastguard Worker subtype : Sub-MIME type of the non-text part 470*cda5da8dSAndroid Build Coastguard Worker filename : Filename of the non-text part 471*cda5da8dSAndroid Build Coastguard Worker description: Description associated with the non-text part 472*cda5da8dSAndroid Build Coastguard Worker encoding : Content transfer encoding of the non-text part 473*cda5da8dSAndroid Build Coastguard Worker 474*cda5da8dSAndroid Build Coastguard Worker The default value for fmt is None, meaning 475*cda5da8dSAndroid Build Coastguard Worker 476*cda5da8dSAndroid Build Coastguard Worker [Non-text (%(type)s) part of message omitted, filename %(filename)s] 477*cda5da8dSAndroid Build Coastguard Worker """ 478*cda5da8dSAndroid Build Coastguard Worker Generator.__init__(self, outfp, mangle_from_, maxheaderlen, 479*cda5da8dSAndroid Build Coastguard Worker policy=policy) 480*cda5da8dSAndroid Build Coastguard Worker if fmt is None: 481*cda5da8dSAndroid Build Coastguard Worker self._fmt = _FMT 482*cda5da8dSAndroid Build Coastguard Worker else: 483*cda5da8dSAndroid Build Coastguard Worker self._fmt = fmt 484*cda5da8dSAndroid Build Coastguard Worker 485*cda5da8dSAndroid Build Coastguard Worker def _dispatch(self, msg): 486*cda5da8dSAndroid Build Coastguard Worker for part in msg.walk(): 487*cda5da8dSAndroid Build Coastguard Worker maintype = part.get_content_maintype() 488*cda5da8dSAndroid Build Coastguard Worker if maintype == 'text': 489*cda5da8dSAndroid Build Coastguard Worker print(part.get_payload(decode=False), file=self) 490*cda5da8dSAndroid Build Coastguard Worker elif maintype == 'multipart': 491*cda5da8dSAndroid Build Coastguard Worker # Just skip this 492*cda5da8dSAndroid Build Coastguard Worker pass 493*cda5da8dSAndroid Build Coastguard Worker else: 494*cda5da8dSAndroid Build Coastguard Worker print(self._fmt % { 495*cda5da8dSAndroid Build Coastguard Worker 'type' : part.get_content_type(), 496*cda5da8dSAndroid Build Coastguard Worker 'maintype' : part.get_content_maintype(), 497*cda5da8dSAndroid Build Coastguard Worker 'subtype' : part.get_content_subtype(), 498*cda5da8dSAndroid Build Coastguard Worker 'filename' : part.get_filename('[no filename]'), 499*cda5da8dSAndroid Build Coastguard Worker 'description': part.get('Content-Description', 500*cda5da8dSAndroid Build Coastguard Worker '[no description]'), 501*cda5da8dSAndroid Build Coastguard Worker 'encoding' : part.get('Content-Transfer-Encoding', 502*cda5da8dSAndroid Build Coastguard Worker '[no encoding]'), 503*cda5da8dSAndroid Build Coastguard Worker }, file=self) 504*cda5da8dSAndroid Build Coastguard Worker 505*cda5da8dSAndroid Build Coastguard Worker 506*cda5da8dSAndroid Build Coastguard Worker 507*cda5da8dSAndroid Build Coastguard Worker# Helper used by Generator._make_boundary 508*cda5da8dSAndroid Build Coastguard Worker_width = len(repr(sys.maxsize-1)) 509*cda5da8dSAndroid Build Coastguard Worker_fmt = '%%0%dd' % _width 510*cda5da8dSAndroid Build Coastguard Worker 511*cda5da8dSAndroid Build Coastguard Worker# Backward compatibility 512*cda5da8dSAndroid Build Coastguard Worker_make_boundary = Generator._make_boundary 513