blob: 3242d8374ad77a7dc5af78b6450c15a5113cb40a [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
181 '"' : '\\"', '\\' : '\\\\',
182
183 '\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
184 '\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
185 '\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
186 '\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
187 '\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
188 '\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
189 '\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
190 '\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
191 '\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
192 '\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
193 '\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
194 '\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
195 '\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
196 '\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
197 '\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
198 '\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
199 '\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
200 '\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
201 '\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
202 '\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
203 '\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
204 '\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
205 '\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
206 '\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
207 '\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
208 '\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
209 '\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
210 '\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
211 '\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
212 '\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
213 '\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
214 '\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
215 '\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
216 '\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
217 '\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
218 '\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
219 '\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
220 '\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
221 '\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
222 '\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
223 '\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
224 '\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
225 '\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
226 }
227
Walter Dörwald3f1e65c2007-06-08 15:33:46 +0000228def _quote(str, LegalChars=_LegalChars):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000229 #
230 # If the string does not need to be double-quoted,
231 # then just return the string. Otherwise, surround
232 # the string in doublequotes and precede quote (with a \)
233 # special characters.
234 #
Guido van Rossum0c41e882007-07-03 16:46:40 +0000235 if all(c in LegalChars for c in str):
Fred Drakeff5364a2000-08-24 14:40:35 +0000236 return str
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000237 else:
Fred Draked451ec12002-04-26 02:29:55 +0000238 return '"' + _nulljoin( map(_Translator.get, str, str) ) + '"'
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000239# end _quote
240
241
242_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
243_QuotePatt = re.compile(r"[\\].")
244
Fred Draked451ec12002-04-26 02:29:55 +0000245def _unquote(str):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000246 # If there aren't any doublequotes,
247 # then there can't be any special characters. See RFC 2109.
248 if len(str) < 2:
249 return str
250 if str[0] != '"' or str[-1] != '"':
251 return str
252
253 # We have to assume that we must decode this string.
254 # Down to work.
255
256 # Remove the "s
257 str = str[1:-1]
Fred Drakeff5364a2000-08-24 14:40:35 +0000258
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000259 # Check for special sequences. Examples:
260 # \012 --> \n
261 # \" --> "
262 #
263 i = 0
264 n = len(str)
265 res = []
266 while 0 <= i < n:
267 Omatch = _OctalPatt.search(str, i)
268 Qmatch = _QuotePatt.search(str, i)
269 if not Omatch and not Qmatch: # Neither matched
270 res.append(str[i:])
271 break
272 # else:
273 j = k = -1
274 if Omatch: j = Omatch.start(0)
275 if Qmatch: k = Qmatch.start(0)
276 if Qmatch and ( not Omatch or k < j ): # QuotePatt matched
277 res.append(str[i:k])
278 res.append(str[k+1])
279 i = k+2
280 else: # OctalPatt matched
281 res.append(str[i:j])
Fred Draked451ec12002-04-26 02:29:55 +0000282 res.append( chr( int(str[j+1:j+4], 8) ) )
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000283 i = j+4
Fred Draked451ec12002-04-26 02:29:55 +0000284 return _nulljoin(res)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000285# end _unquote
286
287# The _getdate() routine is used to set the expiration time in
288# the cookie's HTTP header. By default, _getdate() returns the
Tim Peters88869f92001-01-14 23:36:06 +0000289# current time in the appropriate "expires" format for a
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000290# Set-Cookie header. The one optional argument is an offset from
291# now, in seconds. For example, an offset of -3600 means "one hour ago".
292# The offset may be a floating point number.
293#
294
295_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
296
297_monthname = [None,
298 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
299 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
300
301def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
302 from time import gmtime, time
303 now = time()
304 year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
305 return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
306 (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
307
308
309#
310# A class to hold ONE key,value pair.
311# In a cookie, each such pair may have several attributes.
312# so this class is used to keep the attributes associated
313# with the appropriate key,value pair.
314# This class also includes a coded_value attribute, which
315# is used to hold the network representation of the
316# value. This is most useful when Python objects are
317# pickled for network transit.
318#
319
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000320class Morsel(dict):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000321 # RFC 2109 lists these attributes as reserved:
322 # path comment domain
323 # max-age secure version
Tim Peters88869f92001-01-14 23:36:06 +0000324 #
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000325 # For historical reasons, these attributes are also reserved:
326 # expires
327 #
328 # This dictionary provides a mapping from the lowercase
329 # variant on the left to the appropriate traditional
330 # formatting on the right.
331 _reserved = { "expires" : "expires",
332 "path" : "Path",
333 "comment" : "Comment",
334 "domain" : "Domain",
335 "max-age" : "Max-Age",
336 "secure" : "secure",
337 "version" : "Version",
338 }
Fred Drakeff5364a2000-08-24 14:40:35 +0000339
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000340 def __init__(self):
341 # Set defaults
342 self.key = self.value = self.coded_value = None
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000343
344 # Set default attributes
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000345 for K in self._reserved:
346 dict.__setitem__(self, K, "")
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000347 # end __init__
348
349 def __setitem__(self, K, V):
Fred Draked451ec12002-04-26 02:29:55 +0000350 K = K.lower()
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000351 if not K in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000352 raise CookieError("Invalid Attribute %s" % K)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000353 dict.__setitem__(self, K, V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000354 # end __setitem__
355
356 def isReservedKey(self, K):
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000357 return K.lower() in self._reserved
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000358 # end isReservedKey
359
Walter Dörwald3f1e65c2007-06-08 15:33:46 +0000360 def set(self, key, val, coded_val, LegalChars=_LegalChars):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000361 # First we verify that the key isn't a reserved word
362 # Second we make sure it only contains legal characters
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000363 if key.lower() in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000364 raise CookieError("Attempt to set a reserved key: %s" % key)
Guido van Rossum0c41e882007-07-03 16:46:40 +0000365 if any(c not in LegalChars for c in key):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000366 raise CookieError("Illegal key value: %s" % key)
367
368 # It's a good key, so save it.
369 self.key = key
370 self.value = val
371 self.coded_value = coded_val
372 # end set
373
374 def output(self, attrs=None, header = "Set-Cookie:"):
375 return "%s %s" % ( header, self.OutputString(attrs) )
376
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000377 __str__ = output
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000378
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000379 def __repr__(self):
380 return '<%s: %s=%s>' % (self.__class__.__name__,
381 self.key, repr(self.value) )
Fred Drakeff5364a2000-08-24 14:40:35 +0000382
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000383 def js_output(self, attrs=None):
384 # Print javascript
385 return """
Georg Brandl03a33ea2005-06-26 21:02:49 +0000386 <script type="text/javascript">
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000387 <!-- begin hiding
Georg Brandl03a33ea2005-06-26 21:02:49 +0000388 document.cookie = \"%s\";
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000389 // end hiding -->
390 </script>
391 """ % ( self.OutputString(attrs), )
392 # end js_output()
393
394 def OutputString(self, attrs=None):
395 # Build up our result
396 #
397 result = []
398 RA = result.append
Fred Drakeff5364a2000-08-24 14:40:35 +0000399
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000400 # First, the key=value pair
Georg Brandl532efab2005-08-24 22:34:21 +0000401 RA("%s=%s" % (self.key, self.coded_value))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000402
403 # Now add any defined attributes
Fred Drake8152d322000-12-12 23:20:45 +0000404 if attrs is None:
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000405 attrs = self._reserved
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000406 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000407 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000408 if V == "": continue
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000409 if K not in attrs: continue
410 if K == "expires" and type(V) == type(1):
Georg Brandl532efab2005-08-24 22:34:21 +0000411 RA("%s=%s" % (self._reserved[K], _getdate(V)))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000412 elif K == "max-age" and type(V) == type(1):
Georg Brandl532efab2005-08-24 22:34:21 +0000413 RA("%s=%d" % (self._reserved[K], V))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000414 elif K == "secure":
Georg Brandl532efab2005-08-24 22:34:21 +0000415 RA(str(self._reserved[K]))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000416 else:
Georg Brandl532efab2005-08-24 22:34:21 +0000417 RA("%s=%s" % (self._reserved[K], V))
Fred Drakeff5364a2000-08-24 14:40:35 +0000418
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000419 # Return the result
Georg Brandl532efab2005-08-24 22:34:21 +0000420 return _semispacejoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000421 # end OutputString
422# end Morsel class
423
424
425
426#
427# Pattern for finding cookie
428#
429# This used to be strict parsing based on the RFC2109 and RFC2068
430# specifications. I have since discovered that MSIE 3.0x doesn't
431# follow the character rules outlined in those specs. As a
432# result, the parsing rules here are less strict.
433#
434
Andrew M. Kuchlingc05abb32001-02-20 22:11:24 +0000435_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000436_CookiePattern = re.compile(
437 r"(?x)" # This is a Verbose pattern
438 r"(?P<key>" # Start of group 'key'
Andrew M. Kuchlingc05abb32001-02-20 22:11:24 +0000439 ""+ _LegalCharsPatt +"+?" # Any word of at least one letter, nongreedy
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000440 r")" # End of group 'key'
441 r"\s*=\s*" # Equal Sign
442 r"(?P<val>" # Start of group 'val'
443 r'"(?:[^\\"]|\\.)*"' # Any doublequoted string
444 r"|" # or
445 ""+ _LegalCharsPatt +"*" # Any word or empty string
446 r")" # End of group 'val'
447 r"\s*;?" # Probably ending in a semi-colon
Antoine Pitroufd036452008-08-19 17:56:33 +0000448 , re.ASCII) # May be removed if safe.
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000449
450
451# At long last, here is the cookie class.
452# Using this class is almost just like using a dictionary.
453# See this module's docstring for example usage.
454#
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000455class BaseCookie(dict):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000456 # A container class for a set of Morsels
457 #
Fred Drakeff5364a2000-08-24 14:40:35 +0000458
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000459 def value_decode(self, val):
460 """real_value, coded_value = value_decode(STRING)
461 Called prior to setting a cookie's value from the network
462 representation. The VALUE is the value read from HTTP
463 header.
464 Override this function to modify the behavior of cookies.
465 """
466 return val, val
467 # end value_encode
468
469 def value_encode(self, val):
470 """real_value, coded_value = value_encode(VALUE)
471 Called prior to setting a cookie's value from the dictionary
472 representation. The VALUE is the value being assigned.
473 Override this function to modify the behavior of cookies.
474 """
475 strval = str(val)
476 return strval, strval
477 # end value_encode
Fred Drakeff5364a2000-08-24 14:40:35 +0000478
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000479 def __init__(self, input=None):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000480 if input: self.load(input)
481 # end __init__
482
483 def __set(self, key, real_value, coded_value):
484 """Private method for setting a cookie's value"""
485 M = self.get(key, Morsel())
486 M.set(key, real_value, coded_value)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000487 dict.__setitem__(self, key, M)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000488 # end __set
489
490 def __setitem__(self, key, value):
491 """Dictionary style assignment."""
492 rval, cval = self.value_encode(value)
493 self.__set(key, rval, cval)
494 # end __setitem__
495
Georg Brandl532efab2005-08-24 22:34:21 +0000496 def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000497 """Return a string suitable for HTTP."""
498 result = []
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000499 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000500 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000501 result.append( V.output(attrs, header) )
Fred Draked451ec12002-04-26 02:29:55 +0000502 return sep.join(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000503 # end output
504
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000505 __str__ = output
506
507 def __repr__(self):
508 L = []
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000509 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000510 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000511 L.append( '%s=%s' % (K,repr(V.value) ) )
Georg Brandl8246c432005-08-25 07:32:42 +0000512 return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
Fred Drakeff5364a2000-08-24 14:40:35 +0000513
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000514 def js_output(self, attrs=None):
515 """Return a string suitable for JavaScript."""
516 result = []
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000517 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000518 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000519 result.append( V.js_output(attrs) )
Fred Draked451ec12002-04-26 02:29:55 +0000520 return _nulljoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000521 # end js_output
522
523 def load(self, rawdata):
524 """Load cookies from a string (presumably HTTP_COOKIE) or
525 from a dictionary. Loading cookies from a dictionary 'd'
526 is equivalent to calling:
527 map(Cookie.__setitem__, d.keys(), d.values())
528 """
529 if type(rawdata) == type(""):
530 self.__ParseString(rawdata)
531 else:
532 self.update(rawdata)
533 return
534 # end load()
Fred Drakeff5364a2000-08-24 14:40:35 +0000535
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000536 def __ParseString(self, str, patt=_CookiePattern):
537 i = 0 # Our starting point
538 n = len(str) # Length of string
539 M = None # current morsel
540
541 while 0 <= i < n:
542 # Start looking for a cookie
543 match = patt.search(str, i)
544 if not match: break # No more cookies
545
546 K,V = match.group("key"), match.group("val")
547 i = match.end(0)
548
549 # Parse the key, value in case it's metainfo
550 if K[0] == "$":
551 # We ignore attributes which pertain to the cookie
552 # mechanism as a whole. See RFC 2109.
553 # (Does anyone care?)
554 if M:
555 M[ K[1:] ] = V
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000556 elif K.lower() in Morsel._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000557 if M:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000558 M[ K ] = _unquote(V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000559 else:
560 rval, cval = self.value_decode(V)
561 self.__set(K, rval, cval)
562 M = self[K]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000563 # end __ParseString
564# end BaseCookie class
565
566class SimpleCookie(BaseCookie):
567 """SimpleCookie
568 SimpleCookie supports strings as cookie values. When setting
569 the value using the dictionary assignment notation, SimpleCookie
570 calls the builtin str() to convert the value to a string. Values
571 received from HTTP are kept as strings.
572 """
573 def value_decode(self, val):
574 return _unquote( val ), val
575 def value_encode(self, val):
576 strval = str(val)
577 return strval, _quote( strval )
578# end SimpleCookie
579
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000580#
581###########################################################
582
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000583def _test():
Georg Brandl24420152008-05-26 16:32:26 +0000584 import doctest, http.cookies
585 return doctest.testmod(http.cookies)
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000586
587if __name__ == "__main__":
588 _test()