blob: a6ca6545b7da5811e8eaebb4867fb9aaa1161469 [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
Andrew M. Kuchling3c76ad02002-12-17 18:56:26 +000054three flavors, each with slightly different encoding semantics, but
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000055more 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
Walter Dörwaldf0dfc7a2003-10-20 14:01:56 +000079attributes by using the .output() function
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000080
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 +0000219
220try:
221 from cPickle import dumps, loads
222except ImportError:
223 from pickle import dumps, loads
224
Andrew M. Kuchling7877a762002-12-29 16:44:31 +0000225import re, warnings
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000226
Skip Montanaroe99d5ea2001-01-20 19:54:20 +0000227__all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie",
228 "SmartCookie","Cookie"]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000229
Fred Draked451ec12002-04-26 02:29:55 +0000230_nulljoin = ''.join
231_spacejoin = ' '.join
232
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000233#
234# Define an exception visible to External modules
235#
236class CookieError(Exception):
237 pass
238
239
240# These quoting routines conform to the RFC2109 specification, which in
241# turn references the character definitions from RFC2068. They provide
242# a two-way quoting algorithm. Any non-text character is translated
243# into a 4 character sequence: a forward-slash followed by the
244# three-digit octal equivalent of the character. Any '\' or '"' is
245# quoted with a preceeding '\' slash.
Tim Peters88869f92001-01-14 23:36:06 +0000246#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000247# These are taken from RFC2068 and RFC2109.
248# _LegalChars is the list of chars which don't require "'s
249# _Translator hash-table for fast quoting
250#
Fred Drake79e75e12001-07-20 19:05:50 +0000251_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~"
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000252_Translator = {
253 '\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
254 '\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
255 '\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
256 '\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
257 '\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
258 '\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
259 '\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
260 '\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
261 '\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
262 '\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
263 '\036' : '\\036', '\037' : '\\037',
264
265 '"' : '\\"', '\\' : '\\\\',
266
267 '\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
268 '\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
269 '\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
270 '\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
271 '\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
272 '\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
273 '\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
274 '\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
275 '\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
276 '\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
277 '\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
278 '\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
279 '\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
280 '\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
281 '\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
282 '\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
283 '\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
284 '\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
285 '\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
286 '\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
287 '\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
288 '\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
289 '\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
290 '\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
291 '\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
292 '\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
293 '\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
294 '\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
295 '\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
296 '\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
297 '\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
298 '\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
299 '\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
300 '\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
301 '\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
302 '\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
303 '\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
304 '\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
305 '\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
306 '\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
307 '\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
308 '\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
309 '\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
310 }
311
312def _quote(str, LegalChars=_LegalChars,
Fred Draked451ec12002-04-26 02:29:55 +0000313 idmap=string._idmap, translate=string.translate):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000314 #
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.
319 #
320 if "" == translate(str, idmap, LegalChars):
Fred Drakeff5364a2000-08-24 14:40:35 +0000321 return str
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000322 else:
Fred Draked451ec12002-04-26 02:29:55 +0000323 return '"' + _nulljoin( map(_Translator.get, str, str) ) + '"'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000324# end _quote
325
326
327_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
328_QuotePatt = re.compile(r"[\\].")
329
Fred Draked451ec12002-04-26 02:29:55 +0000330def _unquote(str):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000331 # If there aren't any doublequotes,
332 # then there can't be any special characters. See RFC 2109.
333 if len(str) < 2:
334 return str
335 if str[0] != '"' or str[-1] != '"':
336 return str
337
338 # We have to assume that we must decode this string.
339 # Down to work.
340
341 # Remove the "s
342 str = str[1:-1]
Fred Drakeff5364a2000-08-24 14:40:35 +0000343
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000344 # Check for special sequences. Examples:
345 # \012 --> \n
346 # \" --> "
347 #
348 i = 0
349 n = len(str)
350 res = []
351 while 0 <= i < n:
352 Omatch = _OctalPatt.search(str, i)
353 Qmatch = _QuotePatt.search(str, i)
354 if not Omatch and not Qmatch: # Neither matched
355 res.append(str[i:])
356 break
357 # else:
358 j = k = -1
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
362 res.append(str[i:k])
363 res.append(str[k+1])
364 i = k+2
365 else: # OctalPatt matched
366 res.append(str[i:j])
Fred Draked451ec12002-04-26 02:29:55 +0000367 res.append( chr( int(str[j+1:j+4], 8) ) )
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000368 i = j+4
Fred Draked451ec12002-04-26 02:29:55 +0000369 return _nulljoin(res)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000370# end _unquote
371
372# The _getdate() routine is used to set the expiration time in
373# the cookie's HTTP header. By default, _getdate() returns the
Tim Peters88869f92001-01-14 23:36:06 +0000374# current time in the appropriate "expires" format for a
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000375# 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.
378#
379
380_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
381
382_monthname = [None,
383 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
384 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
385
386def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
387 from time import gmtime, time
388 now = 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)
392
393
394#
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.
403#
404
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000405class Morsel(dict):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000406 # RFC 2109 lists these attributes as reserved:
407 # path comment domain
408 # max-age secure version
Tim Peters88869f92001-01-14 23:36:06 +0000409 #
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000410 # For historical reasons, these attributes are also reserved:
411 # expires
412 #
413 # This dictionary provides a mapping from the lowercase
414 # variant on the left to the appropriate traditional
415 # formatting on the right.
416 _reserved = { "expires" : "expires",
417 "path" : "Path",
418 "comment" : "Comment",
419 "domain" : "Domain",
420 "max-age" : "Max-Age",
421 "secure" : "secure",
422 "version" : "Version",
423 }
Fred Drakeff5364a2000-08-24 14:40:35 +0000424
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000425 def __init__(self):
426 # Set defaults
427 self.key = self.value = self.coded_value = None
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000428
429 # Set default attributes
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000430 for K in self._reserved:
431 dict.__setitem__(self, K, "")
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000432 # end __init__
433
434 def __setitem__(self, K, V):
Fred Draked451ec12002-04-26 02:29:55 +0000435 K = K.lower()
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000436 if not K in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000437 raise CookieError("Invalid Attribute %s" % K)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000438 dict.__setitem__(self, K, V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000439 # end __setitem__
440
441 def isReservedKey(self, K):
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000442 return K.lower() in self._reserved
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000443 # end isReservedKey
444
445 def set(self, key, val, coded_val,
446 LegalChars=_LegalChars,
447 idmap=string._idmap, translate=string.translate ):
448 # First we verify that the key isn't a reserved word
449 # Second we make sure it only contains legal characters
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000450 if key.lower() in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000451 raise CookieError("Attempt to set a reserved key: %s" % key)
452 if "" != translate(key, idmap, LegalChars):
453 raise CookieError("Illegal key value: %s" % key)
454
455 # It's a good key, so save it.
456 self.key = key
457 self.value = val
458 self.coded_value = coded_val
459 # end set
460
461 def output(self, attrs=None, header = "Set-Cookie:"):
462 return "%s %s" % ( header, self.OutputString(attrs) )
463
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000464 __str__ = output
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000465
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000466 def __repr__(self):
467 return '<%s: %s=%s>' % (self.__class__.__name__,
468 self.key, repr(self.value) )
Fred Drakeff5364a2000-08-24 14:40:35 +0000469
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000470 def js_output(self, attrs=None):
471 # Print javascript
472 return """
473 <SCRIPT LANGUAGE="JavaScript">
474 <!-- begin hiding
475 document.cookie = \"%s\"
476 // end hiding -->
477 </script>
478 """ % ( self.OutputString(attrs), )
479 # end js_output()
480
481 def OutputString(self, attrs=None):
482 # Build up our result
483 #
484 result = []
485 RA = result.append
Fred Drakeff5364a2000-08-24 14:40:35 +0000486
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000487 # First, the key=value pair
488 RA("%s=%s;" % (self.key, self.coded_value))
489
490 # Now add any defined attributes
Fred Drake8152d322000-12-12 23:20:45 +0000491 if attrs is None:
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000492 attrs = self._reserved
Tim Peters2f228e72001-05-13 00:19:31 +0000493 items = self.items()
494 items.sort()
495 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000496 if V == "": continue
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000497 if K not in attrs: continue
498 if K == "expires" and type(V) == type(1):
499 RA("%s=%s;" % (self._reserved[K], _getdate(V)))
500 elif K == "max-age" and type(V) == type(1):
501 RA("%s=%d;" % (self._reserved[K], V))
502 elif K == "secure":
503 RA("%s;" % self._reserved[K])
504 else:
505 RA("%s=%s;" % (self._reserved[K], V))
Fred Drakeff5364a2000-08-24 14:40:35 +0000506
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000507 # Return the result
Fred Draked451ec12002-04-26 02:29:55 +0000508 return _spacejoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000509 # end OutputString
510# end Morsel class
511
512
513
514#
515# Pattern for finding cookie
516#
517# This used to be strict parsing based on the RFC2109 and RFC2068
518# specifications. I have since discovered that MSIE 3.0x doesn't
519# follow the character rules outlined in those specs. As a
520# result, the parsing rules here are less strict.
521#
522
Andrew M. Kuchlingc05abb32001-02-20 22:11:24 +0000523_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000524_CookiePattern = re.compile(
525 r"(?x)" # This is a Verbose pattern
526 r"(?P<key>" # Start of group 'key'
Andrew M. Kuchlingc05abb32001-02-20 22:11:24 +0000527 ""+ _LegalCharsPatt +"+?" # Any word of at least one letter, nongreedy
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000528 r")" # End of group 'key'
529 r"\s*=\s*" # Equal Sign
530 r"(?P<val>" # Start of group 'val'
531 r'"(?:[^\\"]|\\.)*"' # Any doublequoted string
532 r"|" # or
533 ""+ _LegalCharsPatt +"*" # Any word or empty string
534 r")" # End of group 'val'
535 r"\s*;?" # Probably ending in a semi-colon
536 )
537
538
539# At long last, here is the cookie class.
540# Using this class is almost just like using a dictionary.
541# See this module's docstring for example usage.
542#
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000543class BaseCookie(dict):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000544 # A container class for a set of Morsels
545 #
Fred Drakeff5364a2000-08-24 14:40:35 +0000546
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000547 def value_decode(self, val):
548 """real_value, coded_value = value_decode(STRING)
549 Called prior to setting a cookie's value from the network
550 representation. The VALUE is the value read from HTTP
551 header.
552 Override this function to modify the behavior of cookies.
553 """
554 return val, val
555 # end value_encode
556
557 def value_encode(self, val):
558 """real_value, coded_value = value_encode(VALUE)
559 Called prior to setting a cookie's value from the dictionary
560 representation. The VALUE is the value being assigned.
561 Override this function to modify the behavior of cookies.
562 """
563 strval = str(val)
564 return strval, strval
565 # end value_encode
Fred Drakeff5364a2000-08-24 14:40:35 +0000566
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000567 def __init__(self, input=None):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000568 if input: self.load(input)
569 # end __init__
570
571 def __set(self, key, real_value, coded_value):
572 """Private method for setting a cookie's value"""
573 M = self.get(key, Morsel())
574 M.set(key, real_value, coded_value)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000575 dict.__setitem__(self, key, M)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000576 # end __set
577
578 def __setitem__(self, key, value):
579 """Dictionary style assignment."""
580 rval, cval = self.value_encode(value)
581 self.__set(key, rval, cval)
582 # end __setitem__
583
584 def output(self, attrs=None, header="Set-Cookie:", sep="\n"):
585 """Return a string suitable for HTTP."""
586 result = []
Tim Peters2f228e72001-05-13 00:19:31 +0000587 items = self.items()
588 items.sort()
589 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000590 result.append( V.output(attrs, header) )
Fred Draked451ec12002-04-26 02:29:55 +0000591 return sep.join(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000592 # end output
593
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000594 __str__ = output
595
596 def __repr__(self):
597 L = []
Tim Peters2f228e72001-05-13 00:19:31 +0000598 items = self.items()
599 items.sort()
600 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000601 L.append( '%s=%s' % (K,repr(V.value) ) )
Fred Draked451ec12002-04-26 02:29:55 +0000602 return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
Fred Drakeff5364a2000-08-24 14:40:35 +0000603
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000604 def js_output(self, attrs=None):
605 """Return a string suitable for JavaScript."""
606 result = []
Tim Peters2f228e72001-05-13 00:19:31 +0000607 items = self.items()
608 items.sort()
609 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000610 result.append( V.js_output(attrs) )
Fred Draked451ec12002-04-26 02:29:55 +0000611 return _nulljoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000612 # end js_output
613
614 def load(self, rawdata):
615 """Load cookies from a string (presumably HTTP_COOKIE) or
616 from a dictionary. Loading cookies from a dictionary 'd'
617 is equivalent to calling:
618 map(Cookie.__setitem__, d.keys(), d.values())
619 """
620 if type(rawdata) == type(""):
621 self.__ParseString(rawdata)
622 else:
623 self.update(rawdata)
624 return
625 # end load()
Fred Drakeff5364a2000-08-24 14:40:35 +0000626
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000627 def __ParseString(self, str, patt=_CookiePattern):
628 i = 0 # Our starting point
629 n = len(str) # Length of string
630 M = None # current morsel
631
632 while 0 <= i < n:
633 # Start looking for a cookie
634 match = patt.search(str, i)
635 if not match: break # No more cookies
636
637 K,V = match.group("key"), match.group("val")
638 i = match.end(0)
639
640 # Parse the key, value in case it's metainfo
641 if K[0] == "$":
642 # We ignore attributes which pertain to the cookie
643 # mechanism as a whole. See RFC 2109.
644 # (Does anyone care?)
645 if M:
646 M[ K[1:] ] = V
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000647 elif K.lower() in Morsel._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000648 if M:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000649 M[ K ] = _unquote(V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000650 else:
651 rval, cval = self.value_decode(V)
652 self.__set(K, rval, cval)
653 M = self[K]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000654 # end __ParseString
655# end BaseCookie class
656
657class SimpleCookie(BaseCookie):
658 """SimpleCookie
659 SimpleCookie supports strings as cookie values. When setting
660 the value using the dictionary assignment notation, SimpleCookie
661 calls the builtin str() to convert the value to a string. Values
662 received from HTTP are kept as strings.
663 """
664 def value_decode(self, val):
665 return _unquote( val ), val
666 def value_encode(self, val):
667 strval = str(val)
668 return strval, _quote( strval )
669# end SimpleCookie
670
671class SerialCookie(BaseCookie):
672 """SerialCookie
673 SerialCookie supports arbitrary objects as cookie values. All
674 values are serialized (using cPickle) before being sent to the
675 client. All incoming values are assumed to be valid Pickle
676 representations. IF AN INCOMING VALUE IS NOT IN A VALID PICKLE
677 FORMAT, THEN AN EXCEPTION WILL BE RAISED.
678
679 Note: Large cookie values add overhead because they must be
680 retransmitted on every HTTP transaction.
Fred Drakeff5364a2000-08-24 14:40:35 +0000681
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000682 Note: HTTP has a 2k limit on the size of a cookie. This class
683 does not check for this limit, so be careful!!!
684 """
Andrew M. Kuchling7877a762002-12-29 16:44:31 +0000685 def __init__(self, input=None):
686 warnings.warn("SerialCookie class is insecure; do not use it",
687 DeprecationWarning)
688 BaseCookie.__init__(self, input)
689 # end __init__
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000690 def value_decode(self, val):
691 # This could raise an exception!
692 return loads( _unquote(val) ), val
693 def value_encode(self, val):
694 return val, _quote( dumps(val) )
695# end SerialCookie
696
697class SmartCookie(BaseCookie):
698 """SmartCookie
699 SmartCookie supports arbitrary objects as cookie values. If the
700 object is a string, then it is quoted. If the object is not a
701 string, however, then SmartCookie will use cPickle to serialize
702 the object into a string representation.
703
704 Note: Large cookie values add overhead because they must be
705 retransmitted on every HTTP transaction.
Fred Drakeff5364a2000-08-24 14:40:35 +0000706
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000707 Note: HTTP has a 2k limit on the size of a cookie. This class
708 does not check for this limit, so be careful!!!
709 """
Andrew M. Kuchling7877a762002-12-29 16:44:31 +0000710 def __init__(self, input=None):
711 warnings.warn("Cookie/SmartCookie class is insecure; do not use it",
712 DeprecationWarning)
713 BaseCookie.__init__(self, input)
714 # end __init__
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000715 def value_decode(self, val):
716 strval = _unquote(val)
717 try:
718 return loads(strval), val
719 except:
720 return strval, val
721 def value_encode(self, val):
722 if type(val) == type(""):
723 return val, _quote(val)
724 else:
725 return val, _quote( dumps(val) )
726# end SmartCookie
727
728
729###########################################################
730# Backwards Compatibility: Don't break any existing code!
731
732# We provide Cookie() as an alias for SmartCookie()
733Cookie = SmartCookie
734
735#
736###########################################################
737
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000738def _test():
739 import doctest, Cookie
740 return doctest.testmod(Cookie)
741
742if __name__ == "__main__":
743 _test()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000744
745
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000746#Local Variables:
747#tab-width: 4
748#end: