5 # Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
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
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.
29 # Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
30 # by Timothy O'Malley <timo@alum.mit.edu>
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.
36 # The original idea to treat Cookies as a dictionary came from
37 # Dave Mitchell (davem@magnet.com) in 1995, when he released the
38 # first version of nscookie.py.
43 Here's a sample session to show how to use this module.
44 At the moment, this is the only documentation.
53 Most of the time you start by creating a cookie. Cookies come in
54 three flavors, each with slightly different encoding semantics, but
57 >>> C = Cookie.SimpleCookie()
58 >>> C = Cookie.SerialCookie()
59 >>> C = Cookie.SmartCookie()
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.]
66 Once you've created your Cookie, you can add values just as if it were
69 >>> C = Cookie.SmartCookie()
70 >>> C["fig"] = "newton"
71 >>> C["sugar"] = "wafer"
73 Set-Cookie: fig=newton;
74 Set-Cookie: sugar=wafer;
76 Notice that the printable representation of a Cookie is the
77 appropriate format for a Set-Cookie: header. This is the
78 default behavior. You can change the header and printed
79 attributes by using the the .output() function
81 >>> C = Cookie.SmartCookie()
82 >>> C["rocky"] = "road"
83 >>> C["rocky"]["path"] = "/cookie"
84 >>> print C.output(header="Cookie:")
85 Cookie: rocky=road; Path=/cookie;
86 >>> print C.output(attrs=[], header="Cookie:")
89 The load() method of a Cookie extracts cookies from a string. In a
90 CGI script, you would use this method to extract the cookies from the
91 HTTP_COOKIE environment variable.
93 >>> C = Cookie.SmartCookie()
94 >>> C.load("chips=ahoy; vienna=finger")
96 Set-Cookie: chips=ahoy;
97 Set-Cookie: vienna=finger;
99 The load() method is darn-tootin smart about identifying cookies
100 within a string. Escaped quotation marks, nested semicolons, and other
101 such trickeries do not confuse it.
103 >>> C = Cookie.SmartCookie()
104 >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
106 Set-Cookie: keebler="E=everybody; L=\"Loves
\"; fudge
=\012;";
108 Each element of the Cookie also supports all of the RFC 2109
109 Cookie attributes. Here's an example which sets the Path
112 >>> C = Cookie.SmartCookie()
113 >>> C["oreo
"] = "doublestuff
"
114 >>> C["oreo
"]["path
"] = "/"
116 Set-Cookie: oreo=doublestuff; Path=/;
118 Each dictionary element has a 'value' attribute, which gives you
119 back the value associated with the key.
121 >>> C = Cookie.SmartCookie()
122 >>> C["twix
"] = "none
for you
"
130 As mentioned before, there are three different flavors of Cookie
131 objects, each with different encoding/decoding semantics. This
132 section briefly discusses the differences.
136 The SimpleCookie expects that all values should be standard strings.
137 Just to be sure, SimpleCookie invokes the str() builtin to convert
138 the value to a string, when the values are set dictionary-style.
140 >>> C = Cookie.SimpleCookie()
142 >>> C["string
"] = "seven
"
143 >>> C["number
"].value
145 >>> C["string
"].value
148 Set-Cookie: number=7;
149 Set-Cookie: string=seven;
154 The SerialCookie expects that all values should be serialized using
155 cPickle (or pickle, if cPickle isn't available). As a result of
156 serializing, SerialCookie can save almost any Python object to a
157 value, and recover the exact same object when the cookie has been
158 returned. (SerialCookie can yield some strange-looking cookie
161 >>> C = Cookie.SerialCookie()
163 >>> C["string
"] = "seven
"
164 >>> C["number
"].value
166 >>> C["string
"].value
169 Set-Cookie: number="I7
\012.";
170 Set-Cookie: string="S
'seven'\012p1
\012.";
172 Be warned, however, if SerialCookie cannot de-serialize a value (because
173 it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
178 The SmartCookie combines aspects of each of the other two flavors.
179 When setting a value in a dictionary-fashion, the SmartCookie will
180 serialize (ala cPickle) the value *if and only if* it isn't a
181 Python string. String objects are *not* serialized. Similarly,
182 when the load() method parses out values, it attempts to de-serialize
183 the value. If it fails, then it fallsback to treating the value
186 >>> C = Cookie.SmartCookie()
188 >>> C["string
"] = "seven
"
189 >>> C["number
"].value
191 >>> C["string
"].value
194 Set-Cookie: number="I7
\012.";
195 Set-Cookie: string=seven;
198 Backwards Compatibility
199 -----------------------
201 In order to keep compatibilty with earlier versions of Cookie.py,
202 it is still possible to use Cookie.Cookie() to create a Cookie. In
203 fact, this simply returns a SmartCookie.
205 >>> C = Cookie.Cookie()
206 >>> print C.__class__.__name__
213 # |----helps out font-lock
216 # Import our required modules
221 from cPickle
import dumps
, loads
223 from pickle
import dumps
, loads
227 __all__
= ["CookieError","BaseCookie","SimpleCookie","SerialCookie",
228 "SmartCookie","Cookie"]
231 _spacejoin
= ' '.join
234 # Define an exception visible to External modules
236 class CookieError(Exception):
240 # These quoting routines conform to the RFC2109 specification, which in
241 # turn references the character definitions from RFC2068. They provide
242 # a two-way quoting algorithm. Any non-text character is translated
243 # into a 4 character sequence: a forward-slash followed by the
244 # three-digit octal equivalent of the character. Any '\' or '"' is
245 # quoted with a preceeding '\' slash.
247 # These are taken from RFC2068 and RFC2109.
248 # _LegalChars is the list of chars which don't require "'s
249 # _Translator hash-table for fast quoting
251 _LegalChars
= string
.ascii_letters
+ string
.digits
+ "!#$%&'*+-.^_`|~"
253 '\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
254 '\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
255 '\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
256 '\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
257 '\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
258 '\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
259 '\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
260 '\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
261 '\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
262 '\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
263 '\036' : '\\036', '\037' : '\\037',
265 '"' : '\\"', '\\' : '\\\\',
267 '\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
268 '\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
269 '\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
270 '\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
271 '\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
272 '\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
273 '\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
274 '\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
275 '\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
276 '\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
277 '\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
278 '\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
279 '\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
280 '\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
281 '\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
282 '\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
283 '\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
284 '\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
285 '\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
286 '\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
287 '\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
288 '\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
289 '\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
290 '\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
291 '\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
292 '\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
293 '\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
294 '\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
295 '\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
296 '\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
297 '\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
298 '\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
299 '\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
300 '\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
301 '\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
302 '\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
303 '\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
304 '\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
305 '\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
306 '\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
307 '\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
308 '\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
309 '\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
312 def _quote(str, LegalChars
=_LegalChars
,
313 idmap
=string
._idmap
, translate
=string
.translate
):
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.
320 if "" == translate(str, idmap
, LegalChars
):
323 return '"' + _nulljoin( map(_Translator
.get
, str, str) ) + '"'
327 _OctalPatt
= re
.compile(r
"\\[0-3][0-7][0-7]")
328 _QuotePatt
= re
.compile(r
"[\\].")
331 # If there aren't any doublequotes,
332 # then there can't be any special characters. See RFC 2109.
335 if str[0] != '"' or str[-1] != '"':
338 # We have to assume that we must decode this string.
344 # Check for special sequences. Examples:
352 Omatch
= _OctalPatt
.search(str, i
)
353 Qmatch
= _QuotePatt
.search(str, i
)
354 if not Omatch
and not Qmatch
: # Neither matched
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
365 else: # OctalPatt matched
367 res
.append( chr( int(str[j
+1:j
+4], 8) ) )
369 return _nulljoin(res
)
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.
380 _weekdayname
= ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
383 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
384 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
386 def _getdate(future
=0, weekdayname
=_weekdayname
, monthname
=_monthname
):
387 from time
import gmtime
, 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
)
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.
406 # RFC 2109 lists these attributes as reserved:
407 # path comment domain
408 # max-age secure version
410 # For historical reasons, these attributes are also reserved:
413 # This dictionary provides a mapping from the lowercase
414 # variant on the left to the appropriate traditional
415 # formatting on the right.
416 _reserved
= { "expires" : "expires",
418 "comment" : "Comment",
420 "max-age" : "Max-Age",
422 "version" : "Version",
427 self
.key
= self
.value
= self
.coded_value
= None
429 # Set default attributes
430 for K
in self
._reserved
:
431 dict.__setitem
__(self
, K
, "")
434 def __setitem__(self
, K
, V
):
436 if not K
in self
._reserved
:
437 raise CookieError("Invalid Attribute %s" % K
)
438 dict.__setitem
__(self
, K
, V
)
441 def isReservedKey(self
, K
):
442 return K
.lower() in self
._reserved
445 def set(self
, key
, val
, coded_val
,
446 LegalChars
=_LegalChars
,
447 idmap
=string
._idmap
, translate
=string
.translate
):
448 # First we verify that the key isn't a reserved word
449 # Second we make sure it only contains legal characters
450 if key
.lower() in self
._reserved
:
451 raise CookieError("Attempt to set a reserved key: %s" % key
)
452 if "" != translate(key
, idmap
, LegalChars
):
453 raise CookieError("Illegal key value: %s" % key
)
455 # It's a good key, so save it.
458 self
.coded_value
= coded_val
461 def output(self
, attrs
=None, header
= "Set-Cookie:"):
462 return "%s %s" % ( header
, self
.OutputString(attrs
) )
467 return '<%s: %s=%s>' % (self
.__class
__.__name
__,
468 self
.key
, repr(self
.value
) )
470 def js_output(self
, attrs
=None):
473 <SCRIPT LANGUAGE="JavaScript">
475 document.cookie = \"%s\"
478 """ % ( self
.OutputString(attrs
), )
481 def OutputString(self
, attrs
=None):
482 # Build up our result
487 # First, the key=value pair
488 RA("%s=%s;" % (self
.key
, self
.coded_value
))
490 # Now add any defined attributes
492 attrs
= self
._reserved
497 if K
not in attrs
: continue
498 if K
== "expires" and type(V
) == type(1):
499 RA("%s=%s;" % (self
._reserved
[K
], _getdate(V
)))
500 elif K
== "max-age" and type(V
) == type(1):
501 RA("%s=%d;" % (self
._reserved
[K
], V
))
503 RA("%s;" % self
._reserved
[K
])
505 RA("%s=%s;" % (self
._reserved
[K
], V
))
508 return _spacejoin(result
)
515 # Pattern for finding cookie
517 # This used to be strict parsing based on the RFC2109 and RFC2068
518 # specifications. I have since discovered that MSIE 3.0x doesn't
519 # follow the character rules outlined in those specs. As a
520 # result, the parsing rules here are less strict.
523 _LegalCharsPatt
= r
"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
524 _CookiePattern
= re
.compile(
525 r
"(?x)" # This is a Verbose pattern
526 r
"(?P<key>" # Start of group 'key'
527 ""+ _LegalCharsPatt
+"+?" # Any word of at least one letter, nongreedy
528 r
")" # End of group 'key'
529 r
"\s*=\s*" # Equal Sign
530 r
"(?P<val>" # Start of group 'val'
531 r
'"(?:[^\\"]|\\.)*"' # Any doublequoted string
533 ""+ _LegalCharsPatt
+"*" # Any word or empty string
534 r
")" # End of group 'val'
535 r
"\s*;?" # Probably ending in a semi-colon
539 # At long last, here is the cookie class.
540 # Using this class is almost just like using a dictionary.
541 # See this module's docstring for example usage.
543 class BaseCookie(dict):
544 # A container class for a set of Morsels
547 def value_decode(self
, val
):
548 """real_value, coded_value = value_decode(STRING)
549 Called prior to setting a cookie's value from the network
550 representation. The VALUE is the value read from HTTP
552 Override this function to modify the behavior of cookies.
557 def value_encode(self
, val
):
558 """real_value, coded_value = value_encode(VALUE)
559 Called prior to setting a cookie's value from the dictionary
560 representation. The VALUE is the value being assigned.
561 Override this function to modify the behavior of cookies.
564 return strval
, strval
567 def __init__(self
, input=None):
568 if input: self
.load(input)
571 def __set(self
, key
, real_value
, coded_value
):
572 """Private method for setting a cookie's value"""
573 M
= self
.get(key
, Morsel())
574 M
.set(key
, real_value
, coded_value
)
575 dict.__setitem
__(self
, key
, M
)
578 def __setitem__(self
, key
, value
):
579 """Dictionary style assignment."""
580 rval
, cval
= self
.value_encode(value
)
581 self
.__set
(key
, rval
, cval
)
584 def output(self
, attrs
=None, header
="Set-Cookie:", sep
="\n"):
585 """Return a string suitable for HTTP."""
590 result
.append( V
.output(attrs
, header
) )
591 return sep
.join(result
)
601 L
.append( '%s=%s' % (K
,repr(V
.value
) ) )
602 return '<%s: %s>' % (self
.__class
__.__name
__, _spacejoin(L
))
604 def js_output(self
, attrs
=None):
605 """Return a string suitable for JavaScript."""
610 result
.append( V
.js_output(attrs
) )
611 return _nulljoin(result
)
614 def load(self
, rawdata
):
615 """Load cookies from a string (presumably HTTP_COOKIE) or
616 from a dictionary. Loading cookies from a dictionary 'd'
617 is equivalent to calling:
618 map(Cookie.__setitem__, d.keys(), d.values())
620 if type(rawdata
) == type(""):
621 self
.__ParseString
(rawdata
)
627 def __ParseString(self
, str, patt
=_CookiePattern
):
628 i
= 0 # Our starting point
629 n
= len(str) # Length of string
630 M
= None # current morsel
633 # Start looking for a cookie
634 match
= patt
.search(str, i
)
635 if not match
: break # No more cookies
637 K
,V
= match
.group("key"), match
.group("val")
640 # Parse the key, value in case it's metainfo
642 # We ignore attributes which pertain to the cookie
643 # mechanism as a whole. See RFC 2109.
644 # (Does anyone care?)
647 elif K
.lower() in Morsel
._reserved
:
651 rval
, cval
= self
.value_decode(V
)
652 self
.__set
(K
, rval
, cval
)
655 # end BaseCookie class
657 class SimpleCookie(BaseCookie
):
659 SimpleCookie supports strings as cookie values. When setting
660 the value using the dictionary assignment notation, SimpleCookie
661 calls the builtin str() to convert the value to a string. Values
662 received from HTTP are kept as strings.
664 def value_decode(self
, val
):
665 return _unquote( val
), val
666 def value_encode(self
, val
):
668 return strval
, _quote( strval
)
671 class SerialCookie(BaseCookie
):
673 SerialCookie supports arbitrary objects as cookie values. All
674 values are serialized (using cPickle) before being sent to the
675 client. All incoming values are assumed to be valid Pickle
676 representations. IF AN INCOMING VALUE IS NOT IN A VALID PICKLE
677 FORMAT, THEN AN EXCEPTION WILL BE RAISED.
679 Note: Large cookie values add overhead because they must be
680 retransmitted on every HTTP transaction.
682 Note: HTTP has a 2k limit on the size of a cookie. This class
683 does not check for this limit, so be careful!!!
685 def __init__(self
, input=None):
686 warnings
.warn("SerialCookie class is insecure; do not use it",
688 BaseCookie
.__init
__(self
, input)
690 def value_decode(self
, val
):
691 # This could raise an exception!
692 return loads( _unquote(val
) ), val
693 def value_encode(self
, val
):
694 return val
, _quote( dumps(val
) )
697 class SmartCookie(BaseCookie
):
699 SmartCookie supports arbitrary objects as cookie values. If the
700 object is a string, then it is quoted. If the object is not a
701 string, however, then SmartCookie will use cPickle to serialize
702 the object into a string representation.
704 Note: Large cookie values add overhead because they must be
705 retransmitted on every HTTP transaction.
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!!!
710 def __init__(self
, input=None):
711 warnings
.warn("Cookie/SmartCookie class is insecure; do not use it",
713 BaseCookie
.__init
__(self
, input)
715 def value_decode(self
, val
):
716 strval
= _unquote(val
)
718 return loads(strval
), val
721 def value_encode(self
, val
):
722 if type(val
) == type(""):
723 return val
, _quote(val
)
725 return val
, _quote( dumps(val
) )
729 ###########################################################
730 # Backwards Compatibility: Don't break any existing code!
732 # We provide Cookie() as an alias for SmartCookie()
736 ###########################################################
739 import doctest
, Cookie
740 return doctest
.testmod(Cookie
)
742 if __name__
== "__main__":