183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Copyright (C) 2001-2010 Python Software Foundation
283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Author: Barry Warsaw
383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Contact: email-sig@python.org
483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""Miscellaneous utilities."""
683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh__all__ = [
883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'collapse_rfc2231_value',
983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'decode_params',
1083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'decode_rfc2231',
1183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'encode_rfc2231',
1283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'formataddr',
1383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'formatdate',
1483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'getaddresses',
1583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'make_msgid',
1683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'mktime_tz',
1783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'parseaddr',
1883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'parsedate',
1983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'parsedate_tz',
2083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'unquote',
2183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ]
2283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport os
2483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport re
2583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport time
2683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport base64
2783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport random
2883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport socket
2983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport urllib
3083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport warnings
3183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom email._parseaddr import quote
3383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom email._parseaddr import AddressList as _AddressList
3483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom email._parseaddr import mktime_tz
3583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# We need wormarounds for bugs in these methods in older Pythons (see below)
3783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom email._parseaddr import parsedate as _parsedate
3883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom email._parseaddr import parsedate_tz as _parsedate_tz
3983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom quopri import decodestring as _qdecode
4183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Intrapackage imports
4383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom email.encoders import _bencode, _qencode
4483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehCOMMASPACE = ', '
4683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehEMPTYSTRING = ''
4783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehUEMPTYSTRING = u''
4883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehCRLF = '\r\n'
4983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehTICK = "'"
5083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehspecialsre = re.compile(r'[][\\()<>@,:;".]')
5283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehescapesre = re.compile(r'[][\\()"]')
5383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Helpers
5783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _identity(s):
5983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return s
6083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _bdecode(s):
6383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Decodes a base64 string.
6483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    This function is equivalent to base64.decodestring and it's retained only
6683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    for backward compatibility. It used to remove the last \\n of the decoded
6783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    string, if it had any (see issue 7143).
6883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
6983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not s:
7083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s
7183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return base64.decodestring(s)
7283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
7383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
7483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
7583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef fix_eols(s):
7683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Replace all line-ending characters with \\r\\n."""
7783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Fix newlines with no preceding carriage return
7883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    s = re.sub(r'(?<!\r)\n', CRLF, s)
7983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Fix carriage returns with no following newline
8083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    s = re.sub(r'\r(?!\n)', CRLF, s)
8183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return s
8283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef formataddr(pair):
8683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """The inverse of parseaddr(), this takes a 2-tuple of the form
8783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    (realname, email_address) and returns the string value suitable
8883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    for an RFC 2822 From, To or Cc header.
8983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    If the first element of pair is false, then the second element is
9183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    returned unmodified.
9283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
9383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    name, address = pair
9483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if name:
9583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        quotes = ''
9683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if specialsre.search(name):
9783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            quotes = '"'
9883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        name = escapesre.sub(r'\\\g<0>', name)
9983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return '%s%s%s <%s>' % (quotes, name, quotes, address)
10083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return address
10183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef getaddresses(fieldvalues):
10583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Return a list of (REALNAME, EMAIL) for each fieldvalue."""
10683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    all = COMMASPACE.join(fieldvalues)
10783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    a = _AddressList(all)
10883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return a.addresslist
10983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehecre = re.compile(r'''
11383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  =\?                   # literal =?
11483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  (?P<charset>[^?]*?)   # non-greedy up to the next ? is the charset
11583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  \?                    # literal ?
11683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  (?P<encoding>[qb])    # either a "q" or a "b", case insensitive
11783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  \?                    # literal ?
11883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  (?P<atom>.*?)         # non-greedy up to the next ?= is the atom
11983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  \?=                   # literal ?=
12083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  ''', re.VERBOSE | re.IGNORECASE)
12183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef formatdate(timeval=None, localtime=False, usegmt=False):
12583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Returns a date string as specified by RFC 2822, e.g.:
12683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Fri, 09 Nov 2001 01:08:47 -0000
12883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Optional timeval if given is a floating point time value as accepted by
13083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    gmtime() and localtime(), otherwise the current time is used.
13183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
13283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Optional localtime is a flag that when True, interprets timeval, and
13383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    returns a date relative to the local timezone instead of UTC, properly
13483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    taking daylight savings time into account.
13583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
13683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Optional argument usegmt means that the timezone is written out as
13783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    an ascii string, not numeric one (so "GMT" instead of "+0000"). This
13883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    is needed for HTTP, and is only used when localtime==False.
13983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
14083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Note: we cannot use strftime() because that honors the locale and RFC
14183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # 2822 requires that day and month names be the English abbreviations.
14283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if timeval is None:
14383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        timeval = time.time()
14483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if localtime:
14583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        now = time.localtime(timeval)
14683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Calculate timezone offset, based on whether the local zone has
14783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # daylight savings time, and whether DST is in effect.
14883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if time.daylight and now[-1]:
14983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            offset = time.altzone
15083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
15183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            offset = time.timezone
15283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        hours, minutes = divmod(abs(offset), 3600)
15383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Remember offset is in seconds west of UTC, but the timezone is in
15483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # minutes east of UTC, so the signs differ.
15583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if offset > 0:
15683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            sign = '-'
15783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
15883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            sign = '+'
15983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        zone = '%s%02d%02d' % (sign, hours, minutes // 60)
16083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
16183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        now = time.gmtime(timeval)
16283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Timezone offset is always -0000
16383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if usegmt:
16483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            zone = 'GMT'
16583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
16683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            zone = '-0000'
16783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return '%s, %02d %s %04d %02d:%02d:%02d %s' % (
16883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][now[6]],
16983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        now[2],
17083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
17183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh         'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][now[1] - 1],
17283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        now[0], now[3], now[4], now[5],
17383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        zone)
17483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
17583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
17683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
17783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef make_msgid(idstring=None):
17883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Returns a string suitable for RFC 2822 compliant Message-ID, e.g:
17983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
18083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    <20020201195627.33539.96671@nightshade.la.mastaler.com>
18183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
18283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Optional idstring if given is a string used to strengthen the
18383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    uniqueness of the message id.
18483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
18583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    timeval = time.time()
18683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval))
18783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    pid = os.getpid()
18883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    randint = random.randrange(100000)
18983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if idstring is None:
19083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        idstring = ''
19183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
19283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        idstring = '.' + idstring
19383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    idhost = socket.getfqdn()
19483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, idhost)
19583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return msgid
19683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# These functions are in the standalone mimelib version only because they've
20083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# subsequently been fixed in the latest Python versions.  We use this to worm
20183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# around broken older Pythons.
20283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef parsedate(data):
20383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not data:
20483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return None
20583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return _parsedate(data)
20683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef parsedate_tz(data):
20983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not data:
21083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return None
21183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return _parsedate_tz(data)
21283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
21383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
21483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef parseaddr(addr):
21583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    addrs = _AddressList(addr).addresslist
21683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not addrs:
21783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return '', ''
21883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return addrs[0]
21983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
22083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
22183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# rfc822.unquote() doesn't properly de-backslash-ify in Python pre-2.3.
22283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef unquote(str):
22383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Remove quotes from a string."""
22483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if len(str) > 1:
22583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if str.startswith('"') and str.endswith('"'):
22683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return str[1:-1].replace('\\\\', '\\').replace('\\"', '"')
22783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if str.startswith('<') and str.endswith('>'):
22883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return str[1:-1]
22983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return str
23083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
23183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
23283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
23383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# RFC2231-related functions - parameter encoding and decoding
23483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef decode_rfc2231(s):
23583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Decode string according to RFC 2231"""
23683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    parts = s.split(TICK, 2)
23783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if len(parts) <= 2:
23883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return None, None, s
23983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return parts
24083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
24183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
24283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef encode_rfc2231(s, charset=None, language=None):
24383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Encode string according to RFC 2231.
24483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
24583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    If neither charset nor language is given, then s is returned as-is.  If
24683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    charset is given but not language, the string is encoded using the empty
24783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    string for language.
24883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
24983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import urllib
25083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    s = urllib.quote(s, safe='')
25183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if charset is None and language is None:
25283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s
25383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if language is None:
25483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        language = ''
25583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return "%s'%s'%s" % (charset, language, s)
25683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
25783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
25883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehrfc2231_continuation = re.compile(r'^(?P<name>\w+)\*((?P<num>[0-9]+)\*?)?$')
25983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
26083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef decode_params(params):
26183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Decode parameters list according to RFC 2231.
26283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
26383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    params is a sequence of 2-tuples containing (param name, string value).
26483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
26583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Copy params so we don't mess with the original
26683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    params = params[:]
26783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    new_params = []
26883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Map parameter's name to a list of continuations.  The values are a
26983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # 3-tuple of the continuation number, the string value, and a flag
27083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # specifying whether a particular segment is %-encoded.
27183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    rfc2231_params = {}
27283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    name, value = params.pop(0)
27383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    new_params.append((name, value))
27483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    while params:
27583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        name, value = params.pop(0)
27683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if name.endswith('*'):
27783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            encoded = True
27883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
27983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            encoded = False
28083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        value = unquote(value)
28183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        mo = rfc2231_continuation.match(name)
28283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if mo:
28383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            name, num = mo.group('name', 'num')
28483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if num is not None:
28583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                num = int(num)
28683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            rfc2231_params.setdefault(name, []).append((num, value, encoded))
28783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
28883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            new_params.append((name, '"%s"' % quote(value)))
28983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if rfc2231_params:
29083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for name, continuations in rfc2231_params.items():
29183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            value = []
29283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            extended = False
29383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # Sort by number
29483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            continuations.sort()
29583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # And now append all values in numerical order, converting
29683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # %-encodings for the encoded segments.  If any of the
29783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # continuation names ends in a *, then the entire string, after
29883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # decoding segments and concatenating, must have the charset and
29983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # language specifiers at the beginning of the string.
30083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for num, s, encoded in continuations:
30183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if encoded:
30283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    s = urllib.unquote(s)
30383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    extended = True
30483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                value.append(s)
30583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            value = quote(EMPTYSTRING.join(value))
30683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if extended:
30783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                charset, language, value = decode_rfc2231(value)
30883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                new_params.append((name, (charset, language, '"%s"' % value)))
30983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else:
31083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                new_params.append((name, '"%s"' % value))
31183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return new_params
31283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
31383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef collapse_rfc2231_value(value, errors='replace',
31483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                           fallback_charset='us-ascii'):
31583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if isinstance(value, tuple):
31683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        rawval = unquote(value[2])
31783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        charset = value[0] or 'us-ascii'
31883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
31983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return unicode(rawval, charset, errors)
32083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except LookupError:
32183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # XXX charset is unknown to Python.
32283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return unicode(rawval, fallback_charset, errors)
32383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
32483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return unquote(value)
325