xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/email/utils.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker# Copyright (C) 2001-2010 Python Software Foundation
2*cda5da8dSAndroid Build Coastguard Worker# Author: Barry Warsaw
3*cda5da8dSAndroid Build Coastguard Worker# Contact: [email protected]
4*cda5da8dSAndroid Build Coastguard Worker
5*cda5da8dSAndroid Build Coastguard Worker"""Miscellaneous utilities."""
6*cda5da8dSAndroid Build Coastguard Worker
7*cda5da8dSAndroid Build Coastguard Worker__all__ = [
8*cda5da8dSAndroid Build Coastguard Worker    'collapse_rfc2231_value',
9*cda5da8dSAndroid Build Coastguard Worker    'decode_params',
10*cda5da8dSAndroid Build Coastguard Worker    'decode_rfc2231',
11*cda5da8dSAndroid Build Coastguard Worker    'encode_rfc2231',
12*cda5da8dSAndroid Build Coastguard Worker    'formataddr',
13*cda5da8dSAndroid Build Coastguard Worker    'formatdate',
14*cda5da8dSAndroid Build Coastguard Worker    'format_datetime',
15*cda5da8dSAndroid Build Coastguard Worker    'getaddresses',
16*cda5da8dSAndroid Build Coastguard Worker    'make_msgid',
17*cda5da8dSAndroid Build Coastguard Worker    'mktime_tz',
18*cda5da8dSAndroid Build Coastguard Worker    'parseaddr',
19*cda5da8dSAndroid Build Coastguard Worker    'parsedate',
20*cda5da8dSAndroid Build Coastguard Worker    'parsedate_tz',
21*cda5da8dSAndroid Build Coastguard Worker    'parsedate_to_datetime',
22*cda5da8dSAndroid Build Coastguard Worker    'unquote',
23*cda5da8dSAndroid Build Coastguard Worker    ]
24*cda5da8dSAndroid Build Coastguard Worker
25*cda5da8dSAndroid Build Coastguard Workerimport os
26*cda5da8dSAndroid Build Coastguard Workerimport re
27*cda5da8dSAndroid Build Coastguard Workerimport time
28*cda5da8dSAndroid Build Coastguard Workerimport random
29*cda5da8dSAndroid Build Coastguard Workerimport socket
30*cda5da8dSAndroid Build Coastguard Workerimport datetime
31*cda5da8dSAndroid Build Coastguard Workerimport urllib.parse
32*cda5da8dSAndroid Build Coastguard Worker
33*cda5da8dSAndroid Build Coastguard Workerfrom email._parseaddr import quote
34*cda5da8dSAndroid Build Coastguard Workerfrom email._parseaddr import AddressList as _AddressList
35*cda5da8dSAndroid Build Coastguard Workerfrom email._parseaddr import mktime_tz
36*cda5da8dSAndroid Build Coastguard Worker
37*cda5da8dSAndroid Build Coastguard Workerfrom email._parseaddr import parsedate, parsedate_tz, _parsedate_tz
38*cda5da8dSAndroid Build Coastguard Worker
39*cda5da8dSAndroid Build Coastguard Worker# Intrapackage imports
40*cda5da8dSAndroid Build Coastguard Workerfrom email.charset import Charset
41*cda5da8dSAndroid Build Coastguard Worker
42*cda5da8dSAndroid Build Coastguard WorkerCOMMASPACE = ', '
43*cda5da8dSAndroid Build Coastguard WorkerEMPTYSTRING = ''
44*cda5da8dSAndroid Build Coastguard WorkerUEMPTYSTRING = ''
45*cda5da8dSAndroid Build Coastguard WorkerCRLF = '\r\n'
46*cda5da8dSAndroid Build Coastguard WorkerTICK = "'"
47*cda5da8dSAndroid Build Coastguard Worker
48*cda5da8dSAndroid Build Coastguard Workerspecialsre = re.compile(r'[][\\()<>@,:;".]')
49*cda5da8dSAndroid Build Coastguard Workerescapesre = re.compile(r'[\\"]')
50*cda5da8dSAndroid Build Coastguard Worker
51*cda5da8dSAndroid Build Coastguard Workerdef _has_surrogates(s):
52*cda5da8dSAndroid Build Coastguard Worker    """Return True if s contains surrogate-escaped binary data."""
53*cda5da8dSAndroid Build Coastguard Worker    # This check is based on the fact that unless there are surrogates, utf8
54*cda5da8dSAndroid Build Coastguard Worker    # (Python's default encoding) can encode any string.  This is the fastest
55*cda5da8dSAndroid Build Coastguard Worker    # way to check for surrogates, see issue 11454 for timings.
56*cda5da8dSAndroid Build Coastguard Worker    try:
57*cda5da8dSAndroid Build Coastguard Worker        s.encode()
58*cda5da8dSAndroid Build Coastguard Worker        return False
59*cda5da8dSAndroid Build Coastguard Worker    except UnicodeEncodeError:
60*cda5da8dSAndroid Build Coastguard Worker        return True
61*cda5da8dSAndroid Build Coastguard Worker
62*cda5da8dSAndroid Build Coastguard Worker# How to deal with a string containing bytes before handing it to the
63*cda5da8dSAndroid Build Coastguard Worker# application through the 'normal' interface.
64*cda5da8dSAndroid Build Coastguard Workerdef _sanitize(string):
65*cda5da8dSAndroid Build Coastguard Worker    # Turn any escaped bytes into unicode 'unknown' char.  If the escaped
66*cda5da8dSAndroid Build Coastguard Worker    # bytes happen to be utf-8 they will instead get decoded, even if they
67*cda5da8dSAndroid Build Coastguard Worker    # were invalid in the charset the source was supposed to be in.  This
68*cda5da8dSAndroid Build Coastguard Worker    # seems like it is not a bad thing; a defect was still registered.
69*cda5da8dSAndroid Build Coastguard Worker    original_bytes = string.encode('utf-8', 'surrogateescape')
70*cda5da8dSAndroid Build Coastguard Worker    return original_bytes.decode('utf-8', 'replace')
71*cda5da8dSAndroid Build Coastguard Worker
72*cda5da8dSAndroid Build Coastguard Worker
73*cda5da8dSAndroid Build Coastguard Worker
74*cda5da8dSAndroid Build Coastguard Worker# Helpers
75*cda5da8dSAndroid Build Coastguard Worker
76*cda5da8dSAndroid Build Coastguard Workerdef formataddr(pair, charset='utf-8'):
77*cda5da8dSAndroid Build Coastguard Worker    """The inverse of parseaddr(), this takes a 2-tuple of the form
78*cda5da8dSAndroid Build Coastguard Worker    (realname, email_address) and returns the string value suitable
79*cda5da8dSAndroid Build Coastguard Worker    for an RFC 2822 From, To or Cc header.
80*cda5da8dSAndroid Build Coastguard Worker
81*cda5da8dSAndroid Build Coastguard Worker    If the first element of pair is false, then the second element is
82*cda5da8dSAndroid Build Coastguard Worker    returned unmodified.
83*cda5da8dSAndroid Build Coastguard Worker
84*cda5da8dSAndroid Build Coastguard Worker    The optional charset is the character set that is used to encode
85*cda5da8dSAndroid Build Coastguard Worker    realname in case realname is not ASCII safe.  Can be an instance of str or
86*cda5da8dSAndroid Build Coastguard Worker    a Charset-like object which has a header_encode method.  Default is
87*cda5da8dSAndroid Build Coastguard Worker    'utf-8'.
88*cda5da8dSAndroid Build Coastguard Worker    """
89*cda5da8dSAndroid Build Coastguard Worker    name, address = pair
90*cda5da8dSAndroid Build Coastguard Worker    # The address MUST (per RFC) be ascii, so raise a UnicodeError if it isn't.
91*cda5da8dSAndroid Build Coastguard Worker    address.encode('ascii')
92*cda5da8dSAndroid Build Coastguard Worker    if name:
93*cda5da8dSAndroid Build Coastguard Worker        try:
94*cda5da8dSAndroid Build Coastguard Worker            name.encode('ascii')
95*cda5da8dSAndroid Build Coastguard Worker        except UnicodeEncodeError:
96*cda5da8dSAndroid Build Coastguard Worker            if isinstance(charset, str):
97*cda5da8dSAndroid Build Coastguard Worker                charset = Charset(charset)
98*cda5da8dSAndroid Build Coastguard Worker            encoded_name = charset.header_encode(name)
99*cda5da8dSAndroid Build Coastguard Worker            return "%s <%s>" % (encoded_name, address)
100*cda5da8dSAndroid Build Coastguard Worker        else:
101*cda5da8dSAndroid Build Coastguard Worker            quotes = ''
102*cda5da8dSAndroid Build Coastguard Worker            if specialsre.search(name):
103*cda5da8dSAndroid Build Coastguard Worker                quotes = '"'
104*cda5da8dSAndroid Build Coastguard Worker            name = escapesre.sub(r'\\\g<0>', name)
105*cda5da8dSAndroid Build Coastguard Worker            return '%s%s%s <%s>' % (quotes, name, quotes, address)
106*cda5da8dSAndroid Build Coastguard Worker    return address
107*cda5da8dSAndroid Build Coastguard Worker
108*cda5da8dSAndroid Build Coastguard Worker
109*cda5da8dSAndroid Build Coastguard Worker
110*cda5da8dSAndroid Build Coastguard Workerdef getaddresses(fieldvalues):
111*cda5da8dSAndroid Build Coastguard Worker    """Return a list of (REALNAME, EMAIL) for each fieldvalue."""
112*cda5da8dSAndroid Build Coastguard Worker    all = COMMASPACE.join(str(v) for v in fieldvalues)
113*cda5da8dSAndroid Build Coastguard Worker    a = _AddressList(all)
114*cda5da8dSAndroid Build Coastguard Worker    return a.addresslist
115*cda5da8dSAndroid Build Coastguard Worker
116*cda5da8dSAndroid Build Coastguard Worker
117*cda5da8dSAndroid Build Coastguard Workerdef _format_timetuple_and_zone(timetuple, zone):
118*cda5da8dSAndroid Build Coastguard Worker    return '%s, %02d %s %04d %02d:%02d:%02d %s' % (
119*cda5da8dSAndroid Build Coastguard Worker        ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][timetuple[6]],
120*cda5da8dSAndroid Build Coastguard Worker        timetuple[2],
121*cda5da8dSAndroid Build Coastguard Worker        ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
122*cda5da8dSAndroid Build Coastguard Worker         'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][timetuple[1] - 1],
123*cda5da8dSAndroid Build Coastguard Worker        timetuple[0], timetuple[3], timetuple[4], timetuple[5],
124*cda5da8dSAndroid Build Coastguard Worker        zone)
125*cda5da8dSAndroid Build Coastguard Worker
126*cda5da8dSAndroid Build Coastguard Workerdef formatdate(timeval=None, localtime=False, usegmt=False):
127*cda5da8dSAndroid Build Coastguard Worker    """Returns a date string as specified by RFC 2822, e.g.:
128*cda5da8dSAndroid Build Coastguard Worker
129*cda5da8dSAndroid Build Coastguard Worker    Fri, 09 Nov 2001 01:08:47 -0000
130*cda5da8dSAndroid Build Coastguard Worker
131*cda5da8dSAndroid Build Coastguard Worker    Optional timeval if given is a floating point time value as accepted by
132*cda5da8dSAndroid Build Coastguard Worker    gmtime() and localtime(), otherwise the current time is used.
133*cda5da8dSAndroid Build Coastguard Worker
134*cda5da8dSAndroid Build Coastguard Worker    Optional localtime is a flag that when True, interprets timeval, and
135*cda5da8dSAndroid Build Coastguard Worker    returns a date relative to the local timezone instead of UTC, properly
136*cda5da8dSAndroid Build Coastguard Worker    taking daylight savings time into account.
137*cda5da8dSAndroid Build Coastguard Worker
138*cda5da8dSAndroid Build Coastguard Worker    Optional argument usegmt means that the timezone is written out as
139*cda5da8dSAndroid Build Coastguard Worker    an ascii string, not numeric one (so "GMT" instead of "+0000"). This
140*cda5da8dSAndroid Build Coastguard Worker    is needed for HTTP, and is only used when localtime==False.
141*cda5da8dSAndroid Build Coastguard Worker    """
142*cda5da8dSAndroid Build Coastguard Worker    # Note: we cannot use strftime() because that honors the locale and RFC
143*cda5da8dSAndroid Build Coastguard Worker    # 2822 requires that day and month names be the English abbreviations.
144*cda5da8dSAndroid Build Coastguard Worker    if timeval is None:
145*cda5da8dSAndroid Build Coastguard Worker        timeval = time.time()
146*cda5da8dSAndroid Build Coastguard Worker    if localtime or usegmt:
147*cda5da8dSAndroid Build Coastguard Worker        dt = datetime.datetime.fromtimestamp(timeval, datetime.timezone.utc)
148*cda5da8dSAndroid Build Coastguard Worker    else:
149*cda5da8dSAndroid Build Coastguard Worker        dt = datetime.datetime.utcfromtimestamp(timeval)
150*cda5da8dSAndroid Build Coastguard Worker    if localtime:
151*cda5da8dSAndroid Build Coastguard Worker        dt = dt.astimezone()
152*cda5da8dSAndroid Build Coastguard Worker        usegmt = False
153*cda5da8dSAndroid Build Coastguard Worker    return format_datetime(dt, usegmt)
154*cda5da8dSAndroid Build Coastguard Worker
155*cda5da8dSAndroid Build Coastguard Workerdef format_datetime(dt, usegmt=False):
156*cda5da8dSAndroid Build Coastguard Worker    """Turn a datetime into a date string as specified in RFC 2822.
157*cda5da8dSAndroid Build Coastguard Worker
158*cda5da8dSAndroid Build Coastguard Worker    If usegmt is True, dt must be an aware datetime with an offset of zero.  In
159*cda5da8dSAndroid Build Coastguard Worker    this case 'GMT' will be rendered instead of the normal +0000 required by
160*cda5da8dSAndroid Build Coastguard Worker    RFC2822.  This is to support HTTP headers involving date stamps.
161*cda5da8dSAndroid Build Coastguard Worker    """
162*cda5da8dSAndroid Build Coastguard Worker    now = dt.timetuple()
163*cda5da8dSAndroid Build Coastguard Worker    if usegmt:
164*cda5da8dSAndroid Build Coastguard Worker        if dt.tzinfo is None or dt.tzinfo != datetime.timezone.utc:
165*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("usegmt option requires a UTC datetime")
166*cda5da8dSAndroid Build Coastguard Worker        zone = 'GMT'
167*cda5da8dSAndroid Build Coastguard Worker    elif dt.tzinfo is None:
168*cda5da8dSAndroid Build Coastguard Worker        zone = '-0000'
169*cda5da8dSAndroid Build Coastguard Worker    else:
170*cda5da8dSAndroid Build Coastguard Worker        zone = dt.strftime("%z")
171*cda5da8dSAndroid Build Coastguard Worker    return _format_timetuple_and_zone(now, zone)
172*cda5da8dSAndroid Build Coastguard Worker
173*cda5da8dSAndroid Build Coastguard Worker
174*cda5da8dSAndroid Build Coastguard Workerdef make_msgid(idstring=None, domain=None):
175*cda5da8dSAndroid Build Coastguard Worker    """Returns a string suitable for RFC 2822 compliant Message-ID, e.g:
176*cda5da8dSAndroid Build Coastguard Worker
177*cda5da8dSAndroid Build Coastguard Worker    <142480216486.20800.16526388040877946887@nightshade.la.mastaler.com>
178*cda5da8dSAndroid Build Coastguard Worker
179*cda5da8dSAndroid Build Coastguard Worker    Optional idstring if given is a string used to strengthen the
180*cda5da8dSAndroid Build Coastguard Worker    uniqueness of the message id.  Optional domain if given provides the
181*cda5da8dSAndroid Build Coastguard Worker    portion of the message id after the '@'.  It defaults to the locally
182*cda5da8dSAndroid Build Coastguard Worker    defined hostname.
183*cda5da8dSAndroid Build Coastguard Worker    """
184*cda5da8dSAndroid Build Coastguard Worker    timeval = int(time.time()*100)
185*cda5da8dSAndroid Build Coastguard Worker    pid = os.getpid()
186*cda5da8dSAndroid Build Coastguard Worker    randint = random.getrandbits(64)
187*cda5da8dSAndroid Build Coastguard Worker    if idstring is None:
188*cda5da8dSAndroid Build Coastguard Worker        idstring = ''
189*cda5da8dSAndroid Build Coastguard Worker    else:
190*cda5da8dSAndroid Build Coastguard Worker        idstring = '.' + idstring
191*cda5da8dSAndroid Build Coastguard Worker    if domain is None:
192*cda5da8dSAndroid Build Coastguard Worker        domain = socket.getfqdn()
193*cda5da8dSAndroid Build Coastguard Worker    msgid = '<%d.%d.%d%s@%s>' % (timeval, pid, randint, idstring, domain)
194*cda5da8dSAndroid Build Coastguard Worker    return msgid
195*cda5da8dSAndroid Build Coastguard Worker
196*cda5da8dSAndroid Build Coastguard Worker
197*cda5da8dSAndroid Build Coastguard Workerdef parsedate_to_datetime(data):
198*cda5da8dSAndroid Build Coastguard Worker    parsed_date_tz = _parsedate_tz(data)
199*cda5da8dSAndroid Build Coastguard Worker    if parsed_date_tz is None:
200*cda5da8dSAndroid Build Coastguard Worker        raise ValueError('Invalid date value or format "%s"' % str(data))
201*cda5da8dSAndroid Build Coastguard Worker    *dtuple, tz = parsed_date_tz
202*cda5da8dSAndroid Build Coastguard Worker    if tz is None:
203*cda5da8dSAndroid Build Coastguard Worker        return datetime.datetime(*dtuple[:6])
204*cda5da8dSAndroid Build Coastguard Worker    return datetime.datetime(*dtuple[:6],
205*cda5da8dSAndroid Build Coastguard Worker            tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))
206*cda5da8dSAndroid Build Coastguard Worker
207*cda5da8dSAndroid Build Coastguard Worker
208*cda5da8dSAndroid Build Coastguard Workerdef parseaddr(addr):
209*cda5da8dSAndroid Build Coastguard Worker    """
210*cda5da8dSAndroid Build Coastguard Worker    Parse addr into its constituent realname and email address parts.
211*cda5da8dSAndroid Build Coastguard Worker
212*cda5da8dSAndroid Build Coastguard Worker    Return a tuple of realname and email address, unless the parse fails, in
213*cda5da8dSAndroid Build Coastguard Worker    which case return a 2-tuple of ('', '').
214*cda5da8dSAndroid Build Coastguard Worker    """
215*cda5da8dSAndroid Build Coastguard Worker    addrs = _AddressList(addr).addresslist
216*cda5da8dSAndroid Build Coastguard Worker    if not addrs:
217*cda5da8dSAndroid Build Coastguard Worker        return '', ''
218*cda5da8dSAndroid Build Coastguard Worker    return addrs[0]
219*cda5da8dSAndroid Build Coastguard Worker
220*cda5da8dSAndroid Build Coastguard Worker
221*cda5da8dSAndroid Build Coastguard Worker# rfc822.unquote() doesn't properly de-backslash-ify in Python pre-2.3.
222*cda5da8dSAndroid Build Coastguard Workerdef unquote(str):
223*cda5da8dSAndroid Build Coastguard Worker    """Remove quotes from a string."""
224*cda5da8dSAndroid Build Coastguard Worker    if len(str) > 1:
225*cda5da8dSAndroid Build Coastguard Worker        if str.startswith('"') and str.endswith('"'):
226*cda5da8dSAndroid Build Coastguard Worker            return str[1:-1].replace('\\\\', '\\').replace('\\"', '"')
227*cda5da8dSAndroid Build Coastguard Worker        if str.startswith('<') and str.endswith('>'):
228*cda5da8dSAndroid Build Coastguard Worker            return str[1:-1]
229*cda5da8dSAndroid Build Coastguard Worker    return str
230*cda5da8dSAndroid Build Coastguard Worker
231*cda5da8dSAndroid Build Coastguard Worker
232*cda5da8dSAndroid Build Coastguard Worker
233*cda5da8dSAndroid Build Coastguard Worker# RFC2231-related functions - parameter encoding and decoding
234*cda5da8dSAndroid Build Coastguard Workerdef decode_rfc2231(s):
235*cda5da8dSAndroid Build Coastguard Worker    """Decode string according to RFC 2231"""
236*cda5da8dSAndroid Build Coastguard Worker    parts = s.split(TICK, 2)
237*cda5da8dSAndroid Build Coastguard Worker    if len(parts) <= 2:
238*cda5da8dSAndroid Build Coastguard Worker        return None, None, s
239*cda5da8dSAndroid Build Coastguard Worker    return parts
240*cda5da8dSAndroid Build Coastguard Worker
241*cda5da8dSAndroid Build Coastguard Worker
242*cda5da8dSAndroid Build Coastguard Workerdef encode_rfc2231(s, charset=None, language=None):
243*cda5da8dSAndroid Build Coastguard Worker    """Encode string according to RFC 2231.
244*cda5da8dSAndroid Build Coastguard Worker
245*cda5da8dSAndroid Build Coastguard Worker    If neither charset nor language is given, then s is returned as-is.  If
246*cda5da8dSAndroid Build Coastguard Worker    charset is given but not language, the string is encoded using the empty
247*cda5da8dSAndroid Build Coastguard Worker    string for language.
248*cda5da8dSAndroid Build Coastguard Worker    """
249*cda5da8dSAndroid Build Coastguard Worker    s = urllib.parse.quote(s, safe='', encoding=charset or 'ascii')
250*cda5da8dSAndroid Build Coastguard Worker    if charset is None and language is None:
251*cda5da8dSAndroid Build Coastguard Worker        return s
252*cda5da8dSAndroid Build Coastguard Worker    if language is None:
253*cda5da8dSAndroid Build Coastguard Worker        language = ''
254*cda5da8dSAndroid Build Coastguard Worker    return "%s'%s'%s" % (charset, language, s)
255*cda5da8dSAndroid Build Coastguard Worker
256*cda5da8dSAndroid Build Coastguard Worker
257*cda5da8dSAndroid Build Coastguard Workerrfc2231_continuation = re.compile(r'^(?P<name>\w+)\*((?P<num>[0-9]+)\*?)?$',
258*cda5da8dSAndroid Build Coastguard Worker    re.ASCII)
259*cda5da8dSAndroid Build Coastguard Worker
260*cda5da8dSAndroid Build Coastguard Workerdef decode_params(params):
261*cda5da8dSAndroid Build Coastguard Worker    """Decode parameters list according to RFC 2231.
262*cda5da8dSAndroid Build Coastguard Worker
263*cda5da8dSAndroid Build Coastguard Worker    params is a sequence of 2-tuples containing (param name, string value).
264*cda5da8dSAndroid Build Coastguard Worker    """
265*cda5da8dSAndroid Build Coastguard Worker    new_params = [params[0]]
266*cda5da8dSAndroid Build Coastguard Worker    # Map parameter's name to a list of continuations.  The values are a
267*cda5da8dSAndroid Build Coastguard Worker    # 3-tuple of the continuation number, the string value, and a flag
268*cda5da8dSAndroid Build Coastguard Worker    # specifying whether a particular segment is %-encoded.
269*cda5da8dSAndroid Build Coastguard Worker    rfc2231_params = {}
270*cda5da8dSAndroid Build Coastguard Worker    for name, value in params[1:]:
271*cda5da8dSAndroid Build Coastguard Worker        encoded = name.endswith('*')
272*cda5da8dSAndroid Build Coastguard Worker        value = unquote(value)
273*cda5da8dSAndroid Build Coastguard Worker        mo = rfc2231_continuation.match(name)
274*cda5da8dSAndroid Build Coastguard Worker        if mo:
275*cda5da8dSAndroid Build Coastguard Worker            name, num = mo.group('name', 'num')
276*cda5da8dSAndroid Build Coastguard Worker            if num is not None:
277*cda5da8dSAndroid Build Coastguard Worker                num = int(num)
278*cda5da8dSAndroid Build Coastguard Worker            rfc2231_params.setdefault(name, []).append((num, value, encoded))
279*cda5da8dSAndroid Build Coastguard Worker        else:
280*cda5da8dSAndroid Build Coastguard Worker            new_params.append((name, '"%s"' % quote(value)))
281*cda5da8dSAndroid Build Coastguard Worker    if rfc2231_params:
282*cda5da8dSAndroid Build Coastguard Worker        for name, continuations in rfc2231_params.items():
283*cda5da8dSAndroid Build Coastguard Worker            value = []
284*cda5da8dSAndroid Build Coastguard Worker            extended = False
285*cda5da8dSAndroid Build Coastguard Worker            # Sort by number
286*cda5da8dSAndroid Build Coastguard Worker            continuations.sort()
287*cda5da8dSAndroid Build Coastguard Worker            # And now append all values in numerical order, converting
288*cda5da8dSAndroid Build Coastguard Worker            # %-encodings for the encoded segments.  If any of the
289*cda5da8dSAndroid Build Coastguard Worker            # continuation names ends in a *, then the entire string, after
290*cda5da8dSAndroid Build Coastguard Worker            # decoding segments and concatenating, must have the charset and
291*cda5da8dSAndroid Build Coastguard Worker            # language specifiers at the beginning of the string.
292*cda5da8dSAndroid Build Coastguard Worker            for num, s, encoded in continuations:
293*cda5da8dSAndroid Build Coastguard Worker                if encoded:
294*cda5da8dSAndroid Build Coastguard Worker                    # Decode as "latin-1", so the characters in s directly
295*cda5da8dSAndroid Build Coastguard Worker                    # represent the percent-encoded octet values.
296*cda5da8dSAndroid Build Coastguard Worker                    # collapse_rfc2231_value treats this as an octet sequence.
297*cda5da8dSAndroid Build Coastguard Worker                    s = urllib.parse.unquote(s, encoding="latin-1")
298*cda5da8dSAndroid Build Coastguard Worker                    extended = True
299*cda5da8dSAndroid Build Coastguard Worker                value.append(s)
300*cda5da8dSAndroid Build Coastguard Worker            value = quote(EMPTYSTRING.join(value))
301*cda5da8dSAndroid Build Coastguard Worker            if extended:
302*cda5da8dSAndroid Build Coastguard Worker                charset, language, value = decode_rfc2231(value)
303*cda5da8dSAndroid Build Coastguard Worker                new_params.append((name, (charset, language, '"%s"' % value)))
304*cda5da8dSAndroid Build Coastguard Worker            else:
305*cda5da8dSAndroid Build Coastguard Worker                new_params.append((name, '"%s"' % value))
306*cda5da8dSAndroid Build Coastguard Worker    return new_params
307*cda5da8dSAndroid Build Coastguard Worker
308*cda5da8dSAndroid Build Coastguard Workerdef collapse_rfc2231_value(value, errors='replace',
309*cda5da8dSAndroid Build Coastguard Worker                           fallback_charset='us-ascii'):
310*cda5da8dSAndroid Build Coastguard Worker    if not isinstance(value, tuple) or len(value) != 3:
311*cda5da8dSAndroid Build Coastguard Worker        return unquote(value)
312*cda5da8dSAndroid Build Coastguard Worker    # While value comes to us as a unicode string, we need it to be a bytes
313*cda5da8dSAndroid Build Coastguard Worker    # object.  We do not want bytes() normal utf-8 decoder, we want a straight
314*cda5da8dSAndroid Build Coastguard Worker    # interpretation of the string as character bytes.
315*cda5da8dSAndroid Build Coastguard Worker    charset, language, text = value
316*cda5da8dSAndroid Build Coastguard Worker    if charset is None:
317*cda5da8dSAndroid Build Coastguard Worker        # Issue 17369: if charset/lang is None, decode_rfc2231 couldn't parse
318*cda5da8dSAndroid Build Coastguard Worker        # the value, so use the fallback_charset.
319*cda5da8dSAndroid Build Coastguard Worker        charset = fallback_charset
320*cda5da8dSAndroid Build Coastguard Worker    rawbytes = bytes(text, 'raw-unicode-escape')
321*cda5da8dSAndroid Build Coastguard Worker    try:
322*cda5da8dSAndroid Build Coastguard Worker        return str(rawbytes, charset, errors)
323*cda5da8dSAndroid Build Coastguard Worker    except LookupError:
324*cda5da8dSAndroid Build Coastguard Worker        # charset is not a known codec.
325*cda5da8dSAndroid Build Coastguard Worker        return unquote(text)
326*cda5da8dSAndroid Build Coastguard Worker
327*cda5da8dSAndroid Build Coastguard Worker
328*cda5da8dSAndroid Build Coastguard Worker#
329*cda5da8dSAndroid Build Coastguard Worker# datetime doesn't provide a localtime function yet, so provide one.  Code
330*cda5da8dSAndroid Build Coastguard Worker# adapted from the patch in issue 9527.  This may not be perfect, but it is
331*cda5da8dSAndroid Build Coastguard Worker# better than not having it.
332*cda5da8dSAndroid Build Coastguard Worker#
333*cda5da8dSAndroid Build Coastguard Worker
334*cda5da8dSAndroid Build Coastguard Workerdef localtime(dt=None, isdst=-1):
335*cda5da8dSAndroid Build Coastguard Worker    """Return local time as an aware datetime object.
336*cda5da8dSAndroid Build Coastguard Worker
337*cda5da8dSAndroid Build Coastguard Worker    If called without arguments, return current time.  Otherwise *dt*
338*cda5da8dSAndroid Build Coastguard Worker    argument should be a datetime instance, and it is converted to the
339*cda5da8dSAndroid Build Coastguard Worker    local time zone according to the system time zone database.  If *dt* is
340*cda5da8dSAndroid Build Coastguard Worker    naive (that is, dt.tzinfo is None), it is assumed to be in local time.
341*cda5da8dSAndroid Build Coastguard Worker    In this case, a positive or zero value for *isdst* causes localtime to
342*cda5da8dSAndroid Build Coastguard Worker    presume initially that summer time (for example, Daylight Saving Time)
343*cda5da8dSAndroid Build Coastguard Worker    is or is not (respectively) in effect for the specified time.  A
344*cda5da8dSAndroid Build Coastguard Worker    negative value for *isdst* causes the localtime() function to attempt
345*cda5da8dSAndroid Build Coastguard Worker    to divine whether summer time is in effect for the specified time.
346*cda5da8dSAndroid Build Coastguard Worker
347*cda5da8dSAndroid Build Coastguard Worker    """
348*cda5da8dSAndroid Build Coastguard Worker    if dt is None:
349*cda5da8dSAndroid Build Coastguard Worker        return datetime.datetime.now(datetime.timezone.utc).astimezone()
350*cda5da8dSAndroid Build Coastguard Worker    if dt.tzinfo is not None:
351*cda5da8dSAndroid Build Coastguard Worker        return dt.astimezone()
352*cda5da8dSAndroid Build Coastguard Worker    # We have a naive datetime.  Convert to a (localtime) timetuple and pass to
353*cda5da8dSAndroid Build Coastguard Worker    # system mktime together with the isdst hint.  System mktime will return
354*cda5da8dSAndroid Build Coastguard Worker    # seconds since epoch.
355*cda5da8dSAndroid Build Coastguard Worker    tm = dt.timetuple()[:-1] + (isdst,)
356*cda5da8dSAndroid Build Coastguard Worker    seconds = time.mktime(tm)
357*cda5da8dSAndroid Build Coastguard Worker    localtm = time.localtime(seconds)
358*cda5da8dSAndroid Build Coastguard Worker    try:
359*cda5da8dSAndroid Build Coastguard Worker        delta = datetime.timedelta(seconds=localtm.tm_gmtoff)
360*cda5da8dSAndroid Build Coastguard Worker        tz = datetime.timezone(delta, localtm.tm_zone)
361*cda5da8dSAndroid Build Coastguard Worker    except AttributeError:
362*cda5da8dSAndroid Build Coastguard Worker        # Compute UTC offset and compare with the value implied by tm_isdst.
363*cda5da8dSAndroid Build Coastguard Worker        # If the values match, use the zone name implied by tm_isdst.
364*cda5da8dSAndroid Build Coastguard Worker        delta = dt - datetime.datetime(*time.gmtime(seconds)[:6])
365*cda5da8dSAndroid Build Coastguard Worker        dst = time.daylight and localtm.tm_isdst > 0
366*cda5da8dSAndroid Build Coastguard Worker        gmtoff = -(time.altzone if dst else time.timezone)
367*cda5da8dSAndroid Build Coastguard Worker        if delta == datetime.timedelta(seconds=gmtoff):
368*cda5da8dSAndroid Build Coastguard Worker            tz = datetime.timezone(delta, time.tzname[dst])
369*cda5da8dSAndroid Build Coastguard Worker        else:
370*cda5da8dSAndroid Build Coastguard Worker            tz = datetime.timezone(delta)
371*cda5da8dSAndroid Build Coastguard Worker    return dt.replace(tzinfo=tz)
372