blob: 452d4e3c75cf74fe13c2d5ba2b877c9fe9407eff [file] [log] [blame]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +00001####
2# Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
Tim Peters88869f92001-01-14 23:36:06 +00003#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +00004# All Rights Reserved
Tim Peters88869f92001-01-14 23:36:06 +00005#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +00006# 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
Tim Peters88869f92001-01-14 23:36:06 +000013# prior permission.
14#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000015# 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
Tim Peters88869f92001-01-14 23:36:06 +000022# PERFORMANCE OF THIS SOFTWARE.
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000023#
24####
Tim Peters88869f92001-01-14 23:36:06 +000025#
26# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000027# by Timothy O'Malley <timo@alum.mit.edu>
28#
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.
32#
33# The original idea to treat Cookies as a dictionary came from
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +000034# Dave Mitchell (davem@magnet.com) in 1995, when he released the
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000035# first version of nscookie.py.
36#
37####
38
Guido van Rossum58b6f5b2001-04-06 19:39:11 +000039r"""
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000040Here's a sample session to show how to use this module.
41At the moment, this is the only documentation.
42
43The Basics
44----------
45
46Importing is easy..
47
48 >>> import Cookie
49
50Most of the time you start by creating a cookie. Cookies come in
Andrew M. Kuchling3c76ad02002-12-17 18:56:26 +000051three flavors, each with slightly different encoding semantics, but
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000052more on that later.
53
54 >>> C = Cookie.SimpleCookie()
55 >>> C = Cookie.SerialCookie()
56 >>> C = Cookie.SmartCookie()
57
58[Note: Long-time users of Cookie.py will remember using
59Cookie.Cookie() to create an Cookie object. Although deprecated, it
60is still supported by the code. See the Backward Compatibility notes
61for more information.]
62
63Once you've created your Cookie, you can add values just as if it were
64a dictionary.
65
66 >>> C = Cookie.SmartCookie()
67 >>> C["fig"] = "newton"
68 >>> C["sugar"] = "wafer"
Georg Brandl532efab2005-08-24 22:34:21 +000069 >>> C.output()
70 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000071
72Notice that the printable representation of a Cookie is the
73appropriate format for a Set-Cookie: header. This is the
74default behavior. You can change the header and printed
Walter Dörwaldf0dfc7a2003-10-20 14:01:56 +000075attributes by using the .output() function
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000076
77 >>> C = Cookie.SmartCookie()
78 >>> C["rocky"] = "road"
79 >>> C["rocky"]["path"] = "/cookie"
80 >>> print C.output(header="Cookie:")
Georg Brandl532efab2005-08-24 22:34:21 +000081 Cookie: rocky=road; Path=/cookie
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000082 >>> print C.output(attrs=[], header="Cookie:")
Georg Brandl532efab2005-08-24 22:34:21 +000083 Cookie: rocky=road
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000084
85The load() method of a Cookie extracts cookies from a string. In a
86CGI script, you would use this method to extract the cookies from the
87HTTP_COOKIE environment variable.
88
89 >>> C = Cookie.SmartCookie()
90 >>> C.load("chips=ahoy; vienna=finger")
Georg Brandl532efab2005-08-24 22:34:21 +000091 >>> C.output()
92 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000093
94The load() method is darn-tootin smart about identifying cookies
95within a string. Escaped quotation marks, nested semicolons, and other
96such trickeries do not confuse it.
97
98 >>> C = Cookie.SmartCookie()
99 >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000100 >>> print C
Georg Brandl532efab2005-08-24 22:34:21 +0000101 Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000102
103Each element of the Cookie also supports all of the RFC 2109
104Cookie attributes. Here's an example which sets the Path
105attribute.
106
107 >>> C = Cookie.SmartCookie()
108 >>> C["oreo"] = "doublestuff"
109 >>> C["oreo"]["path"] = "/"
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000110 >>> print C
Georg Brandl532efab2005-08-24 22:34:21 +0000111 Set-Cookie: oreo=doublestuff; Path=/
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000112
113Each dictionary element has a 'value' attribute, which gives you
Tim Peters88869f92001-01-14 23:36:06 +0000114back the value associated with the key.
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000115
116 >>> C = Cookie.SmartCookie()
117 >>> C["twix"] = "none for you"
118 >>> C["twix"].value
119 'none for you'
120
121
122A Bit More Advanced
123-------------------
124
125As mentioned before, there are three different flavors of Cookie
126objects, each with different encoding/decoding semantics. This
127section briefly discusses the differences.
128
129SimpleCookie
130
131The SimpleCookie expects that all values should be standard strings.
132Just to be sure, SimpleCookie invokes the str() builtin to convert
133the value to a string, when the values are set dictionary-style.
134
135 >>> C = Cookie.SimpleCookie()
136 >>> C["number"] = 7
137 >>> C["string"] = "seven"
138 >>> C["number"].value
139 '7'
140 >>> C["string"].value
141 'seven'
Georg Brandl532efab2005-08-24 22:34:21 +0000142 >>> C.output()
143 'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000144
Tim Peters88869f92001-01-14 23:36:06 +0000145
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000146SerialCookie
147
148The SerialCookie expects that all values should be serialized using
149cPickle (or pickle, if cPickle isn't available). As a result of
150serializing, SerialCookie can save almost any Python object to a
151value, and recover the exact same object when the cookie has been
152returned. (SerialCookie can yield some strange-looking cookie
153values, however.)
154
155 >>> C = Cookie.SerialCookie()
156 >>> C["number"] = 7
157 >>> C["string"] = "seven"
158 >>> C["number"].value
159 7
160 >>> C["string"].value
161 'seven'
Georg Brandl532efab2005-08-24 22:34:21 +0000162 >>> C.output()
163 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string="S\'seven\'\\012p1\\012."'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000164
165Be warned, however, if SerialCookie cannot de-serialize a value (because
166it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
167
168
169SmartCookie
170
171The SmartCookie combines aspects of each of the other two flavors.
172When setting a value in a dictionary-fashion, the SmartCookie will
173serialize (ala cPickle) the value *if and only if* it isn't a
174Python string. String objects are *not* serialized. Similarly,
175when the load() method parses out values, it attempts to de-serialize
176the value. If it fails, then it fallsback to treating the value
177as a string.
178
179 >>> C = Cookie.SmartCookie()
180 >>> C["number"] = 7
181 >>> C["string"] = "seven"
182 >>> C["number"].value
183 7
184 >>> C["string"].value
185 'seven'
Georg Brandl532efab2005-08-24 22:34:21 +0000186 >>> C.output()
187 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string=seven'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000188
189
190Backwards Compatibility
191-----------------------
192
193In order to keep compatibilty with earlier versions of Cookie.py,
194it is still possible to use Cookie.Cookie() to create a Cookie. In
195fact, this simply returns a SmartCookie.
196
197 >>> C = Cookie.Cookie()
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000198 >>> print C.__class__.__name__
199 SmartCookie
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000200
201
202Finis.
203""" #"
204# ^
205# |----helps out font-lock
206
207#
208# Import our required modules
Tim Peters88869f92001-01-14 23:36:06 +0000209#
Martin v. Löwis02d893c2001-08-02 07:15:29 +0000210import string
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000211
212try:
213 from cPickle import dumps, loads
214except ImportError:
215 from pickle import dumps, loads
216
Andrew M. Kuchling7877a762002-12-29 16:44:31 +0000217import re, warnings
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000218
Skip Montanaroe99d5ea2001-01-20 19:54:20 +0000219__all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie",
220 "SmartCookie","Cookie"]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000221
Fred Draked451ec12002-04-26 02:29:55 +0000222_nulljoin = ''.join
Georg Brandl532efab2005-08-24 22:34:21 +0000223_semispacejoin = '; '.join
Georg Brandl8246c432005-08-25 07:32:42 +0000224_spacejoin = ' '.join
Fred Draked451ec12002-04-26 02:29:55 +0000225
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000226#
227# Define an exception visible to External modules
228#
229class CookieError(Exception):
230 pass
231
232
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
Ezio Melottif5469cf2013-08-17 15:43:51 +0300238# quoted with a preceding '\' slash.
Tim Peters88869f92001-01-14 23:36:06 +0000239#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000240# 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
243#
Fred Drake79e75e12001-07-20 19:05:50 +0000244_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~"
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000245_Translator = {
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',
257
R. David Murray08fc7012010-12-28 19:11:03 +0000258 # Because of the way browsers really handle cookies (as opposed
259 # to what the RFC says) we also encode , and ;
260
261 ',' : '\\054', ';' : '\\073',
262
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000263 '"' : '\\"', '\\' : '\\\\',
264
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'
308 }
Tim Petersc02c1c82006-08-15 00:25:04 +0000309
Georg Brandld76bd692006-08-14 22:01:24 +0000310_idmap = ''.join(chr(x) for x in xrange(256))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000311
312def _quote(str, LegalChars=_LegalChars,
Georg Brandld76bd692006-08-14 22:01:24 +0000313 idmap=_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)
Senthil Kumaranf439a362012-05-20 12:02:44 +0800390 return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000391 (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 #
Benjamin Peterson6ac7d7c2008-09-06 19:28:11 +0000413 # This is an extension from Microsoft:
414 # httponly
415 #
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000416 # 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",
420 "path" : "Path",
421 "comment" : "Comment",
422 "domain" : "Domain",
423 "max-age" : "Max-Age",
424 "secure" : "secure",
Benjamin Peterson6ac7d7c2008-09-06 19:28:11 +0000425 "httponly" : "httponly",
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000426 "version" : "Version",
427 }
Fred Drakeff5364a2000-08-24 14:40:35 +0000428
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000429 def __init__(self):
430 # Set defaults
431 self.key = self.value = self.coded_value = None
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000432
433 # Set default attributes
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000434 for K in self._reserved:
435 dict.__setitem__(self, K, "")
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000436 # end __init__
437
438 def __setitem__(self, K, V):
Fred Draked451ec12002-04-26 02:29:55 +0000439 K = K.lower()
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000440 if not K in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000441 raise CookieError("Invalid Attribute %s" % K)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000442 dict.__setitem__(self, K, V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000443 # end __setitem__
444
445 def isReservedKey(self, K):
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000446 return K.lower() in self._reserved
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000447 # end isReservedKey
448
449 def set(self, key, val, coded_val,
450 LegalChars=_LegalChars,
Georg Brandld76bd692006-08-14 22:01:24 +0000451 idmap=_idmap, translate=string.translate):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000452 # First we verify that the key isn't a reserved word
453 # Second we make sure it only contains legal characters
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000454 if key.lower() in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000455 raise CookieError("Attempt to set a reserved key: %s" % key)
456 if "" != translate(key, idmap, LegalChars):
457 raise CookieError("Illegal key value: %s" % key)
458
459 # It's a good key, so save it.
460 self.key = key
461 self.value = val
462 self.coded_value = coded_val
463 # end set
464
465 def output(self, attrs=None, header = "Set-Cookie:"):
466 return "%s %s" % ( header, self.OutputString(attrs) )
467
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000468 __str__ = output
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000469
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000470 def __repr__(self):
471 return '<%s: %s=%s>' % (self.__class__.__name__,
472 self.key, repr(self.value) )
Fred Drakeff5364a2000-08-24 14:40:35 +0000473
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000474 def js_output(self, attrs=None):
475 # Print javascript
476 return """
Georg Brandl03a33ea2005-06-26 21:02:49 +0000477 <script type="text/javascript">
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000478 <!-- begin hiding
Georg Brandl03a33ea2005-06-26 21:02:49 +0000479 document.cookie = \"%s\";
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000480 // end hiding -->
481 </script>
Senthil Kumaranc730a6a2009-04-02 03:00:34 +0000482 """ % ( self.OutputString(attrs).replace('"',r'\"'), )
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000483 # end js_output()
484
485 def OutputString(self, attrs=None):
486 # Build up our result
487 #
488 result = []
489 RA = result.append
Fred Drakeff5364a2000-08-24 14:40:35 +0000490
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000491 # First, the key=value pair
Georg Brandl532efab2005-08-24 22:34:21 +0000492 RA("%s=%s" % (self.key, self.coded_value))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000493
494 # Now add any defined attributes
Fred Drake8152d322000-12-12 23:20:45 +0000495 if attrs is None:
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000496 attrs = self._reserved
Tim Peters2f228e72001-05-13 00:19:31 +0000497 items = self.items()
498 items.sort()
499 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000500 if V == "": continue
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000501 if K not in attrs: continue
502 if K == "expires" and type(V) == type(1):
Georg Brandl532efab2005-08-24 22:34:21 +0000503 RA("%s=%s" % (self._reserved[K], _getdate(V)))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000504 elif K == "max-age" and type(V) == type(1):
Georg Brandl532efab2005-08-24 22:34:21 +0000505 RA("%s=%d" % (self._reserved[K], V))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000506 elif K == "secure":
Georg Brandl532efab2005-08-24 22:34:21 +0000507 RA(str(self._reserved[K]))
Benjamin Peterson6ac7d7c2008-09-06 19:28:11 +0000508 elif K == "httponly":
509 RA(str(self._reserved[K]))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000510 else:
Georg Brandl532efab2005-08-24 22:34:21 +0000511 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
Georg Brandl532efab2005-08-24 22:34:21 +0000514 return _semispacejoin(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
Senthil Kumaran9cffd882012-05-20 16:56:24 +0800539 r"\w{3},\s[\s\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr
Georg Brandl78e69572010-08-01 18:52:52 +0000540 r"|" # or
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000541 ""+ _LegalCharsPatt +"*" # Any word or empty string
542 r")" # End of group 'val'
543 r"\s*;?" # Probably ending in a semi-colon
544 )
545
546
547# At long last, here is the cookie class.
548# Using this class is almost just like using a dictionary.
549# See this module's docstring for example usage.
550#
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000551class BaseCookie(dict):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000552 # A container class for a set of Morsels
553 #
Fred Drakeff5364a2000-08-24 14:40:35 +0000554
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000555 def value_decode(self, val):
556 """real_value, coded_value = value_decode(STRING)
557 Called prior to setting a cookie's value from the network
558 representation. The VALUE is the value read from HTTP
559 header.
560 Override this function to modify the behavior of cookies.
561 """
562 return val, val
563 # end value_encode
564
565 def value_encode(self, val):
566 """real_value, coded_value = value_encode(VALUE)
567 Called prior to setting a cookie's value from the dictionary
568 representation. The VALUE is the value being assigned.
569 Override this function to modify the behavior of cookies.
570 """
571 strval = str(val)
572 return strval, strval
573 # end value_encode
Fred Drakeff5364a2000-08-24 14:40:35 +0000574
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000575 def __init__(self, input=None):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000576 if input: self.load(input)
577 # end __init__
578
579 def __set(self, key, real_value, coded_value):
580 """Private method for setting a cookie's value"""
581 M = self.get(key, Morsel())
582 M.set(key, real_value, coded_value)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000583 dict.__setitem__(self, key, M)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000584 # end __set
585
586 def __setitem__(self, key, value):
587 """Dictionary style assignment."""
588 rval, cval = self.value_encode(value)
589 self.__set(key, rval, cval)
590 # end __setitem__
591
Georg Brandl532efab2005-08-24 22:34:21 +0000592 def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000593 """Return a string suitable for HTTP."""
594 result = []
Tim Peters2f228e72001-05-13 00:19:31 +0000595 items = self.items()
596 items.sort()
597 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000598 result.append( V.output(attrs, header) )
Fred Draked451ec12002-04-26 02:29:55 +0000599 return sep.join(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000600 # end output
601
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000602 __str__ = output
603
604 def __repr__(self):
605 L = []
Tim Peters2f228e72001-05-13 00:19:31 +0000606 items = self.items()
607 items.sort()
608 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000609 L.append( '%s=%s' % (K,repr(V.value) ) )
Georg Brandl8246c432005-08-25 07:32:42 +0000610 return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
Fred Drakeff5364a2000-08-24 14:40:35 +0000611
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000612 def js_output(self, attrs=None):
613 """Return a string suitable for JavaScript."""
614 result = []
Tim Peters2f228e72001-05-13 00:19:31 +0000615 items = self.items()
616 items.sort()
617 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000618 result.append( V.js_output(attrs) )
Fred Draked451ec12002-04-26 02:29:55 +0000619 return _nulljoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000620 # end js_output
621
622 def load(self, rawdata):
623 """Load cookies from a string (presumably HTTP_COOKIE) or
624 from a dictionary. Loading cookies from a dictionary 'd'
625 is equivalent to calling:
626 map(Cookie.__setitem__, d.keys(), d.values())
627 """
628 if type(rawdata) == type(""):
629 self.__ParseString(rawdata)
630 else:
Georg Brandld22b9512009-09-04 08:17:04 +0000631 # self.update() wouldn't call our custom __setitem__
632 for k, v in rawdata.items():
633 self[k] = v
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000634 return
635 # end load()
Fred Drakeff5364a2000-08-24 14:40:35 +0000636
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000637 def __ParseString(self, str, patt=_CookiePattern):
638 i = 0 # Our starting point
639 n = len(str) # Length of string
640 M = None # current morsel
641
642 while 0 <= i < n:
643 # Start looking for a cookie
644 match = patt.search(str, i)
645 if not match: break # No more cookies
646
647 K,V = match.group("key"), match.group("val")
648 i = match.end(0)
649
650 # Parse the key, value in case it's metainfo
651 if K[0] == "$":
652 # We ignore attributes which pertain to the cookie
653 # mechanism as a whole. See RFC 2109.
654 # (Does anyone care?)
655 if M:
656 M[ K[1:] ] = V
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000657 elif K.lower() in Morsel._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000658 if M:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000659 M[ K ] = _unquote(V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000660 else:
661 rval, cval = self.value_decode(V)
662 self.__set(K, rval, cval)
663 M = self[K]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000664 # end __ParseString
665# end BaseCookie class
666
667class SimpleCookie(BaseCookie):
668 """SimpleCookie
669 SimpleCookie supports strings as cookie values. When setting
670 the value using the dictionary assignment notation, SimpleCookie
671 calls the builtin str() to convert the value to a string. Values
672 received from HTTP are kept as strings.
673 """
674 def value_decode(self, val):
675 return _unquote( val ), val
676 def value_encode(self, val):
677 strval = str(val)
678 return strval, _quote( strval )
679# end SimpleCookie
680
681class SerialCookie(BaseCookie):
682 """SerialCookie
683 SerialCookie supports arbitrary objects as cookie values. All
684 values are serialized (using cPickle) before being sent to the
685 client. All incoming values are assumed to be valid Pickle
686 representations. IF AN INCOMING VALUE IS NOT IN A VALID PICKLE
687 FORMAT, THEN AN EXCEPTION WILL BE RAISED.
688
689 Note: Large cookie values add overhead because they must be
690 retransmitted on every HTTP transaction.
Fred Drakeff5364a2000-08-24 14:40:35 +0000691
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000692 Note: HTTP has a 2k limit on the size of a cookie. This class
693 does not check for this limit, so be careful!!!
694 """
Andrew M. Kuchling7877a762002-12-29 16:44:31 +0000695 def __init__(self, input=None):
696 warnings.warn("SerialCookie class is insecure; do not use it",
697 DeprecationWarning)
698 BaseCookie.__init__(self, input)
699 # end __init__
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000700 def value_decode(self, val):
701 # This could raise an exception!
702 return loads( _unquote(val) ), val
703 def value_encode(self, val):
704 return val, _quote( dumps(val) )
705# end SerialCookie
706
707class SmartCookie(BaseCookie):
708 """SmartCookie
709 SmartCookie supports arbitrary objects as cookie values. If the
710 object is a string, then it is quoted. If the object is not a
711 string, however, then SmartCookie will use cPickle to serialize
712 the object into a string representation.
713
714 Note: Large cookie values add overhead because they must be
715 retransmitted on every HTTP transaction.
Fred Drakeff5364a2000-08-24 14:40:35 +0000716
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000717 Note: HTTP has a 2k limit on the size of a cookie. This class
718 does not check for this limit, so be careful!!!
719 """
Andrew M. Kuchling7877a762002-12-29 16:44:31 +0000720 def __init__(self, input=None):
721 warnings.warn("Cookie/SmartCookie class is insecure; do not use it",
722 DeprecationWarning)
723 BaseCookie.__init__(self, input)
724 # end __init__
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000725 def value_decode(self, val):
726 strval = _unquote(val)
727 try:
728 return loads(strval), val
729 except:
730 return strval, val
731 def value_encode(self, val):
732 if type(val) == type(""):
733 return val, _quote(val)
734 else:
735 return val, _quote( dumps(val) )
736# end SmartCookie
737
738
739###########################################################
740# Backwards Compatibility: Don't break any existing code!
741
742# We provide Cookie() as an alias for SmartCookie()
743Cookie = SmartCookie
744
745#
746###########################################################
747
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000748def _test():
749 import doctest, Cookie
750 return doctest.testmod(Cookie)
751
752if __name__ == "__main__":
753 _test()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000754
755
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000756#Local Variables:
757#tab-width: 4
758#end: