xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/email/message.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker# Copyright (C) 2001-2007 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"""Basic message object for the email package object model."""
6*cda5da8dSAndroid Build Coastguard Worker
7*cda5da8dSAndroid Build Coastguard Worker__all__ = ['Message', 'EmailMessage']
8*cda5da8dSAndroid Build Coastguard Worker
9*cda5da8dSAndroid Build Coastguard Workerimport binascii
10*cda5da8dSAndroid Build Coastguard Workerimport re
11*cda5da8dSAndroid Build Coastguard Workerimport quopri
12*cda5da8dSAndroid Build Coastguard Workerfrom io import BytesIO, StringIO
13*cda5da8dSAndroid Build Coastguard Worker
14*cda5da8dSAndroid Build Coastguard Worker# Intrapackage imports
15*cda5da8dSAndroid Build Coastguard Workerfrom email import utils
16*cda5da8dSAndroid Build Coastguard Workerfrom email import errors
17*cda5da8dSAndroid Build Coastguard Workerfrom email._policybase import Policy, compat32
18*cda5da8dSAndroid Build Coastguard Workerfrom email import charset as _charset
19*cda5da8dSAndroid Build Coastguard Workerfrom email._encoded_words import decode_b
20*cda5da8dSAndroid Build Coastguard WorkerCharset = _charset.Charset
21*cda5da8dSAndroid Build Coastguard Worker
22*cda5da8dSAndroid Build Coastguard WorkerSEMISPACE = '; '
23*cda5da8dSAndroid Build Coastguard Worker
24*cda5da8dSAndroid Build Coastguard Worker# Regular expression that matches `special' characters in parameters, the
25*cda5da8dSAndroid Build Coastguard Worker# existence of which force quoting of the parameter value.
26*cda5da8dSAndroid Build Coastguard Workertspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]')
27*cda5da8dSAndroid Build Coastguard Worker
28*cda5da8dSAndroid Build Coastguard Worker
29*cda5da8dSAndroid Build Coastguard Workerdef _splitparam(param):
30*cda5da8dSAndroid Build Coastguard Worker    # Split header parameters.  BAW: this may be too simple.  It isn't
31*cda5da8dSAndroid Build Coastguard Worker    # strictly RFC 2045 (section 5.1) compliant, but it catches most headers
32*cda5da8dSAndroid Build Coastguard Worker    # found in the wild.  We may eventually need a full fledged parser.
33*cda5da8dSAndroid Build Coastguard Worker    # RDM: we might have a Header here; for now just stringify it.
34*cda5da8dSAndroid Build Coastguard Worker    a, sep, b = str(param).partition(';')
35*cda5da8dSAndroid Build Coastguard Worker    if not sep:
36*cda5da8dSAndroid Build Coastguard Worker        return a.strip(), None
37*cda5da8dSAndroid Build Coastguard Worker    return a.strip(), b.strip()
38*cda5da8dSAndroid Build Coastguard Worker
39*cda5da8dSAndroid Build Coastguard Workerdef _formatparam(param, value=None, quote=True):
40*cda5da8dSAndroid Build Coastguard Worker    """Convenience function to format and return a key=value pair.
41*cda5da8dSAndroid Build Coastguard Worker
42*cda5da8dSAndroid Build Coastguard Worker    This will quote the value if needed or if quote is true.  If value is a
43*cda5da8dSAndroid Build Coastguard Worker    three tuple (charset, language, value), it will be encoded according
44*cda5da8dSAndroid Build Coastguard Worker    to RFC2231 rules.  If it contains non-ascii characters it will likewise
45*cda5da8dSAndroid Build Coastguard Worker    be encoded according to RFC2231 rules, using the utf-8 charset and
46*cda5da8dSAndroid Build Coastguard Worker    a null language.
47*cda5da8dSAndroid Build Coastguard Worker    """
48*cda5da8dSAndroid Build Coastguard Worker    if value is not None and len(value) > 0:
49*cda5da8dSAndroid Build Coastguard Worker        # A tuple is used for RFC 2231 encoded parameter values where items
50*cda5da8dSAndroid Build Coastguard Worker        # are (charset, language, value).  charset is a string, not a Charset
51*cda5da8dSAndroid Build Coastguard Worker        # instance.  RFC 2231 encoded values are never quoted, per RFC.
52*cda5da8dSAndroid Build Coastguard Worker        if isinstance(value, tuple):
53*cda5da8dSAndroid Build Coastguard Worker            # Encode as per RFC 2231
54*cda5da8dSAndroid Build Coastguard Worker            param += '*'
55*cda5da8dSAndroid Build Coastguard Worker            value = utils.encode_rfc2231(value[2], value[0], value[1])
56*cda5da8dSAndroid Build Coastguard Worker            return '%s=%s' % (param, value)
57*cda5da8dSAndroid Build Coastguard Worker        else:
58*cda5da8dSAndroid Build Coastguard Worker            try:
59*cda5da8dSAndroid Build Coastguard Worker                value.encode('ascii')
60*cda5da8dSAndroid Build Coastguard Worker            except UnicodeEncodeError:
61*cda5da8dSAndroid Build Coastguard Worker                param += '*'
62*cda5da8dSAndroid Build Coastguard Worker                value = utils.encode_rfc2231(value, 'utf-8', '')
63*cda5da8dSAndroid Build Coastguard Worker                return '%s=%s' % (param, value)
64*cda5da8dSAndroid Build Coastguard Worker        # BAW: Please check this.  I think that if quote is set it should
65*cda5da8dSAndroid Build Coastguard Worker        # force quoting even if not necessary.
66*cda5da8dSAndroid Build Coastguard Worker        if quote or tspecials.search(value):
67*cda5da8dSAndroid Build Coastguard Worker            return '%s="%s"' % (param, utils.quote(value))
68*cda5da8dSAndroid Build Coastguard Worker        else:
69*cda5da8dSAndroid Build Coastguard Worker            return '%s=%s' % (param, value)
70*cda5da8dSAndroid Build Coastguard Worker    else:
71*cda5da8dSAndroid Build Coastguard Worker        return param
72*cda5da8dSAndroid Build Coastguard Worker
73*cda5da8dSAndroid Build Coastguard Workerdef _parseparam(s):
74*cda5da8dSAndroid Build Coastguard Worker    # RDM This might be a Header, so for now stringify it.
75*cda5da8dSAndroid Build Coastguard Worker    s = ';' + str(s)
76*cda5da8dSAndroid Build Coastguard Worker    plist = []
77*cda5da8dSAndroid Build Coastguard Worker    while s[:1] == ';':
78*cda5da8dSAndroid Build Coastguard Worker        s = s[1:]
79*cda5da8dSAndroid Build Coastguard Worker        end = s.find(';')
80*cda5da8dSAndroid Build Coastguard Worker        while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2:
81*cda5da8dSAndroid Build Coastguard Worker            end = s.find(';', end + 1)
82*cda5da8dSAndroid Build Coastguard Worker        if end < 0:
83*cda5da8dSAndroid Build Coastguard Worker            end = len(s)
84*cda5da8dSAndroid Build Coastguard Worker        f = s[:end]
85*cda5da8dSAndroid Build Coastguard Worker        if '=' in f:
86*cda5da8dSAndroid Build Coastguard Worker            i = f.index('=')
87*cda5da8dSAndroid Build Coastguard Worker            f = f[:i].strip().lower() + '=' + f[i+1:].strip()
88*cda5da8dSAndroid Build Coastguard Worker        plist.append(f.strip())
89*cda5da8dSAndroid Build Coastguard Worker        s = s[end:]
90*cda5da8dSAndroid Build Coastguard Worker    return plist
91*cda5da8dSAndroid Build Coastguard Worker
92*cda5da8dSAndroid Build Coastguard Worker
93*cda5da8dSAndroid Build Coastguard Workerdef _unquotevalue(value):
94*cda5da8dSAndroid Build Coastguard Worker    # This is different than utils.collapse_rfc2231_value() because it doesn't
95*cda5da8dSAndroid Build Coastguard Worker    # try to convert the value to a unicode.  Message.get_param() and
96*cda5da8dSAndroid Build Coastguard Worker    # Message.get_params() are both currently defined to return the tuple in
97*cda5da8dSAndroid Build Coastguard Worker    # the face of RFC 2231 parameters.
98*cda5da8dSAndroid Build Coastguard Worker    if isinstance(value, tuple):
99*cda5da8dSAndroid Build Coastguard Worker        return value[0], value[1], utils.unquote(value[2])
100*cda5da8dSAndroid Build Coastguard Worker    else:
101*cda5da8dSAndroid Build Coastguard Worker        return utils.unquote(value)
102*cda5da8dSAndroid Build Coastguard Worker
103*cda5da8dSAndroid Build Coastguard Worker
104*cda5da8dSAndroid Build Coastguard Workerdef _decode_uu(encoded):
105*cda5da8dSAndroid Build Coastguard Worker    """Decode uuencoded data."""
106*cda5da8dSAndroid Build Coastguard Worker    decoded_lines = []
107*cda5da8dSAndroid Build Coastguard Worker    encoded_lines_iter = iter(encoded.splitlines())
108*cda5da8dSAndroid Build Coastguard Worker    for line in encoded_lines_iter:
109*cda5da8dSAndroid Build Coastguard Worker        if line.startswith(b"begin "):
110*cda5da8dSAndroid Build Coastguard Worker            mode, _, path = line.removeprefix(b"begin ").partition(b" ")
111*cda5da8dSAndroid Build Coastguard Worker            try:
112*cda5da8dSAndroid Build Coastguard Worker                int(mode, base=8)
113*cda5da8dSAndroid Build Coastguard Worker            except ValueError:
114*cda5da8dSAndroid Build Coastguard Worker                continue
115*cda5da8dSAndroid Build Coastguard Worker            else:
116*cda5da8dSAndroid Build Coastguard Worker                break
117*cda5da8dSAndroid Build Coastguard Worker    else:
118*cda5da8dSAndroid Build Coastguard Worker        raise ValueError("`begin` line not found")
119*cda5da8dSAndroid Build Coastguard Worker    for line in encoded_lines_iter:
120*cda5da8dSAndroid Build Coastguard Worker        if not line:
121*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Truncated input")
122*cda5da8dSAndroid Build Coastguard Worker        elif line.strip(b' \t\r\n\f') == b'end':
123*cda5da8dSAndroid Build Coastguard Worker            break
124*cda5da8dSAndroid Build Coastguard Worker        try:
125*cda5da8dSAndroid Build Coastguard Worker            decoded_line = binascii.a2b_uu(line)
126*cda5da8dSAndroid Build Coastguard Worker        except binascii.Error:
127*cda5da8dSAndroid Build Coastguard Worker            # Workaround for broken uuencoders by /Fredrik Lundh
128*cda5da8dSAndroid Build Coastguard Worker            nbytes = (((line[0]-32) & 63) * 4 + 5) // 3
129*cda5da8dSAndroid Build Coastguard Worker            decoded_line = binascii.a2b_uu(line[:nbytes])
130*cda5da8dSAndroid Build Coastguard Worker        decoded_lines.append(decoded_line)
131*cda5da8dSAndroid Build Coastguard Worker
132*cda5da8dSAndroid Build Coastguard Worker    return b''.join(decoded_lines)
133*cda5da8dSAndroid Build Coastguard Worker
134*cda5da8dSAndroid Build Coastguard Worker
135*cda5da8dSAndroid Build Coastguard Workerclass Message:
136*cda5da8dSAndroid Build Coastguard Worker    """Basic message object.
137*cda5da8dSAndroid Build Coastguard Worker
138*cda5da8dSAndroid Build Coastguard Worker    A message object is defined as something that has a bunch of RFC 2822
139*cda5da8dSAndroid Build Coastguard Worker    headers and a payload.  It may optionally have an envelope header
140*cda5da8dSAndroid Build Coastguard Worker    (a.k.a. Unix-From or From_ header).  If the message is a container (i.e. a
141*cda5da8dSAndroid Build Coastguard Worker    multipart or a message/rfc822), then the payload is a list of Message
142*cda5da8dSAndroid Build Coastguard Worker    objects, otherwise it is a string.
143*cda5da8dSAndroid Build Coastguard Worker
144*cda5da8dSAndroid Build Coastguard Worker    Message objects implement part of the `mapping' interface, which assumes
145*cda5da8dSAndroid Build Coastguard Worker    there is exactly one occurrence of the header per message.  Some headers
146*cda5da8dSAndroid Build Coastguard Worker    do in fact appear multiple times (e.g. Received) and for those headers,
147*cda5da8dSAndroid Build Coastguard Worker    you must use the explicit API to set or get all the headers.  Not all of
148*cda5da8dSAndroid Build Coastguard Worker    the mapping methods are implemented.
149*cda5da8dSAndroid Build Coastguard Worker    """
150*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, policy=compat32):
151*cda5da8dSAndroid Build Coastguard Worker        self.policy = policy
152*cda5da8dSAndroid Build Coastguard Worker        self._headers = []
153*cda5da8dSAndroid Build Coastguard Worker        self._unixfrom = None
154*cda5da8dSAndroid Build Coastguard Worker        self._payload = None
155*cda5da8dSAndroid Build Coastguard Worker        self._charset = None
156*cda5da8dSAndroid Build Coastguard Worker        # Defaults for multipart messages
157*cda5da8dSAndroid Build Coastguard Worker        self.preamble = self.epilogue = None
158*cda5da8dSAndroid Build Coastguard Worker        self.defects = []
159*cda5da8dSAndroid Build Coastguard Worker        # Default content type
160*cda5da8dSAndroid Build Coastguard Worker        self._default_type = 'text/plain'
161*cda5da8dSAndroid Build Coastguard Worker
162*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
163*cda5da8dSAndroid Build Coastguard Worker        """Return the entire formatted message as a string.
164*cda5da8dSAndroid Build Coastguard Worker        """
165*cda5da8dSAndroid Build Coastguard Worker        return self.as_string()
166*cda5da8dSAndroid Build Coastguard Worker
167*cda5da8dSAndroid Build Coastguard Worker    def as_string(self, unixfrom=False, maxheaderlen=0, policy=None):
168*cda5da8dSAndroid Build Coastguard Worker        """Return the entire formatted message as a string.
169*cda5da8dSAndroid Build Coastguard Worker
170*cda5da8dSAndroid Build Coastguard Worker        Optional 'unixfrom', when true, means include the Unix From_ envelope
171*cda5da8dSAndroid Build Coastguard Worker        header.  For backward compatibility reasons, if maxheaderlen is
172*cda5da8dSAndroid Build Coastguard Worker        not specified it defaults to 0, so you must override it explicitly
173*cda5da8dSAndroid Build Coastguard Worker        if you want a different maxheaderlen.  'policy' is passed to the
174*cda5da8dSAndroid Build Coastguard Worker        Generator instance used to serialize the message; if it is not
175*cda5da8dSAndroid Build Coastguard Worker        specified the policy associated with the message instance is used.
176*cda5da8dSAndroid Build Coastguard Worker
177*cda5da8dSAndroid Build Coastguard Worker        If the message object contains binary data that is not encoded
178*cda5da8dSAndroid Build Coastguard Worker        according to RFC standards, the non-compliant data will be replaced by
179*cda5da8dSAndroid Build Coastguard Worker        unicode "unknown character" code points.
180*cda5da8dSAndroid Build Coastguard Worker        """
181*cda5da8dSAndroid Build Coastguard Worker        from email.generator import Generator
182*cda5da8dSAndroid Build Coastguard Worker        policy = self.policy if policy is None else policy
183*cda5da8dSAndroid Build Coastguard Worker        fp = StringIO()
184*cda5da8dSAndroid Build Coastguard Worker        g = Generator(fp,
185*cda5da8dSAndroid Build Coastguard Worker                      mangle_from_=False,
186*cda5da8dSAndroid Build Coastguard Worker                      maxheaderlen=maxheaderlen,
187*cda5da8dSAndroid Build Coastguard Worker                      policy=policy)
188*cda5da8dSAndroid Build Coastguard Worker        g.flatten(self, unixfrom=unixfrom)
189*cda5da8dSAndroid Build Coastguard Worker        return fp.getvalue()
190*cda5da8dSAndroid Build Coastguard Worker
191*cda5da8dSAndroid Build Coastguard Worker    def __bytes__(self):
192*cda5da8dSAndroid Build Coastguard Worker        """Return the entire formatted message as a bytes object.
193*cda5da8dSAndroid Build Coastguard Worker        """
194*cda5da8dSAndroid Build Coastguard Worker        return self.as_bytes()
195*cda5da8dSAndroid Build Coastguard Worker
196*cda5da8dSAndroid Build Coastguard Worker    def as_bytes(self, unixfrom=False, policy=None):
197*cda5da8dSAndroid Build Coastguard Worker        """Return the entire formatted message as a bytes object.
198*cda5da8dSAndroid Build Coastguard Worker
199*cda5da8dSAndroid Build Coastguard Worker        Optional 'unixfrom', when true, means include the Unix From_ envelope
200*cda5da8dSAndroid Build Coastguard Worker        header.  'policy' is passed to the BytesGenerator instance used to
201*cda5da8dSAndroid Build Coastguard Worker        serialize the message; if not specified the policy associated with
202*cda5da8dSAndroid Build Coastguard Worker        the message instance is used.
203*cda5da8dSAndroid Build Coastguard Worker        """
204*cda5da8dSAndroid Build Coastguard Worker        from email.generator import BytesGenerator
205*cda5da8dSAndroid Build Coastguard Worker        policy = self.policy if policy is None else policy
206*cda5da8dSAndroid Build Coastguard Worker        fp = BytesIO()
207*cda5da8dSAndroid Build Coastguard Worker        g = BytesGenerator(fp, mangle_from_=False, policy=policy)
208*cda5da8dSAndroid Build Coastguard Worker        g.flatten(self, unixfrom=unixfrom)
209*cda5da8dSAndroid Build Coastguard Worker        return fp.getvalue()
210*cda5da8dSAndroid Build Coastguard Worker
211*cda5da8dSAndroid Build Coastguard Worker    def is_multipart(self):
212*cda5da8dSAndroid Build Coastguard Worker        """Return True if the message consists of multiple parts."""
213*cda5da8dSAndroid Build Coastguard Worker        return isinstance(self._payload, list)
214*cda5da8dSAndroid Build Coastguard Worker
215*cda5da8dSAndroid Build Coastguard Worker    #
216*cda5da8dSAndroid Build Coastguard Worker    # Unix From_ line
217*cda5da8dSAndroid Build Coastguard Worker    #
218*cda5da8dSAndroid Build Coastguard Worker    def set_unixfrom(self, unixfrom):
219*cda5da8dSAndroid Build Coastguard Worker        self._unixfrom = unixfrom
220*cda5da8dSAndroid Build Coastguard Worker
221*cda5da8dSAndroid Build Coastguard Worker    def get_unixfrom(self):
222*cda5da8dSAndroid Build Coastguard Worker        return self._unixfrom
223*cda5da8dSAndroid Build Coastguard Worker
224*cda5da8dSAndroid Build Coastguard Worker    #
225*cda5da8dSAndroid Build Coastguard Worker    # Payload manipulation.
226*cda5da8dSAndroid Build Coastguard Worker    #
227*cda5da8dSAndroid Build Coastguard Worker    def attach(self, payload):
228*cda5da8dSAndroid Build Coastguard Worker        """Add the given payload to the current payload.
229*cda5da8dSAndroid Build Coastguard Worker
230*cda5da8dSAndroid Build Coastguard Worker        The current payload will always be a list of objects after this method
231*cda5da8dSAndroid Build Coastguard Worker        is called.  If you want to set the payload to a scalar object, use
232*cda5da8dSAndroid Build Coastguard Worker        set_payload() instead.
233*cda5da8dSAndroid Build Coastguard Worker        """
234*cda5da8dSAndroid Build Coastguard Worker        if self._payload is None:
235*cda5da8dSAndroid Build Coastguard Worker            self._payload = [payload]
236*cda5da8dSAndroid Build Coastguard Worker        else:
237*cda5da8dSAndroid Build Coastguard Worker            try:
238*cda5da8dSAndroid Build Coastguard Worker                self._payload.append(payload)
239*cda5da8dSAndroid Build Coastguard Worker            except AttributeError:
240*cda5da8dSAndroid Build Coastguard Worker                raise TypeError("Attach is not valid on a message with a"
241*cda5da8dSAndroid Build Coastguard Worker                                " non-multipart payload")
242*cda5da8dSAndroid Build Coastguard Worker
243*cda5da8dSAndroid Build Coastguard Worker    def get_payload(self, i=None, decode=False):
244*cda5da8dSAndroid Build Coastguard Worker        """Return a reference to the payload.
245*cda5da8dSAndroid Build Coastguard Worker
246*cda5da8dSAndroid Build Coastguard Worker        The payload will either be a list object or a string.  If you mutate
247*cda5da8dSAndroid Build Coastguard Worker        the list object, you modify the message's payload in place.  Optional
248*cda5da8dSAndroid Build Coastguard Worker        i returns that index into the payload.
249*cda5da8dSAndroid Build Coastguard Worker
250*cda5da8dSAndroid Build Coastguard Worker        Optional decode is a flag indicating whether the payload should be
251*cda5da8dSAndroid Build Coastguard Worker        decoded or not, according to the Content-Transfer-Encoding header
252*cda5da8dSAndroid Build Coastguard Worker        (default is False).
253*cda5da8dSAndroid Build Coastguard Worker
254*cda5da8dSAndroid Build Coastguard Worker        When True and the message is not a multipart, the payload will be
255*cda5da8dSAndroid Build Coastguard Worker        decoded if this header's value is `quoted-printable' or `base64'.  If
256*cda5da8dSAndroid Build Coastguard Worker        some other encoding is used, or the header is missing, or if the
257*cda5da8dSAndroid Build Coastguard Worker        payload has bogus data (i.e. bogus base64 or uuencoded data), the
258*cda5da8dSAndroid Build Coastguard Worker        payload is returned as-is.
259*cda5da8dSAndroid Build Coastguard Worker
260*cda5da8dSAndroid Build Coastguard Worker        If the message is a multipart and the decode flag is True, then None
261*cda5da8dSAndroid Build Coastguard Worker        is returned.
262*cda5da8dSAndroid Build Coastguard Worker        """
263*cda5da8dSAndroid Build Coastguard Worker        # Here is the logic table for this code, based on the email5.0.0 code:
264*cda5da8dSAndroid Build Coastguard Worker        #   i     decode  is_multipart  result
265*cda5da8dSAndroid Build Coastguard Worker        # ------  ------  ------------  ------------------------------
266*cda5da8dSAndroid Build Coastguard Worker        #  None   True    True          None
267*cda5da8dSAndroid Build Coastguard Worker        #   i     True    True          None
268*cda5da8dSAndroid Build Coastguard Worker        #  None   False   True          _payload (a list)
269*cda5da8dSAndroid Build Coastguard Worker        #   i     False   True          _payload element i (a Message)
270*cda5da8dSAndroid Build Coastguard Worker        #   i     False   False         error (not a list)
271*cda5da8dSAndroid Build Coastguard Worker        #   i     True    False         error (not a list)
272*cda5da8dSAndroid Build Coastguard Worker        #  None   False   False         _payload
273*cda5da8dSAndroid Build Coastguard Worker        #  None   True    False         _payload decoded (bytes)
274*cda5da8dSAndroid Build Coastguard Worker        # Note that Barry planned to factor out the 'decode' case, but that
275*cda5da8dSAndroid Build Coastguard Worker        # isn't so easy now that we handle the 8 bit data, which needs to be
276*cda5da8dSAndroid Build Coastguard Worker        # converted in both the decode and non-decode path.
277*cda5da8dSAndroid Build Coastguard Worker        if self.is_multipart():
278*cda5da8dSAndroid Build Coastguard Worker            if decode:
279*cda5da8dSAndroid Build Coastguard Worker                return None
280*cda5da8dSAndroid Build Coastguard Worker            if i is None:
281*cda5da8dSAndroid Build Coastguard Worker                return self._payload
282*cda5da8dSAndroid Build Coastguard Worker            else:
283*cda5da8dSAndroid Build Coastguard Worker                return self._payload[i]
284*cda5da8dSAndroid Build Coastguard Worker        # For backward compatibility, Use isinstance and this error message
285*cda5da8dSAndroid Build Coastguard Worker        # instead of the more logical is_multipart test.
286*cda5da8dSAndroid Build Coastguard Worker        if i is not None and not isinstance(self._payload, list):
287*cda5da8dSAndroid Build Coastguard Worker            raise TypeError('Expected list, got %s' % type(self._payload))
288*cda5da8dSAndroid Build Coastguard Worker        payload = self._payload
289*cda5da8dSAndroid Build Coastguard Worker        # cte might be a Header, so for now stringify it.
290*cda5da8dSAndroid Build Coastguard Worker        cte = str(self.get('content-transfer-encoding', '')).lower()
291*cda5da8dSAndroid Build Coastguard Worker        # payload may be bytes here.
292*cda5da8dSAndroid Build Coastguard Worker        if isinstance(payload, str):
293*cda5da8dSAndroid Build Coastguard Worker            if utils._has_surrogates(payload):
294*cda5da8dSAndroid Build Coastguard Worker                bpayload = payload.encode('ascii', 'surrogateescape')
295*cda5da8dSAndroid Build Coastguard Worker                if not decode:
296*cda5da8dSAndroid Build Coastguard Worker                    try:
297*cda5da8dSAndroid Build Coastguard Worker                        payload = bpayload.decode(self.get_param('charset', 'ascii'), 'replace')
298*cda5da8dSAndroid Build Coastguard Worker                    except LookupError:
299*cda5da8dSAndroid Build Coastguard Worker                        payload = bpayload.decode('ascii', 'replace')
300*cda5da8dSAndroid Build Coastguard Worker            elif decode:
301*cda5da8dSAndroid Build Coastguard Worker                try:
302*cda5da8dSAndroid Build Coastguard Worker                    bpayload = payload.encode('ascii')
303*cda5da8dSAndroid Build Coastguard Worker                except UnicodeError:
304*cda5da8dSAndroid Build Coastguard Worker                    # This won't happen for RFC compliant messages (messages
305*cda5da8dSAndroid Build Coastguard Worker                    # containing only ASCII code points in the unicode input).
306*cda5da8dSAndroid Build Coastguard Worker                    # If it does happen, turn the string into bytes in a way
307*cda5da8dSAndroid Build Coastguard Worker                    # guaranteed not to fail.
308*cda5da8dSAndroid Build Coastguard Worker                    bpayload = payload.encode('raw-unicode-escape')
309*cda5da8dSAndroid Build Coastguard Worker        if not decode:
310*cda5da8dSAndroid Build Coastguard Worker            return payload
311*cda5da8dSAndroid Build Coastguard Worker        if cte == 'quoted-printable':
312*cda5da8dSAndroid Build Coastguard Worker            return quopri.decodestring(bpayload)
313*cda5da8dSAndroid Build Coastguard Worker        elif cte == 'base64':
314*cda5da8dSAndroid Build Coastguard Worker            # XXX: this is a bit of a hack; decode_b should probably be factored
315*cda5da8dSAndroid Build Coastguard Worker            # out somewhere, but I haven't figured out where yet.
316*cda5da8dSAndroid Build Coastguard Worker            value, defects = decode_b(b''.join(bpayload.splitlines()))
317*cda5da8dSAndroid Build Coastguard Worker            for defect in defects:
318*cda5da8dSAndroid Build Coastguard Worker                self.policy.handle_defect(self, defect)
319*cda5da8dSAndroid Build Coastguard Worker            return value
320*cda5da8dSAndroid Build Coastguard Worker        elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
321*cda5da8dSAndroid Build Coastguard Worker            try:
322*cda5da8dSAndroid Build Coastguard Worker                return _decode_uu(bpayload)
323*cda5da8dSAndroid Build Coastguard Worker            except ValueError:
324*cda5da8dSAndroid Build Coastguard Worker                # Some decoding problem.
325*cda5da8dSAndroid Build Coastguard Worker                return bpayload
326*cda5da8dSAndroid Build Coastguard Worker        if isinstance(payload, str):
327*cda5da8dSAndroid Build Coastguard Worker            return bpayload
328*cda5da8dSAndroid Build Coastguard Worker        return payload
329*cda5da8dSAndroid Build Coastguard Worker
330*cda5da8dSAndroid Build Coastguard Worker    def set_payload(self, payload, charset=None):
331*cda5da8dSAndroid Build Coastguard Worker        """Set the payload to the given value.
332*cda5da8dSAndroid Build Coastguard Worker
333*cda5da8dSAndroid Build Coastguard Worker        Optional charset sets the message's default character set.  See
334*cda5da8dSAndroid Build Coastguard Worker        set_charset() for details.
335*cda5da8dSAndroid Build Coastguard Worker        """
336*cda5da8dSAndroid Build Coastguard Worker        if hasattr(payload, 'encode'):
337*cda5da8dSAndroid Build Coastguard Worker            if charset is None:
338*cda5da8dSAndroid Build Coastguard Worker                self._payload = payload
339*cda5da8dSAndroid Build Coastguard Worker                return
340*cda5da8dSAndroid Build Coastguard Worker            if not isinstance(charset, Charset):
341*cda5da8dSAndroid Build Coastguard Worker                charset = Charset(charset)
342*cda5da8dSAndroid Build Coastguard Worker            payload = payload.encode(charset.output_charset)
343*cda5da8dSAndroid Build Coastguard Worker        if hasattr(payload, 'decode'):
344*cda5da8dSAndroid Build Coastguard Worker            self._payload = payload.decode('ascii', 'surrogateescape')
345*cda5da8dSAndroid Build Coastguard Worker        else:
346*cda5da8dSAndroid Build Coastguard Worker            self._payload = payload
347*cda5da8dSAndroid Build Coastguard Worker        if charset is not None:
348*cda5da8dSAndroid Build Coastguard Worker            self.set_charset(charset)
349*cda5da8dSAndroid Build Coastguard Worker
350*cda5da8dSAndroid Build Coastguard Worker    def set_charset(self, charset):
351*cda5da8dSAndroid Build Coastguard Worker        """Set the charset of the payload to a given character set.
352*cda5da8dSAndroid Build Coastguard Worker
353*cda5da8dSAndroid Build Coastguard Worker        charset can be a Charset instance, a string naming a character set, or
354*cda5da8dSAndroid Build Coastguard Worker        None.  If it is a string it will be converted to a Charset instance.
355*cda5da8dSAndroid Build Coastguard Worker        If charset is None, the charset parameter will be removed from the
356*cda5da8dSAndroid Build Coastguard Worker        Content-Type field.  Anything else will generate a TypeError.
357*cda5da8dSAndroid Build Coastguard Worker
358*cda5da8dSAndroid Build Coastguard Worker        The message will be assumed to be of type text/* encoded with
359*cda5da8dSAndroid Build Coastguard Worker        charset.input_charset.  It will be converted to charset.output_charset
360*cda5da8dSAndroid Build Coastguard Worker        and encoded properly, if needed, when generating the plain text
361*cda5da8dSAndroid Build Coastguard Worker        representation of the message.  MIME headers (MIME-Version,
362*cda5da8dSAndroid Build Coastguard Worker        Content-Type, Content-Transfer-Encoding) will be added as needed.
363*cda5da8dSAndroid Build Coastguard Worker        """
364*cda5da8dSAndroid Build Coastguard Worker        if charset is None:
365*cda5da8dSAndroid Build Coastguard Worker            self.del_param('charset')
366*cda5da8dSAndroid Build Coastguard Worker            self._charset = None
367*cda5da8dSAndroid Build Coastguard Worker            return
368*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(charset, Charset):
369*cda5da8dSAndroid Build Coastguard Worker            charset = Charset(charset)
370*cda5da8dSAndroid Build Coastguard Worker        self._charset = charset
371*cda5da8dSAndroid Build Coastguard Worker        if 'MIME-Version' not in self:
372*cda5da8dSAndroid Build Coastguard Worker            self.add_header('MIME-Version', '1.0')
373*cda5da8dSAndroid Build Coastguard Worker        if 'Content-Type' not in self:
374*cda5da8dSAndroid Build Coastguard Worker            self.add_header('Content-Type', 'text/plain',
375*cda5da8dSAndroid Build Coastguard Worker                            charset=charset.get_output_charset())
376*cda5da8dSAndroid Build Coastguard Worker        else:
377*cda5da8dSAndroid Build Coastguard Worker            self.set_param('charset', charset.get_output_charset())
378*cda5da8dSAndroid Build Coastguard Worker        if charset != charset.get_output_charset():
379*cda5da8dSAndroid Build Coastguard Worker            self._payload = charset.body_encode(self._payload)
380*cda5da8dSAndroid Build Coastguard Worker        if 'Content-Transfer-Encoding' not in self:
381*cda5da8dSAndroid Build Coastguard Worker            cte = charset.get_body_encoding()
382*cda5da8dSAndroid Build Coastguard Worker            try:
383*cda5da8dSAndroid Build Coastguard Worker                cte(self)
384*cda5da8dSAndroid Build Coastguard Worker            except TypeError:
385*cda5da8dSAndroid Build Coastguard Worker                # This 'if' is for backward compatibility, it allows unicode
386*cda5da8dSAndroid Build Coastguard Worker                # through even though that won't work correctly if the
387*cda5da8dSAndroid Build Coastguard Worker                # message is serialized.
388*cda5da8dSAndroid Build Coastguard Worker                payload = self._payload
389*cda5da8dSAndroid Build Coastguard Worker                if payload:
390*cda5da8dSAndroid Build Coastguard Worker                    try:
391*cda5da8dSAndroid Build Coastguard Worker                        payload = payload.encode('ascii', 'surrogateescape')
392*cda5da8dSAndroid Build Coastguard Worker                    except UnicodeError:
393*cda5da8dSAndroid Build Coastguard Worker                        payload = payload.encode(charset.output_charset)
394*cda5da8dSAndroid Build Coastguard Worker                self._payload = charset.body_encode(payload)
395*cda5da8dSAndroid Build Coastguard Worker                self.add_header('Content-Transfer-Encoding', cte)
396*cda5da8dSAndroid Build Coastguard Worker
397*cda5da8dSAndroid Build Coastguard Worker    def get_charset(self):
398*cda5da8dSAndroid Build Coastguard Worker        """Return the Charset instance associated with the message's payload.
399*cda5da8dSAndroid Build Coastguard Worker        """
400*cda5da8dSAndroid Build Coastguard Worker        return self._charset
401*cda5da8dSAndroid Build Coastguard Worker
402*cda5da8dSAndroid Build Coastguard Worker    #
403*cda5da8dSAndroid Build Coastguard Worker    # MAPPING INTERFACE (partial)
404*cda5da8dSAndroid Build Coastguard Worker    #
405*cda5da8dSAndroid Build Coastguard Worker    def __len__(self):
406*cda5da8dSAndroid Build Coastguard Worker        """Return the total number of headers, including duplicates."""
407*cda5da8dSAndroid Build Coastguard Worker        return len(self._headers)
408*cda5da8dSAndroid Build Coastguard Worker
409*cda5da8dSAndroid Build Coastguard Worker    def __getitem__(self, name):
410*cda5da8dSAndroid Build Coastguard Worker        """Get a header value.
411*cda5da8dSAndroid Build Coastguard Worker
412*cda5da8dSAndroid Build Coastguard Worker        Return None if the header is missing instead of raising an exception.
413*cda5da8dSAndroid Build Coastguard Worker
414*cda5da8dSAndroid Build Coastguard Worker        Note that if the header appeared multiple times, exactly which
415*cda5da8dSAndroid Build Coastguard Worker        occurrence gets returned is undefined.  Use get_all() to get all
416*cda5da8dSAndroid Build Coastguard Worker        the values matching a header field name.
417*cda5da8dSAndroid Build Coastguard Worker        """
418*cda5da8dSAndroid Build Coastguard Worker        return self.get(name)
419*cda5da8dSAndroid Build Coastguard Worker
420*cda5da8dSAndroid Build Coastguard Worker    def __setitem__(self, name, val):
421*cda5da8dSAndroid Build Coastguard Worker        """Set the value of a header.
422*cda5da8dSAndroid Build Coastguard Worker
423*cda5da8dSAndroid Build Coastguard Worker        Note: this does not overwrite an existing header with the same field
424*cda5da8dSAndroid Build Coastguard Worker        name.  Use __delitem__() first to delete any existing headers.
425*cda5da8dSAndroid Build Coastguard Worker        """
426*cda5da8dSAndroid Build Coastguard Worker        max_count = self.policy.header_max_count(name)
427*cda5da8dSAndroid Build Coastguard Worker        if max_count:
428*cda5da8dSAndroid Build Coastguard Worker            lname = name.lower()
429*cda5da8dSAndroid Build Coastguard Worker            found = 0
430*cda5da8dSAndroid Build Coastguard Worker            for k, v in self._headers:
431*cda5da8dSAndroid Build Coastguard Worker                if k.lower() == lname:
432*cda5da8dSAndroid Build Coastguard Worker                    found += 1
433*cda5da8dSAndroid Build Coastguard Worker                    if found >= max_count:
434*cda5da8dSAndroid Build Coastguard Worker                        raise ValueError("There may be at most {} {} headers "
435*cda5da8dSAndroid Build Coastguard Worker                                         "in a message".format(max_count, name))
436*cda5da8dSAndroid Build Coastguard Worker        self._headers.append(self.policy.header_store_parse(name, val))
437*cda5da8dSAndroid Build Coastguard Worker
438*cda5da8dSAndroid Build Coastguard Worker    def __delitem__(self, name):
439*cda5da8dSAndroid Build Coastguard Worker        """Delete all occurrences of a header, if present.
440*cda5da8dSAndroid Build Coastguard Worker
441*cda5da8dSAndroid Build Coastguard Worker        Does not raise an exception if the header is missing.
442*cda5da8dSAndroid Build Coastguard Worker        """
443*cda5da8dSAndroid Build Coastguard Worker        name = name.lower()
444*cda5da8dSAndroid Build Coastguard Worker        newheaders = []
445*cda5da8dSAndroid Build Coastguard Worker        for k, v in self._headers:
446*cda5da8dSAndroid Build Coastguard Worker            if k.lower() != name:
447*cda5da8dSAndroid Build Coastguard Worker                newheaders.append((k, v))
448*cda5da8dSAndroid Build Coastguard Worker        self._headers = newheaders
449*cda5da8dSAndroid Build Coastguard Worker
450*cda5da8dSAndroid Build Coastguard Worker    def __contains__(self, name):
451*cda5da8dSAndroid Build Coastguard Worker        return name.lower() in [k.lower() for k, v in self._headers]
452*cda5da8dSAndroid Build Coastguard Worker
453*cda5da8dSAndroid Build Coastguard Worker    def __iter__(self):
454*cda5da8dSAndroid Build Coastguard Worker        for field, value in self._headers:
455*cda5da8dSAndroid Build Coastguard Worker            yield field
456*cda5da8dSAndroid Build Coastguard Worker
457*cda5da8dSAndroid Build Coastguard Worker    def keys(self):
458*cda5da8dSAndroid Build Coastguard Worker        """Return a list of all the message's header field names.
459*cda5da8dSAndroid Build Coastguard Worker
460*cda5da8dSAndroid Build Coastguard Worker        These will be sorted in the order they appeared in the original
461*cda5da8dSAndroid Build Coastguard Worker        message, or were added to the message, and may contain duplicates.
462*cda5da8dSAndroid Build Coastguard Worker        Any fields deleted and re-inserted are always appended to the header
463*cda5da8dSAndroid Build Coastguard Worker        list.
464*cda5da8dSAndroid Build Coastguard Worker        """
465*cda5da8dSAndroid Build Coastguard Worker        return [k for k, v in self._headers]
466*cda5da8dSAndroid Build Coastguard Worker
467*cda5da8dSAndroid Build Coastguard Worker    def values(self):
468*cda5da8dSAndroid Build Coastguard Worker        """Return a list of all the message's header values.
469*cda5da8dSAndroid Build Coastguard Worker
470*cda5da8dSAndroid Build Coastguard Worker        These will be sorted in the order they appeared in the original
471*cda5da8dSAndroid Build Coastguard Worker        message, or were added to the message, and may contain duplicates.
472*cda5da8dSAndroid Build Coastguard Worker        Any fields deleted and re-inserted are always appended to the header
473*cda5da8dSAndroid Build Coastguard Worker        list.
474*cda5da8dSAndroid Build Coastguard Worker        """
475*cda5da8dSAndroid Build Coastguard Worker        return [self.policy.header_fetch_parse(k, v)
476*cda5da8dSAndroid Build Coastguard Worker                for k, v in self._headers]
477*cda5da8dSAndroid Build Coastguard Worker
478*cda5da8dSAndroid Build Coastguard Worker    def items(self):
479*cda5da8dSAndroid Build Coastguard Worker        """Get all the message's header fields and values.
480*cda5da8dSAndroid Build Coastguard Worker
481*cda5da8dSAndroid Build Coastguard Worker        These will be sorted in the order they appeared in the original
482*cda5da8dSAndroid Build Coastguard Worker        message, or were added to the message, and may contain duplicates.
483*cda5da8dSAndroid Build Coastguard Worker        Any fields deleted and re-inserted are always appended to the header
484*cda5da8dSAndroid Build Coastguard Worker        list.
485*cda5da8dSAndroid Build Coastguard Worker        """
486*cda5da8dSAndroid Build Coastguard Worker        return [(k, self.policy.header_fetch_parse(k, v))
487*cda5da8dSAndroid Build Coastguard Worker                for k, v in self._headers]
488*cda5da8dSAndroid Build Coastguard Worker
489*cda5da8dSAndroid Build Coastguard Worker    def get(self, name, failobj=None):
490*cda5da8dSAndroid Build Coastguard Worker        """Get a header value.
491*cda5da8dSAndroid Build Coastguard Worker
492*cda5da8dSAndroid Build Coastguard Worker        Like __getitem__() but return failobj instead of None when the field
493*cda5da8dSAndroid Build Coastguard Worker        is missing.
494*cda5da8dSAndroid Build Coastguard Worker        """
495*cda5da8dSAndroid Build Coastguard Worker        name = name.lower()
496*cda5da8dSAndroid Build Coastguard Worker        for k, v in self._headers:
497*cda5da8dSAndroid Build Coastguard Worker            if k.lower() == name:
498*cda5da8dSAndroid Build Coastguard Worker                return self.policy.header_fetch_parse(k, v)
499*cda5da8dSAndroid Build Coastguard Worker        return failobj
500*cda5da8dSAndroid Build Coastguard Worker
501*cda5da8dSAndroid Build Coastguard Worker    #
502*cda5da8dSAndroid Build Coastguard Worker    # "Internal" methods (public API, but only intended for use by a parser
503*cda5da8dSAndroid Build Coastguard Worker    # or generator, not normal application code.
504*cda5da8dSAndroid Build Coastguard Worker    #
505*cda5da8dSAndroid Build Coastguard Worker
506*cda5da8dSAndroid Build Coastguard Worker    def set_raw(self, name, value):
507*cda5da8dSAndroid Build Coastguard Worker        """Store name and value in the model without modification.
508*cda5da8dSAndroid Build Coastguard Worker
509*cda5da8dSAndroid Build Coastguard Worker        This is an "internal" API, intended only for use by a parser.
510*cda5da8dSAndroid Build Coastguard Worker        """
511*cda5da8dSAndroid Build Coastguard Worker        self._headers.append((name, value))
512*cda5da8dSAndroid Build Coastguard Worker
513*cda5da8dSAndroid Build Coastguard Worker    def raw_items(self):
514*cda5da8dSAndroid Build Coastguard Worker        """Return the (name, value) header pairs without modification.
515*cda5da8dSAndroid Build Coastguard Worker
516*cda5da8dSAndroid Build Coastguard Worker        This is an "internal" API, intended only for use by a generator.
517*cda5da8dSAndroid Build Coastguard Worker        """
518*cda5da8dSAndroid Build Coastguard Worker        return iter(self._headers.copy())
519*cda5da8dSAndroid Build Coastguard Worker
520*cda5da8dSAndroid Build Coastguard Worker    #
521*cda5da8dSAndroid Build Coastguard Worker    # Additional useful stuff
522*cda5da8dSAndroid Build Coastguard Worker    #
523*cda5da8dSAndroid Build Coastguard Worker
524*cda5da8dSAndroid Build Coastguard Worker    def get_all(self, name, failobj=None):
525*cda5da8dSAndroid Build Coastguard Worker        """Return a list of all the values for the named field.
526*cda5da8dSAndroid Build Coastguard Worker
527*cda5da8dSAndroid Build Coastguard Worker        These will be sorted in the order they appeared in the original
528*cda5da8dSAndroid Build Coastguard Worker        message, and may contain duplicates.  Any fields deleted and
529*cda5da8dSAndroid Build Coastguard Worker        re-inserted are always appended to the header list.
530*cda5da8dSAndroid Build Coastguard Worker
531*cda5da8dSAndroid Build Coastguard Worker        If no such fields exist, failobj is returned (defaults to None).
532*cda5da8dSAndroid Build Coastguard Worker        """
533*cda5da8dSAndroid Build Coastguard Worker        values = []
534*cda5da8dSAndroid Build Coastguard Worker        name = name.lower()
535*cda5da8dSAndroid Build Coastguard Worker        for k, v in self._headers:
536*cda5da8dSAndroid Build Coastguard Worker            if k.lower() == name:
537*cda5da8dSAndroid Build Coastguard Worker                values.append(self.policy.header_fetch_parse(k, v))
538*cda5da8dSAndroid Build Coastguard Worker        if not values:
539*cda5da8dSAndroid Build Coastguard Worker            return failobj
540*cda5da8dSAndroid Build Coastguard Worker        return values
541*cda5da8dSAndroid Build Coastguard Worker
542*cda5da8dSAndroid Build Coastguard Worker    def add_header(self, _name, _value, **_params):
543*cda5da8dSAndroid Build Coastguard Worker        """Extended header setting.
544*cda5da8dSAndroid Build Coastguard Worker
545*cda5da8dSAndroid Build Coastguard Worker        name is the header field to add.  keyword arguments can be used to set
546*cda5da8dSAndroid Build Coastguard Worker        additional parameters for the header field, with underscores converted
547*cda5da8dSAndroid Build Coastguard Worker        to dashes.  Normally the parameter will be added as key="value" unless
548*cda5da8dSAndroid Build Coastguard Worker        value is None, in which case only the key will be added.  If a
549*cda5da8dSAndroid Build Coastguard Worker        parameter value contains non-ASCII characters it can be specified as a
550*cda5da8dSAndroid Build Coastguard Worker        three-tuple of (charset, language, value), in which case it will be
551*cda5da8dSAndroid Build Coastguard Worker        encoded according to RFC2231 rules.  Otherwise it will be encoded using
552*cda5da8dSAndroid Build Coastguard Worker        the utf-8 charset and a language of ''.
553*cda5da8dSAndroid Build Coastguard Worker
554*cda5da8dSAndroid Build Coastguard Worker        Examples:
555*cda5da8dSAndroid Build Coastguard Worker
556*cda5da8dSAndroid Build Coastguard Worker        msg.add_header('content-disposition', 'attachment', filename='bud.gif')
557*cda5da8dSAndroid Build Coastguard Worker        msg.add_header('content-disposition', 'attachment',
558*cda5da8dSAndroid Build Coastguard Worker                       filename=('utf-8', '', Fußballer.ppt'))
559*cda5da8dSAndroid Build Coastguard Worker        msg.add_header('content-disposition', 'attachment',
560*cda5da8dSAndroid Build Coastguard Worker                       filename='Fußballer.ppt'))
561*cda5da8dSAndroid Build Coastguard Worker        """
562*cda5da8dSAndroid Build Coastguard Worker        parts = []
563*cda5da8dSAndroid Build Coastguard Worker        for k, v in _params.items():
564*cda5da8dSAndroid Build Coastguard Worker            if v is None:
565*cda5da8dSAndroid Build Coastguard Worker                parts.append(k.replace('_', '-'))
566*cda5da8dSAndroid Build Coastguard Worker            else:
567*cda5da8dSAndroid Build Coastguard Worker                parts.append(_formatparam(k.replace('_', '-'), v))
568*cda5da8dSAndroid Build Coastguard Worker        if _value is not None:
569*cda5da8dSAndroid Build Coastguard Worker            parts.insert(0, _value)
570*cda5da8dSAndroid Build Coastguard Worker        self[_name] = SEMISPACE.join(parts)
571*cda5da8dSAndroid Build Coastguard Worker
572*cda5da8dSAndroid Build Coastguard Worker    def replace_header(self, _name, _value):
573*cda5da8dSAndroid Build Coastguard Worker        """Replace a header.
574*cda5da8dSAndroid Build Coastguard Worker
575*cda5da8dSAndroid Build Coastguard Worker        Replace the first matching header found in the message, retaining
576*cda5da8dSAndroid Build Coastguard Worker        header order and case.  If no matching header was found, a KeyError is
577*cda5da8dSAndroid Build Coastguard Worker        raised.
578*cda5da8dSAndroid Build Coastguard Worker        """
579*cda5da8dSAndroid Build Coastguard Worker        _name = _name.lower()
580*cda5da8dSAndroid Build Coastguard Worker        for i, (k, v) in zip(range(len(self._headers)), self._headers):
581*cda5da8dSAndroid Build Coastguard Worker            if k.lower() == _name:
582*cda5da8dSAndroid Build Coastguard Worker                self._headers[i] = self.policy.header_store_parse(k, _value)
583*cda5da8dSAndroid Build Coastguard Worker                break
584*cda5da8dSAndroid Build Coastguard Worker        else:
585*cda5da8dSAndroid Build Coastguard Worker            raise KeyError(_name)
586*cda5da8dSAndroid Build Coastguard Worker
587*cda5da8dSAndroid Build Coastguard Worker    #
588*cda5da8dSAndroid Build Coastguard Worker    # Use these three methods instead of the three above.
589*cda5da8dSAndroid Build Coastguard Worker    #
590*cda5da8dSAndroid Build Coastguard Worker
591*cda5da8dSAndroid Build Coastguard Worker    def get_content_type(self):
592*cda5da8dSAndroid Build Coastguard Worker        """Return the message's content type.
593*cda5da8dSAndroid Build Coastguard Worker
594*cda5da8dSAndroid Build Coastguard Worker        The returned string is coerced to lower case of the form
595*cda5da8dSAndroid Build Coastguard Worker        `maintype/subtype'.  If there was no Content-Type header in the
596*cda5da8dSAndroid Build Coastguard Worker        message, the default type as given by get_default_type() will be
597*cda5da8dSAndroid Build Coastguard Worker        returned.  Since according to RFC 2045, messages always have a default
598*cda5da8dSAndroid Build Coastguard Worker        type this will always return a value.
599*cda5da8dSAndroid Build Coastguard Worker
600*cda5da8dSAndroid Build Coastguard Worker        RFC 2045 defines a message's default type to be text/plain unless it
601*cda5da8dSAndroid Build Coastguard Worker        appears inside a multipart/digest container, in which case it would be
602*cda5da8dSAndroid Build Coastguard Worker        message/rfc822.
603*cda5da8dSAndroid Build Coastguard Worker        """
604*cda5da8dSAndroid Build Coastguard Worker        missing = object()
605*cda5da8dSAndroid Build Coastguard Worker        value = self.get('content-type', missing)
606*cda5da8dSAndroid Build Coastguard Worker        if value is missing:
607*cda5da8dSAndroid Build Coastguard Worker            # This should have no parameters
608*cda5da8dSAndroid Build Coastguard Worker            return self.get_default_type()
609*cda5da8dSAndroid Build Coastguard Worker        ctype = _splitparam(value)[0].lower()
610*cda5da8dSAndroid Build Coastguard Worker        # RFC 2045, section 5.2 says if its invalid, use text/plain
611*cda5da8dSAndroid Build Coastguard Worker        if ctype.count('/') != 1:
612*cda5da8dSAndroid Build Coastguard Worker            return 'text/plain'
613*cda5da8dSAndroid Build Coastguard Worker        return ctype
614*cda5da8dSAndroid Build Coastguard Worker
615*cda5da8dSAndroid Build Coastguard Worker    def get_content_maintype(self):
616*cda5da8dSAndroid Build Coastguard Worker        """Return the message's main content type.
617*cda5da8dSAndroid Build Coastguard Worker
618*cda5da8dSAndroid Build Coastguard Worker        This is the `maintype' part of the string returned by
619*cda5da8dSAndroid Build Coastguard Worker        get_content_type().
620*cda5da8dSAndroid Build Coastguard Worker        """
621*cda5da8dSAndroid Build Coastguard Worker        ctype = self.get_content_type()
622*cda5da8dSAndroid Build Coastguard Worker        return ctype.split('/')[0]
623*cda5da8dSAndroid Build Coastguard Worker
624*cda5da8dSAndroid Build Coastguard Worker    def get_content_subtype(self):
625*cda5da8dSAndroid Build Coastguard Worker        """Returns the message's sub-content type.
626*cda5da8dSAndroid Build Coastguard Worker
627*cda5da8dSAndroid Build Coastguard Worker        This is the `subtype' part of the string returned by
628*cda5da8dSAndroid Build Coastguard Worker        get_content_type().
629*cda5da8dSAndroid Build Coastguard Worker        """
630*cda5da8dSAndroid Build Coastguard Worker        ctype = self.get_content_type()
631*cda5da8dSAndroid Build Coastguard Worker        return ctype.split('/')[1]
632*cda5da8dSAndroid Build Coastguard Worker
633*cda5da8dSAndroid Build Coastguard Worker    def get_default_type(self):
634*cda5da8dSAndroid Build Coastguard Worker        """Return the `default' content type.
635*cda5da8dSAndroid Build Coastguard Worker
636*cda5da8dSAndroid Build Coastguard Worker        Most messages have a default content type of text/plain, except for
637*cda5da8dSAndroid Build Coastguard Worker        messages that are subparts of multipart/digest containers.  Such
638*cda5da8dSAndroid Build Coastguard Worker        subparts have a default content type of message/rfc822.
639*cda5da8dSAndroid Build Coastguard Worker        """
640*cda5da8dSAndroid Build Coastguard Worker        return self._default_type
641*cda5da8dSAndroid Build Coastguard Worker
642*cda5da8dSAndroid Build Coastguard Worker    def set_default_type(self, ctype):
643*cda5da8dSAndroid Build Coastguard Worker        """Set the `default' content type.
644*cda5da8dSAndroid Build Coastguard Worker
645*cda5da8dSAndroid Build Coastguard Worker        ctype should be either "text/plain" or "message/rfc822", although this
646*cda5da8dSAndroid Build Coastguard Worker        is not enforced.  The default content type is not stored in the
647*cda5da8dSAndroid Build Coastguard Worker        Content-Type header.
648*cda5da8dSAndroid Build Coastguard Worker        """
649*cda5da8dSAndroid Build Coastguard Worker        self._default_type = ctype
650*cda5da8dSAndroid Build Coastguard Worker
651*cda5da8dSAndroid Build Coastguard Worker    def _get_params_preserve(self, failobj, header):
652*cda5da8dSAndroid Build Coastguard Worker        # Like get_params() but preserves the quoting of values.  BAW:
653*cda5da8dSAndroid Build Coastguard Worker        # should this be part of the public interface?
654*cda5da8dSAndroid Build Coastguard Worker        missing = object()
655*cda5da8dSAndroid Build Coastguard Worker        value = self.get(header, missing)
656*cda5da8dSAndroid Build Coastguard Worker        if value is missing:
657*cda5da8dSAndroid Build Coastguard Worker            return failobj
658*cda5da8dSAndroid Build Coastguard Worker        params = []
659*cda5da8dSAndroid Build Coastguard Worker        for p in _parseparam(value):
660*cda5da8dSAndroid Build Coastguard Worker            try:
661*cda5da8dSAndroid Build Coastguard Worker                name, val = p.split('=', 1)
662*cda5da8dSAndroid Build Coastguard Worker                name = name.strip()
663*cda5da8dSAndroid Build Coastguard Worker                val = val.strip()
664*cda5da8dSAndroid Build Coastguard Worker            except ValueError:
665*cda5da8dSAndroid Build Coastguard Worker                # Must have been a bare attribute
666*cda5da8dSAndroid Build Coastguard Worker                name = p.strip()
667*cda5da8dSAndroid Build Coastguard Worker                val = ''
668*cda5da8dSAndroid Build Coastguard Worker            params.append((name, val))
669*cda5da8dSAndroid Build Coastguard Worker        params = utils.decode_params(params)
670*cda5da8dSAndroid Build Coastguard Worker        return params
671*cda5da8dSAndroid Build Coastguard Worker
672*cda5da8dSAndroid Build Coastguard Worker    def get_params(self, failobj=None, header='content-type', unquote=True):
673*cda5da8dSAndroid Build Coastguard Worker        """Return the message's Content-Type parameters, as a list.
674*cda5da8dSAndroid Build Coastguard Worker
675*cda5da8dSAndroid Build Coastguard Worker        The elements of the returned list are 2-tuples of key/value pairs, as
676*cda5da8dSAndroid Build Coastguard Worker        split on the `=' sign.  The left hand side of the `=' is the key,
677*cda5da8dSAndroid Build Coastguard Worker        while the right hand side is the value.  If there is no `=' sign in
678*cda5da8dSAndroid Build Coastguard Worker        the parameter the value is the empty string.  The value is as
679*cda5da8dSAndroid Build Coastguard Worker        described in the get_param() method.
680*cda5da8dSAndroid Build Coastguard Worker
681*cda5da8dSAndroid Build Coastguard Worker        Optional failobj is the object to return if there is no Content-Type
682*cda5da8dSAndroid Build Coastguard Worker        header.  Optional header is the header to search instead of
683*cda5da8dSAndroid Build Coastguard Worker        Content-Type.  If unquote is True, the value is unquoted.
684*cda5da8dSAndroid Build Coastguard Worker        """
685*cda5da8dSAndroid Build Coastguard Worker        missing = object()
686*cda5da8dSAndroid Build Coastguard Worker        params = self._get_params_preserve(missing, header)
687*cda5da8dSAndroid Build Coastguard Worker        if params is missing:
688*cda5da8dSAndroid Build Coastguard Worker            return failobj
689*cda5da8dSAndroid Build Coastguard Worker        if unquote:
690*cda5da8dSAndroid Build Coastguard Worker            return [(k, _unquotevalue(v)) for k, v in params]
691*cda5da8dSAndroid Build Coastguard Worker        else:
692*cda5da8dSAndroid Build Coastguard Worker            return params
693*cda5da8dSAndroid Build Coastguard Worker
694*cda5da8dSAndroid Build Coastguard Worker    def get_param(self, param, failobj=None, header='content-type',
695*cda5da8dSAndroid Build Coastguard Worker                  unquote=True):
696*cda5da8dSAndroid Build Coastguard Worker        """Return the parameter value if found in the Content-Type header.
697*cda5da8dSAndroid Build Coastguard Worker
698*cda5da8dSAndroid Build Coastguard Worker        Optional failobj is the object to return if there is no Content-Type
699*cda5da8dSAndroid Build Coastguard Worker        header, or the Content-Type header has no such parameter.  Optional
700*cda5da8dSAndroid Build Coastguard Worker        header is the header to search instead of Content-Type.
701*cda5da8dSAndroid Build Coastguard Worker
702*cda5da8dSAndroid Build Coastguard Worker        Parameter keys are always compared case insensitively.  The return
703*cda5da8dSAndroid Build Coastguard Worker        value can either be a string, or a 3-tuple if the parameter was RFC
704*cda5da8dSAndroid Build Coastguard Worker        2231 encoded.  When it's a 3-tuple, the elements of the value are of
705*cda5da8dSAndroid Build Coastguard Worker        the form (CHARSET, LANGUAGE, VALUE).  Note that both CHARSET and
706*cda5da8dSAndroid Build Coastguard Worker        LANGUAGE can be None, in which case you should consider VALUE to be
707*cda5da8dSAndroid Build Coastguard Worker        encoded in the us-ascii charset.  You can usually ignore LANGUAGE.
708*cda5da8dSAndroid Build Coastguard Worker        The parameter value (either the returned string, or the VALUE item in
709*cda5da8dSAndroid Build Coastguard Worker        the 3-tuple) is always unquoted, unless unquote is set to False.
710*cda5da8dSAndroid Build Coastguard Worker
711*cda5da8dSAndroid Build Coastguard Worker        If your application doesn't care whether the parameter was RFC 2231
712*cda5da8dSAndroid Build Coastguard Worker        encoded, it can turn the return value into a string as follows:
713*cda5da8dSAndroid Build Coastguard Worker
714*cda5da8dSAndroid Build Coastguard Worker            rawparam = msg.get_param('foo')
715*cda5da8dSAndroid Build Coastguard Worker            param = email.utils.collapse_rfc2231_value(rawparam)
716*cda5da8dSAndroid Build Coastguard Worker
717*cda5da8dSAndroid Build Coastguard Worker        """
718*cda5da8dSAndroid Build Coastguard Worker        if header not in self:
719*cda5da8dSAndroid Build Coastguard Worker            return failobj
720*cda5da8dSAndroid Build Coastguard Worker        for k, v in self._get_params_preserve(failobj, header):
721*cda5da8dSAndroid Build Coastguard Worker            if k.lower() == param.lower():
722*cda5da8dSAndroid Build Coastguard Worker                if unquote:
723*cda5da8dSAndroid Build Coastguard Worker                    return _unquotevalue(v)
724*cda5da8dSAndroid Build Coastguard Worker                else:
725*cda5da8dSAndroid Build Coastguard Worker                    return v
726*cda5da8dSAndroid Build Coastguard Worker        return failobj
727*cda5da8dSAndroid Build Coastguard Worker
728*cda5da8dSAndroid Build Coastguard Worker    def set_param(self, param, value, header='Content-Type', requote=True,
729*cda5da8dSAndroid Build Coastguard Worker                  charset=None, language='', replace=False):
730*cda5da8dSAndroid Build Coastguard Worker        """Set a parameter in the Content-Type header.
731*cda5da8dSAndroid Build Coastguard Worker
732*cda5da8dSAndroid Build Coastguard Worker        If the parameter already exists in the header, its value will be
733*cda5da8dSAndroid Build Coastguard Worker        replaced with the new value.
734*cda5da8dSAndroid Build Coastguard Worker
735*cda5da8dSAndroid Build Coastguard Worker        If header is Content-Type and has not yet been defined for this
736*cda5da8dSAndroid Build Coastguard Worker        message, it will be set to "text/plain" and the new parameter and
737*cda5da8dSAndroid Build Coastguard Worker        value will be appended as per RFC 2045.
738*cda5da8dSAndroid Build Coastguard Worker
739*cda5da8dSAndroid Build Coastguard Worker        An alternate header can be specified in the header argument, and all
740*cda5da8dSAndroid Build Coastguard Worker        parameters will be quoted as necessary unless requote is False.
741*cda5da8dSAndroid Build Coastguard Worker
742*cda5da8dSAndroid Build Coastguard Worker        If charset is specified, the parameter will be encoded according to RFC
743*cda5da8dSAndroid Build Coastguard Worker        2231.  Optional language specifies the RFC 2231 language, defaulting
744*cda5da8dSAndroid Build Coastguard Worker        to the empty string.  Both charset and language should be strings.
745*cda5da8dSAndroid Build Coastguard Worker        """
746*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(value, tuple) and charset:
747*cda5da8dSAndroid Build Coastguard Worker            value = (charset, language, value)
748*cda5da8dSAndroid Build Coastguard Worker
749*cda5da8dSAndroid Build Coastguard Worker        if header not in self and header.lower() == 'content-type':
750*cda5da8dSAndroid Build Coastguard Worker            ctype = 'text/plain'
751*cda5da8dSAndroid Build Coastguard Worker        else:
752*cda5da8dSAndroid Build Coastguard Worker            ctype = self.get(header)
753*cda5da8dSAndroid Build Coastguard Worker        if not self.get_param(param, header=header):
754*cda5da8dSAndroid Build Coastguard Worker            if not ctype:
755*cda5da8dSAndroid Build Coastguard Worker                ctype = _formatparam(param, value, requote)
756*cda5da8dSAndroid Build Coastguard Worker            else:
757*cda5da8dSAndroid Build Coastguard Worker                ctype = SEMISPACE.join(
758*cda5da8dSAndroid Build Coastguard Worker                    [ctype, _formatparam(param, value, requote)])
759*cda5da8dSAndroid Build Coastguard Worker        else:
760*cda5da8dSAndroid Build Coastguard Worker            ctype = ''
761*cda5da8dSAndroid Build Coastguard Worker            for old_param, old_value in self.get_params(header=header,
762*cda5da8dSAndroid Build Coastguard Worker                                                        unquote=requote):
763*cda5da8dSAndroid Build Coastguard Worker                append_param = ''
764*cda5da8dSAndroid Build Coastguard Worker                if old_param.lower() == param.lower():
765*cda5da8dSAndroid Build Coastguard Worker                    append_param = _formatparam(param, value, requote)
766*cda5da8dSAndroid Build Coastguard Worker                else:
767*cda5da8dSAndroid Build Coastguard Worker                    append_param = _formatparam(old_param, old_value, requote)
768*cda5da8dSAndroid Build Coastguard Worker                if not ctype:
769*cda5da8dSAndroid Build Coastguard Worker                    ctype = append_param
770*cda5da8dSAndroid Build Coastguard Worker                else:
771*cda5da8dSAndroid Build Coastguard Worker                    ctype = SEMISPACE.join([ctype, append_param])
772*cda5da8dSAndroid Build Coastguard Worker        if ctype != self.get(header):
773*cda5da8dSAndroid Build Coastguard Worker            if replace:
774*cda5da8dSAndroid Build Coastguard Worker                self.replace_header(header, ctype)
775*cda5da8dSAndroid Build Coastguard Worker            else:
776*cda5da8dSAndroid Build Coastguard Worker                del self[header]
777*cda5da8dSAndroid Build Coastguard Worker                self[header] = ctype
778*cda5da8dSAndroid Build Coastguard Worker
779*cda5da8dSAndroid Build Coastguard Worker    def del_param(self, param, header='content-type', requote=True):
780*cda5da8dSAndroid Build Coastguard Worker        """Remove the given parameter completely from the Content-Type header.
781*cda5da8dSAndroid Build Coastguard Worker
782*cda5da8dSAndroid Build Coastguard Worker        The header will be re-written in place without the parameter or its
783*cda5da8dSAndroid Build Coastguard Worker        value. All values will be quoted as necessary unless requote is
784*cda5da8dSAndroid Build Coastguard Worker        False.  Optional header specifies an alternative to the Content-Type
785*cda5da8dSAndroid Build Coastguard Worker        header.
786*cda5da8dSAndroid Build Coastguard Worker        """
787*cda5da8dSAndroid Build Coastguard Worker        if header not in self:
788*cda5da8dSAndroid Build Coastguard Worker            return
789*cda5da8dSAndroid Build Coastguard Worker        new_ctype = ''
790*cda5da8dSAndroid Build Coastguard Worker        for p, v in self.get_params(header=header, unquote=requote):
791*cda5da8dSAndroid Build Coastguard Worker            if p.lower() != param.lower():
792*cda5da8dSAndroid Build Coastguard Worker                if not new_ctype:
793*cda5da8dSAndroid Build Coastguard Worker                    new_ctype = _formatparam(p, v, requote)
794*cda5da8dSAndroid Build Coastguard Worker                else:
795*cda5da8dSAndroid Build Coastguard Worker                    new_ctype = SEMISPACE.join([new_ctype,
796*cda5da8dSAndroid Build Coastguard Worker                                                _formatparam(p, v, requote)])
797*cda5da8dSAndroid Build Coastguard Worker        if new_ctype != self.get(header):
798*cda5da8dSAndroid Build Coastguard Worker            del self[header]
799*cda5da8dSAndroid Build Coastguard Worker            self[header] = new_ctype
800*cda5da8dSAndroid Build Coastguard Worker
801*cda5da8dSAndroid Build Coastguard Worker    def set_type(self, type, header='Content-Type', requote=True):
802*cda5da8dSAndroid Build Coastguard Worker        """Set the main type and subtype for the Content-Type header.
803*cda5da8dSAndroid Build Coastguard Worker
804*cda5da8dSAndroid Build Coastguard Worker        type must be a string in the form "maintype/subtype", otherwise a
805*cda5da8dSAndroid Build Coastguard Worker        ValueError is raised.
806*cda5da8dSAndroid Build Coastguard Worker
807*cda5da8dSAndroid Build Coastguard Worker        This method replaces the Content-Type header, keeping all the
808*cda5da8dSAndroid Build Coastguard Worker        parameters in place.  If requote is False, this leaves the existing
809*cda5da8dSAndroid Build Coastguard Worker        header's quoting as is.  Otherwise, the parameters will be quoted (the
810*cda5da8dSAndroid Build Coastguard Worker        default).
811*cda5da8dSAndroid Build Coastguard Worker
812*cda5da8dSAndroid Build Coastguard Worker        An alternative header can be specified in the header argument.  When
813*cda5da8dSAndroid Build Coastguard Worker        the Content-Type header is set, we'll always also add a MIME-Version
814*cda5da8dSAndroid Build Coastguard Worker        header.
815*cda5da8dSAndroid Build Coastguard Worker        """
816*cda5da8dSAndroid Build Coastguard Worker        # BAW: should we be strict?
817*cda5da8dSAndroid Build Coastguard Worker        if not type.count('/') == 1:
818*cda5da8dSAndroid Build Coastguard Worker            raise ValueError
819*cda5da8dSAndroid Build Coastguard Worker        # Set the Content-Type, you get a MIME-Version
820*cda5da8dSAndroid Build Coastguard Worker        if header.lower() == 'content-type':
821*cda5da8dSAndroid Build Coastguard Worker            del self['mime-version']
822*cda5da8dSAndroid Build Coastguard Worker            self['MIME-Version'] = '1.0'
823*cda5da8dSAndroid Build Coastguard Worker        if header not in self:
824*cda5da8dSAndroid Build Coastguard Worker            self[header] = type
825*cda5da8dSAndroid Build Coastguard Worker            return
826*cda5da8dSAndroid Build Coastguard Worker        params = self.get_params(header=header, unquote=requote)
827*cda5da8dSAndroid Build Coastguard Worker        del self[header]
828*cda5da8dSAndroid Build Coastguard Worker        self[header] = type
829*cda5da8dSAndroid Build Coastguard Worker        # Skip the first param; it's the old type.
830*cda5da8dSAndroid Build Coastguard Worker        for p, v in params[1:]:
831*cda5da8dSAndroid Build Coastguard Worker            self.set_param(p, v, header, requote)
832*cda5da8dSAndroid Build Coastguard Worker
833*cda5da8dSAndroid Build Coastguard Worker    def get_filename(self, failobj=None):
834*cda5da8dSAndroid Build Coastguard Worker        """Return the filename associated with the payload if present.
835*cda5da8dSAndroid Build Coastguard Worker
836*cda5da8dSAndroid Build Coastguard Worker        The filename is extracted from the Content-Disposition header's
837*cda5da8dSAndroid Build Coastguard Worker        `filename' parameter, and it is unquoted.  If that header is missing
838*cda5da8dSAndroid Build Coastguard Worker        the `filename' parameter, this method falls back to looking for the
839*cda5da8dSAndroid Build Coastguard Worker        `name' parameter.
840*cda5da8dSAndroid Build Coastguard Worker        """
841*cda5da8dSAndroid Build Coastguard Worker        missing = object()
842*cda5da8dSAndroid Build Coastguard Worker        filename = self.get_param('filename', missing, 'content-disposition')
843*cda5da8dSAndroid Build Coastguard Worker        if filename is missing:
844*cda5da8dSAndroid Build Coastguard Worker            filename = self.get_param('name', missing, 'content-type')
845*cda5da8dSAndroid Build Coastguard Worker        if filename is missing:
846*cda5da8dSAndroid Build Coastguard Worker            return failobj
847*cda5da8dSAndroid Build Coastguard Worker        return utils.collapse_rfc2231_value(filename).strip()
848*cda5da8dSAndroid Build Coastguard Worker
849*cda5da8dSAndroid Build Coastguard Worker    def get_boundary(self, failobj=None):
850*cda5da8dSAndroid Build Coastguard Worker        """Return the boundary associated with the payload if present.
851*cda5da8dSAndroid Build Coastguard Worker
852*cda5da8dSAndroid Build Coastguard Worker        The boundary is extracted from the Content-Type header's `boundary'
853*cda5da8dSAndroid Build Coastguard Worker        parameter, and it is unquoted.
854*cda5da8dSAndroid Build Coastguard Worker        """
855*cda5da8dSAndroid Build Coastguard Worker        missing = object()
856*cda5da8dSAndroid Build Coastguard Worker        boundary = self.get_param('boundary', missing)
857*cda5da8dSAndroid Build Coastguard Worker        if boundary is missing:
858*cda5da8dSAndroid Build Coastguard Worker            return failobj
859*cda5da8dSAndroid Build Coastguard Worker        # RFC 2046 says that boundaries may begin but not end in w/s
860*cda5da8dSAndroid Build Coastguard Worker        return utils.collapse_rfc2231_value(boundary).rstrip()
861*cda5da8dSAndroid Build Coastguard Worker
862*cda5da8dSAndroid Build Coastguard Worker    def set_boundary(self, boundary):
863*cda5da8dSAndroid Build Coastguard Worker        """Set the boundary parameter in Content-Type to 'boundary'.
864*cda5da8dSAndroid Build Coastguard Worker
865*cda5da8dSAndroid Build Coastguard Worker        This is subtly different than deleting the Content-Type header and
866*cda5da8dSAndroid Build Coastguard Worker        adding a new one with a new boundary parameter via add_header().  The
867*cda5da8dSAndroid Build Coastguard Worker        main difference is that using the set_boundary() method preserves the
868*cda5da8dSAndroid Build Coastguard Worker        order of the Content-Type header in the original message.
869*cda5da8dSAndroid Build Coastguard Worker
870*cda5da8dSAndroid Build Coastguard Worker        HeaderParseError is raised if the message has no Content-Type header.
871*cda5da8dSAndroid Build Coastguard Worker        """
872*cda5da8dSAndroid Build Coastguard Worker        missing = object()
873*cda5da8dSAndroid Build Coastguard Worker        params = self._get_params_preserve(missing, 'content-type')
874*cda5da8dSAndroid Build Coastguard Worker        if params is missing:
875*cda5da8dSAndroid Build Coastguard Worker            # There was no Content-Type header, and we don't know what type
876*cda5da8dSAndroid Build Coastguard Worker            # to set it to, so raise an exception.
877*cda5da8dSAndroid Build Coastguard Worker            raise errors.HeaderParseError('No Content-Type header found')
878*cda5da8dSAndroid Build Coastguard Worker        newparams = []
879*cda5da8dSAndroid Build Coastguard Worker        foundp = False
880*cda5da8dSAndroid Build Coastguard Worker        for pk, pv in params:
881*cda5da8dSAndroid Build Coastguard Worker            if pk.lower() == 'boundary':
882*cda5da8dSAndroid Build Coastguard Worker                newparams.append(('boundary', '"%s"' % boundary))
883*cda5da8dSAndroid Build Coastguard Worker                foundp = True
884*cda5da8dSAndroid Build Coastguard Worker            else:
885*cda5da8dSAndroid Build Coastguard Worker                newparams.append((pk, pv))
886*cda5da8dSAndroid Build Coastguard Worker        if not foundp:
887*cda5da8dSAndroid Build Coastguard Worker            # The original Content-Type header had no boundary attribute.
888*cda5da8dSAndroid Build Coastguard Worker            # Tack one on the end.  BAW: should we raise an exception
889*cda5da8dSAndroid Build Coastguard Worker            # instead???
890*cda5da8dSAndroid Build Coastguard Worker            newparams.append(('boundary', '"%s"' % boundary))
891*cda5da8dSAndroid Build Coastguard Worker        # Replace the existing Content-Type header with the new value
892*cda5da8dSAndroid Build Coastguard Worker        newheaders = []
893*cda5da8dSAndroid Build Coastguard Worker        for h, v in self._headers:
894*cda5da8dSAndroid Build Coastguard Worker            if h.lower() == 'content-type':
895*cda5da8dSAndroid Build Coastguard Worker                parts = []
896*cda5da8dSAndroid Build Coastguard Worker                for k, v in newparams:
897*cda5da8dSAndroid Build Coastguard Worker                    if v == '':
898*cda5da8dSAndroid Build Coastguard Worker                        parts.append(k)
899*cda5da8dSAndroid Build Coastguard Worker                    else:
900*cda5da8dSAndroid Build Coastguard Worker                        parts.append('%s=%s' % (k, v))
901*cda5da8dSAndroid Build Coastguard Worker                val = SEMISPACE.join(parts)
902*cda5da8dSAndroid Build Coastguard Worker                newheaders.append(self.policy.header_store_parse(h, val))
903*cda5da8dSAndroid Build Coastguard Worker
904*cda5da8dSAndroid Build Coastguard Worker            else:
905*cda5da8dSAndroid Build Coastguard Worker                newheaders.append((h, v))
906*cda5da8dSAndroid Build Coastguard Worker        self._headers = newheaders
907*cda5da8dSAndroid Build Coastguard Worker
908*cda5da8dSAndroid Build Coastguard Worker    def get_content_charset(self, failobj=None):
909*cda5da8dSAndroid Build Coastguard Worker        """Return the charset parameter of the Content-Type header.
910*cda5da8dSAndroid Build Coastguard Worker
911*cda5da8dSAndroid Build Coastguard Worker        The returned string is always coerced to lower case.  If there is no
912*cda5da8dSAndroid Build Coastguard Worker        Content-Type header, or if that header has no charset parameter,
913*cda5da8dSAndroid Build Coastguard Worker        failobj is returned.
914*cda5da8dSAndroid Build Coastguard Worker        """
915*cda5da8dSAndroid Build Coastguard Worker        missing = object()
916*cda5da8dSAndroid Build Coastguard Worker        charset = self.get_param('charset', missing)
917*cda5da8dSAndroid Build Coastguard Worker        if charset is missing:
918*cda5da8dSAndroid Build Coastguard Worker            return failobj
919*cda5da8dSAndroid Build Coastguard Worker        if isinstance(charset, tuple):
920*cda5da8dSAndroid Build Coastguard Worker            # RFC 2231 encoded, so decode it, and it better end up as ascii.
921*cda5da8dSAndroid Build Coastguard Worker            pcharset = charset[0] or 'us-ascii'
922*cda5da8dSAndroid Build Coastguard Worker            try:
923*cda5da8dSAndroid Build Coastguard Worker                # LookupError will be raised if the charset isn't known to
924*cda5da8dSAndroid Build Coastguard Worker                # Python.  UnicodeError will be raised if the encoded text
925*cda5da8dSAndroid Build Coastguard Worker                # contains a character not in the charset.
926*cda5da8dSAndroid Build Coastguard Worker                as_bytes = charset[2].encode('raw-unicode-escape')
927*cda5da8dSAndroid Build Coastguard Worker                charset = str(as_bytes, pcharset)
928*cda5da8dSAndroid Build Coastguard Worker            except (LookupError, UnicodeError):
929*cda5da8dSAndroid Build Coastguard Worker                charset = charset[2]
930*cda5da8dSAndroid Build Coastguard Worker        # charset characters must be in us-ascii range
931*cda5da8dSAndroid Build Coastguard Worker        try:
932*cda5da8dSAndroid Build Coastguard Worker            charset.encode('us-ascii')
933*cda5da8dSAndroid Build Coastguard Worker        except UnicodeError:
934*cda5da8dSAndroid Build Coastguard Worker            return failobj
935*cda5da8dSAndroid Build Coastguard Worker        # RFC 2046, $4.1.2 says charsets are not case sensitive
936*cda5da8dSAndroid Build Coastguard Worker        return charset.lower()
937*cda5da8dSAndroid Build Coastguard Worker
938*cda5da8dSAndroid Build Coastguard Worker    def get_charsets(self, failobj=None):
939*cda5da8dSAndroid Build Coastguard Worker        """Return a list containing the charset(s) used in this message.
940*cda5da8dSAndroid Build Coastguard Worker
941*cda5da8dSAndroid Build Coastguard Worker        The returned list of items describes the Content-Type headers'
942*cda5da8dSAndroid Build Coastguard Worker        charset parameter for this message and all the subparts in its
943*cda5da8dSAndroid Build Coastguard Worker        payload.
944*cda5da8dSAndroid Build Coastguard Worker
945*cda5da8dSAndroid Build Coastguard Worker        Each item will either be a string (the value of the charset parameter
946*cda5da8dSAndroid Build Coastguard Worker        in the Content-Type header of that part) or the value of the
947*cda5da8dSAndroid Build Coastguard Worker        'failobj' parameter (defaults to None), if the part does not have a
948*cda5da8dSAndroid Build Coastguard Worker        main MIME type of "text", or the charset is not defined.
949*cda5da8dSAndroid Build Coastguard Worker
950*cda5da8dSAndroid Build Coastguard Worker        The list will contain one string for each part of the message, plus
951*cda5da8dSAndroid Build Coastguard Worker        one for the container message (i.e. self), so that a non-multipart
952*cda5da8dSAndroid Build Coastguard Worker        message will still return a list of length 1.
953*cda5da8dSAndroid Build Coastguard Worker        """
954*cda5da8dSAndroid Build Coastguard Worker        return [part.get_content_charset(failobj) for part in self.walk()]
955*cda5da8dSAndroid Build Coastguard Worker
956*cda5da8dSAndroid Build Coastguard Worker    def get_content_disposition(self):
957*cda5da8dSAndroid Build Coastguard Worker        """Return the message's content-disposition if it exists, or None.
958*cda5da8dSAndroid Build Coastguard Worker
959*cda5da8dSAndroid Build Coastguard Worker        The return values can be either 'inline', 'attachment' or None
960*cda5da8dSAndroid Build Coastguard Worker        according to the rfc2183.
961*cda5da8dSAndroid Build Coastguard Worker        """
962*cda5da8dSAndroid Build Coastguard Worker        value = self.get('content-disposition')
963*cda5da8dSAndroid Build Coastguard Worker        if value is None:
964*cda5da8dSAndroid Build Coastguard Worker            return None
965*cda5da8dSAndroid Build Coastguard Worker        c_d = _splitparam(value)[0].lower()
966*cda5da8dSAndroid Build Coastguard Worker        return c_d
967*cda5da8dSAndroid Build Coastguard Worker
968*cda5da8dSAndroid Build Coastguard Worker    # I.e. def walk(self): ...
969*cda5da8dSAndroid Build Coastguard Worker    from email.iterators import walk
970*cda5da8dSAndroid Build Coastguard Worker
971*cda5da8dSAndroid Build Coastguard Worker
972*cda5da8dSAndroid Build Coastguard Workerclass MIMEPart(Message):
973*cda5da8dSAndroid Build Coastguard Worker
974*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, policy=None):
975*cda5da8dSAndroid Build Coastguard Worker        if policy is None:
976*cda5da8dSAndroid Build Coastguard Worker            from email.policy import default
977*cda5da8dSAndroid Build Coastguard Worker            policy = default
978*cda5da8dSAndroid Build Coastguard Worker        super().__init__(policy)
979*cda5da8dSAndroid Build Coastguard Worker
980*cda5da8dSAndroid Build Coastguard Worker
981*cda5da8dSAndroid Build Coastguard Worker    def as_string(self, unixfrom=False, maxheaderlen=None, policy=None):
982*cda5da8dSAndroid Build Coastguard Worker        """Return the entire formatted message as a string.
983*cda5da8dSAndroid Build Coastguard Worker
984*cda5da8dSAndroid Build Coastguard Worker        Optional 'unixfrom', when true, means include the Unix From_ envelope
985*cda5da8dSAndroid Build Coastguard Worker        header.  maxheaderlen is retained for backward compatibility with the
986*cda5da8dSAndroid Build Coastguard Worker        base Message class, but defaults to None, meaning that the policy value
987*cda5da8dSAndroid Build Coastguard Worker        for max_line_length controls the header maximum length.  'policy' is
988*cda5da8dSAndroid Build Coastguard Worker        passed to the Generator instance used to serialize the message; if it
989*cda5da8dSAndroid Build Coastguard Worker        is not specified the policy associated with the message instance is
990*cda5da8dSAndroid Build Coastguard Worker        used.
991*cda5da8dSAndroid Build Coastguard Worker        """
992*cda5da8dSAndroid Build Coastguard Worker        policy = self.policy if policy is None else policy
993*cda5da8dSAndroid Build Coastguard Worker        if maxheaderlen is None:
994*cda5da8dSAndroid Build Coastguard Worker            maxheaderlen = policy.max_line_length
995*cda5da8dSAndroid Build Coastguard Worker        return super().as_string(unixfrom, maxheaderlen, policy)
996*cda5da8dSAndroid Build Coastguard Worker
997*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
998*cda5da8dSAndroid Build Coastguard Worker        return self.as_string(policy=self.policy.clone(utf8=True))
999*cda5da8dSAndroid Build Coastguard Worker
1000*cda5da8dSAndroid Build Coastguard Worker    def is_attachment(self):
1001*cda5da8dSAndroid Build Coastguard Worker        c_d = self.get('content-disposition')
1002*cda5da8dSAndroid Build Coastguard Worker        return False if c_d is None else c_d.content_disposition == 'attachment'
1003*cda5da8dSAndroid Build Coastguard Worker
1004*cda5da8dSAndroid Build Coastguard Worker    def _find_body(self, part, preferencelist):
1005*cda5da8dSAndroid Build Coastguard Worker        if part.is_attachment():
1006*cda5da8dSAndroid Build Coastguard Worker            return
1007*cda5da8dSAndroid Build Coastguard Worker        maintype, subtype = part.get_content_type().split('/')
1008*cda5da8dSAndroid Build Coastguard Worker        if maintype == 'text':
1009*cda5da8dSAndroid Build Coastguard Worker            if subtype in preferencelist:
1010*cda5da8dSAndroid Build Coastguard Worker                yield (preferencelist.index(subtype), part)
1011*cda5da8dSAndroid Build Coastguard Worker            return
1012*cda5da8dSAndroid Build Coastguard Worker        if maintype != 'multipart' or not self.is_multipart():
1013*cda5da8dSAndroid Build Coastguard Worker            return
1014*cda5da8dSAndroid Build Coastguard Worker        if subtype != 'related':
1015*cda5da8dSAndroid Build Coastguard Worker            for subpart in part.iter_parts():
1016*cda5da8dSAndroid Build Coastguard Worker                yield from self._find_body(subpart, preferencelist)
1017*cda5da8dSAndroid Build Coastguard Worker            return
1018*cda5da8dSAndroid Build Coastguard Worker        if 'related' in preferencelist:
1019*cda5da8dSAndroid Build Coastguard Worker            yield (preferencelist.index('related'), part)
1020*cda5da8dSAndroid Build Coastguard Worker        candidate = None
1021*cda5da8dSAndroid Build Coastguard Worker        start = part.get_param('start')
1022*cda5da8dSAndroid Build Coastguard Worker        if start:
1023*cda5da8dSAndroid Build Coastguard Worker            for subpart in part.iter_parts():
1024*cda5da8dSAndroid Build Coastguard Worker                if subpart['content-id'] == start:
1025*cda5da8dSAndroid Build Coastguard Worker                    candidate = subpart
1026*cda5da8dSAndroid Build Coastguard Worker                    break
1027*cda5da8dSAndroid Build Coastguard Worker        if candidate is None:
1028*cda5da8dSAndroid Build Coastguard Worker            subparts = part.get_payload()
1029*cda5da8dSAndroid Build Coastguard Worker            candidate = subparts[0] if subparts else None
1030*cda5da8dSAndroid Build Coastguard Worker        if candidate is not None:
1031*cda5da8dSAndroid Build Coastguard Worker            yield from self._find_body(candidate, preferencelist)
1032*cda5da8dSAndroid Build Coastguard Worker
1033*cda5da8dSAndroid Build Coastguard Worker    def get_body(self, preferencelist=('related', 'html', 'plain')):
1034*cda5da8dSAndroid Build Coastguard Worker        """Return best candidate mime part for display as 'body' of message.
1035*cda5da8dSAndroid Build Coastguard Worker
1036*cda5da8dSAndroid Build Coastguard Worker        Do a depth first search, starting with self, looking for the first part
1037*cda5da8dSAndroid Build Coastguard Worker        matching each of the items in preferencelist, and return the part
1038*cda5da8dSAndroid Build Coastguard Worker        corresponding to the first item that has a match, or None if no items
1039*cda5da8dSAndroid Build Coastguard Worker        have a match.  If 'related' is not included in preferencelist, consider
1040*cda5da8dSAndroid Build Coastguard Worker        the root part of any multipart/related encountered as a candidate
1041*cda5da8dSAndroid Build Coastguard Worker        match.  Ignore parts with 'Content-Disposition: attachment'.
1042*cda5da8dSAndroid Build Coastguard Worker        """
1043*cda5da8dSAndroid Build Coastguard Worker        best_prio = len(preferencelist)
1044*cda5da8dSAndroid Build Coastguard Worker        body = None
1045*cda5da8dSAndroid Build Coastguard Worker        for prio, part in self._find_body(self, preferencelist):
1046*cda5da8dSAndroid Build Coastguard Worker            if prio < best_prio:
1047*cda5da8dSAndroid Build Coastguard Worker                best_prio = prio
1048*cda5da8dSAndroid Build Coastguard Worker                body = part
1049*cda5da8dSAndroid Build Coastguard Worker                if prio == 0:
1050*cda5da8dSAndroid Build Coastguard Worker                    break
1051*cda5da8dSAndroid Build Coastguard Worker        return body
1052*cda5da8dSAndroid Build Coastguard Worker
1053*cda5da8dSAndroid Build Coastguard Worker    _body_types = {('text', 'plain'),
1054*cda5da8dSAndroid Build Coastguard Worker                   ('text', 'html'),
1055*cda5da8dSAndroid Build Coastguard Worker                   ('multipart', 'related'),
1056*cda5da8dSAndroid Build Coastguard Worker                   ('multipart', 'alternative')}
1057*cda5da8dSAndroid Build Coastguard Worker    def iter_attachments(self):
1058*cda5da8dSAndroid Build Coastguard Worker        """Return an iterator over the non-main parts of a multipart.
1059*cda5da8dSAndroid Build Coastguard Worker
1060*cda5da8dSAndroid Build Coastguard Worker        Skip the first of each occurrence of text/plain, text/html,
1061*cda5da8dSAndroid Build Coastguard Worker        multipart/related, or multipart/alternative in the multipart (unless
1062*cda5da8dSAndroid Build Coastguard Worker        they have a 'Content-Disposition: attachment' header) and include all
1063*cda5da8dSAndroid Build Coastguard Worker        remaining subparts in the returned iterator.  When applied to a
1064*cda5da8dSAndroid Build Coastguard Worker        multipart/related, return all parts except the root part.  Return an
1065*cda5da8dSAndroid Build Coastguard Worker        empty iterator when applied to a multipart/alternative or a
1066*cda5da8dSAndroid Build Coastguard Worker        non-multipart.
1067*cda5da8dSAndroid Build Coastguard Worker        """
1068*cda5da8dSAndroid Build Coastguard Worker        maintype, subtype = self.get_content_type().split('/')
1069*cda5da8dSAndroid Build Coastguard Worker        if maintype != 'multipart' or subtype == 'alternative':
1070*cda5da8dSAndroid Build Coastguard Worker            return
1071*cda5da8dSAndroid Build Coastguard Worker        payload = self.get_payload()
1072*cda5da8dSAndroid Build Coastguard Worker        # Certain malformed messages can have content type set to `multipart/*`
1073*cda5da8dSAndroid Build Coastguard Worker        # but still have single part body, in which case payload.copy() can
1074*cda5da8dSAndroid Build Coastguard Worker        # fail with AttributeError.
1075*cda5da8dSAndroid Build Coastguard Worker        try:
1076*cda5da8dSAndroid Build Coastguard Worker            parts = payload.copy()
1077*cda5da8dSAndroid Build Coastguard Worker        except AttributeError:
1078*cda5da8dSAndroid Build Coastguard Worker            # payload is not a list, it is most probably a string.
1079*cda5da8dSAndroid Build Coastguard Worker            return
1080*cda5da8dSAndroid Build Coastguard Worker
1081*cda5da8dSAndroid Build Coastguard Worker        if maintype == 'multipart' and subtype == 'related':
1082*cda5da8dSAndroid Build Coastguard Worker            # For related, we treat everything but the root as an attachment.
1083*cda5da8dSAndroid Build Coastguard Worker            # The root may be indicated by 'start'; if there's no start or we
1084*cda5da8dSAndroid Build Coastguard Worker            # can't find the named start, treat the first subpart as the root.
1085*cda5da8dSAndroid Build Coastguard Worker            start = self.get_param('start')
1086*cda5da8dSAndroid Build Coastguard Worker            if start:
1087*cda5da8dSAndroid Build Coastguard Worker                found = False
1088*cda5da8dSAndroid Build Coastguard Worker                attachments = []
1089*cda5da8dSAndroid Build Coastguard Worker                for part in parts:
1090*cda5da8dSAndroid Build Coastguard Worker                    if part.get('content-id') == start:
1091*cda5da8dSAndroid Build Coastguard Worker                        found = True
1092*cda5da8dSAndroid Build Coastguard Worker                    else:
1093*cda5da8dSAndroid Build Coastguard Worker                        attachments.append(part)
1094*cda5da8dSAndroid Build Coastguard Worker                if found:
1095*cda5da8dSAndroid Build Coastguard Worker                    yield from attachments
1096*cda5da8dSAndroid Build Coastguard Worker                    return
1097*cda5da8dSAndroid Build Coastguard Worker            parts.pop(0)
1098*cda5da8dSAndroid Build Coastguard Worker            yield from parts
1099*cda5da8dSAndroid Build Coastguard Worker            return
1100*cda5da8dSAndroid Build Coastguard Worker        # Otherwise we more or less invert the remaining logic in get_body.
1101*cda5da8dSAndroid Build Coastguard Worker        # This only really works in edge cases (ex: non-text related or
1102*cda5da8dSAndroid Build Coastguard Worker        # alternatives) if the sending agent sets content-disposition.
1103*cda5da8dSAndroid Build Coastguard Worker        seen = []   # Only skip the first example of each candidate type.
1104*cda5da8dSAndroid Build Coastguard Worker        for part in parts:
1105*cda5da8dSAndroid Build Coastguard Worker            maintype, subtype = part.get_content_type().split('/')
1106*cda5da8dSAndroid Build Coastguard Worker            if ((maintype, subtype) in self._body_types and
1107*cda5da8dSAndroid Build Coastguard Worker                    not part.is_attachment() and subtype not in seen):
1108*cda5da8dSAndroid Build Coastguard Worker                seen.append(subtype)
1109*cda5da8dSAndroid Build Coastguard Worker                continue
1110*cda5da8dSAndroid Build Coastguard Worker            yield part
1111*cda5da8dSAndroid Build Coastguard Worker
1112*cda5da8dSAndroid Build Coastguard Worker    def iter_parts(self):
1113*cda5da8dSAndroid Build Coastguard Worker        """Return an iterator over all immediate subparts of a multipart.
1114*cda5da8dSAndroid Build Coastguard Worker
1115*cda5da8dSAndroid Build Coastguard Worker        Return an empty iterator for a non-multipart.
1116*cda5da8dSAndroid Build Coastguard Worker        """
1117*cda5da8dSAndroid Build Coastguard Worker        if self.is_multipart():
1118*cda5da8dSAndroid Build Coastguard Worker            yield from self.get_payload()
1119*cda5da8dSAndroid Build Coastguard Worker
1120*cda5da8dSAndroid Build Coastguard Worker    def get_content(self, *args, content_manager=None, **kw):
1121*cda5da8dSAndroid Build Coastguard Worker        if content_manager is None:
1122*cda5da8dSAndroid Build Coastguard Worker            content_manager = self.policy.content_manager
1123*cda5da8dSAndroid Build Coastguard Worker        return content_manager.get_content(self, *args, **kw)
1124*cda5da8dSAndroid Build Coastguard Worker
1125*cda5da8dSAndroid Build Coastguard Worker    def set_content(self, *args, content_manager=None, **kw):
1126*cda5da8dSAndroid Build Coastguard Worker        if content_manager is None:
1127*cda5da8dSAndroid Build Coastguard Worker            content_manager = self.policy.content_manager
1128*cda5da8dSAndroid Build Coastguard Worker        content_manager.set_content(self, *args, **kw)
1129*cda5da8dSAndroid Build Coastguard Worker
1130*cda5da8dSAndroid Build Coastguard Worker    def _make_multipart(self, subtype, disallowed_subtypes, boundary):
1131*cda5da8dSAndroid Build Coastguard Worker        if self.get_content_maintype() == 'multipart':
1132*cda5da8dSAndroid Build Coastguard Worker            existing_subtype = self.get_content_subtype()
1133*cda5da8dSAndroid Build Coastguard Worker            disallowed_subtypes = disallowed_subtypes + (subtype,)
1134*cda5da8dSAndroid Build Coastguard Worker            if existing_subtype in disallowed_subtypes:
1135*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("Cannot convert {} to {}".format(
1136*cda5da8dSAndroid Build Coastguard Worker                    existing_subtype, subtype))
1137*cda5da8dSAndroid Build Coastguard Worker        keep_headers = []
1138*cda5da8dSAndroid Build Coastguard Worker        part_headers = []
1139*cda5da8dSAndroid Build Coastguard Worker        for name, value in self._headers:
1140*cda5da8dSAndroid Build Coastguard Worker            if name.lower().startswith('content-'):
1141*cda5da8dSAndroid Build Coastguard Worker                part_headers.append((name, value))
1142*cda5da8dSAndroid Build Coastguard Worker            else:
1143*cda5da8dSAndroid Build Coastguard Worker                keep_headers.append((name, value))
1144*cda5da8dSAndroid Build Coastguard Worker        if part_headers:
1145*cda5da8dSAndroid Build Coastguard Worker            # There is existing content, move it to the first subpart.
1146*cda5da8dSAndroid Build Coastguard Worker            part = type(self)(policy=self.policy)
1147*cda5da8dSAndroid Build Coastguard Worker            part._headers = part_headers
1148*cda5da8dSAndroid Build Coastguard Worker            part._payload = self._payload
1149*cda5da8dSAndroid Build Coastguard Worker            self._payload = [part]
1150*cda5da8dSAndroid Build Coastguard Worker        else:
1151*cda5da8dSAndroid Build Coastguard Worker            self._payload = []
1152*cda5da8dSAndroid Build Coastguard Worker        self._headers = keep_headers
1153*cda5da8dSAndroid Build Coastguard Worker        self['Content-Type'] = 'multipart/' + subtype
1154*cda5da8dSAndroid Build Coastguard Worker        if boundary is not None:
1155*cda5da8dSAndroid Build Coastguard Worker            self.set_param('boundary', boundary)
1156*cda5da8dSAndroid Build Coastguard Worker
1157*cda5da8dSAndroid Build Coastguard Worker    def make_related(self, boundary=None):
1158*cda5da8dSAndroid Build Coastguard Worker        self._make_multipart('related', ('alternative', 'mixed'), boundary)
1159*cda5da8dSAndroid Build Coastguard Worker
1160*cda5da8dSAndroid Build Coastguard Worker    def make_alternative(self, boundary=None):
1161*cda5da8dSAndroid Build Coastguard Worker        self._make_multipart('alternative', ('mixed',), boundary)
1162*cda5da8dSAndroid Build Coastguard Worker
1163*cda5da8dSAndroid Build Coastguard Worker    def make_mixed(self, boundary=None):
1164*cda5da8dSAndroid Build Coastguard Worker        self._make_multipart('mixed', (), boundary)
1165*cda5da8dSAndroid Build Coastguard Worker
1166*cda5da8dSAndroid Build Coastguard Worker    def _add_multipart(self, _subtype, *args, _disp=None, **kw):
1167*cda5da8dSAndroid Build Coastguard Worker        if (self.get_content_maintype() != 'multipart' or
1168*cda5da8dSAndroid Build Coastguard Worker                self.get_content_subtype() != _subtype):
1169*cda5da8dSAndroid Build Coastguard Worker            getattr(self, 'make_' + _subtype)()
1170*cda5da8dSAndroid Build Coastguard Worker        part = type(self)(policy=self.policy)
1171*cda5da8dSAndroid Build Coastguard Worker        part.set_content(*args, **kw)
1172*cda5da8dSAndroid Build Coastguard Worker        if _disp and 'content-disposition' not in part:
1173*cda5da8dSAndroid Build Coastguard Worker            part['Content-Disposition'] = _disp
1174*cda5da8dSAndroid Build Coastguard Worker        self.attach(part)
1175*cda5da8dSAndroid Build Coastguard Worker
1176*cda5da8dSAndroid Build Coastguard Worker    def add_related(self, *args, **kw):
1177*cda5da8dSAndroid Build Coastguard Worker        self._add_multipart('related', *args, _disp='inline', **kw)
1178*cda5da8dSAndroid Build Coastguard Worker
1179*cda5da8dSAndroid Build Coastguard Worker    def add_alternative(self, *args, **kw):
1180*cda5da8dSAndroid Build Coastguard Worker        self._add_multipart('alternative', *args, **kw)
1181*cda5da8dSAndroid Build Coastguard Worker
1182*cda5da8dSAndroid Build Coastguard Worker    def add_attachment(self, *args, **kw):
1183*cda5da8dSAndroid Build Coastguard Worker        self._add_multipart('mixed', *args, _disp='attachment', **kw)
1184*cda5da8dSAndroid Build Coastguard Worker
1185*cda5da8dSAndroid Build Coastguard Worker    def clear(self):
1186*cda5da8dSAndroid Build Coastguard Worker        self._headers = []
1187*cda5da8dSAndroid Build Coastguard Worker        self._payload = None
1188*cda5da8dSAndroid Build Coastguard Worker
1189*cda5da8dSAndroid Build Coastguard Worker    def clear_content(self):
1190*cda5da8dSAndroid Build Coastguard Worker        self._headers = [(n, v) for n, v in self._headers
1191*cda5da8dSAndroid Build Coastguard Worker                         if not n.lower().startswith('content-')]
1192*cda5da8dSAndroid Build Coastguard Worker        self._payload = None
1193*cda5da8dSAndroid Build Coastguard Worker
1194*cda5da8dSAndroid Build Coastguard Worker
1195*cda5da8dSAndroid Build Coastguard Workerclass EmailMessage(MIMEPart):
1196*cda5da8dSAndroid Build Coastguard Worker
1197*cda5da8dSAndroid Build Coastguard Worker    def set_content(self, *args, **kw):
1198*cda5da8dSAndroid Build Coastguard Worker        super().set_content(*args, **kw)
1199*cda5da8dSAndroid Build Coastguard Worker        if 'MIME-Version' not in self:
1200*cda5da8dSAndroid Build Coastguard Worker            self['MIME-Version'] = '1.0'
1201