17db96d56Sopenharmony_ci####
27db96d56Sopenharmony_ci# Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
37db96d56Sopenharmony_ci#
47db96d56Sopenharmony_ci#                All Rights Reserved
57db96d56Sopenharmony_ci#
67db96d56Sopenharmony_ci# Permission to use, copy, modify, and distribute this software
77db96d56Sopenharmony_ci# and its documentation for any purpose and without fee is hereby
87db96d56Sopenharmony_ci# granted, provided that the above copyright notice appear in all
97db96d56Sopenharmony_ci# copies and that both that copyright notice and this permission
107db96d56Sopenharmony_ci# notice appear in supporting documentation, and that the name of
117db96d56Sopenharmony_ci# Timothy O'Malley  not be used in advertising or publicity
127db96d56Sopenharmony_ci# pertaining to distribution of the software without specific, written
137db96d56Sopenharmony_ci# prior permission.
147db96d56Sopenharmony_ci#
157db96d56Sopenharmony_ci# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
167db96d56Sopenharmony_ci# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
177db96d56Sopenharmony_ci# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
187db96d56Sopenharmony_ci# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
197db96d56Sopenharmony_ci# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
207db96d56Sopenharmony_ci# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
217db96d56Sopenharmony_ci# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
227db96d56Sopenharmony_ci# PERFORMANCE OF THIS SOFTWARE.
237db96d56Sopenharmony_ci#
247db96d56Sopenharmony_ci####
257db96d56Sopenharmony_ci#
267db96d56Sopenharmony_ci# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
277db96d56Sopenharmony_ci#   by Timothy O'Malley <timo@alum.mit.edu>
287db96d56Sopenharmony_ci#
297db96d56Sopenharmony_ci#  Cookie.py is a Python module for the handling of HTTP
307db96d56Sopenharmony_ci#  cookies as a Python dictionary.  See RFC 2109 for more
317db96d56Sopenharmony_ci#  information on cookies.
327db96d56Sopenharmony_ci#
337db96d56Sopenharmony_ci#  The original idea to treat Cookies as a dictionary came from
347db96d56Sopenharmony_ci#  Dave Mitchell (davem@magnet.com) in 1995, when he released the
357db96d56Sopenharmony_ci#  first version of nscookie.py.
367db96d56Sopenharmony_ci#
377db96d56Sopenharmony_ci####
387db96d56Sopenharmony_ci
397db96d56Sopenharmony_cir"""
407db96d56Sopenharmony_ciHere's a sample session to show how to use this module.
417db96d56Sopenharmony_ciAt the moment, this is the only documentation.
427db96d56Sopenharmony_ci
437db96d56Sopenharmony_ciThe Basics
447db96d56Sopenharmony_ci----------
457db96d56Sopenharmony_ci
467db96d56Sopenharmony_ciImporting is easy...
477db96d56Sopenharmony_ci
487db96d56Sopenharmony_ci   >>> from http import cookies
497db96d56Sopenharmony_ci
507db96d56Sopenharmony_ciMost of the time you start by creating a cookie.
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_ci   >>> C = cookies.SimpleCookie()
537db96d56Sopenharmony_ci
547db96d56Sopenharmony_ciOnce you've created your Cookie, you can add values just as if it were
557db96d56Sopenharmony_cia dictionary.
567db96d56Sopenharmony_ci
577db96d56Sopenharmony_ci   >>> C = cookies.SimpleCookie()
587db96d56Sopenharmony_ci   >>> C["fig"] = "newton"
597db96d56Sopenharmony_ci   >>> C["sugar"] = "wafer"
607db96d56Sopenharmony_ci   >>> C.output()
617db96d56Sopenharmony_ci   'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
627db96d56Sopenharmony_ci
637db96d56Sopenharmony_ciNotice that the printable representation of a Cookie is the
647db96d56Sopenharmony_ciappropriate format for a Set-Cookie: header.  This is the
657db96d56Sopenharmony_cidefault behavior.  You can change the header and printed
667db96d56Sopenharmony_ciattributes by using the .output() function
677db96d56Sopenharmony_ci
687db96d56Sopenharmony_ci   >>> C = cookies.SimpleCookie()
697db96d56Sopenharmony_ci   >>> C["rocky"] = "road"
707db96d56Sopenharmony_ci   >>> C["rocky"]["path"] = "/cookie"
717db96d56Sopenharmony_ci   >>> print(C.output(header="Cookie:"))
727db96d56Sopenharmony_ci   Cookie: rocky=road; Path=/cookie
737db96d56Sopenharmony_ci   >>> print(C.output(attrs=[], header="Cookie:"))
747db96d56Sopenharmony_ci   Cookie: rocky=road
757db96d56Sopenharmony_ci
767db96d56Sopenharmony_ciThe load() method of a Cookie extracts cookies from a string.  In a
777db96d56Sopenharmony_ciCGI script, you would use this method to extract the cookies from the
787db96d56Sopenharmony_ciHTTP_COOKIE environment variable.
797db96d56Sopenharmony_ci
807db96d56Sopenharmony_ci   >>> C = cookies.SimpleCookie()
817db96d56Sopenharmony_ci   >>> C.load("chips=ahoy; vienna=finger")
827db96d56Sopenharmony_ci   >>> C.output()
837db96d56Sopenharmony_ci   'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
847db96d56Sopenharmony_ci
857db96d56Sopenharmony_ciThe load() method is darn-tootin smart about identifying cookies
867db96d56Sopenharmony_ciwithin a string.  Escaped quotation marks, nested semicolons, and other
877db96d56Sopenharmony_cisuch trickeries do not confuse it.
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_ci   >>> C = cookies.SimpleCookie()
907db96d56Sopenharmony_ci   >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
917db96d56Sopenharmony_ci   >>> print(C)
927db96d56Sopenharmony_ci   Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
937db96d56Sopenharmony_ci
947db96d56Sopenharmony_ciEach element of the Cookie also supports all of the RFC 2109
957db96d56Sopenharmony_ciCookie attributes.  Here's an example which sets the Path
967db96d56Sopenharmony_ciattribute.
977db96d56Sopenharmony_ci
987db96d56Sopenharmony_ci   >>> C = cookies.SimpleCookie()
997db96d56Sopenharmony_ci   >>> C["oreo"] = "doublestuff"
1007db96d56Sopenharmony_ci   >>> C["oreo"]["path"] = "/"
1017db96d56Sopenharmony_ci   >>> print(C)
1027db96d56Sopenharmony_ci   Set-Cookie: oreo=doublestuff; Path=/
1037db96d56Sopenharmony_ci
1047db96d56Sopenharmony_ciEach dictionary element has a 'value' attribute, which gives you
1057db96d56Sopenharmony_ciback the value associated with the key.
1067db96d56Sopenharmony_ci
1077db96d56Sopenharmony_ci   >>> C = cookies.SimpleCookie()
1087db96d56Sopenharmony_ci   >>> C["twix"] = "none for you"
1097db96d56Sopenharmony_ci   >>> C["twix"].value
1107db96d56Sopenharmony_ci   'none for you'
1117db96d56Sopenharmony_ci
1127db96d56Sopenharmony_ciThe SimpleCookie expects that all values should be standard strings.
1137db96d56Sopenharmony_ciJust to be sure, SimpleCookie invokes the str() builtin to convert
1147db96d56Sopenharmony_cithe value to a string, when the values are set dictionary-style.
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_ci   >>> C = cookies.SimpleCookie()
1177db96d56Sopenharmony_ci   >>> C["number"] = 7
1187db96d56Sopenharmony_ci   >>> C["string"] = "seven"
1197db96d56Sopenharmony_ci   >>> C["number"].value
1207db96d56Sopenharmony_ci   '7'
1217db96d56Sopenharmony_ci   >>> C["string"].value
1227db96d56Sopenharmony_ci   'seven'
1237db96d56Sopenharmony_ci   >>> C.output()
1247db96d56Sopenharmony_ci   'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
1257db96d56Sopenharmony_ci
1267db96d56Sopenharmony_ciFinis.
1277db96d56Sopenharmony_ci"""
1287db96d56Sopenharmony_ci
1297db96d56Sopenharmony_ci#
1307db96d56Sopenharmony_ci# Import our required modules
1317db96d56Sopenharmony_ci#
1327db96d56Sopenharmony_ciimport re
1337db96d56Sopenharmony_ciimport string
1347db96d56Sopenharmony_ciimport types
1357db96d56Sopenharmony_ci
1367db96d56Sopenharmony_ci__all__ = ["CookieError", "BaseCookie", "SimpleCookie"]
1377db96d56Sopenharmony_ci
1387db96d56Sopenharmony_ci_nulljoin = ''.join
1397db96d56Sopenharmony_ci_semispacejoin = '; '.join
1407db96d56Sopenharmony_ci_spacejoin = ' '.join
1417db96d56Sopenharmony_ci
1427db96d56Sopenharmony_ci#
1437db96d56Sopenharmony_ci# Define an exception visible to External modules
1447db96d56Sopenharmony_ci#
1457db96d56Sopenharmony_ciclass CookieError(Exception):
1467db96d56Sopenharmony_ci    pass
1477db96d56Sopenharmony_ci
1487db96d56Sopenharmony_ci
1497db96d56Sopenharmony_ci# These quoting routines conform to the RFC2109 specification, which in
1507db96d56Sopenharmony_ci# turn references the character definitions from RFC2068.  They provide
1517db96d56Sopenharmony_ci# a two-way quoting algorithm.  Any non-text character is translated
1527db96d56Sopenharmony_ci# into a 4 character sequence: a forward-slash followed by the
1537db96d56Sopenharmony_ci# three-digit octal equivalent of the character.  Any '\' or '"' is
1547db96d56Sopenharmony_ci# quoted with a preceding '\' slash.
1557db96d56Sopenharmony_ci# Because of the way browsers really handle cookies (as opposed to what
1567db96d56Sopenharmony_ci# the RFC says) we also encode "," and ";".
1577db96d56Sopenharmony_ci#
1587db96d56Sopenharmony_ci# These are taken from RFC2068 and RFC2109.
1597db96d56Sopenharmony_ci#       _LegalChars       is the list of chars which don't require "'s
1607db96d56Sopenharmony_ci#       _Translator       hash-table for fast quoting
1617db96d56Sopenharmony_ci#
1627db96d56Sopenharmony_ci_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~:"
1637db96d56Sopenharmony_ci_UnescapedChars = _LegalChars + ' ()/<=>?@[]{}'
1647db96d56Sopenharmony_ci
1657db96d56Sopenharmony_ci_Translator = {n: '\\%03o' % n
1667db96d56Sopenharmony_ci               for n in set(range(256)) - set(map(ord, _UnescapedChars))}
1677db96d56Sopenharmony_ci_Translator.update({
1687db96d56Sopenharmony_ci    ord('"'): '\\"',
1697db96d56Sopenharmony_ci    ord('\\'): '\\\\',
1707db96d56Sopenharmony_ci})
1717db96d56Sopenharmony_ci
1727db96d56Sopenharmony_ci_is_legal_key = re.compile('[%s]+' % re.escape(_LegalChars)).fullmatch
1737db96d56Sopenharmony_ci
1747db96d56Sopenharmony_cidef _quote(str):
1757db96d56Sopenharmony_ci    r"""Quote a string for use in a cookie header.
1767db96d56Sopenharmony_ci
1777db96d56Sopenharmony_ci    If the string does not need to be double-quoted, then just return the
1787db96d56Sopenharmony_ci    string.  Otherwise, surround the string in doublequotes and quote
1797db96d56Sopenharmony_ci    (with a \) special characters.
1807db96d56Sopenharmony_ci    """
1817db96d56Sopenharmony_ci    if str is None or _is_legal_key(str):
1827db96d56Sopenharmony_ci        return str
1837db96d56Sopenharmony_ci    else:
1847db96d56Sopenharmony_ci        return '"' + str.translate(_Translator) + '"'
1857db96d56Sopenharmony_ci
1867db96d56Sopenharmony_ci
1877db96d56Sopenharmony_ci_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
1887db96d56Sopenharmony_ci_QuotePatt = re.compile(r"[\\].")
1897db96d56Sopenharmony_ci
1907db96d56Sopenharmony_cidef _unquote(str):
1917db96d56Sopenharmony_ci    # If there aren't any doublequotes,
1927db96d56Sopenharmony_ci    # then there can't be any special characters.  See RFC 2109.
1937db96d56Sopenharmony_ci    if str is None or len(str) < 2:
1947db96d56Sopenharmony_ci        return str
1957db96d56Sopenharmony_ci    if str[0] != '"' or str[-1] != '"':
1967db96d56Sopenharmony_ci        return str
1977db96d56Sopenharmony_ci
1987db96d56Sopenharmony_ci    # We have to assume that we must decode this string.
1997db96d56Sopenharmony_ci    # Down to work.
2007db96d56Sopenharmony_ci
2017db96d56Sopenharmony_ci    # Remove the "s
2027db96d56Sopenharmony_ci    str = str[1:-1]
2037db96d56Sopenharmony_ci
2047db96d56Sopenharmony_ci    # Check for special sequences.  Examples:
2057db96d56Sopenharmony_ci    #    \012 --> \n
2067db96d56Sopenharmony_ci    #    \"   --> "
2077db96d56Sopenharmony_ci    #
2087db96d56Sopenharmony_ci    i = 0
2097db96d56Sopenharmony_ci    n = len(str)
2107db96d56Sopenharmony_ci    res = []
2117db96d56Sopenharmony_ci    while 0 <= i < n:
2127db96d56Sopenharmony_ci        o_match = _OctalPatt.search(str, i)
2137db96d56Sopenharmony_ci        q_match = _QuotePatt.search(str, i)
2147db96d56Sopenharmony_ci        if not o_match and not q_match:              # Neither matched
2157db96d56Sopenharmony_ci            res.append(str[i:])
2167db96d56Sopenharmony_ci            break
2177db96d56Sopenharmony_ci        # else:
2187db96d56Sopenharmony_ci        j = k = -1
2197db96d56Sopenharmony_ci        if o_match:
2207db96d56Sopenharmony_ci            j = o_match.start(0)
2217db96d56Sopenharmony_ci        if q_match:
2227db96d56Sopenharmony_ci            k = q_match.start(0)
2237db96d56Sopenharmony_ci        if q_match and (not o_match or k < j):     # QuotePatt matched
2247db96d56Sopenharmony_ci            res.append(str[i:k])
2257db96d56Sopenharmony_ci            res.append(str[k+1])
2267db96d56Sopenharmony_ci            i = k + 2
2277db96d56Sopenharmony_ci        else:                                      # OctalPatt matched
2287db96d56Sopenharmony_ci            res.append(str[i:j])
2297db96d56Sopenharmony_ci            res.append(chr(int(str[j+1:j+4], 8)))
2307db96d56Sopenharmony_ci            i = j + 4
2317db96d56Sopenharmony_ci    return _nulljoin(res)
2327db96d56Sopenharmony_ci
2337db96d56Sopenharmony_ci# The _getdate() routine is used to set the expiration time in the cookie's HTTP
2347db96d56Sopenharmony_ci# header.  By default, _getdate() returns the current time in the appropriate
2357db96d56Sopenharmony_ci# "expires" format for a Set-Cookie header.  The one optional argument is an
2367db96d56Sopenharmony_ci# offset from now, in seconds.  For example, an offset of -3600 means "one hour
2377db96d56Sopenharmony_ci# ago".  The offset may be a floating point number.
2387db96d56Sopenharmony_ci#
2397db96d56Sopenharmony_ci
2407db96d56Sopenharmony_ci_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
2417db96d56Sopenharmony_ci
2427db96d56Sopenharmony_ci_monthname = [None,
2437db96d56Sopenharmony_ci              'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
2447db96d56Sopenharmony_ci              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
2457db96d56Sopenharmony_ci
2467db96d56Sopenharmony_cidef _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
2477db96d56Sopenharmony_ci    from time import gmtime, time
2487db96d56Sopenharmony_ci    now = time()
2497db96d56Sopenharmony_ci    year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
2507db96d56Sopenharmony_ci    return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \
2517db96d56Sopenharmony_ci           (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
2527db96d56Sopenharmony_ci
2537db96d56Sopenharmony_ci
2547db96d56Sopenharmony_ciclass Morsel(dict):
2557db96d56Sopenharmony_ci    """A class to hold ONE (key, value) pair.
2567db96d56Sopenharmony_ci
2577db96d56Sopenharmony_ci    In a cookie, each such pair may have several attributes, so this class is
2587db96d56Sopenharmony_ci    used to keep the attributes associated with the appropriate key,value pair.
2597db96d56Sopenharmony_ci    This class also includes a coded_value attribute, which is used to hold
2607db96d56Sopenharmony_ci    the network representation of the value.
2617db96d56Sopenharmony_ci    """
2627db96d56Sopenharmony_ci    # RFC 2109 lists these attributes as reserved:
2637db96d56Sopenharmony_ci    #   path       comment         domain
2647db96d56Sopenharmony_ci    #   max-age    secure      version
2657db96d56Sopenharmony_ci    #
2667db96d56Sopenharmony_ci    # For historical reasons, these attributes are also reserved:
2677db96d56Sopenharmony_ci    #   expires
2687db96d56Sopenharmony_ci    #
2697db96d56Sopenharmony_ci    # This is an extension from Microsoft:
2707db96d56Sopenharmony_ci    #   httponly
2717db96d56Sopenharmony_ci    #
2727db96d56Sopenharmony_ci    # This dictionary provides a mapping from the lowercase
2737db96d56Sopenharmony_ci    # variant on the left to the appropriate traditional
2747db96d56Sopenharmony_ci    # formatting on the right.
2757db96d56Sopenharmony_ci    _reserved = {
2767db96d56Sopenharmony_ci        "expires"  : "expires",
2777db96d56Sopenharmony_ci        "path"     : "Path",
2787db96d56Sopenharmony_ci        "comment"  : "Comment",
2797db96d56Sopenharmony_ci        "domain"   : "Domain",
2807db96d56Sopenharmony_ci        "max-age"  : "Max-Age",
2817db96d56Sopenharmony_ci        "secure"   : "Secure",
2827db96d56Sopenharmony_ci        "httponly" : "HttpOnly",
2837db96d56Sopenharmony_ci        "version"  : "Version",
2847db96d56Sopenharmony_ci        "samesite" : "SameSite",
2857db96d56Sopenharmony_ci    }
2867db96d56Sopenharmony_ci
2877db96d56Sopenharmony_ci    _flags = {'secure', 'httponly'}
2887db96d56Sopenharmony_ci
2897db96d56Sopenharmony_ci    def __init__(self):
2907db96d56Sopenharmony_ci        # Set defaults
2917db96d56Sopenharmony_ci        self._key = self._value = self._coded_value = None
2927db96d56Sopenharmony_ci
2937db96d56Sopenharmony_ci        # Set default attributes
2947db96d56Sopenharmony_ci        for key in self._reserved:
2957db96d56Sopenharmony_ci            dict.__setitem__(self, key, "")
2967db96d56Sopenharmony_ci
2977db96d56Sopenharmony_ci    @property
2987db96d56Sopenharmony_ci    def key(self):
2997db96d56Sopenharmony_ci        return self._key
3007db96d56Sopenharmony_ci
3017db96d56Sopenharmony_ci    @property
3027db96d56Sopenharmony_ci    def value(self):
3037db96d56Sopenharmony_ci        return self._value
3047db96d56Sopenharmony_ci
3057db96d56Sopenharmony_ci    @property
3067db96d56Sopenharmony_ci    def coded_value(self):
3077db96d56Sopenharmony_ci        return self._coded_value
3087db96d56Sopenharmony_ci
3097db96d56Sopenharmony_ci    def __setitem__(self, K, V):
3107db96d56Sopenharmony_ci        K = K.lower()
3117db96d56Sopenharmony_ci        if not K in self._reserved:
3127db96d56Sopenharmony_ci            raise CookieError("Invalid attribute %r" % (K,))
3137db96d56Sopenharmony_ci        dict.__setitem__(self, K, V)
3147db96d56Sopenharmony_ci
3157db96d56Sopenharmony_ci    def setdefault(self, key, val=None):
3167db96d56Sopenharmony_ci        key = key.lower()
3177db96d56Sopenharmony_ci        if key not in self._reserved:
3187db96d56Sopenharmony_ci            raise CookieError("Invalid attribute %r" % (key,))
3197db96d56Sopenharmony_ci        return dict.setdefault(self, key, val)
3207db96d56Sopenharmony_ci
3217db96d56Sopenharmony_ci    def __eq__(self, morsel):
3227db96d56Sopenharmony_ci        if not isinstance(morsel, Morsel):
3237db96d56Sopenharmony_ci            return NotImplemented
3247db96d56Sopenharmony_ci        return (dict.__eq__(self, morsel) and
3257db96d56Sopenharmony_ci                self._value == morsel._value and
3267db96d56Sopenharmony_ci                self._key == morsel._key and
3277db96d56Sopenharmony_ci                self._coded_value == morsel._coded_value)
3287db96d56Sopenharmony_ci
3297db96d56Sopenharmony_ci    __ne__ = object.__ne__
3307db96d56Sopenharmony_ci
3317db96d56Sopenharmony_ci    def copy(self):
3327db96d56Sopenharmony_ci        morsel = Morsel()
3337db96d56Sopenharmony_ci        dict.update(morsel, self)
3347db96d56Sopenharmony_ci        morsel.__dict__.update(self.__dict__)
3357db96d56Sopenharmony_ci        return morsel
3367db96d56Sopenharmony_ci
3377db96d56Sopenharmony_ci    def update(self, values):
3387db96d56Sopenharmony_ci        data = {}
3397db96d56Sopenharmony_ci        for key, val in dict(values).items():
3407db96d56Sopenharmony_ci            key = key.lower()
3417db96d56Sopenharmony_ci            if key not in self._reserved:
3427db96d56Sopenharmony_ci                raise CookieError("Invalid attribute %r" % (key,))
3437db96d56Sopenharmony_ci            data[key] = val
3447db96d56Sopenharmony_ci        dict.update(self, data)
3457db96d56Sopenharmony_ci
3467db96d56Sopenharmony_ci    def isReservedKey(self, K):
3477db96d56Sopenharmony_ci        return K.lower() in self._reserved
3487db96d56Sopenharmony_ci
3497db96d56Sopenharmony_ci    def set(self, key, val, coded_val):
3507db96d56Sopenharmony_ci        if key.lower() in self._reserved:
3517db96d56Sopenharmony_ci            raise CookieError('Attempt to set a reserved key %r' % (key,))
3527db96d56Sopenharmony_ci        if not _is_legal_key(key):
3537db96d56Sopenharmony_ci            raise CookieError('Illegal key %r' % (key,))
3547db96d56Sopenharmony_ci
3557db96d56Sopenharmony_ci        # It's a good key, so save it.
3567db96d56Sopenharmony_ci        self._key = key
3577db96d56Sopenharmony_ci        self._value = val
3587db96d56Sopenharmony_ci        self._coded_value = coded_val
3597db96d56Sopenharmony_ci
3607db96d56Sopenharmony_ci    def __getstate__(self):
3617db96d56Sopenharmony_ci        return {
3627db96d56Sopenharmony_ci            'key': self._key,
3637db96d56Sopenharmony_ci            'value': self._value,
3647db96d56Sopenharmony_ci            'coded_value': self._coded_value,
3657db96d56Sopenharmony_ci        }
3667db96d56Sopenharmony_ci
3677db96d56Sopenharmony_ci    def __setstate__(self, state):
3687db96d56Sopenharmony_ci        self._key = state['key']
3697db96d56Sopenharmony_ci        self._value = state['value']
3707db96d56Sopenharmony_ci        self._coded_value = state['coded_value']
3717db96d56Sopenharmony_ci
3727db96d56Sopenharmony_ci    def output(self, attrs=None, header="Set-Cookie:"):
3737db96d56Sopenharmony_ci        return "%s %s" % (header, self.OutputString(attrs))
3747db96d56Sopenharmony_ci
3757db96d56Sopenharmony_ci    __str__ = output
3767db96d56Sopenharmony_ci
3777db96d56Sopenharmony_ci    def __repr__(self):
3787db96d56Sopenharmony_ci        return '<%s: %s>' % (self.__class__.__name__, self.OutputString())
3797db96d56Sopenharmony_ci
3807db96d56Sopenharmony_ci    def js_output(self, attrs=None):
3817db96d56Sopenharmony_ci        # Print javascript
3827db96d56Sopenharmony_ci        return """
3837db96d56Sopenharmony_ci        <script type="text/javascript">
3847db96d56Sopenharmony_ci        <!-- begin hiding
3857db96d56Sopenharmony_ci        document.cookie = \"%s\";
3867db96d56Sopenharmony_ci        // end hiding -->
3877db96d56Sopenharmony_ci        </script>
3887db96d56Sopenharmony_ci        """ % (self.OutputString(attrs).replace('"', r'\"'))
3897db96d56Sopenharmony_ci
3907db96d56Sopenharmony_ci    def OutputString(self, attrs=None):
3917db96d56Sopenharmony_ci        # Build up our result
3927db96d56Sopenharmony_ci        #
3937db96d56Sopenharmony_ci        result = []
3947db96d56Sopenharmony_ci        append = result.append
3957db96d56Sopenharmony_ci
3967db96d56Sopenharmony_ci        # First, the key=value pair
3977db96d56Sopenharmony_ci        append("%s=%s" % (self.key, self.coded_value))
3987db96d56Sopenharmony_ci
3997db96d56Sopenharmony_ci        # Now add any defined attributes
4007db96d56Sopenharmony_ci        if attrs is None:
4017db96d56Sopenharmony_ci            attrs = self._reserved
4027db96d56Sopenharmony_ci        items = sorted(self.items())
4037db96d56Sopenharmony_ci        for key, value in items:
4047db96d56Sopenharmony_ci            if value == "":
4057db96d56Sopenharmony_ci                continue
4067db96d56Sopenharmony_ci            if key not in attrs:
4077db96d56Sopenharmony_ci                continue
4087db96d56Sopenharmony_ci            if key == "expires" and isinstance(value, int):
4097db96d56Sopenharmony_ci                append("%s=%s" % (self._reserved[key], _getdate(value)))
4107db96d56Sopenharmony_ci            elif key == "max-age" and isinstance(value, int):
4117db96d56Sopenharmony_ci                append("%s=%d" % (self._reserved[key], value))
4127db96d56Sopenharmony_ci            elif key == "comment" and isinstance(value, str):
4137db96d56Sopenharmony_ci                append("%s=%s" % (self._reserved[key], _quote(value)))
4147db96d56Sopenharmony_ci            elif key in self._flags:
4157db96d56Sopenharmony_ci                if value:
4167db96d56Sopenharmony_ci                    append(str(self._reserved[key]))
4177db96d56Sopenharmony_ci            else:
4187db96d56Sopenharmony_ci                append("%s=%s" % (self._reserved[key], value))
4197db96d56Sopenharmony_ci
4207db96d56Sopenharmony_ci        # Return the result
4217db96d56Sopenharmony_ci        return _semispacejoin(result)
4227db96d56Sopenharmony_ci
4237db96d56Sopenharmony_ci    __class_getitem__ = classmethod(types.GenericAlias)
4247db96d56Sopenharmony_ci
4257db96d56Sopenharmony_ci
4267db96d56Sopenharmony_ci#
4277db96d56Sopenharmony_ci# Pattern for finding cookie
4287db96d56Sopenharmony_ci#
4297db96d56Sopenharmony_ci# This used to be strict parsing based on the RFC2109 and RFC2068
4307db96d56Sopenharmony_ci# specifications.  I have since discovered that MSIE 3.0x doesn't
4317db96d56Sopenharmony_ci# follow the character rules outlined in those specs.  As a
4327db96d56Sopenharmony_ci# result, the parsing rules here are less strict.
4337db96d56Sopenharmony_ci#
4347db96d56Sopenharmony_ci
4357db96d56Sopenharmony_ci_LegalKeyChars  = r"\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\="
4367db96d56Sopenharmony_ci_LegalValueChars = _LegalKeyChars + r'\[\]'
4377db96d56Sopenharmony_ci_CookiePattern = re.compile(r"""
4387db96d56Sopenharmony_ci    \s*                            # Optional whitespace at start of cookie
4397db96d56Sopenharmony_ci    (?P<key>                       # Start of group 'key'
4407db96d56Sopenharmony_ci    [""" + _LegalKeyChars + r"""]+?   # Any word of at least one letter
4417db96d56Sopenharmony_ci    )                              # End of group 'key'
4427db96d56Sopenharmony_ci    (                              # Optional group: there may not be a value.
4437db96d56Sopenharmony_ci    \s*=\s*                          # Equal Sign
4447db96d56Sopenharmony_ci    (?P<val>                         # Start of group 'val'
4457db96d56Sopenharmony_ci    "(?:[^\\"]|\\.)*"                  # Any doublequoted string
4467db96d56Sopenharmony_ci    |                                  # or
4477db96d56Sopenharmony_ci    \w{3},\s[\w\d\s-]{9,11}\s[\d:]{8}\sGMT  # Special case for "expires" attr
4487db96d56Sopenharmony_ci    |                                  # or
4497db96d56Sopenharmony_ci    [""" + _LegalValueChars + r"""]*      # Any word or empty string
4507db96d56Sopenharmony_ci    )                                # End of group 'val'
4517db96d56Sopenharmony_ci    )?                             # End of optional value group
4527db96d56Sopenharmony_ci    \s*                            # Any number of spaces.
4537db96d56Sopenharmony_ci    (\s+|;|$)                      # Ending either at space, semicolon, or EOS.
4547db96d56Sopenharmony_ci    """, re.ASCII | re.VERBOSE)    # re.ASCII may be removed if safe.
4557db96d56Sopenharmony_ci
4567db96d56Sopenharmony_ci
4577db96d56Sopenharmony_ci# At long last, here is the cookie class.  Using this class is almost just like
4587db96d56Sopenharmony_ci# using a dictionary.  See this module's docstring for example usage.
4597db96d56Sopenharmony_ci#
4607db96d56Sopenharmony_ciclass BaseCookie(dict):
4617db96d56Sopenharmony_ci    """A container class for a set of Morsels."""
4627db96d56Sopenharmony_ci
4637db96d56Sopenharmony_ci    def value_decode(self, val):
4647db96d56Sopenharmony_ci        """real_value, coded_value = value_decode(STRING)
4657db96d56Sopenharmony_ci        Called prior to setting a cookie's value from the network
4667db96d56Sopenharmony_ci        representation.  The VALUE is the value read from HTTP
4677db96d56Sopenharmony_ci        header.
4687db96d56Sopenharmony_ci        Override this function to modify the behavior of cookies.
4697db96d56Sopenharmony_ci        """
4707db96d56Sopenharmony_ci        return val, val
4717db96d56Sopenharmony_ci
4727db96d56Sopenharmony_ci    def value_encode(self, val):
4737db96d56Sopenharmony_ci        """real_value, coded_value = value_encode(VALUE)
4747db96d56Sopenharmony_ci        Called prior to setting a cookie's value from the dictionary
4757db96d56Sopenharmony_ci        representation.  The VALUE is the value being assigned.
4767db96d56Sopenharmony_ci        Override this function to modify the behavior of cookies.
4777db96d56Sopenharmony_ci        """
4787db96d56Sopenharmony_ci        strval = str(val)
4797db96d56Sopenharmony_ci        return strval, strval
4807db96d56Sopenharmony_ci
4817db96d56Sopenharmony_ci    def __init__(self, input=None):
4827db96d56Sopenharmony_ci        if input:
4837db96d56Sopenharmony_ci            self.load(input)
4847db96d56Sopenharmony_ci
4857db96d56Sopenharmony_ci    def __set(self, key, real_value, coded_value):
4867db96d56Sopenharmony_ci        """Private method for setting a cookie's value"""
4877db96d56Sopenharmony_ci        M = self.get(key, Morsel())
4887db96d56Sopenharmony_ci        M.set(key, real_value, coded_value)
4897db96d56Sopenharmony_ci        dict.__setitem__(self, key, M)
4907db96d56Sopenharmony_ci
4917db96d56Sopenharmony_ci    def __setitem__(self, key, value):
4927db96d56Sopenharmony_ci        """Dictionary style assignment."""
4937db96d56Sopenharmony_ci        if isinstance(value, Morsel):
4947db96d56Sopenharmony_ci            # allow assignment of constructed Morsels (e.g. for pickling)
4957db96d56Sopenharmony_ci            dict.__setitem__(self, key, value)
4967db96d56Sopenharmony_ci        else:
4977db96d56Sopenharmony_ci            rval, cval = self.value_encode(value)
4987db96d56Sopenharmony_ci            self.__set(key, rval, cval)
4997db96d56Sopenharmony_ci
5007db96d56Sopenharmony_ci    def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
5017db96d56Sopenharmony_ci        """Return a string suitable for HTTP."""
5027db96d56Sopenharmony_ci        result = []
5037db96d56Sopenharmony_ci        items = sorted(self.items())
5047db96d56Sopenharmony_ci        for key, value in items:
5057db96d56Sopenharmony_ci            result.append(value.output(attrs, header))
5067db96d56Sopenharmony_ci        return sep.join(result)
5077db96d56Sopenharmony_ci
5087db96d56Sopenharmony_ci    __str__ = output
5097db96d56Sopenharmony_ci
5107db96d56Sopenharmony_ci    def __repr__(self):
5117db96d56Sopenharmony_ci        l = []
5127db96d56Sopenharmony_ci        items = sorted(self.items())
5137db96d56Sopenharmony_ci        for key, value in items:
5147db96d56Sopenharmony_ci            l.append('%s=%s' % (key, repr(value.value)))
5157db96d56Sopenharmony_ci        return '<%s: %s>' % (self.__class__.__name__, _spacejoin(l))
5167db96d56Sopenharmony_ci
5177db96d56Sopenharmony_ci    def js_output(self, attrs=None):
5187db96d56Sopenharmony_ci        """Return a string suitable for JavaScript."""
5197db96d56Sopenharmony_ci        result = []
5207db96d56Sopenharmony_ci        items = sorted(self.items())
5217db96d56Sopenharmony_ci        for key, value in items:
5227db96d56Sopenharmony_ci            result.append(value.js_output(attrs))
5237db96d56Sopenharmony_ci        return _nulljoin(result)
5247db96d56Sopenharmony_ci
5257db96d56Sopenharmony_ci    def load(self, rawdata):
5267db96d56Sopenharmony_ci        """Load cookies from a string (presumably HTTP_COOKIE) or
5277db96d56Sopenharmony_ci        from a dictionary.  Loading cookies from a dictionary 'd'
5287db96d56Sopenharmony_ci        is equivalent to calling:
5297db96d56Sopenharmony_ci            map(Cookie.__setitem__, d.keys(), d.values())
5307db96d56Sopenharmony_ci        """
5317db96d56Sopenharmony_ci        if isinstance(rawdata, str):
5327db96d56Sopenharmony_ci            self.__parse_string(rawdata)
5337db96d56Sopenharmony_ci        else:
5347db96d56Sopenharmony_ci            # self.update() wouldn't call our custom __setitem__
5357db96d56Sopenharmony_ci            for key, value in rawdata.items():
5367db96d56Sopenharmony_ci                self[key] = value
5377db96d56Sopenharmony_ci        return
5387db96d56Sopenharmony_ci
5397db96d56Sopenharmony_ci    def __parse_string(self, str, patt=_CookiePattern):
5407db96d56Sopenharmony_ci        i = 0                 # Our starting point
5417db96d56Sopenharmony_ci        n = len(str)          # Length of string
5427db96d56Sopenharmony_ci        parsed_items = []     # Parsed (type, key, value) triples
5437db96d56Sopenharmony_ci        morsel_seen = False   # A key=value pair was previously encountered
5447db96d56Sopenharmony_ci
5457db96d56Sopenharmony_ci        TYPE_ATTRIBUTE = 1
5467db96d56Sopenharmony_ci        TYPE_KEYVALUE = 2
5477db96d56Sopenharmony_ci
5487db96d56Sopenharmony_ci        # We first parse the whole cookie string and reject it if it's
5497db96d56Sopenharmony_ci        # syntactically invalid (this helps avoid some classes of injection
5507db96d56Sopenharmony_ci        # attacks).
5517db96d56Sopenharmony_ci        while 0 <= i < n:
5527db96d56Sopenharmony_ci            # Start looking for a cookie
5537db96d56Sopenharmony_ci            match = patt.match(str, i)
5547db96d56Sopenharmony_ci            if not match:
5557db96d56Sopenharmony_ci                # No more cookies
5567db96d56Sopenharmony_ci                break
5577db96d56Sopenharmony_ci
5587db96d56Sopenharmony_ci            key, value = match.group("key"), match.group("val")
5597db96d56Sopenharmony_ci            i = match.end(0)
5607db96d56Sopenharmony_ci
5617db96d56Sopenharmony_ci            if key[0] == "$":
5627db96d56Sopenharmony_ci                if not morsel_seen:
5637db96d56Sopenharmony_ci                    # We ignore attributes which pertain to the cookie
5647db96d56Sopenharmony_ci                    # mechanism as a whole, such as "$Version".
5657db96d56Sopenharmony_ci                    # See RFC 2965. (Does anyone care?)
5667db96d56Sopenharmony_ci                    continue
5677db96d56Sopenharmony_ci                parsed_items.append((TYPE_ATTRIBUTE, key[1:], value))
5687db96d56Sopenharmony_ci            elif key.lower() in Morsel._reserved:
5697db96d56Sopenharmony_ci                if not morsel_seen:
5707db96d56Sopenharmony_ci                    # Invalid cookie string
5717db96d56Sopenharmony_ci                    return
5727db96d56Sopenharmony_ci                if value is None:
5737db96d56Sopenharmony_ci                    if key.lower() in Morsel._flags:
5747db96d56Sopenharmony_ci                        parsed_items.append((TYPE_ATTRIBUTE, key, True))
5757db96d56Sopenharmony_ci                    else:
5767db96d56Sopenharmony_ci                        # Invalid cookie string
5777db96d56Sopenharmony_ci                        return
5787db96d56Sopenharmony_ci                else:
5797db96d56Sopenharmony_ci                    parsed_items.append((TYPE_ATTRIBUTE, key, _unquote(value)))
5807db96d56Sopenharmony_ci            elif value is not None:
5817db96d56Sopenharmony_ci                parsed_items.append((TYPE_KEYVALUE, key, self.value_decode(value)))
5827db96d56Sopenharmony_ci                morsel_seen = True
5837db96d56Sopenharmony_ci            else:
5847db96d56Sopenharmony_ci                # Invalid cookie string
5857db96d56Sopenharmony_ci                return
5867db96d56Sopenharmony_ci
5877db96d56Sopenharmony_ci        # The cookie string is valid, apply it.
5887db96d56Sopenharmony_ci        M = None         # current morsel
5897db96d56Sopenharmony_ci        for tp, key, value in parsed_items:
5907db96d56Sopenharmony_ci            if tp == TYPE_ATTRIBUTE:
5917db96d56Sopenharmony_ci                assert M is not None
5927db96d56Sopenharmony_ci                M[key] = value
5937db96d56Sopenharmony_ci            else:
5947db96d56Sopenharmony_ci                assert tp == TYPE_KEYVALUE
5957db96d56Sopenharmony_ci                rval, cval = value
5967db96d56Sopenharmony_ci                self.__set(key, rval, cval)
5977db96d56Sopenharmony_ci                M = self[key]
5987db96d56Sopenharmony_ci
5997db96d56Sopenharmony_ci
6007db96d56Sopenharmony_ciclass SimpleCookie(BaseCookie):
6017db96d56Sopenharmony_ci    """
6027db96d56Sopenharmony_ci    SimpleCookie supports strings as cookie values.  When setting
6037db96d56Sopenharmony_ci    the value using the dictionary assignment notation, SimpleCookie
6047db96d56Sopenharmony_ci    calls the builtin str() to convert the value to a string.  Values
6057db96d56Sopenharmony_ci    received from HTTP are kept as strings.
6067db96d56Sopenharmony_ci    """
6077db96d56Sopenharmony_ci    def value_decode(self, val):
6087db96d56Sopenharmony_ci        return _unquote(val), val
6097db96d56Sopenharmony_ci
6107db96d56Sopenharmony_ci    def value_encode(self, val):
6117db96d56Sopenharmony_ci        strval = str(val)
6127db96d56Sopenharmony_ci        return strval, _quote(strval)
613