blob: e584396caf7dc343d0ceac7fd25f0cfd45b248e5 [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 #
Benjamin Peterson35e661c2008-09-06 19:37:35 +0000328 # This is an extension from Microsoft:
329 # httponly
330 #
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000331 # This dictionary provides a mapping from the lowercase
332 # variant on the left to the appropriate traditional
333 # formatting on the right.
334 _reserved = { "expires" : "expires",
335 "path" : "Path",
336 "comment" : "Comment",
337 "domain" : "Domain",
338 "max-age" : "Max-Age",
339 "secure" : "secure",
Benjamin Peterson35e661c2008-09-06 19:37:35 +0000340 "httponly" : "httponly",
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000341 "version" : "Version",
342 }
Fred Drakeff5364a2000-08-24 14:40:35 +0000343
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000344 def __init__(self):
345 # Set defaults
346 self.key = self.value = self.coded_value = None
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000347
348 # Set default attributes
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000349 for K in self._reserved:
350 dict.__setitem__(self, K, "")
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000351 # end __init__
352
353 def __setitem__(self, K, V):
Fred Draked451ec12002-04-26 02:29:55 +0000354 K = K.lower()
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000355 if not K in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000356 raise CookieError("Invalid Attribute %s" % K)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000357 dict.__setitem__(self, K, V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000358 # end __setitem__
359
360 def isReservedKey(self, K):
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000361 return K.lower() in self._reserved
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000362 # end isReservedKey
363
Walter Dörwald3f1e65c2007-06-08 15:33:46 +0000364 def set(self, key, val, coded_val, LegalChars=_LegalChars):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000365 # First we verify that the key isn't a reserved word
366 # Second we make sure it only contains legal characters
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000367 if key.lower() in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000368 raise CookieError("Attempt to set a reserved key: %s" % key)
Guido van Rossum0c41e882007-07-03 16:46:40 +0000369 if any(c not in LegalChars for c in key):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000370 raise CookieError("Illegal key value: %s" % key)
371
372 # It's a good key, so save it.
373 self.key = key
374 self.value = val
375 self.coded_value = coded_val
376 # end set
377
378 def output(self, attrs=None, header = "Set-Cookie:"):
379 return "%s %s" % ( header, self.OutputString(attrs) )
380
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000381 __str__ = output
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000382
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000383 def __repr__(self):
384 return '<%s: %s=%s>' % (self.__class__.__name__,
385 self.key, repr(self.value) )
Fred Drakeff5364a2000-08-24 14:40:35 +0000386
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000387 def js_output(self, attrs=None):
388 # Print javascript
389 return """
Georg Brandl03a33ea2005-06-26 21:02:49 +0000390 <script type="text/javascript">
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000391 <!-- begin hiding
Georg Brandl03a33ea2005-06-26 21:02:49 +0000392 document.cookie = \"%s\";
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000393 // end hiding -->
394 </script>
Senthil Kumaran3e2ea792009-04-02 03:02:03 +0000395 """ % ( self.OutputString(attrs).replace('"',r'\"'))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000396 # end js_output()
397
398 def OutputString(self, attrs=None):
399 # Build up our result
400 #
401 result = []
402 RA = result.append
Fred Drakeff5364a2000-08-24 14:40:35 +0000403
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000404 # First, the key=value pair
Georg Brandl532efab2005-08-24 22:34:21 +0000405 RA("%s=%s" % (self.key, self.coded_value))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000406
407 # Now add any defined attributes
Fred Drake8152d322000-12-12 23:20:45 +0000408 if attrs is None:
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000409 attrs = self._reserved
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000410 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000411 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000412 if V == "": continue
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000413 if K not in attrs: continue
414 if K == "expires" and type(V) == type(1):
Georg Brandl532efab2005-08-24 22:34:21 +0000415 RA("%s=%s" % (self._reserved[K], _getdate(V)))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000416 elif K == "max-age" and type(V) == type(1):
Georg Brandl532efab2005-08-24 22:34:21 +0000417 RA("%s=%d" % (self._reserved[K], V))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000418 elif K == "secure":
Georg Brandl532efab2005-08-24 22:34:21 +0000419 RA(str(self._reserved[K]))
Benjamin Peterson35e661c2008-09-06 19:37:35 +0000420 elif K == "httponly":
421 RA(str(self._reserved[K]))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000422 else:
Georg Brandl532efab2005-08-24 22:34:21 +0000423 RA("%s=%s" % (self._reserved[K], V))
Fred Drakeff5364a2000-08-24 14:40:35 +0000424
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000425 # Return the result
Georg Brandl532efab2005-08-24 22:34:21 +0000426 return _semispacejoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000427 # end OutputString
428# end Morsel class
429
430
431
432#
433# Pattern for finding cookie
434#
435# This used to be strict parsing based on the RFC2109 and RFC2068
436# specifications. I have since discovered that MSIE 3.0x doesn't
437# follow the character rules outlined in those specs. As a
438# result, the parsing rules here are less strict.
439#
440
Andrew M. Kuchlingc05abb32001-02-20 22:11:24 +0000441_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
Georg Brandlcea7e552010-08-01 18:56:30 +0000442_CookiePattern = re.compile(r"""
443 (?x) # This is a verbose pattern
444 (?P<key> # Start of group 'key'
445 """ + _LegalCharsPatt + r"""+? # Any word of at least one letter
446 ) # End of group 'key'
447 \s*=\s* # Equal Sign
448 (?P<val> # Start of group 'val'
449 "(?:[^\\"]|\\.)*" # Any doublequoted string
450 | # or
451 \w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGMT # Special case for "expires" attr
452 | # or
453 """ + _LegalCharsPatt + r"""* # Any word or empty string
454 ) # End of group 'val'
455 \s*;? # Probably ending in a semi-colon
456 """, re.ASCII) # May be removed if safe.
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000457
458
459# At long last, here is the cookie class.
460# Using this class is almost just like using a dictionary.
461# See this module's docstring for example usage.
462#
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000463class BaseCookie(dict):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000464 # A container class for a set of Morsels
465 #
Fred Drakeff5364a2000-08-24 14:40:35 +0000466
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000467 def value_decode(self, val):
468 """real_value, coded_value = value_decode(STRING)
469 Called prior to setting a cookie's value from the network
470 representation. The VALUE is the value read from HTTP
471 header.
472 Override this function to modify the behavior of cookies.
473 """
474 return val, val
475 # end value_encode
476
477 def value_encode(self, val):
478 """real_value, coded_value = value_encode(VALUE)
479 Called prior to setting a cookie's value from the dictionary
480 representation. The VALUE is the value being assigned.
481 Override this function to modify the behavior of cookies.
482 """
483 strval = str(val)
484 return strval, strval
485 # end value_encode
Fred Drakeff5364a2000-08-24 14:40:35 +0000486
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000487 def __init__(self, input=None):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000488 if input: self.load(input)
489 # end __init__
490
491 def __set(self, key, real_value, coded_value):
492 """Private method for setting a cookie's value"""
493 M = self.get(key, Morsel())
494 M.set(key, real_value, coded_value)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000495 dict.__setitem__(self, key, M)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000496 # end __set
497
498 def __setitem__(self, key, value):
499 """Dictionary style assignment."""
500 rval, cval = self.value_encode(value)
501 self.__set(key, rval, cval)
502 # end __setitem__
503
Georg Brandl532efab2005-08-24 22:34:21 +0000504 def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000505 """Return a string suitable for HTTP."""
506 result = []
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000507 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000508 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000509 result.append( V.output(attrs, header) )
Fred Draked451ec12002-04-26 02:29:55 +0000510 return sep.join(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000511 # end output
512
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000513 __str__ = output
514
515 def __repr__(self):
516 L = []
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. Kuchling0b29b112000-08-24 11:52:33 +0000519 L.append( '%s=%s' % (K,repr(V.value) ) )
Georg Brandl8246c432005-08-25 07:32:42 +0000520 return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
Fred Drakeff5364a2000-08-24 14:40:35 +0000521
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000522 def js_output(self, attrs=None):
523 """Return a string suitable for JavaScript."""
524 result = []
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000525 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000526 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000527 result.append( V.js_output(attrs) )
Fred Draked451ec12002-04-26 02:29:55 +0000528 return _nulljoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000529 # end js_output
530
531 def load(self, rawdata):
532 """Load cookies from a string (presumably HTTP_COOKIE) or
533 from a dictionary. Loading cookies from a dictionary 'd'
534 is equivalent to calling:
535 map(Cookie.__setitem__, d.keys(), d.values())
536 """
537 if type(rawdata) == type(""):
538 self.__ParseString(rawdata)
539 else:
Benjamin Petersona8332062009-09-11 22:36:27 +0000540 # self.update() wouldn't call our custom __setitem__
541 for k, v in rawdata.items():
542 self[k] = v
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000543 return
544 # end load()
Fred Drakeff5364a2000-08-24 14:40:35 +0000545
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000546 def __ParseString(self, str, patt=_CookiePattern):
547 i = 0 # Our starting point
548 n = len(str) # Length of string
549 M = None # current morsel
550
551 while 0 <= i < n:
552 # Start looking for a cookie
553 match = patt.search(str, i)
554 if not match: break # No more cookies
555
556 K,V = match.group("key"), match.group("val")
557 i = match.end(0)
558
559 # Parse the key, value in case it's metainfo
560 if K[0] == "$":
561 # We ignore attributes which pertain to the cookie
562 # mechanism as a whole. See RFC 2109.
563 # (Does anyone care?)
564 if M:
565 M[ K[1:] ] = V
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000566 elif K.lower() in Morsel._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000567 if M:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000568 M[ K ] = _unquote(V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000569 else:
570 rval, cval = self.value_decode(V)
571 self.__set(K, rval, cval)
572 M = self[K]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000573 # end __ParseString
574# end BaseCookie class
575
576class SimpleCookie(BaseCookie):
577 """SimpleCookie
578 SimpleCookie supports strings as cookie values. When setting
579 the value using the dictionary assignment notation, SimpleCookie
580 calls the builtin str() to convert the value to a string. Values
581 received from HTTP are kept as strings.
582 """
583 def value_decode(self, val):
584 return _unquote( val ), val
585 def value_encode(self, val):
586 strval = str(val)
587 return strval, _quote( strval )
588# end SimpleCookie
589
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000590#
591###########################################################
592
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000593def _test():
Georg Brandl24420152008-05-26 16:32:26 +0000594 import doctest, http.cookies
595 return doctest.testmod(http.cookies)
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000596
597if __name__ == "__main__":
598 _test()