blob: 565e6f3fb498ba04338e22793d6b75d323bfa452 [file] [log] [blame]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +00001#!/usr/bin/env python
2#
3
4####
5# Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
Tim Peters88869f92001-01-14 23:36:06 +00006#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +00007# All Rights Reserved
Tim Peters88869f92001-01-14 23:36:06 +00008#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +00009# 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
Tim Peters88869f92001-01-14 23:36:06 +000016# prior permission.
17#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000018# 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
Tim Peters88869f92001-01-14 23:36:06 +000025# PERFORMANCE OF THIS SOFTWARE.
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000026#
27####
Tim Peters88869f92001-01-14 23:36:06 +000028#
29# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000030# by Timothy O'Malley <timo@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
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +000037# Dave Mitchell (davem@magnet.com) in 1995, when he released the
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000038# first version of nscookie.py.
39#
40####
41
42"""
43Here's a sample session to show how to use this module.
44At the moment, this is the only documentation.
45
46The Basics
47----------
48
49Importing is easy..
50
51 >>> import Cookie
52
53Most of the time you start by creating a cookie. Cookies come in
54three flavors, each with slighly different encoding semanitcs, but
55more 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
62Cookie.Cookie() to create an Cookie object. Although deprecated, it
63is still supported by the code. See the Backward Compatibility notes
64for more information.]
65
66Once you've created your Cookie, you can add values just as if it were
67a dictionary.
68
69 >>> C = Cookie.SmartCookie()
70 >>> C["fig"] = "newton"
71 >>> C["sugar"] = "wafer"
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +000072 >>> print C
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000073 Set-Cookie: sugar=wafer;
74 Set-Cookie: fig=newton;
75
76Notice that the printable representation of a Cookie is the
77appropriate format for a Set-Cookie: header. This is the
78default behavior. You can change the header and printed
79attributes by using the the .output() function
80
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:")
87 Cookie: rocky=road;
88
89The load() method of a Cookie extracts cookies from a string. In a
90CGI script, you would use this method to extract the cookies from the
91HTTP_COOKIE environment variable.
92
93 >>> C = Cookie.SmartCookie()
94 >>> C.load("chips=ahoy; vienna=finger")
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +000095 >>> print C
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000096 Set-Cookie: vienna=finger;
97 Set-Cookie: chips=ahoy;
98
99The load() method is darn-tootin smart about identifying cookies
100within a string. Escaped quotation marks, nested semicolons, and other
101such trickeries do not confuse it.
102
103 >>> C = Cookie.SmartCookie()
104 >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000105 >>> print C
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000106 Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;";
107
108Each element of the Cookie also supports all of the RFC 2109
109Cookie attributes. Here's an example which sets the Path
110attribute.
111
112 >>> C = Cookie.SmartCookie()
113 >>> C["oreo"] = "doublestuff"
114 >>> C["oreo"]["path"] = "/"
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000115 >>> print C
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000116 Set-Cookie: oreo="doublestuff"; Path=/;
117
118Each dictionary element has a 'value' attribute, which gives you
Tim Peters88869f92001-01-14 23:36:06 +0000119back the value associated with the key.
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000120
121 >>> C = Cookie.SmartCookie()
122 >>> C["twix"] = "none for you"
123 >>> C["twix"].value
124 'none for you'
125
126
127A Bit More Advanced
128-------------------
129
130As mentioned before, there are three different flavors of Cookie
131objects, each with different encoding/decoding semantics. This
132section briefly discusses the differences.
133
134SimpleCookie
135
136The SimpleCookie expects that all values should be standard strings.
137Just to be sure, SimpleCookie invokes the str() builtin to convert
138the value to a string, when the values are set dictionary-style.
139
140 >>> C = Cookie.SimpleCookie()
141 >>> C["number"] = 7
142 >>> C["string"] = "seven"
143 >>> C["number"].value
144 '7'
145 >>> C["string"].value
146 'seven'
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000147 >>> print C
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000148 Set-Cookie: number=7;
149 Set-Cookie: string=seven;
150
Tim Peters88869f92001-01-14 23:36:06 +0000151
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000152SerialCookie
153
154The SerialCookie expects that all values should be serialized using
155cPickle (or pickle, if cPickle isn't available). As a result of
156serializing, SerialCookie can save almost any Python object to a
157value, and recover the exact same object when the cookie has been
158returned. (SerialCookie can yield some strange-looking cookie
159values, however.)
160
161 >>> C = Cookie.SerialCookie()
162 >>> C["number"] = 7
163 >>> C["string"] = "seven"
164 >>> C["number"].value
165 7
166 >>> C["string"].value
167 'seven'
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000168 >>> print C
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000169 Set-Cookie: number="I7\012.";
170 Set-Cookie: string="S'seven'\012p1\012.";
171
172Be warned, however, if SerialCookie cannot de-serialize a value (because
173it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
174
175
176SmartCookie
177
178The SmartCookie combines aspects of each of the other two flavors.
179When setting a value in a dictionary-fashion, the SmartCookie will
180serialize (ala cPickle) the value *if and only if* it isn't a
181Python string. String objects are *not* serialized. Similarly,
182when the load() method parses out values, it attempts to de-serialize
183the value. If it fails, then it fallsback to treating the value
184as a string.
185
186 >>> C = Cookie.SmartCookie()
187 >>> C["number"] = 7
188 >>> C["string"] = "seven"
189 >>> C["number"].value
190 7
191 >>> C["string"].value
192 'seven'
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000193 >>> print C
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000194 Set-Cookie: number="I7\012.";
195 Set-Cookie: string=seven;
196
197
198Backwards Compatibility
199-----------------------
200
201In order to keep compatibilty with earlier versions of Cookie.py,
202it is still possible to use Cookie.Cookie() to create a Cookie. In
203fact, this simply returns a SmartCookie.
204
205 >>> C = Cookie.Cookie()
206 >>> C.__class__
207 <class Cookie.SmartCookie at 99f88>
208
209
210Finis.
211""" #"
212# ^
213# |----helps out font-lock
214
215#
216# Import our required modules
Tim Peters88869f92001-01-14 23:36:06 +0000217#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000218import string, sys
219from UserDict import UserDict
220
221try:
222 from cPickle import dumps, loads
223except ImportError:
224 from pickle import dumps, loads
225
226try:
227 import re
228except ImportError:
229 raise ImportError, "Cookie.py requires 're' from Python 1.5 or later"
230
Skip Montanaroe99d5ea2001-01-20 19:54:20 +0000231__all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie",
232 "SmartCookie","Cookie"]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000233
234#
235# Define an exception visible to External modules
236#
237class CookieError(Exception):
238 pass
239
240
241# These quoting routines conform to the RFC2109 specification, which in
242# turn references the character definitions from RFC2068. They provide
243# a two-way quoting algorithm. Any non-text character is translated
244# into a 4 character sequence: a forward-slash followed by the
245# three-digit octal equivalent of the character. Any '\' or '"' is
246# quoted with a preceeding '\' slash.
Tim Peters88869f92001-01-14 23:36:06 +0000247#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000248# These are taken from RFC2068 and RFC2109.
249# _LegalChars is the list of chars which don't require "'s
250# _Translator hash-table for fast quoting
251#
252_LegalChars = string.letters + string.digits + "!#$%&'*+-.^_`|~"
253_Translator = {
254 '\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
255 '\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
256 '\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
257 '\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
258 '\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
259 '\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
260 '\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
261 '\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
262 '\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
263 '\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
264 '\036' : '\\036', '\037' : '\\037',
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
313def _quote(str, LegalChars=_LegalChars,
Fred Drakeff5364a2000-08-24 14:40:35 +0000314 join=string.join, idmap=string._idmap, translate=string.translate):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000315 #
316 # If the string does not need to be double-quoted,
317 # then just return the string. Otherwise, surround
318 # the string in doublequotes and precede quote (with a \)
319 # special characters.
320 #
321 if "" == translate(str, idmap, LegalChars):
Fred Drakeff5364a2000-08-24 14:40:35 +0000322 return str
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000323 else:
Tim Peters88869f92001-01-14 23:36:06 +0000324 return '"' + join( map(_Translator.get, str, str), "" ) + '"'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000325# end _quote
326
327
328_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
329_QuotePatt = re.compile(r"[\\].")
330
331def _unquote(str, join=string.join, atoi=string.atoi):
332 # If there aren't any doublequotes,
333 # then there can't be any special characters. See RFC 2109.
334 if len(str) < 2:
335 return str
336 if str[0] != '"' or str[-1] != '"':
337 return str
338
339 # We have to assume that we must decode this string.
340 # Down to work.
341
342 # Remove the "s
343 str = str[1:-1]
Fred Drakeff5364a2000-08-24 14:40:35 +0000344
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000345 # Check for special sequences. Examples:
346 # \012 --> \n
347 # \" --> "
348 #
349 i = 0
350 n = len(str)
351 res = []
352 while 0 <= i < n:
353 Omatch = _OctalPatt.search(str, i)
354 Qmatch = _QuotePatt.search(str, i)
355 if not Omatch and not Qmatch: # Neither matched
356 res.append(str[i:])
357 break
358 # else:
359 j = k = -1
360 if Omatch: j = Omatch.start(0)
361 if Qmatch: k = Qmatch.start(0)
362 if Qmatch and ( not Omatch or k < j ): # QuotePatt matched
363 res.append(str[i:k])
364 res.append(str[k+1])
365 i = k+2
366 else: # OctalPatt matched
367 res.append(str[i:j])
368 res.append( chr( atoi(str[j+1:j+4], 8) ) )
369 i = j+4
370 return join(res, "")
371# end _unquote
372
373# The _getdate() routine is used to set the expiration time in
374# the cookie's HTTP header. By default, _getdate() returns the
Tim Peters88869f92001-01-14 23:36:06 +0000375# current time in the appropriate "expires" format for a
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000376# Set-Cookie header. The one optional argument is an offset from
377# now, in seconds. For example, an offset of -3600 means "one hour ago".
378# The offset may be a floating point number.
379#
380
381_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
382
383_monthname = [None,
384 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
385 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
386
387def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
388 from time import gmtime, time
389 now = time()
390 year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
391 return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
392 (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
393
394
395#
396# A class to hold ONE key,value pair.
397# In a cookie, each such pair may have several attributes.
398# so this class is used to keep the attributes associated
399# with the appropriate key,value pair.
400# This class also includes a coded_value attribute, which
401# is used to hold the network representation of the
402# value. This is most useful when Python objects are
403# pickled for network transit.
404#
405
406class Morsel(UserDict):
407 # RFC 2109 lists these attributes as reserved:
408 # path comment domain
409 # max-age secure version
Tim Peters88869f92001-01-14 23:36:06 +0000410 #
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000411 # For historical reasons, these attributes are also reserved:
412 # expires
413 #
414 # This dictionary provides a mapping from the lowercase
415 # variant on the left to the appropriate traditional
416 # formatting on the right.
417 _reserved = { "expires" : "expires",
418 "path" : "Path",
419 "comment" : "Comment",
420 "domain" : "Domain",
421 "max-age" : "Max-Age",
422 "secure" : "secure",
423 "version" : "Version",
424 }
425 _reserved_keys = _reserved.keys()
Fred Drakeff5364a2000-08-24 14:40:35 +0000426
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000427 def __init__(self):
428 # Set defaults
429 self.key = self.value = self.coded_value = None
430 UserDict.__init__(self)
431
432 # Set default attributes
433 for K in self._reserved_keys:
434 UserDict.__setitem__(self, K, "")
435 # end __init__
436
437 def __setitem__(self, K, V):
438 K = string.lower(K)
439 if not K in self._reserved_keys:
440 raise CookieError("Invalid Attribute %s" % K)
441 UserDict.__setitem__(self, K, V)
442 # end __setitem__
443
444 def isReservedKey(self, K):
Fred Drakeff5364a2000-08-24 14:40:35 +0000445 return string.lower(K) in self._reserved_keys
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000446 # end isReservedKey
447
448 def set(self, key, val, coded_val,
449 LegalChars=_LegalChars,
450 idmap=string._idmap, translate=string.translate ):
451 # First we verify that the key isn't a reserved word
452 # Second we make sure it only contains legal characters
453 if string.lower(key) in self._reserved_keys:
454 raise CookieError("Attempt to set a reserved key: %s" % key)
455 if "" != translate(key, idmap, LegalChars):
456 raise CookieError("Illegal key value: %s" % key)
457
458 # It's a good key, so save it.
459 self.key = key
460 self.value = val
461 self.coded_value = coded_val
462 # end set
463
464 def output(self, attrs=None, header = "Set-Cookie:"):
465 return "%s %s" % ( header, self.OutputString(attrs) )
466
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000467 __str__ = output
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000468
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000469 def __repr__(self):
470 return '<%s: %s=%s>' % (self.__class__.__name__,
471 self.key, repr(self.value) )
Fred Drakeff5364a2000-08-24 14:40:35 +0000472
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000473 def js_output(self, attrs=None):
474 # Print javascript
475 return """
476 <SCRIPT LANGUAGE="JavaScript">
477 <!-- begin hiding
478 document.cookie = \"%s\"
479 // end hiding -->
480 </script>
481 """ % ( self.OutputString(attrs), )
482 # end js_output()
483
484 def OutputString(self, attrs=None):
485 # Build up our result
486 #
487 result = []
488 RA = result.append
Fred Drakeff5364a2000-08-24 14:40:35 +0000489
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000490 # First, the key=value pair
491 RA("%s=%s;" % (self.key, self.coded_value))
492
493 # Now add any defined attributes
Fred Drake8152d322000-12-12 23:20:45 +0000494 if attrs is None:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000495 attrs = self._reserved_keys
496 for K,V in self.items():
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000497 if V == "": continue
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000498 if K not in attrs: continue
499 if K == "expires" and type(V) == type(1):
500 RA("%s=%s;" % (self._reserved[K], _getdate(V)))
501 elif K == "max-age" and type(V) == type(1):
502 RA("%s=%d;" % (self._reserved[K], V))
503 elif K == "secure":
504 RA("%s;" % self._reserved[K])
505 else:
506 RA("%s=%s;" % (self._reserved[K], V))
Fred Drakeff5364a2000-08-24 14:40:35 +0000507
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000508 # Return the result
509 return string.join(result, " ")
510 # end OutputString
511# end Morsel class
512
513
514
515#
516# Pattern for finding cookie
517#
518# This used to be strict parsing based on the RFC2109 and RFC2068
519# specifications. I have since discovered that MSIE 3.0x doesn't
520# follow the character rules outlined in those specs. As a
521# result, the parsing rules here are less strict.
522#
523
524_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{]"
525_CookiePattern = re.compile(
526 r"(?x)" # This is a Verbose pattern
527 r"(?P<key>" # Start of group 'key'
528 ""+ _LegalCharsPatt +"+" # Any word of at least one letter
529 r")" # End of group 'key'
530 r"\s*=\s*" # Equal Sign
531 r"(?P<val>" # Start of group 'val'
532 r'"(?:[^\\"]|\\.)*"' # Any doublequoted string
533 r"|" # or
534 ""+ _LegalCharsPatt +"*" # Any word or empty string
535 r")" # End of group 'val'
536 r"\s*;?" # Probably ending in a semi-colon
537 )
538
539
540# At long last, here is the cookie class.
541# Using this class is almost just like using a dictionary.
542# See this module's docstring for example usage.
543#
544class BaseCookie(UserDict):
545 # A container class for a set of Morsels
546 #
Fred Drakeff5364a2000-08-24 14:40:35 +0000547
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000548 def value_decode(self, val):
549 """real_value, coded_value = value_decode(STRING)
550 Called prior to setting a cookie's value from the network
551 representation. The VALUE is the value read from HTTP
552 header.
553 Override this function to modify the behavior of cookies.
554 """
555 return val, val
556 # end value_encode
557
558 def value_encode(self, val):
559 """real_value, coded_value = value_encode(VALUE)
560 Called prior to setting a cookie's value from the dictionary
561 representation. The VALUE is the value being assigned.
562 Override this function to modify the behavior of cookies.
563 """
564 strval = str(val)
565 return strval, strval
566 # end value_encode
Fred Drakeff5364a2000-08-24 14:40:35 +0000567
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000568 def __init__(self, input=None):
569 UserDict.__init__(self)
570 if input: self.load(input)
571 # end __init__
572
573 def __set(self, key, real_value, coded_value):
574 """Private method for setting a cookie's value"""
575 M = self.get(key, Morsel())
576 M.set(key, real_value, coded_value)
577 UserDict.__setitem__(self, key, M)
578 # end __set
579
580 def __setitem__(self, key, value):
581 """Dictionary style assignment."""
582 rval, cval = self.value_encode(value)
583 self.__set(key, rval, cval)
584 # end __setitem__
585
586 def output(self, attrs=None, header="Set-Cookie:", sep="\n"):
587 """Return a string suitable for HTTP."""
588 result = []
589 for K,V in self.items():
590 result.append( V.output(attrs, header) )
591 return string.join(result, sep)
592 # end output
593
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000594 __str__ = output
595
596 def __repr__(self):
597 L = []
598 for K,V in self.items():
599 L.append( '%s=%s' % (K,repr(V.value) ) )
600 return '<%s: %s>' % (self.__class__.__name__, string.join(L))
Fred Drakeff5364a2000-08-24 14:40:35 +0000601
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000602 def js_output(self, attrs=None):
603 """Return a string suitable for JavaScript."""
604 result = []
605 for K,V in self.items():
606 result.append( V.js_output(attrs) )
607 return string.join(result, "")
608 # end js_output
609
610 def load(self, rawdata):
611 """Load cookies from a string (presumably HTTP_COOKIE) or
612 from a dictionary. Loading cookies from a dictionary 'd'
613 is equivalent to calling:
614 map(Cookie.__setitem__, d.keys(), d.values())
615 """
616 if type(rawdata) == type(""):
617 self.__ParseString(rawdata)
618 else:
619 self.update(rawdata)
620 return
621 # end load()
Fred Drakeff5364a2000-08-24 14:40:35 +0000622
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000623 def __ParseString(self, str, patt=_CookiePattern):
624 i = 0 # Our starting point
625 n = len(str) # Length of string
626 M = None # current morsel
627
628 while 0 <= i < n:
629 # Start looking for a cookie
630 match = patt.search(str, i)
631 if not match: break # No more cookies
632
633 K,V = match.group("key"), match.group("val")
634 i = match.end(0)
635
636 # Parse the key, value in case it's metainfo
637 if K[0] == "$":
638 # We ignore attributes which pertain to the cookie
639 # mechanism as a whole. See RFC 2109.
640 # (Does anyone care?)
641 if M:
642 M[ K[1:] ] = V
643 elif string.lower(K) in Morsel._reserved_keys:
644 if M:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000645 M[ K ] = _unquote(V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000646 else:
647 rval, cval = self.value_decode(V)
648 self.__set(K, rval, cval)
649 M = self[K]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000650 # end __ParseString
651# end BaseCookie class
652
653class SimpleCookie(BaseCookie):
654 """SimpleCookie
655 SimpleCookie supports strings as cookie values. When setting
656 the value using the dictionary assignment notation, SimpleCookie
657 calls the builtin str() to convert the value to a string. Values
658 received from HTTP are kept as strings.
659 """
660 def value_decode(self, val):
661 return _unquote( val ), val
662 def value_encode(self, val):
663 strval = str(val)
664 return strval, _quote( strval )
665# end SimpleCookie
666
667class SerialCookie(BaseCookie):
668 """SerialCookie
669 SerialCookie supports arbitrary objects as cookie values. All
670 values are serialized (using cPickle) before being sent to the
671 client. All incoming values are assumed to be valid Pickle
672 representations. IF AN INCOMING VALUE IS NOT IN A VALID PICKLE
673 FORMAT, THEN AN EXCEPTION WILL BE RAISED.
674
675 Note: Large cookie values add overhead because they must be
676 retransmitted on every HTTP transaction.
Fred Drakeff5364a2000-08-24 14:40:35 +0000677
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000678 Note: HTTP has a 2k limit on the size of a cookie. This class
679 does not check for this limit, so be careful!!!
680 """
681 def value_decode(self, val):
682 # This could raise an exception!
683 return loads( _unquote(val) ), val
684 def value_encode(self, val):
685 return val, _quote( dumps(val) )
686# end SerialCookie
687
688class SmartCookie(BaseCookie):
689 """SmartCookie
690 SmartCookie supports arbitrary objects as cookie values. If the
691 object is a string, then it is quoted. If the object is not a
692 string, however, then SmartCookie will use cPickle to serialize
693 the object into a string representation.
694
695 Note: Large cookie values add overhead because they must be
696 retransmitted on every HTTP transaction.
Fred Drakeff5364a2000-08-24 14:40:35 +0000697
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000698 Note: HTTP has a 2k limit on the size of a cookie. This class
699 does not check for this limit, so be careful!!!
700 """
701 def value_decode(self, val):
702 strval = _unquote(val)
703 try:
704 return loads(strval), val
705 except:
706 return strval, val
707 def value_encode(self, val):
708 if type(val) == type(""):
709 return val, _quote(val)
710 else:
711 return val, _quote( dumps(val) )
712# end SmartCookie
713
714
715###########################################################
716# Backwards Compatibility: Don't break any existing code!
717
718# We provide Cookie() as an alias for SmartCookie()
719Cookie = SmartCookie
720
721#
722###########################################################
723
724
725
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000726#Local Variables:
727#tab-width: 4
728#end: