1#### 2# Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu> 3# 4# All Rights Reserved 5# 6# Permission to use, copy, modify, and distribute this software 7# and its documentation for any purpose and without fee is hereby 8# granted, provided that the above copyright notice appear in all 9# copies and that both that copyright notice and this permission 10# notice appear in supporting documentation, and that the name of 11# Timothy O'Malley not be used in advertising or publicity 12# pertaining to distribution of the software without specific, written 13# prior permission. 14# 15# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 16# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 17# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR 18# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 20# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 21# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 22# PERFORMANCE OF THIS SOFTWARE. 23# 24#### 25# 26# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp 27# by Timothy O'Malley <timo@alum.mit.edu> 28# 29# Cookie.py is a Python module for the handling of HTTP 30# cookies as a Python dictionary. See RFC 2109 for more 31# information on cookies. 32# 33# The original idea to treat Cookies as a dictionary came from 34# Dave Mitchell (davem@magnet.com) in 1995, when he released the 35# first version of nscookie.py. 36# 37#### 38 39r""" 40Here's a sample session to show how to use this module. 41At the moment, this is the only documentation. 42 43The Basics 44---------- 45 46Importing is easy.. 47 48 >>> import Cookie 49 50Most of the time you start by creating a cookie. Cookies come in 51three flavors, each with slightly different encoding semantics, but 52more on that later. 53 54 >>> C = Cookie.SimpleCookie() 55 >>> C = Cookie.SerialCookie() 56 >>> C = Cookie.SmartCookie() 57 58[Note: Long-time users of Cookie.py will remember using 59Cookie.Cookie() to create a Cookie object. Although deprecated, it 60is still supported by the code. See the Backward Compatibility notes 61for more information.] 62 63Once you've created your Cookie, you can add values just as if it were 64a dictionary. 65 66 >>> C = Cookie.SmartCookie() 67 >>> C["fig"] = "newton" 68 >>> C["sugar"] = "wafer" 69 >>> C.output() 70 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer' 71 72Notice that the printable representation of a Cookie is the 73appropriate format for a Set-Cookie: header. This is the 74default behavior. You can change the header and printed 75attributes by using the .output() function 76 77 >>> C = Cookie.SmartCookie() 78 >>> C["rocky"] = "road" 79 >>> C["rocky"]["path"] = "/cookie" 80 >>> print C.output(header="Cookie:") 81 Cookie: rocky=road; Path=/cookie 82 >>> print C.output(attrs=[], header="Cookie:") 83 Cookie: rocky=road 84 85The load() method of a Cookie extracts cookies from a string. In a 86CGI script, you would use this method to extract the cookies from the 87HTTP_COOKIE environment variable. 88 89 >>> C = Cookie.SmartCookie() 90 >>> C.load("chips=ahoy; vienna=finger") 91 >>> C.output() 92 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger' 93 94The load() method is darn-tootin smart about identifying cookies 95within a string. Escaped quotation marks, nested semicolons, and other 96such trickeries do not confuse it. 97 98 >>> C = Cookie.SmartCookie() 99 >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') 100 >>> print C 101 Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;" 102 103Each element of the Cookie also supports all of the RFC 2109 104Cookie attributes. Here's an example which sets the Path 105attribute. 106 107 >>> C = Cookie.SmartCookie() 108 >>> C["oreo"] = "doublestuff" 109 >>> C["oreo"]["path"] = "/" 110 >>> print C 111 Set-Cookie: oreo=doublestuff; Path=/ 112 113Each dictionary element has a 'value' attribute, which gives you 114back the value associated with the key. 115 116 >>> C = Cookie.SmartCookie() 117 >>> C["twix"] = "none for you" 118 >>> C["twix"].value 119 'none for you' 120 121 122A Bit More Advanced 123------------------- 124 125As mentioned before, there are three different flavors of Cookie 126objects, each with different encoding/decoding semantics. This 127section briefly discusses the differences. 128 129SimpleCookie 130 131The SimpleCookie expects that all values should be standard strings. 132Just to be sure, SimpleCookie invokes the str() builtin to convert 133the value to a string, when the values are set dictionary-style. 134 135 >>> C = Cookie.SimpleCookie() 136 >>> C["number"] = 7 137 >>> C["string"] = "seven" 138 >>> C["number"].value 139 '7' 140 >>> C["string"].value 141 'seven' 142 >>> C.output() 143 'Set-Cookie: number=7\r\nSet-Cookie: string=seven' 144 145 146SerialCookie 147 148The SerialCookie expects that all values should be serialized using 149cPickle (or pickle, if cPickle isn't available). As a result of 150serializing, SerialCookie can save almost any Python object to a 151value, and recover the exact same object when the cookie has been 152returned. (SerialCookie can yield some strange-looking cookie 153values, however.) 154 155 >>> C = Cookie.SerialCookie() 156 >>> C["number"] = 7 157 >>> C["string"] = "seven" 158 >>> C["number"].value 159 7 160 >>> C["string"].value 161 'seven' 162 >>> C.output() 163 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string="S\'seven\'\\012p1\\012."' 164 165Be warned, however, if SerialCookie cannot de-serialize a value (because 166it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION. 167 168 169SmartCookie 170 171The SmartCookie combines aspects of each of the other two flavors. 172When setting a value in a dictionary-fashion, the SmartCookie will 173serialize (ala cPickle) the value *if and only if* it isn't a 174Python string. String objects are *not* serialized. Similarly, 175when the load() method parses out values, it attempts to de-serialize 176the value. If it fails, then it fallsback to treating the value 177as a string. 178 179 >>> C = Cookie.SmartCookie() 180 >>> C["number"] = 7 181 >>> C["string"] = "seven" 182 >>> C["number"].value 183 7 184 >>> C["string"].value 185 'seven' 186 >>> C.output() 187 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string=seven' 188 189 190Backwards Compatibility 191----------------------- 192 193In order to keep compatibility with earlier versions of Cookie.py, 194it is still possible to use Cookie.Cookie() to create a Cookie. In 195fact, this simply returns a SmartCookie. 196 197 >>> C = Cookie.Cookie() 198 >>> print C.__class__.__name__ 199 SmartCookie 200 201 202Finis. 203""" #" 204# ^ 205# |----helps out font-lock 206 207# 208# Import our required modules 209# 210import string 211 212try: 213 from cPickle import dumps, loads 214except ImportError: 215 from pickle import dumps, loads 216 217import re, warnings 218 219__all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie", 220 "SmartCookie","Cookie"] 221 222_nulljoin = ''.join 223_semispacejoin = '; '.join 224_spacejoin = ' '.join 225 226# 227# Define an exception visible to External modules 228# 229class CookieError(Exception): 230 pass 231 232 233# These quoting routines conform to the RFC2109 specification, which in 234# turn references the character definitions from RFC2068. They provide 235# a two-way quoting algorithm. Any non-text character is translated 236# into a 4 character sequence: a forward-slash followed by the 237# three-digit octal equivalent of the character. Any '\' or '"' is 238# quoted with a preceding '\' slash. 239# 240# These are taken from RFC2068 and RFC2109. 241# _LegalChars is the list of chars which don't require "'s 242# _Translator hash-table for fast quoting 243# 244_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~" 245_Translator = { 246 '\000' : '\\000', '\001' : '\\001', '\002' : '\\002', 247 '\003' : '\\003', '\004' : '\\004', '\005' : '\\005', 248 '\006' : '\\006', '\007' : '\\007', '\010' : '\\010', 249 '\011' : '\\011', '\012' : '\\012', '\013' : '\\013', 250 '\014' : '\\014', '\015' : '\\015', '\016' : '\\016', 251 '\017' : '\\017', '\020' : '\\020', '\021' : '\\021', 252 '\022' : '\\022', '\023' : '\\023', '\024' : '\\024', 253 '\025' : '\\025', '\026' : '\\026', '\027' : '\\027', 254 '\030' : '\\030', '\031' : '\\031', '\032' : '\\032', 255 '\033' : '\\033', '\034' : '\\034', '\035' : '\\035', 256 '\036' : '\\036', '\037' : '\\037', 257 258 # Because of the way browsers really handle cookies (as opposed 259 # to what the RFC says) we also encode , and ; 260 261 ',' : '\\054', ';' : '\\073', 262 263 '"' : '\\"', '\\' : '\\\\', 264 265 '\177' : '\\177', '\200' : '\\200', '\201' : '\\201', 266 '\202' : '\\202', '\203' : '\\203', '\204' : '\\204', 267 '\205' : '\\205', '\206' : '\\206', '\207' : '\\207', 268 '\210' : '\\210', '\211' : '\\211', '\212' : '\\212', 269 '\213' : '\\213', '\214' : '\\214', '\215' : '\\215', 270 '\216' : '\\216', '\217' : '\\217', '\220' : '\\220', 271 '\221' : '\\221', '\222' : '\\222', '\223' : '\\223', 272 '\224' : '\\224', '\225' : '\\225', '\226' : '\\226', 273 '\227' : '\\227', '\230' : '\\230', '\231' : '\\231', 274 '\232' : '\\232', '\233' : '\\233', '\234' : '\\234', 275 '\235' : '\\235', '\236' : '\\236', '\237' : '\\237', 276 '\240' : '\\240', '\241' : '\\241', '\242' : '\\242', 277 '\243' : '\\243', '\244' : '\\244', '\245' : '\\245', 278 '\246' : '\\246', '\247' : '\\247', '\250' : '\\250', 279 '\251' : '\\251', '\252' : '\\252', '\253' : '\\253', 280 '\254' : '\\254', '\255' : '\\255', '\256' : '\\256', 281 '\257' : '\\257', '\260' : '\\260', '\261' : '\\261', 282 '\262' : '\\262', '\263' : '\\263', '\264' : '\\264', 283 '\265' : '\\265', '\266' : '\\266', '\267' : '\\267', 284 '\270' : '\\270', '\271' : '\\271', '\272' : '\\272', 285 '\273' : '\\273', '\274' : '\\274', '\275' : '\\275', 286 '\276' : '\\276', '\277' : '\\277', '\300' : '\\300', 287 '\301' : '\\301', '\302' : '\\302', '\303' : '\\303', 288 '\304' : '\\304', '\305' : '\\305', '\306' : '\\306', 289 '\307' : '\\307', '\310' : '\\310', '\311' : '\\311', 290 '\312' : '\\312', '\313' : '\\313', '\314' : '\\314', 291 '\315' : '\\315', '\316' : '\\316', '\317' : '\\317', 292 '\320' : '\\320', '\321' : '\\321', '\322' : '\\322', 293 '\323' : '\\323', '\324' : '\\324', '\325' : '\\325', 294 '\326' : '\\326', '\327' : '\\327', '\330' : '\\330', 295 '\331' : '\\331', '\332' : '\\332', '\333' : '\\333', 296 '\334' : '\\334', '\335' : '\\335', '\336' : '\\336', 297 '\337' : '\\337', '\340' : '\\340', '\341' : '\\341', 298 '\342' : '\\342', '\343' : '\\343', '\344' : '\\344', 299 '\345' : '\\345', '\346' : '\\346', '\347' : '\\347', 300 '\350' : '\\350', '\351' : '\\351', '\352' : '\\352', 301 '\353' : '\\353', '\354' : '\\354', '\355' : '\\355', 302 '\356' : '\\356', '\357' : '\\357', '\360' : '\\360', 303 '\361' : '\\361', '\362' : '\\362', '\363' : '\\363', 304 '\364' : '\\364', '\365' : '\\365', '\366' : '\\366', 305 '\367' : '\\367', '\370' : '\\370', '\371' : '\\371', 306 '\372' : '\\372', '\373' : '\\373', '\374' : '\\374', 307 '\375' : '\\375', '\376' : '\\376', '\377' : '\\377' 308 } 309 310_idmap = ''.join(chr(x) for x in xrange(256)) 311 312def _quote(str, LegalChars=_LegalChars, 313 idmap=_idmap, translate=string.translate): 314 # 315 # If the string does not need to be double-quoted, 316 # then just return the string. Otherwise, surround 317 # the string in doublequotes and precede quote (with a \) 318 # special characters. 319 # 320 if "" == translate(str, idmap, LegalChars): 321 return str 322 else: 323 return '"' + _nulljoin( map(_Translator.get, str, str) ) + '"' 324# end _quote 325 326 327_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]") 328_QuotePatt = re.compile(r"[\\].") 329 330def _unquote(str): 331 # If there aren't any doublequotes, 332 # then there can't be any special characters. See RFC 2109. 333 if len(str) < 2: 334 return str 335 if str[0] != '"' or str[-1] != '"': 336 return str 337 338 # We have to assume that we must decode this string. 339 # Down to work. 340 341 # Remove the "s 342 str = str[1:-1] 343 344 # Check for special sequences. Examples: 345 # \012 --> \n 346 # \" --> " 347 # 348 i = 0 349 n = len(str) 350 res = [] 351 while 0 <= i < n: 352 Omatch = _OctalPatt.search(str, i) 353 Qmatch = _QuotePatt.search(str, i) 354 if not Omatch and not Qmatch: # Neither matched 355 res.append(str[i:]) 356 break 357 # else: 358 j = k = -1 359 if Omatch: j = Omatch.start(0) 360 if Qmatch: k = Qmatch.start(0) 361 if Qmatch and ( not Omatch or k < j ): # QuotePatt matched 362 res.append(str[i:k]) 363 res.append(str[k+1]) 364 i = k+2 365 else: # OctalPatt matched 366 res.append(str[i:j]) 367 res.append( chr( int(str[j+1:j+4], 8) ) ) 368 i = j+4 369 return _nulljoin(res) 370# end _unquote 371 372# The _getdate() routine is used to set the expiration time in 373# the cookie's HTTP header. By default, _getdate() returns the 374# current time in the appropriate "expires" format for a 375# Set-Cookie header. The one optional argument is an offset from 376# now, in seconds. For example, an offset of -3600 means "one hour ago". 377# The offset may be a floating point number. 378# 379 380_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 381 382_monthname = [None, 383 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 384 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 385 386def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname): 387 from time import gmtime, time 388 now = time() 389 year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future) 390 return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \ 391 (weekdayname[wd], day, monthname[month], year, hh, mm, ss) 392 393 394# 395# A class to hold ONE key,value pair. 396# In a cookie, each such pair may have several attributes. 397# so this class is used to keep the attributes associated 398# with the appropriate key,value pair. 399# This class also includes a coded_value attribute, which 400# is used to hold the network representation of the 401# value. This is most useful when Python objects are 402# pickled for network transit. 403# 404 405class Morsel(dict): 406 # RFC 2109 lists these attributes as reserved: 407 # path comment domain 408 # max-age secure version 409 # 410 # For historical reasons, these attributes are also reserved: 411 # expires 412 # 413 # This is an extension from Microsoft: 414 # httponly 415 # 416 # This dictionary provides a mapping from the lowercase 417 # variant on the left to the appropriate traditional 418 # formatting on the right. 419 _reserved = { "expires" : "expires", 420 "path" : "Path", 421 "comment" : "Comment", 422 "domain" : "Domain", 423 "max-age" : "Max-Age", 424 "secure" : "secure", 425 "httponly" : "httponly", 426 "version" : "Version", 427 } 428 429 _flags = {'secure', 'httponly'} 430 431 def __init__(self): 432 # Set defaults 433 self.key = self.value = self.coded_value = None 434 435 # Set default attributes 436 for K in self._reserved: 437 dict.__setitem__(self, K, "") 438 # end __init__ 439 440 def __setitem__(self, K, V): 441 K = K.lower() 442 if not K in self._reserved: 443 raise CookieError("Invalid Attribute %s" % K) 444 dict.__setitem__(self, K, V) 445 # end __setitem__ 446 447 def isReservedKey(self, K): 448 return K.lower() in self._reserved 449 # end isReservedKey 450 451 def set(self, key, val, coded_val, 452 LegalChars=_LegalChars, 453 idmap=_idmap, translate=string.translate): 454 # First we verify that the key isn't a reserved word 455 # Second we make sure it only contains legal characters 456 if key.lower() in self._reserved: 457 raise CookieError("Attempt to set a reserved key: %s" % key) 458 if "" != translate(key, idmap, LegalChars): 459 raise CookieError("Illegal key value: %s" % key) 460 461 # It's a good key, so save it. 462 self.key = key 463 self.value = val 464 self.coded_value = coded_val 465 # end set 466 467 def output(self, attrs=None, header = "Set-Cookie:"): 468 return "%s %s" % ( header, self.OutputString(attrs) ) 469 470 __str__ = output 471 472 def __repr__(self): 473 return '<%s: %s=%s>' % (self.__class__.__name__, 474 self.key, repr(self.value) ) 475 476 def js_output(self, attrs=None): 477 # Print javascript 478 return """ 479 <script type="text/javascript"> 480 <!-- begin hiding 481 document.cookie = \"%s\"; 482 // end hiding --> 483 </script> 484 """ % ( self.OutputString(attrs).replace('"',r'\"'), ) 485 # end js_output() 486 487 def OutputString(self, attrs=None): 488 # Build up our result 489 # 490 result = [] 491 RA = result.append 492 493 # First, the key=value pair 494 RA("%s=%s" % (self.key, self.coded_value)) 495 496 # Now add any defined attributes 497 if attrs is None: 498 attrs = self._reserved 499 items = self.items() 500 items.sort() 501 for K,V in items: 502 if V == "": continue 503 if K not in attrs: continue 504 if K == "expires" and type(V) == type(1): 505 RA("%s=%s" % (self._reserved[K], _getdate(V))) 506 elif K == "max-age" and type(V) == type(1): 507 RA("%s=%d" % (self._reserved[K], V)) 508 elif K == "secure": 509 RA(str(self._reserved[K])) 510 elif K == "httponly": 511 RA(str(self._reserved[K])) 512 else: 513 RA("%s=%s" % (self._reserved[K], V)) 514 515 # Return the result 516 return _semispacejoin(result) 517 # end OutputString 518# end Morsel class 519 520 521 522# 523# Pattern for finding cookie 524# 525# This used to be strict parsing based on the RFC2109 and RFC2068 526# specifications. I have since discovered that MSIE 3.0x doesn't 527# follow the character rules outlined in those specs. As a 528# result, the parsing rules here are less strict. 529# 530 531_LegalKeyChars = r"\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=" 532_LegalValueChars = _LegalKeyChars + r"\[\]" 533_CookiePattern = re.compile( 534 r"(?x)" # This is a Verbose pattern 535 r"\s*" # Optional whitespace at start of cookie 536 r"(?P<key>" # Start of group 'key' 537 "["+ _LegalKeyChars +"]+?" # Any word of at least one letter, nongreedy 538 r")" # End of group 'key' 539 r"(" # Optional group: there may not be a value. 540 r"\s*=\s*" # Equal Sign 541 r"(?P<val>" # Start of group 'val' 542 r'"(?:[^\\"]|\\.)*"' # Any doublequoted string 543 r"|" # or 544 r"\w{3},\s[\s\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr 545 r"|" # or 546 "["+ _LegalValueChars +"]*" # Any word or empty string 547 r")" # End of group 'val' 548 r")?" # End of optional value group 549 r"\s*" # Any number of spaces. 550 r"(\s+|;|$)" # Ending either at space, semicolon, or EOS. 551 ) 552 553 554# At long last, here is the cookie class. 555# Using this class is almost just like using a dictionary. 556# See this module's docstring for example usage. 557# 558class BaseCookie(dict): 559 # A container class for a set of Morsels 560 # 561 562 def value_decode(self, val): 563 """real_value, coded_value = value_decode(STRING) 564 Called prior to setting a cookie's value from the network 565 representation. The VALUE is the value read from HTTP 566 header. 567 Override this function to modify the behavior of cookies. 568 """ 569 return val, val 570 # end value_encode 571 572 def value_encode(self, val): 573 """real_value, coded_value = value_encode(VALUE) 574 Called prior to setting a cookie's value from the dictionary 575 representation. The VALUE is the value being assigned. 576 Override this function to modify the behavior of cookies. 577 """ 578 strval = str(val) 579 return strval, strval 580 # end value_encode 581 582 def __init__(self, input=None): 583 if input: self.load(input) 584 # end __init__ 585 586 def __set(self, key, real_value, coded_value): 587 """Private method for setting a cookie's value""" 588 M = self.get(key, Morsel()) 589 M.set(key, real_value, coded_value) 590 dict.__setitem__(self, key, M) 591 # end __set 592 593 def __setitem__(self, key, value): 594 """Dictionary style assignment.""" 595 if isinstance(value, Morsel): 596 # allow assignment of constructed Morsels (e.g. for pickling) 597 dict.__setitem__(self, key, value) 598 else: 599 rval, cval = self.value_encode(value) 600 self.__set(key, rval, cval) 601 # end __setitem__ 602 603 def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"): 604 """Return a string suitable for HTTP.""" 605 result = [] 606 items = self.items() 607 items.sort() 608 for K,V in items: 609 result.append( V.output(attrs, header) ) 610 return sep.join(result) 611 # end output 612 613 __str__ = output 614 615 def __repr__(self): 616 L = [] 617 items = self.items() 618 items.sort() 619 for K,V in items: 620 L.append( '%s=%s' % (K,repr(V.value) ) ) 621 return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L)) 622 623 def js_output(self, attrs=None): 624 """Return a string suitable for JavaScript.""" 625 result = [] 626 items = self.items() 627 items.sort() 628 for K,V in items: 629 result.append( V.js_output(attrs) ) 630 return _nulljoin(result) 631 # end js_output 632 633 def load(self, rawdata): 634 """Load cookies from a string (presumably HTTP_COOKIE) or 635 from a dictionary. Loading cookies from a dictionary 'd' 636 is equivalent to calling: 637 map(Cookie.__setitem__, d.keys(), d.values()) 638 """ 639 if type(rawdata) == type(""): 640 self.__ParseString(rawdata) 641 else: 642 # self.update() wouldn't call our custom __setitem__ 643 for k, v in rawdata.items(): 644 self[k] = v 645 return 646 # end load() 647 648 def __ParseString(self, str, patt=_CookiePattern): 649 i = 0 # Our starting point 650 n = len(str) # Length of string 651 M = None # current morsel 652 653 while 0 <= i < n: 654 # Start looking for a cookie 655 match = patt.match(str, i) 656 if not match: break # No more cookies 657 658 K,V = match.group("key"), match.group("val") 659 i = match.end(0) 660 661 # Parse the key, value in case it's metainfo 662 if K[0] == "$": 663 # We ignore attributes which pertain to the cookie 664 # mechanism as a whole. See RFC 2109. 665 # (Does anyone care?) 666 if M: 667 M[ K[1:] ] = V 668 elif K.lower() in Morsel._reserved: 669 if M: 670 if V is None: 671 if K.lower() in Morsel._flags: 672 M[K] = True 673 else: 674 M[K] = _unquote(V) 675 elif V is not None: 676 rval, cval = self.value_decode(V) 677 self.__set(K, rval, cval) 678 M = self[K] 679 # end __ParseString 680# end BaseCookie class 681 682class SimpleCookie(BaseCookie): 683 """SimpleCookie 684 SimpleCookie supports strings as cookie values. When setting 685 the value using the dictionary assignment notation, SimpleCookie 686 calls the builtin str() to convert the value to a string. Values 687 received from HTTP are kept as strings. 688 """ 689 def value_decode(self, val): 690 return _unquote( val ), val 691 def value_encode(self, val): 692 strval = str(val) 693 return strval, _quote( strval ) 694# end SimpleCookie 695 696class SerialCookie(BaseCookie): 697 """SerialCookie 698 SerialCookie supports arbitrary objects as cookie values. All 699 values are serialized (using cPickle) before being sent to the 700 client. All incoming values are assumed to be valid Pickle 701 representations. IF AN INCOMING VALUE IS NOT IN A VALID PICKLE 702 FORMAT, THEN AN EXCEPTION WILL BE RAISED. 703 704 Note: Large cookie values add overhead because they must be 705 retransmitted on every HTTP transaction. 706 707 Note: HTTP has a 2k limit on the size of a cookie. This class 708 does not check for this limit, so be careful!!! 709 """ 710 def __init__(self, input=None): 711 warnings.warn("SerialCookie class is insecure; do not use it", 712 DeprecationWarning) 713 BaseCookie.__init__(self, input) 714 # end __init__ 715 def value_decode(self, val): 716 # This could raise an exception! 717 return loads( _unquote(val) ), val 718 def value_encode(self, val): 719 return val, _quote( dumps(val) ) 720# end SerialCookie 721 722class SmartCookie(BaseCookie): 723 """SmartCookie 724 SmartCookie supports arbitrary objects as cookie values. If the 725 object is a string, then it is quoted. If the object is not a 726 string, however, then SmartCookie will use cPickle to serialize 727 the object into a string representation. 728 729 Note: Large cookie values add overhead because they must be 730 retransmitted on every HTTP transaction. 731 732 Note: HTTP has a 2k limit on the size of a cookie. This class 733 does not check for this limit, so be careful!!! 734 """ 735 def __init__(self, input=None): 736 warnings.warn("Cookie/SmartCookie class is insecure; do not use it", 737 DeprecationWarning) 738 BaseCookie.__init__(self, input) 739 # end __init__ 740 def value_decode(self, val): 741 strval = _unquote(val) 742 try: 743 return loads(strval), val 744 except: 745 return strval, val 746 def value_encode(self, val): 747 if type(val) == type(""): 748 return val, _quote(val) 749 else: 750 return val, _quote( dumps(val) ) 751# end SmartCookie 752 753 754########################################################### 755# Backwards Compatibility: Don't break any existing code! 756 757# We provide Cookie() as an alias for SmartCookie() 758Cookie = SmartCookie 759 760# 761########################################################### 762 763def _test(): 764 import doctest, Cookie 765 return doctest.testmod(Cookie) 766 767if __name__ == "__main__": 768 _test() 769 770 771#Local Variables: 772#tab-width: 4 773#end: 774