blob: 0d9e6d0e3ad21d6a3fc097a2a2d3111e80e3fcf4 [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
Georg Brandl24420152008-05-26 16:32:26 +000051 >>> from http import cookies
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000052
Georg Brandl61013952008-05-28 15:56:30 +000053Most of the time you start by creating a cookie.
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000054
Georg Brandl24420152008-05-26 16:32:26 +000055 >>> C = cookies.SimpleCookie()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000056
57Once you've created your Cookie, you can add values just as if it were
58a dictionary.
59
Georg Brandl61013952008-05-28 15:56:30 +000060 >>> C = cookies.SimpleCookie()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000061 >>> C["fig"] = "newton"
62 >>> C["sugar"] = "wafer"
Georg Brandl532efab2005-08-24 22:34:21 +000063 >>> C.output()
64 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000065
66Notice that the printable representation of a Cookie is the
67appropriate format for a Set-Cookie: header. This is the
68default behavior. You can change the header and printed
Walter Dörwaldf0dfc7a2003-10-20 14:01:56 +000069attributes by using the .output() function
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000070
Georg Brandl61013952008-05-28 15:56:30 +000071 >>> C = cookies.SimpleCookie()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000072 >>> C["rocky"] = "road"
73 >>> C["rocky"]["path"] = "/cookie"
Guido van Rossumfff80df2007-02-09 20:33:44 +000074 >>> print(C.output(header="Cookie:"))
Georg Brandl532efab2005-08-24 22:34:21 +000075 Cookie: rocky=road; Path=/cookie
Guido van Rossumfff80df2007-02-09 20:33:44 +000076 >>> print(C.output(attrs=[], header="Cookie:"))
Georg Brandl532efab2005-08-24 22:34:21 +000077 Cookie: rocky=road
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000078
79The load() method of a Cookie extracts cookies from a string. In a
80CGI script, you would use this method to extract the cookies from the
81HTTP_COOKIE environment variable.
82
Georg Brandl61013952008-05-28 15:56:30 +000083 >>> C = cookies.SimpleCookie()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000084 >>> C.load("chips=ahoy; vienna=finger")
Georg Brandl532efab2005-08-24 22:34:21 +000085 >>> C.output()
86 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000087
88The load() method is darn-tootin smart about identifying cookies
89within a string. Escaped quotation marks, nested semicolons, and other
90such trickeries do not confuse it.
91
Georg Brandl61013952008-05-28 15:56:30 +000092 >>> C = cookies.SimpleCookie()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000093 >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
Guido van Rossumfff80df2007-02-09 20:33:44 +000094 >>> print(C)
Georg Brandl532efab2005-08-24 22:34:21 +000095 Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +000096
97Each element of the Cookie also supports all of the RFC 2109
98Cookie attributes. Here's an example which sets the Path
99attribute.
100
Georg Brandl61013952008-05-28 15:56:30 +0000101 >>> C = cookies.SimpleCookie()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000102 >>> C["oreo"] = "doublestuff"
103 >>> C["oreo"]["path"] = "/"
Guido van Rossumfff80df2007-02-09 20:33:44 +0000104 >>> print(C)
Georg Brandl532efab2005-08-24 22:34:21 +0000105 Set-Cookie: oreo=doublestuff; Path=/
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000106
107Each dictionary element has a 'value' attribute, which gives you
Tim Peters88869f92001-01-14 23:36:06 +0000108back the value associated with the key.
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000109
Georg Brandl61013952008-05-28 15:56:30 +0000110 >>> C = cookies.SimpleCookie()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000111 >>> C["twix"] = "none for you"
112 >>> C["twix"].value
113 'none for you'
114
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000115The SimpleCookie expects that all values should be standard strings.
116Just to be sure, SimpleCookie invokes the str() builtin to convert
117the value to a string, when the values are set dictionary-style.
118
Georg Brandl24420152008-05-26 16:32:26 +0000119 >>> C = cookies.SimpleCookie()
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000120 >>> C["number"] = 7
121 >>> C["string"] = "seven"
122 >>> C["number"].value
123 '7'
124 >>> C["string"].value
125 'seven'
Georg Brandl532efab2005-08-24 22:34:21 +0000126 >>> C.output()
127 'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000128
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000129Finis.
130""" #"
131# ^
132# |----helps out font-lock
133
134#
135# Import our required modules
Tim Peters88869f92001-01-14 23:36:06 +0000136#
Martin v. Löwis02d893c2001-08-02 07:15:29 +0000137import string
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000138
Guido van Rossum99603b02007-07-20 00:22:32 +0000139from pickle import dumps, loads
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000140
Andrew M. Kuchling7877a762002-12-29 16:44:31 +0000141import re, warnings
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000142
Georg Brandl61013952008-05-28 15:56:30 +0000143__all__ = ["CookieError", "BaseCookie", "SimpleCookie"]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000144
Fred Draked451ec12002-04-26 02:29:55 +0000145_nulljoin = ''.join
Georg Brandl532efab2005-08-24 22:34:21 +0000146_semispacejoin = '; '.join
Georg Brandl8246c432005-08-25 07:32:42 +0000147_spacejoin = ' '.join
Fred Draked451ec12002-04-26 02:29:55 +0000148
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000149#
150# Define an exception visible to External modules
151#
152class CookieError(Exception):
153 pass
154
155
156# These quoting routines conform to the RFC2109 specification, which in
157# turn references the character definitions from RFC2068. They provide
158# a two-way quoting algorithm. Any non-text character is translated
159# into a 4 character sequence: a forward-slash followed by the
160# three-digit octal equivalent of the character. Any '\' or '"' is
161# quoted with a preceeding '\' slash.
Tim Peters88869f92001-01-14 23:36:06 +0000162#
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000163# These are taken from RFC2068 and RFC2109.
164# _LegalChars is the list of chars which don't require "'s
165# _Translator hash-table for fast quoting
166#
Fred Drake79e75e12001-07-20 19:05:50 +0000167_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~"
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000168_Translator = {
169 '\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
170 '\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
171 '\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
172 '\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
173 '\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
174 '\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
175 '\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
176 '\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
177 '\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
178 '\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
179 '\036' : '\\036', '\037' : '\\037',
180
R. David Murraydaa7ba02010-12-28 18:56:33 +0000181 # Because of the way browsers really handle cookies (as opposed
182 # to what the RFC says) we also encode , and ;
183
184 ',' : '\\054', ';' : '\\073',
185
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000186 '"' : '\\"', '\\' : '\\\\',
187
188 '\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
189 '\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
190 '\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
191 '\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
192 '\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
193 '\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
194 '\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
195 '\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
196 '\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
197 '\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
198 '\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
199 '\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
200 '\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
201 '\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
202 '\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
203 '\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
204 '\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
205 '\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
206 '\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
207 '\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
208 '\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
209 '\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
210 '\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
211 '\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
212 '\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
213 '\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
214 '\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
215 '\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
216 '\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
217 '\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
218 '\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
219 '\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
220 '\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
221 '\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
222 '\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
223 '\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
224 '\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
225 '\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
226 '\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
227 '\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
228 '\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
229 '\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
230 '\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
231 }
232
Walter Dörwald3f1e65c2007-06-08 15:33:46 +0000233def _quote(str, LegalChars=_LegalChars):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000234 #
235 # If the string does not need to be double-quoted,
236 # then just return the string. Otherwise, surround
237 # the string in doublequotes and precede quote (with a \)
238 # special characters.
239 #
Guido van Rossum0c41e882007-07-03 16:46:40 +0000240 if all(c in LegalChars for c in str):
Fred Drakeff5364a2000-08-24 14:40:35 +0000241 return str
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000242 else:
Fred Draked451ec12002-04-26 02:29:55 +0000243 return '"' + _nulljoin( map(_Translator.get, str, str) ) + '"'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000244# end _quote
245
246
247_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
248_QuotePatt = re.compile(r"[\\].")
249
Fred Draked451ec12002-04-26 02:29:55 +0000250def _unquote(str):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000251 # If there aren't any doublequotes,
252 # then there can't be any special characters. See RFC 2109.
253 if len(str) < 2:
254 return str
255 if str[0] != '"' or str[-1] != '"':
256 return str
257
258 # We have to assume that we must decode this string.
259 # Down to work.
260
261 # Remove the "s
262 str = str[1:-1]
Fred Drakeff5364a2000-08-24 14:40:35 +0000263
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000264 # Check for special sequences. Examples:
265 # \012 --> \n
266 # \" --> "
267 #
268 i = 0
269 n = len(str)
270 res = []
271 while 0 <= i < n:
272 Omatch = _OctalPatt.search(str, i)
273 Qmatch = _QuotePatt.search(str, i)
274 if not Omatch and not Qmatch: # Neither matched
275 res.append(str[i:])
276 break
277 # else:
278 j = k = -1
279 if Omatch: j = Omatch.start(0)
280 if Qmatch: k = Qmatch.start(0)
281 if Qmatch and ( not Omatch or k < j ): # QuotePatt matched
282 res.append(str[i:k])
283 res.append(str[k+1])
284 i = k+2
285 else: # OctalPatt matched
286 res.append(str[i:j])
Fred Draked451ec12002-04-26 02:29:55 +0000287 res.append( chr( int(str[j+1:j+4], 8) ) )
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000288 i = j+4
Fred Draked451ec12002-04-26 02:29:55 +0000289 return _nulljoin(res)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000290# end _unquote
291
292# The _getdate() routine is used to set the expiration time in
293# the cookie's HTTP header. By default, _getdate() returns the
Tim Peters88869f92001-01-14 23:36:06 +0000294# current time in the appropriate "expires" format for a
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000295# Set-Cookie header. The one optional argument is an offset from
296# now, in seconds. For example, an offset of -3600 means "one hour ago".
297# The offset may be a floating point number.
298#
299
300_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
301
302_monthname = [None,
303 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
304 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
305
306def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
307 from time import gmtime, time
308 now = time()
309 year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
310 return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
311 (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
312
313
314#
315# A class to hold ONE key,value pair.
316# In a cookie, each such pair may have several attributes.
317# so this class is used to keep the attributes associated
318# with the appropriate key,value pair.
319# This class also includes a coded_value attribute, which
320# is used to hold the network representation of the
321# value. This is most useful when Python objects are
322# pickled for network transit.
323#
324
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000325class Morsel(dict):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000326 # RFC 2109 lists these attributes as reserved:
327 # path comment domain
328 # max-age secure version
Tim Peters88869f92001-01-14 23:36:06 +0000329 #
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000330 # For historical reasons, these attributes are also reserved:
331 # expires
332 #
Benjamin Peterson35e661c2008-09-06 19:37:35 +0000333 # This is an extension from Microsoft:
334 # httponly
335 #
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000336 # This dictionary provides a mapping from the lowercase
337 # variant on the left to the appropriate traditional
338 # formatting on the right.
339 _reserved = { "expires" : "expires",
340 "path" : "Path",
341 "comment" : "Comment",
342 "domain" : "Domain",
343 "max-age" : "Max-Age",
344 "secure" : "secure",
Benjamin Peterson35e661c2008-09-06 19:37:35 +0000345 "httponly" : "httponly",
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000346 "version" : "Version",
347 }
Fred Drakeff5364a2000-08-24 14:40:35 +0000348
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000349 def __init__(self):
350 # Set defaults
351 self.key = self.value = self.coded_value = None
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000352
353 # Set default attributes
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000354 for K in self._reserved:
355 dict.__setitem__(self, K, "")
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000356 # end __init__
357
358 def __setitem__(self, K, V):
Fred Draked451ec12002-04-26 02:29:55 +0000359 K = K.lower()
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000360 if not K in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000361 raise CookieError("Invalid Attribute %s" % K)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000362 dict.__setitem__(self, K, V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000363 # end __setitem__
364
365 def isReservedKey(self, K):
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000366 return K.lower() in self._reserved
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000367 # end isReservedKey
368
Walter Dörwald3f1e65c2007-06-08 15:33:46 +0000369 def set(self, key, val, coded_val, LegalChars=_LegalChars):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000370 # First we verify that the key isn't a reserved word
371 # Second we make sure it only contains legal characters
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000372 if key.lower() in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000373 raise CookieError("Attempt to set a reserved key: %s" % key)
Guido van Rossum0c41e882007-07-03 16:46:40 +0000374 if any(c not in LegalChars for c in key):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000375 raise CookieError("Illegal key value: %s" % key)
376
377 # It's a good key, so save it.
378 self.key = key
379 self.value = val
380 self.coded_value = coded_val
381 # end set
382
383 def output(self, attrs=None, header = "Set-Cookie:"):
384 return "%s %s" % ( header, self.OutputString(attrs) )
385
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000386 __str__ = output
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000387
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000388 def __repr__(self):
389 return '<%s: %s=%s>' % (self.__class__.__name__,
390 self.key, repr(self.value) )
Fred Drakeff5364a2000-08-24 14:40:35 +0000391
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000392 def js_output(self, attrs=None):
393 # Print javascript
394 return """
Georg Brandl03a33ea2005-06-26 21:02:49 +0000395 <script type="text/javascript">
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000396 <!-- begin hiding
Georg Brandl03a33ea2005-06-26 21:02:49 +0000397 document.cookie = \"%s\";
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000398 // end hiding -->
399 </script>
Senthil Kumaran3e2ea792009-04-02 03:02:03 +0000400 """ % ( self.OutputString(attrs).replace('"',r'\"'))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000401 # end js_output()
402
403 def OutputString(self, attrs=None):
404 # Build up our result
405 #
406 result = []
407 RA = result.append
Fred Drakeff5364a2000-08-24 14:40:35 +0000408
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000409 # First, the key=value pair
Georg Brandl532efab2005-08-24 22:34:21 +0000410 RA("%s=%s" % (self.key, self.coded_value))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000411
412 # Now add any defined attributes
Fred Drake8152d322000-12-12 23:20:45 +0000413 if attrs is None:
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000414 attrs = self._reserved
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000415 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000416 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000417 if V == "": continue
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000418 if K not in attrs: continue
419 if K == "expires" and type(V) == type(1):
Georg Brandl532efab2005-08-24 22:34:21 +0000420 RA("%s=%s" % (self._reserved[K], _getdate(V)))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000421 elif K == "max-age" and type(V) == type(1):
Georg Brandl532efab2005-08-24 22:34:21 +0000422 RA("%s=%d" % (self._reserved[K], V))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000423 elif K == "secure":
Georg Brandl532efab2005-08-24 22:34:21 +0000424 RA(str(self._reserved[K]))
Benjamin Peterson35e661c2008-09-06 19:37:35 +0000425 elif K == "httponly":
426 RA(str(self._reserved[K]))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000427 else:
Georg Brandl532efab2005-08-24 22:34:21 +0000428 RA("%s=%s" % (self._reserved[K], V))
Fred Drakeff5364a2000-08-24 14:40:35 +0000429
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000430 # Return the result
Georg Brandl532efab2005-08-24 22:34:21 +0000431 return _semispacejoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000432 # end OutputString
433# end Morsel class
434
435
436
437#
438# Pattern for finding cookie
439#
440# This used to be strict parsing based on the RFC2109 and RFC2068
441# specifications. I have since discovered that MSIE 3.0x doesn't
442# follow the character rules outlined in those specs. As a
443# result, the parsing rules here are less strict.
444#
445
Andrew M. Kuchlingc05abb32001-02-20 22:11:24 +0000446_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
Georg Brandlcea7e552010-08-01 18:56:30 +0000447_CookiePattern = re.compile(r"""
448 (?x) # This is a verbose pattern
449 (?P<key> # Start of group 'key'
450 """ + _LegalCharsPatt + r"""+? # Any word of at least one letter
451 ) # End of group 'key'
452 \s*=\s* # Equal Sign
453 (?P<val> # Start of group 'val'
454 "(?:[^\\"]|\\.)*" # Any doublequoted string
455 | # or
456 \w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGMT # Special case for "expires" attr
457 | # or
458 """ + _LegalCharsPatt + r"""* # Any word or empty string
459 ) # End of group 'val'
460 \s*;? # Probably ending in a semi-colon
461 """, re.ASCII) # May be removed if safe.
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000462
463
464# At long last, here is the cookie class.
465# Using this class is almost just like using a dictionary.
466# See this module's docstring for example usage.
467#
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000468class BaseCookie(dict):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000469 # A container class for a set of Morsels
470 #
Fred Drakeff5364a2000-08-24 14:40:35 +0000471
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000472 def value_decode(self, val):
473 """real_value, coded_value = value_decode(STRING)
474 Called prior to setting a cookie's value from the network
475 representation. The VALUE is the value read from HTTP
476 header.
477 Override this function to modify the behavior of cookies.
478 """
479 return val, val
480 # end value_encode
481
482 def value_encode(self, val):
483 """real_value, coded_value = value_encode(VALUE)
484 Called prior to setting a cookie's value from the dictionary
485 representation. The VALUE is the value being assigned.
486 Override this function to modify the behavior of cookies.
487 """
488 strval = str(val)
489 return strval, strval
490 # end value_encode
Fred Drakeff5364a2000-08-24 14:40:35 +0000491
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000492 def __init__(self, input=None):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000493 if input: self.load(input)
494 # end __init__
495
496 def __set(self, key, real_value, coded_value):
497 """Private method for setting a cookie's value"""
498 M = self.get(key, Morsel())
499 M.set(key, real_value, coded_value)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000500 dict.__setitem__(self, key, M)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000501 # end __set
502
503 def __setitem__(self, key, value):
504 """Dictionary style assignment."""
505 rval, cval = self.value_encode(value)
506 self.__set(key, rval, cval)
507 # end __setitem__
508
Georg Brandl532efab2005-08-24 22:34:21 +0000509 def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000510 """Return a string suitable for HTTP."""
511 result = []
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000512 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000513 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000514 result.append( V.output(attrs, header) )
Fred Draked451ec12002-04-26 02:29:55 +0000515 return sep.join(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000516 # end output
517
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000518 __str__ = output
519
520 def __repr__(self):
521 L = []
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000522 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000523 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000524 L.append( '%s=%s' % (K,repr(V.value) ) )
Georg Brandl8246c432005-08-25 07:32:42 +0000525 return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
Fred Drakeff5364a2000-08-24 14:40:35 +0000526
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000527 def js_output(self, attrs=None):
528 """Return a string suitable for JavaScript."""
529 result = []
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000530 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000531 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000532 result.append( V.js_output(attrs) )
Fred Draked451ec12002-04-26 02:29:55 +0000533 return _nulljoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000534 # end js_output
535
536 def load(self, rawdata):
537 """Load cookies from a string (presumably HTTP_COOKIE) or
538 from a dictionary. Loading cookies from a dictionary 'd'
539 is equivalent to calling:
540 map(Cookie.__setitem__, d.keys(), d.values())
541 """
542 if type(rawdata) == type(""):
543 self.__ParseString(rawdata)
544 else:
Benjamin Petersona8332062009-09-11 22:36:27 +0000545 # self.update() wouldn't call our custom __setitem__
546 for k, v in rawdata.items():
547 self[k] = v
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000548 return
549 # end load()
Fred Drakeff5364a2000-08-24 14:40:35 +0000550
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000551 def __ParseString(self, str, patt=_CookiePattern):
552 i = 0 # Our starting point
553 n = len(str) # Length of string
554 M = None # current morsel
555
556 while 0 <= i < n:
557 # Start looking for a cookie
558 match = patt.search(str, i)
559 if not match: break # No more cookies
560
561 K,V = match.group("key"), match.group("val")
562 i = match.end(0)
563
564 # Parse the key, value in case it's metainfo
565 if K[0] == "$":
566 # We ignore attributes which pertain to the cookie
567 # mechanism as a whole. See RFC 2109.
568 # (Does anyone care?)
569 if M:
570 M[ K[1:] ] = V
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000571 elif K.lower() in Morsel._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000572 if M:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000573 M[ K ] = _unquote(V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000574 else:
575 rval, cval = self.value_decode(V)
576 self.__set(K, rval, cval)
577 M = self[K]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000578 # end __ParseString
579# end BaseCookie class
580
581class SimpleCookie(BaseCookie):
582 """SimpleCookie
583 SimpleCookie supports strings as cookie values. When setting
584 the value using the dictionary assignment notation, SimpleCookie
585 calls the builtin str() to convert the value to a string. Values
586 received from HTTP are kept as strings.
587 """
588 def value_decode(self, val):
589 return _unquote( val ), val
590 def value_encode(self, val):
591 strval = str(val)
592 return strval, _quote( strval )
593# end SimpleCookie
594
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000595#
596###########################################################
597
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000598def _test():
Georg Brandl24420152008-05-26 16:32:26 +0000599 import doctest, http.cookies
600 return doctest.testmod(http.cookies)
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000601
602if __name__ == "__main__":
603 _test()