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