2
# Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
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
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.
26
# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
27
# by Timothy O'Malley <timo@alum.mit.edu>
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.
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.
40
Here's a sample session to show how to use this module.
41
At the moment, this is the only documentation.
50
Most of the time you start by creating a cookie. Cookies come in
51
three flavors, each with slightly different encoding semantics, but
54
>>> C = Cookie.SimpleCookie()
55
>>> C = Cookie.SerialCookie()
56
>>> C = Cookie.SmartCookie()
58
[Note: Long-time users of Cookie.py will remember using
59
Cookie.Cookie() to create a Cookie object. Although deprecated, it
60
is still supported by the code. See the Backward Compatibility notes
61
for more information.]
63
Once you've created your Cookie, you can add values just as if it were
66
>>> C = Cookie.SmartCookie()
67
>>> C["fig"] = "newton"
68
>>> C["sugar"] = "wafer"
70
'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
72
Notice that the printable representation of a Cookie is the
73
appropriate format for a Set-Cookie: header. This is the
74
default behavior. You can change the header and printed
75
attributes by using the .output() function
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:")
85
The load() method of a Cookie extracts cookies from a string. In a
86
CGI script, you would use this method to extract the cookies from the
87
HTTP_COOKIE environment variable.
89
>>> C = Cookie.SmartCookie()
90
>>> C.load("chips=ahoy; vienna=finger")
92
'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
94
The load() method is darn-tootin smart about identifying cookies
95
within a string. Escaped quotation marks, nested semicolons, and other
96
such trickeries do not confuse it.
98
>>> C = Cookie.SmartCookie()
99
>>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
101
Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
103
Each element of the Cookie also supports all of the RFC 2109
104
Cookie attributes. Here's an example which sets the Path
107
>>> C = Cookie.SmartCookie()
108
>>> C["oreo"] = "doublestuff"
109
>>> C["oreo"]["path"] = "/"
111
Set-Cookie: oreo=doublestuff; Path=/
113
Each dictionary element has a 'value' attribute, which gives you
114
back the value associated with the key.
116
>>> C = Cookie.SmartCookie()
117
>>> C["twix"] = "none for you"
125
As mentioned before, there are three different flavors of Cookie
126
objects, each with different encoding/decoding semantics. This
127
section briefly discusses the differences.
131
The SimpleCookie expects that all values should be standard strings.
132
Just to be sure, SimpleCookie invokes the str() builtin to convert
133
the value to a string, when the values are set dictionary-style.
135
>>> C = Cookie.SimpleCookie()
137
>>> C["string"] = "seven"
138
>>> C["number"].value
140
>>> C["string"].value
143
'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
148
The SerialCookie expects that all values should be serialized using
149
cPickle (or pickle, if cPickle isn't available). As a result of
150
serializing, SerialCookie can save almost any Python object to a
151
value, and recover the exact same object when the cookie has been
152
returned. (SerialCookie can yield some strange-looking cookie
155
>>> C = Cookie.SerialCookie()
157
>>> C["string"] = "seven"
158
>>> C["number"].value
160
>>> C["string"].value
163
'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string="S\'seven\'\\012p1\\012."'
165
Be warned, however, if SerialCookie cannot de-serialize a value (because
166
it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
171
The SmartCookie combines aspects of each of the other two flavors.
172
When setting a value in a dictionary-fashion, the SmartCookie will
173
serialize (ala cPickle) the value *if and only if* it isn't a
174
Python string. String objects are *not* serialized. Similarly,
175
when the load() method parses out values, it attempts to de-serialize
176
the value. If it fails, then it fallsback to treating the value
179
>>> C = Cookie.SmartCookie()
181
>>> C["string"] = "seven"
182
>>> C["number"].value
184
>>> C["string"].value
187
'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string=seven'
190
Backwards Compatibility
191
-----------------------
193
In order to keep compatibilty with earlier versions of Cookie.py,
194
it is still possible to use Cookie.Cookie() to create a Cookie. In
195
fact, this simply returns a SmartCookie.
197
>>> C = Cookie.Cookie()
198
>>> print C.__class__.__name__
205
# |----helps out font-lock
208
# Import our required modules
213
from cPickle import dumps, loads
215
from pickle import dumps, loads
219
__all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie",
220
"SmartCookie","Cookie"]
223
_semispacejoin = '; '.join
224
_spacejoin = ' '.join
227
# Define an exception visible to External modules
229
class CookieError(Exception):
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.
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
244
_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~"
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',
258
# Because of the way browsers really handle cookies (as opposed
259
# to what the RFC says) we also encode , and ;
261
',' : '\\054', ';' : '\\073',
263
'"' : '\\"', '\\' : '\\\\',
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'
310
_idmap = ''.join(chr(x) for x in xrange(256))
312
def _quote(str, LegalChars=_LegalChars,
313
idmap=_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 is an extension from Microsoft:
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",
421
"comment" : "Comment",
423
"max-age" : "Max-Age",
425
"httponly" : "httponly",
426
"version" : "Version",
429
_flags = {'secure', 'httponly'}
433
self.key = self.value = self.coded_value = None
435
# Set default attributes
436
for K in self._reserved:
437
dict.__setitem__(self, K, "")
440
def __setitem__(self, K, V):
442
if not K in self._reserved:
443
raise CookieError("Invalid Attribute %s" % K)
444
dict.__setitem__(self, K, V)
447
def isReservedKey(self, K):
448
return K.lower() in self._reserved
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)
461
# It's a good key, so save it.
464
self.coded_value = coded_val
467
def output(self, attrs=None, header = "Set-Cookie:"):
468
return "%s %s" % ( header, self.OutputString(attrs) )
473
return '<%s: %s=%s>' % (self.__class__.__name__,
474
self.key, repr(self.value) )
476
def js_output(self, attrs=None):
479
<script type="text/javascript">
481
document.cookie = \"%s\";
484
""" % ( self.OutputString(attrs).replace('"',r'\"'), )
487
def OutputString(self, attrs=None):
488
# Build up our result
493
# First, the key=value pair
494
RA("%s=%s" % (self.key, self.coded_value))
496
# Now add any defined attributes
498
attrs = self._reserved
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))
509
RA(str(self._reserved[K]))
510
elif K == "httponly":
511
RA(str(self._reserved[K]))
513
RA("%s=%s" % (self._reserved[K], V))
516
return _semispacejoin(result)
523
# Pattern for finding cookie
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.
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
544
r"\w{3},\s[\s\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr
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.
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.
558
class BaseCookie(dict):
559
# A container class for a set of Morsels
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
567
Override this function to modify the behavior of cookies.
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.
579
return strval, strval
582
def __init__(self, input=None):
583
if input: self.load(input)
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)
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)
599
rval, cval = self.value_encode(value)
600
self.__set(key, rval, cval)
603
def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
604
"""Return a string suitable for HTTP."""
609
result.append( V.output(attrs, header) )
610
return sep.join(result)
620
L.append( '%s=%s' % (K,repr(V.value) ) )
621
return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
623
def js_output(self, attrs=None):
624
"""Return a string suitable for JavaScript."""
629
result.append( V.js_output(attrs) )
630
return _nulljoin(result)
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())
639
if type(rawdata) == type(""):
640
self.__ParseString(rawdata)
642
# self.update() wouldn't call our custom __setitem__
643
for k, v in rawdata.items():
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
654
# Start looking for a cookie
655
match = patt.match(str, i)
656
if not match: break # No more cookies
658
K,V = match.group("key"), match.group("val")
661
# Parse the key, value in case it's metainfo
663
# We ignore attributes which pertain to the cookie
664
# mechanism as a whole. See RFC 2109.
665
# (Does anyone care?)
668
elif K.lower() in Morsel._reserved:
671
if K.lower() in Morsel._flags:
676
rval, cval = self.value_decode(V)
677
self.__set(K, rval, cval)
680
# end BaseCookie class
682
class SimpleCookie(BaseCookie):
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.
689
def value_decode(self, val):
690
return _unquote( val ), val
691
def value_encode(self, val):
693
return strval, _quote( strval )
696
class SerialCookie(BaseCookie):
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.
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("SerialCookie class is insecure; do not use it",
713
BaseCookie.__init__(self, input)
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) )
722
class SmartCookie(BaseCookie):
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.
729
Note: Large cookie values add overhead because they must be
730
retransmitted on every HTTP transaction.
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!!!
735
def __init__(self, input=None):
736
warnings.warn("Cookie/SmartCookie class is insecure; do not use it",
738
BaseCookie.__init__(self, input)
740
def value_decode(self, val):
741
strval = _unquote(val)
743
return loads(strval), val
746
def value_encode(self, val):
747
if type(val) == type(""):
748
return val, _quote(val)
750
return val, _quote( dumps(val) )
754
###########################################################
755
# Backwards Compatibility: Don't break any existing code!
757
# We provide Cookie() as an alias for SmartCookie()
761
###########################################################
764
import doctest, Cookie
765
return doctest.testmod(Cookie)
767
if __name__ == "__main__":
2
# Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
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
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.
26
# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
27
# by Timothy O'Malley <timo@alum.mit.edu>
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.
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.
40
Here's a sample session to show how to use this module.
41
At the moment, this is the only documentation.
50
Most of the time you start by creating a cookie. Cookies come in
51
three flavors, each with slightly different encoding semantics, but
54
>>> C = Cookie.SimpleCookie()
55
>>> C = Cookie.SerialCookie()
56
>>> C = Cookie.SmartCookie()
58
[Note: Long-time users of Cookie.py will remember using
59
Cookie.Cookie() to create a Cookie object. Although deprecated, it
60
is still supported by the code. See the Backward Compatibility notes
61
for more information.]
63
Once you've created your Cookie, you can add values just as if it were
66
>>> C = Cookie.SmartCookie()
67
>>> C["fig"] = "newton"
68
>>> C["sugar"] = "wafer"
70
'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
72
Notice that the printable representation of a Cookie is the
73
appropriate format for a Set-Cookie: header. This is the
74
default behavior. You can change the header and printed
75
attributes by using the .output() function
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:")
85
The load() method of a Cookie extracts cookies from a string. In a
86
CGI script, you would use this method to extract the cookies from the
87
HTTP_COOKIE environment variable.
89
>>> C = Cookie.SmartCookie()
90
>>> C.load("chips=ahoy; vienna=finger")
92
'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
94
The load() method is darn-tootin smart about identifying cookies
95
within a string. Escaped quotation marks, nested semicolons, and other
96
such trickeries do not confuse it.
98
>>> C = Cookie.SmartCookie()
99
>>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
101
Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
103
Each element of the Cookie also supports all of the RFC 2109
104
Cookie attributes. Here's an example which sets the Path
107
>>> C = Cookie.SmartCookie()
108
>>> C["oreo"] = "doublestuff"
109
>>> C["oreo"]["path"] = "/"
111
Set-Cookie: oreo=doublestuff; Path=/
113
Each dictionary element has a 'value' attribute, which gives you
114
back the value associated with the key.
116
>>> C = Cookie.SmartCookie()
117
>>> C["twix"] = "none for you"
125
As mentioned before, there are three different flavors of Cookie
126
objects, each with different encoding/decoding semantics. This
127
section briefly discusses the differences.
131
The SimpleCookie expects that all values should be standard strings.
132
Just to be sure, SimpleCookie invokes the str() builtin to convert
133
the value to a string, when the values are set dictionary-style.
135
>>> C = Cookie.SimpleCookie()
137
>>> C["string"] = "seven"
138
>>> C["number"].value
140
>>> C["string"].value
143
'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
148
The SerialCookie expects that all values should be serialized using
149
cPickle (or pickle, if cPickle isn't available). As a result of
150
serializing, SerialCookie can save almost any Python object to a
151
value, and recover the exact same object when the cookie has been
152
returned. (SerialCookie can yield some strange-looking cookie
155
>>> C = Cookie.SerialCookie()
157
>>> C["string"] = "seven"
158
>>> C["number"].value
160
>>> C["string"].value
163
'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string="S\'seven\'\\012p1\\012."'
165
Be warned, however, if SerialCookie cannot de-serialize a value (because
166
it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
171
The SmartCookie combines aspects of each of the other two flavors.
172
When setting a value in a dictionary-fashion, the SmartCookie will
173
serialize (ala cPickle) the value *if and only if* it isn't a
174
Python string. String objects are *not* serialized. Similarly,
175
when the load() method parses out values, it attempts to de-serialize
176
the value. If it fails, then it fallsback to treating the value
179
>>> C = Cookie.SmartCookie()
181
>>> C["string"] = "seven"
182
>>> C["number"].value
184
>>> C["string"].value
187
'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string=seven'
190
Backwards Compatibility
191
-----------------------
193
In order to keep compatibility with earlier versions of Cookie.py,
194
it is still possible to use Cookie.Cookie() to create a Cookie. In
195
fact, this simply returns a SmartCookie.
197
>>> C = Cookie.Cookie()
198
>>> print C.__class__.__name__
205
# |----helps out font-lock
208
# Import our required modules
213
from cPickle import dumps, loads
215
from pickle import dumps, loads
219
__all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie",
220
"SmartCookie","Cookie"]
223
_semispacejoin = '; '.join
224
_spacejoin = ' '.join
227
# Define an exception visible to External modules
229
class CookieError(Exception):
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.
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
244
_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~"
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',
258
# Because of the way browsers really handle cookies (as opposed
259
# to what the RFC says) we also encode , and ;
261
',' : '\\054', ';' : '\\073',
263
'"' : '\\"', '\\' : '\\\\',
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'
310
_idmap = ''.join(chr(x) for x in xrange(256))
312
def _quote(str, LegalChars=_LegalChars,
313
idmap=_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 is an extension from Microsoft:
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",
421
"comment" : "Comment",
423
"max-age" : "Max-Age",
425
"httponly" : "httponly",
426
"version" : "Version",
429
_flags = {'secure', 'httponly'}
433
self.key = self.value = self.coded_value = None
435
# Set default attributes
436
for K in self._reserved:
437
dict.__setitem__(self, K, "")
440
def __setitem__(self, K, V):
442
if not K in self._reserved:
443
raise CookieError("Invalid Attribute %s" % K)
444
dict.__setitem__(self, K, V)
447
def isReservedKey(self, K):
448
return K.lower() in self._reserved
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)
461
# It's a good key, so save it.
464
self.coded_value = coded_val
467
def output(self, attrs=None, header = "Set-Cookie:"):
468
return "%s %s" % ( header, self.OutputString(attrs) )
473
return '<%s: %s=%s>' % (self.__class__.__name__,
474
self.key, repr(self.value) )
476
def js_output(self, attrs=None):
479
<script type="text/javascript">
481
document.cookie = \"%s\";
484
""" % ( self.OutputString(attrs).replace('"',r'\"'), )
487
def OutputString(self, attrs=None):
488
# Build up our result
493
# First, the key=value pair
494
RA("%s=%s" % (self.key, self.coded_value))
496
# Now add any defined attributes
498
attrs = self._reserved
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))
509
RA(str(self._reserved[K]))
510
elif K == "httponly":
511
RA(str(self._reserved[K]))
513
RA("%s=%s" % (self._reserved[K], V))
516
return _semispacejoin(result)
523
# Pattern for finding cookie
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.
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
544
r"\w{3},\s[\s\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr
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.
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.
558
class BaseCookie(dict):
559
# A container class for a set of Morsels
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
567
Override this function to modify the behavior of cookies.
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.
579
return strval, strval
582
def __init__(self, input=None):
583
if input: self.load(input)
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)
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)
599
rval, cval = self.value_encode(value)
600
self.__set(key, rval, cval)
603
def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
604
"""Return a string suitable for HTTP."""
609
result.append( V.output(attrs, header) )
610
return sep.join(result)
620
L.append( '%s=%s' % (K,repr(V.value) ) )
621
return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
623
def js_output(self, attrs=None):
624
"""Return a string suitable for JavaScript."""
629
result.append( V.js_output(attrs) )
630
return _nulljoin(result)
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())
639
if type(rawdata) == type(""):
640
self.__ParseString(rawdata)
642
# self.update() wouldn't call our custom __setitem__
643
for k, v in rawdata.items():
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
654
# Start looking for a cookie
655
match = patt.match(str, i)
656
if not match: break # No more cookies
658
K,V = match.group("key"), match.group("val")
661
# Parse the key, value in case it's metainfo
663
# We ignore attributes which pertain to the cookie
664
# mechanism as a whole. See RFC 2109.
665
# (Does anyone care?)
668
elif K.lower() in Morsel._reserved:
671
if K.lower() in Morsel._flags:
676
rval, cval = self.value_decode(V)
677
self.__set(K, rval, cval)
680
# end BaseCookie class
682
class SimpleCookie(BaseCookie):
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.
689
def value_decode(self, val):
690
return _unquote( val ), val
691
def value_encode(self, val):
693
return strval, _quote( strval )
696
class SerialCookie(BaseCookie):
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.
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("SerialCookie class is insecure; do not use it",
713
BaseCookie.__init__(self, input)
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) )
722
class SmartCookie(BaseCookie):
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.
729
Note: Large cookie values add overhead because they must be
730
retransmitted on every HTTP transaction.
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!!!
735
def __init__(self, input=None):
736
warnings.warn("Cookie/SmartCookie class is insecure; do not use it",
738
BaseCookie.__init__(self, input)
740
def value_decode(self, val):
741
strval = _unquote(val)
743
return loads(strval), val
746
def value_encode(self, val):
747
if type(val) == type(""):
748
return val, _quote(val)
750
return val, _quote( dumps(val) )
754
###########################################################
755
# Backwards Compatibility: Don't break any existing code!
757
# We provide Cookie() as an alias for SmartCookie()
761
###########################################################
764
import doctest, Cookie
765
return doctest.testmod(Cookie)
767
if __name__ == "__main__":