blob: b0a99f44e67c87a1c96387a7c0c43b1f0f28fd74 [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
Guido van Rossum58b6f5b2001-04-06 19:39:11 +000042r"""
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000043Here'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: fig=newton;
Tim Peters2f228e72001-05-13 00:19:31 +000074 Set-Cookie: sugar=wafer;
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000075
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: chips=ahoy;
Tim Peters2f228e72001-05-13 00:19:31 +000097 Set-Cookie: vienna=finger;
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000098
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
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000116 Set-Cookie: oreo=doublestuff; Path=/;
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000117
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()
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000206 >>> print C.__class__.__name__
207 SmartCookie
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000208
209
210Finis.
211""" #"
212# ^
213# |----helps out font-lock
214
215#
216# Import our required modules
Tim Peters88869f92001-01-14 23:36:06 +0000217#
Martin v. Löwis02d893c2001-08-02 07:15:29 +0000218import string
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000219from 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
Fred Draked451ec12002-04-26 02:29:55 +0000234_nulljoin = ''.join
235_spacejoin = ' '.join
236
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000237#
238# Define an exception visible to External modules
239#
240class CookieError(Exception):
241 pass
242
243
244# These quoting routines conform to the RFC2109 specification, which in
245# turn references the character definitions from RFC2068. They provide
246# a two-way quoting algorithm. Any non-text character is translated
247# into a 4 character sequence: a forward-slash followed by the
248# three-digit octal equivalent of the character. Any '\' or '"' is
249# quoted with a preceeding '\' slash.
Tim Peters88869f92001-01-14 23:36:06 +0000250#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000251# These are taken from RFC2068 and RFC2109.
252# _LegalChars is the list of chars which don't require "'s
253# _Translator hash-table for fast quoting
254#
Fred Drake79e75e12001-07-20 19:05:50 +0000255_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~"
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000256_Translator = {
257 '\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
258 '\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
259 '\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
260 '\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
261 '\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
262 '\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
263 '\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
264 '\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
265 '\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
266 '\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
267 '\036' : '\\036', '\037' : '\\037',
268
269 '"' : '\\"', '\\' : '\\\\',
270
271 '\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
272 '\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
273 '\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
274 '\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
275 '\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
276 '\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
277 '\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
278 '\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
279 '\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
280 '\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
281 '\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
282 '\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
283 '\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
284 '\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
285 '\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
286 '\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
287 '\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
288 '\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
289 '\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
290 '\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
291 '\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
292 '\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
293 '\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
294 '\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
295 '\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
296 '\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
297 '\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
298 '\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
299 '\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
300 '\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
301 '\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
302 '\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
303 '\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
304 '\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
305 '\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
306 '\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
307 '\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
308 '\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
309 '\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
310 '\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
311 '\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
312 '\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
313 '\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
314 }
315
316def _quote(str, LegalChars=_LegalChars,
Fred Draked451ec12002-04-26 02:29:55 +0000317 idmap=string._idmap, translate=string.translate):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000318 #
319 # If the string does not need to be double-quoted,
320 # then just return the string. Otherwise, surround
321 # the string in doublequotes and precede quote (with a \)
322 # special characters.
323 #
324 if "" == translate(str, idmap, LegalChars):
Fred Drakeff5364a2000-08-24 14:40:35 +0000325 return str
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000326 else:
Fred Draked451ec12002-04-26 02:29:55 +0000327 return '"' + _nulljoin( map(_Translator.get, str, str) ) + '"'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000328# end _quote
329
330
331_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
332_QuotePatt = re.compile(r"[\\].")
333
Fred Draked451ec12002-04-26 02:29:55 +0000334def _unquote(str):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000335 # If there aren't any doublequotes,
336 # then there can't be any special characters. See RFC 2109.
337 if len(str) < 2:
338 return str
339 if str[0] != '"' or str[-1] != '"':
340 return str
341
342 # We have to assume that we must decode this string.
343 # Down to work.
344
345 # Remove the "s
346 str = str[1:-1]
Fred Drakeff5364a2000-08-24 14:40:35 +0000347
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000348 # Check for special sequences. Examples:
349 # \012 --> \n
350 # \" --> "
351 #
352 i = 0
353 n = len(str)
354 res = []
355 while 0 <= i < n:
356 Omatch = _OctalPatt.search(str, i)
357 Qmatch = _QuotePatt.search(str, i)
358 if not Omatch and not Qmatch: # Neither matched
359 res.append(str[i:])
360 break
361 # else:
362 j = k = -1
363 if Omatch: j = Omatch.start(0)
364 if Qmatch: k = Qmatch.start(0)
365 if Qmatch and ( not Omatch or k < j ): # QuotePatt matched
366 res.append(str[i:k])
367 res.append(str[k+1])
368 i = k+2
369 else: # OctalPatt matched
370 res.append(str[i:j])
Fred Draked451ec12002-04-26 02:29:55 +0000371 res.append( chr( int(str[j+1:j+4], 8) ) )
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000372 i = j+4
Fred Draked451ec12002-04-26 02:29:55 +0000373 return _nulljoin(res)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000374# end _unquote
375
376# The _getdate() routine is used to set the expiration time in
377# the cookie's HTTP header. By default, _getdate() returns the
Tim Peters88869f92001-01-14 23:36:06 +0000378# current time in the appropriate "expires" format for a
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000379# Set-Cookie header. The one optional argument is an offset from
380# now, in seconds. For example, an offset of -3600 means "one hour ago".
381# The offset may be a floating point number.
382#
383
384_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
385
386_monthname = [None,
387 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
388 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
389
390def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
391 from time import gmtime, time
392 now = time()
393 year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
394 return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
395 (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
396
397
398#
399# A class to hold ONE key,value pair.
400# In a cookie, each such pair may have several attributes.
401# so this class is used to keep the attributes associated
402# with the appropriate key,value pair.
403# This class also includes a coded_value attribute, which
404# is used to hold the network representation of the
405# value. This is most useful when Python objects are
406# pickled for network transit.
407#
408
409class Morsel(UserDict):
410 # RFC 2109 lists these attributes as reserved:
411 # path comment domain
412 # max-age secure version
Tim Peters88869f92001-01-14 23:36:06 +0000413 #
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000414 # For historical reasons, these attributes are also reserved:
415 # expires
416 #
417 # This dictionary provides a mapping from the lowercase
418 # variant on the left to the appropriate traditional
419 # formatting on the right.
420 _reserved = { "expires" : "expires",
421 "path" : "Path",
422 "comment" : "Comment",
423 "domain" : "Domain",
424 "max-age" : "Max-Age",
425 "secure" : "secure",
426 "version" : "Version",
427 }
428 _reserved_keys = _reserved.keys()
Fred Drakeff5364a2000-08-24 14:40:35 +0000429
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000430 def __init__(self):
431 # Set defaults
432 self.key = self.value = self.coded_value = None
433 UserDict.__init__(self)
434
435 # Set default attributes
436 for K in self._reserved_keys:
437 UserDict.__setitem__(self, K, "")
438 # end __init__
439
440 def __setitem__(self, K, V):
Fred Draked451ec12002-04-26 02:29:55 +0000441 K = K.lower()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000442 if not K in self._reserved_keys:
443 raise CookieError("Invalid Attribute %s" % K)
444 UserDict.__setitem__(self, K, V)
445 # end __setitem__
446
447 def isReservedKey(self, K):
Fred Draked451ec12002-04-26 02:29:55 +0000448 return K.lower() in self._reserved_keys
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000449 # end isReservedKey
450
451 def set(self, key, val, coded_val,
452 LegalChars=_LegalChars,
453 idmap=string._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
Fred Draked451ec12002-04-26 02:29:55 +0000456 if key.lower() in self._reserved_keys:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000457 raise CookieError("Attempt to set a reserved key: %s" % key)
458 if "" != translate(key, idmap, LegalChars):
459 raise CookieError("Illegal key value: %s" % key)
460
461 # It's a good key, so save it.
462 self.key = key
463 self.value = val
464 self.coded_value = coded_val
465 # end set
466
467 def output(self, attrs=None, header = "Set-Cookie:"):
468 return "%s %s" % ( header, self.OutputString(attrs) )
469
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000470 __str__ = output
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000471
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000472 def __repr__(self):
473 return '<%s: %s=%s>' % (self.__class__.__name__,
474 self.key, repr(self.value) )
Fred Drakeff5364a2000-08-24 14:40:35 +0000475
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000476 def js_output(self, attrs=None):
477 # Print javascript
478 return """
479 <SCRIPT LANGUAGE="JavaScript">
480 <!-- begin hiding
481 document.cookie = \"%s\"
482 // end hiding -->
483 </script>
484 """ % ( self.OutputString(attrs), )
485 # end js_output()
486
487 def OutputString(self, attrs=None):
488 # Build up our result
489 #
490 result = []
491 RA = result.append
Fred Drakeff5364a2000-08-24 14:40:35 +0000492
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000493 # First, the key=value pair
494 RA("%s=%s;" % (self.key, self.coded_value))
495
496 # Now add any defined attributes
Fred Drake8152d322000-12-12 23:20:45 +0000497 if attrs is None:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000498 attrs = self._reserved_keys
Tim Peters2f228e72001-05-13 00:19:31 +0000499 items = self.items()
500 items.sort()
501 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000502 if V == "": continue
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000503 if K not in attrs: continue
504 if K == "expires" and type(V) == type(1):
505 RA("%s=%s;" % (self._reserved[K], _getdate(V)))
506 elif K == "max-age" and type(V) == type(1):
507 RA("%s=%d;" % (self._reserved[K], V))
508 elif K == "secure":
509 RA("%s;" % self._reserved[K])
510 else:
511 RA("%s=%s;" % (self._reserved[K], V))
Fred Drakeff5364a2000-08-24 14:40:35 +0000512
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000513 # Return the result
Fred Draked451ec12002-04-26 02:29:55 +0000514 return _spacejoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000515 # end OutputString
516# end Morsel class
517
518
519
520#
521# Pattern for finding cookie
522#
523# This used to be strict parsing based on the RFC2109 and RFC2068
524# specifications. I have since discovered that MSIE 3.0x doesn't
525# follow the character rules outlined in those specs. As a
526# result, the parsing rules here are less strict.
527#
528
Andrew M. Kuchlingc05abb32001-02-20 22:11:24 +0000529_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000530_CookiePattern = re.compile(
531 r"(?x)" # This is a Verbose pattern
532 r"(?P<key>" # Start of group 'key'
Andrew M. Kuchlingc05abb32001-02-20 22:11:24 +0000533 ""+ _LegalCharsPatt +"+?" # Any word of at least one letter, nongreedy
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000534 r")" # End of group 'key'
535 r"\s*=\s*" # Equal Sign
536 r"(?P<val>" # Start of group 'val'
537 r'"(?:[^\\"]|\\.)*"' # Any doublequoted string
538 r"|" # or
539 ""+ _LegalCharsPatt +"*" # Any word or empty string
540 r")" # End of group 'val'
541 r"\s*;?" # Probably ending in a semi-colon
542 )
543
544
545# At long last, here is the cookie class.
546# Using this class is almost just like using a dictionary.
547# See this module's docstring for example usage.
548#
549class BaseCookie(UserDict):
550 # A container class for a set of Morsels
551 #
Fred Drakeff5364a2000-08-24 14:40:35 +0000552
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000553 def value_decode(self, val):
554 """real_value, coded_value = value_decode(STRING)
555 Called prior to setting a cookie's value from the network
556 representation. The VALUE is the value read from HTTP
557 header.
558 Override this function to modify the behavior of cookies.
559 """
560 return val, val
561 # end value_encode
562
563 def value_encode(self, val):
564 """real_value, coded_value = value_encode(VALUE)
565 Called prior to setting a cookie's value from the dictionary
566 representation. The VALUE is the value being assigned.
567 Override this function to modify the behavior of cookies.
568 """
569 strval = str(val)
570 return strval, strval
571 # end value_encode
Fred Drakeff5364a2000-08-24 14:40:35 +0000572
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000573 def __init__(self, input=None):
574 UserDict.__init__(self)
575 if input: self.load(input)
576 # end __init__
577
578 def __set(self, key, real_value, coded_value):
579 """Private method for setting a cookie's value"""
580 M = self.get(key, Morsel())
581 M.set(key, real_value, coded_value)
582 UserDict.__setitem__(self, key, M)
583 # end __set
584
585 def __setitem__(self, key, value):
586 """Dictionary style assignment."""
587 rval, cval = self.value_encode(value)
588 self.__set(key, rval, cval)
589 # end __setitem__
590
591 def output(self, attrs=None, header="Set-Cookie:", sep="\n"):
592 """Return a string suitable for HTTP."""
593 result = []
Tim Peters2f228e72001-05-13 00:19:31 +0000594 items = self.items()
595 items.sort()
596 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000597 result.append( V.output(attrs, header) )
Fred Draked451ec12002-04-26 02:29:55 +0000598 return sep.join(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000599 # end output
600
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000601 __str__ = output
602
603 def __repr__(self):
604 L = []
Tim Peters2f228e72001-05-13 00:19:31 +0000605 items = self.items()
606 items.sort()
607 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000608 L.append( '%s=%s' % (K,repr(V.value) ) )
Fred Draked451ec12002-04-26 02:29:55 +0000609 return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
Fred Drakeff5364a2000-08-24 14:40:35 +0000610
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000611 def js_output(self, attrs=None):
612 """Return a string suitable for JavaScript."""
613 result = []
Tim Peters2f228e72001-05-13 00:19:31 +0000614 items = self.items()
615 items.sort()
616 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000617 result.append( V.js_output(attrs) )
Fred Draked451ec12002-04-26 02:29:55 +0000618 return _nulljoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000619 # end js_output
620
621 def load(self, rawdata):
622 """Load cookies from a string (presumably HTTP_COOKIE) or
623 from a dictionary. Loading cookies from a dictionary 'd'
624 is equivalent to calling:
625 map(Cookie.__setitem__, d.keys(), d.values())
626 """
627 if type(rawdata) == type(""):
628 self.__ParseString(rawdata)
629 else:
630 self.update(rawdata)
631 return
632 # end load()
Fred Drakeff5364a2000-08-24 14:40:35 +0000633
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000634 def __ParseString(self, str, patt=_CookiePattern):
635 i = 0 # Our starting point
636 n = len(str) # Length of string
637 M = None # current morsel
638
639 while 0 <= i < n:
640 # Start looking for a cookie
641 match = patt.search(str, i)
642 if not match: break # No more cookies
643
644 K,V = match.group("key"), match.group("val")
645 i = match.end(0)
646
647 # Parse the key, value in case it's metainfo
648 if K[0] == "$":
649 # We ignore attributes which pertain to the cookie
650 # mechanism as a whole. See RFC 2109.
651 # (Does anyone care?)
652 if M:
653 M[ K[1:] ] = V
Fred Draked451ec12002-04-26 02:29:55 +0000654 elif K.lower() in Morsel._reserved_keys:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000655 if M:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000656 M[ K ] = _unquote(V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000657 else:
658 rval, cval = self.value_decode(V)
659 self.__set(K, rval, cval)
660 M = self[K]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000661 # end __ParseString
662# end BaseCookie class
663
664class SimpleCookie(BaseCookie):
665 """SimpleCookie
666 SimpleCookie supports strings as cookie values. When setting
667 the value using the dictionary assignment notation, SimpleCookie
668 calls the builtin str() to convert the value to a string. Values
669 received from HTTP are kept as strings.
670 """
671 def value_decode(self, val):
672 return _unquote( val ), val
673 def value_encode(self, val):
674 strval = str(val)
675 return strval, _quote( strval )
676# end SimpleCookie
677
678class SerialCookie(BaseCookie):
679 """SerialCookie
680 SerialCookie supports arbitrary objects as cookie values. All
681 values are serialized (using cPickle) before being sent to the
682 client. All incoming values are assumed to be valid Pickle
683 representations. IF AN INCOMING VALUE IS NOT IN A VALID PICKLE
684 FORMAT, THEN AN EXCEPTION WILL BE RAISED.
685
686 Note: Large cookie values add overhead because they must be
687 retransmitted on every HTTP transaction.
Fred Drakeff5364a2000-08-24 14:40:35 +0000688
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000689 Note: HTTP has a 2k limit on the size of a cookie. This class
690 does not check for this limit, so be careful!!!
691 """
692 def value_decode(self, val):
693 # This could raise an exception!
694 return loads( _unquote(val) ), val
695 def value_encode(self, val):
696 return val, _quote( dumps(val) )
697# end SerialCookie
698
699class SmartCookie(BaseCookie):
700 """SmartCookie
701 SmartCookie supports arbitrary objects as cookie values. If the
702 object is a string, then it is quoted. If the object is not a
703 string, however, then SmartCookie will use cPickle to serialize
704 the object into a string representation.
705
706 Note: Large cookie values add overhead because they must be
707 retransmitted on every HTTP transaction.
Fred Drakeff5364a2000-08-24 14:40:35 +0000708
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000709 Note: HTTP has a 2k limit on the size of a cookie. This class
710 does not check for this limit, so be careful!!!
711 """
712 def value_decode(self, val):
713 strval = _unquote(val)
714 try:
715 return loads(strval), val
716 except:
717 return strval, val
718 def value_encode(self, val):
719 if type(val) == type(""):
720 return val, _quote(val)
721 else:
722 return val, _quote( dumps(val) )
723# end SmartCookie
724
725
726###########################################################
727# Backwards Compatibility: Don't break any existing code!
728
729# We provide Cookie() as an alias for SmartCookie()
730Cookie = SmartCookie
731
732#
733###########################################################
734
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000735def _test():
736 import doctest, Cookie
737 return doctest.testmod(Cookie)
738
739if __name__ == "__main__":
740 _test()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000741
742
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000743#Local Variables:
744#tab-width: 4
745#end: