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