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