blob: 3d7ab4f2f16f074afb131e4c33dbfc4f1441d051 [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
240
241_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
242_QuotePatt = re.compile(r"[\\].")
243
Fred Draked451ec12002-04-26 02:29:55 +0000244def _unquote(str):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000245 # If there aren't any doublequotes,
246 # then there can't be any special characters. See RFC 2109.
247 if len(str) < 2:
248 return str
249 if str[0] != '"' or str[-1] != '"':
250 return str
251
252 # We have to assume that we must decode this string.
253 # Down to work.
254
255 # Remove the "s
256 str = str[1:-1]
Fred Drakeff5364a2000-08-24 14:40:35 +0000257
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000258 # Check for special sequences. Examples:
259 # \012 --> \n
260 # \" --> "
261 #
262 i = 0
263 n = len(str)
264 res = []
265 while 0 <= i < n:
266 Omatch = _OctalPatt.search(str, i)
267 Qmatch = _QuotePatt.search(str, i)
268 if not Omatch and not Qmatch: # Neither matched
269 res.append(str[i:])
270 break
271 # else:
272 j = k = -1
273 if Omatch: j = Omatch.start(0)
274 if Qmatch: k = Qmatch.start(0)
275 if Qmatch and ( not Omatch or k < j ): # QuotePatt matched
276 res.append(str[i:k])
277 res.append(str[k+1])
278 i = k+2
279 else: # OctalPatt matched
280 res.append(str[i:j])
Fred Draked451ec12002-04-26 02:29:55 +0000281 res.append( chr( int(str[j+1:j+4], 8) ) )
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000282 i = j+4
Fred Draked451ec12002-04-26 02:29:55 +0000283 return _nulljoin(res)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000284
285# The _getdate() routine is used to set the expiration time in
286# the cookie's HTTP header. By default, _getdate() returns the
Tim Peters88869f92001-01-14 23:36:06 +0000287# current time in the appropriate "expires" format for a
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000288# Set-Cookie header. The one optional argument is an offset from
289# now, in seconds. For example, an offset of -3600 means "one hour ago".
290# The offset may be a floating point number.
291#
292
293_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
294
295_monthname = [None,
296 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
297 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
298
299def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
300 from time import gmtime, time
301 now = time()
302 year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
303 return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
304 (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
305
306
307#
308# A class to hold ONE key,value pair.
309# In a cookie, each such pair may have several attributes.
310# so this class is used to keep the attributes associated
311# with the appropriate key,value pair.
312# This class also includes a coded_value attribute, which
313# is used to hold the network representation of the
314# value. This is most useful when Python objects are
315# pickled for network transit.
316#
317
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000318class Morsel(dict):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000319 # RFC 2109 lists these attributes as reserved:
320 # path comment domain
321 # max-age secure version
Tim Peters88869f92001-01-14 23:36:06 +0000322 #
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000323 # For historical reasons, these attributes are also reserved:
324 # expires
325 #
Benjamin Peterson35e661c2008-09-06 19:37:35 +0000326 # This is an extension from Microsoft:
327 # httponly
328 #
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000329 # This dictionary provides a mapping from the lowercase
330 # variant on the left to the appropriate traditional
331 # formatting on the right.
332 _reserved = { "expires" : "expires",
333 "path" : "Path",
334 "comment" : "Comment",
335 "domain" : "Domain",
336 "max-age" : "Max-Age",
337 "secure" : "secure",
Benjamin Peterson35e661c2008-09-06 19:37:35 +0000338 "httponly" : "httponly",
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000339 "version" : "Version",
340 }
Fred Drakeff5364a2000-08-24 14:40:35 +0000341
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000342 def __init__(self):
343 # Set defaults
344 self.key = self.value = self.coded_value = None
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000345
346 # Set default attributes
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000347 for K in self._reserved:
348 dict.__setitem__(self, K, "")
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000349
350 def __setitem__(self, K, V):
Fred Draked451ec12002-04-26 02:29:55 +0000351 K = K.lower()
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000352 if not K in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000353 raise CookieError("Invalid Attribute %s" % K)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000354 dict.__setitem__(self, K, V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000355
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
Walter Dörwald3f1e65c2007-06-08 15:33:46 +0000359 def set(self, key, val, coded_val, LegalChars=_LegalChars):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000360 # First we verify that the key isn't a reserved word
361 # Second we make sure it only contains legal characters
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000362 if key.lower() in self._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000363 raise CookieError("Attempt to set a reserved key: %s" % key)
Guido van Rossum0c41e882007-07-03 16:46:40 +0000364 if any(c not in LegalChars for c in key):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000365 raise CookieError("Illegal key value: %s" % key)
366
367 # It's a good key, so save it.
368 self.key = key
369 self.value = val
370 self.coded_value = coded_val
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000371
372 def output(self, attrs=None, header = "Set-Cookie:"):
373 return "%s %s" % ( header, self.OutputString(attrs) )
374
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000375 __str__ = output
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000376
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000377 def __repr__(self):
378 return '<%s: %s=%s>' % (self.__class__.__name__,
379 self.key, repr(self.value) )
Fred Drakeff5364a2000-08-24 14:40:35 +0000380
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000381 def js_output(self, attrs=None):
382 # Print javascript
383 return """
Georg Brandl03a33ea2005-06-26 21:02:49 +0000384 <script type="text/javascript">
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000385 <!-- begin hiding
Georg Brandl03a33ea2005-06-26 21:02:49 +0000386 document.cookie = \"%s\";
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000387 // end hiding -->
388 </script>
Senthil Kumaran3e2ea792009-04-02 03:02:03 +0000389 """ % ( self.OutputString(attrs).replace('"',r'\"'))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000390
391 def OutputString(self, attrs=None):
392 # Build up our result
393 #
394 result = []
395 RA = result.append
Fred Drakeff5364a2000-08-24 14:40:35 +0000396
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000397 # First, the key=value pair
Georg Brandl532efab2005-08-24 22:34:21 +0000398 RA("%s=%s" % (self.key, self.coded_value))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000399
400 # Now add any defined attributes
Fred Drake8152d322000-12-12 23:20:45 +0000401 if attrs is None:
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000402 attrs = self._reserved
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000403 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000404 for K,V in items:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000405 if V == "": continue
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000406 if K not in attrs: continue
407 if K == "expires" and type(V) == type(1):
Georg Brandl532efab2005-08-24 22:34:21 +0000408 RA("%s=%s" % (self._reserved[K], _getdate(V)))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000409 elif K == "max-age" and type(V) == type(1):
Georg Brandl532efab2005-08-24 22:34:21 +0000410 RA("%s=%d" % (self._reserved[K], V))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000411 elif K == "secure":
Georg Brandl532efab2005-08-24 22:34:21 +0000412 RA(str(self._reserved[K]))
Benjamin Peterson35e661c2008-09-06 19:37:35 +0000413 elif K == "httponly":
414 RA(str(self._reserved[K]))
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000415 else:
Georg Brandl532efab2005-08-24 22:34:21 +0000416 RA("%s=%s" % (self._reserved[K], V))
Fred Drakeff5364a2000-08-24 14:40:35 +0000417
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000418 # Return the result
Georg Brandl532efab2005-08-24 22:34:21 +0000419 return _semispacejoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000420
421
422#
423# Pattern for finding cookie
424#
425# This used to be strict parsing based on the RFC2109 and RFC2068
426# specifications. I have since discovered that MSIE 3.0x doesn't
427# follow the character rules outlined in those specs. As a
428# result, the parsing rules here are less strict.
429#
430
Andrew M. Kuchlingc05abb32001-02-20 22:11:24 +0000431_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000432_CookiePattern = re.compile(
433 r"(?x)" # This is a Verbose pattern
434 r"(?P<key>" # Start of group 'key'
Andrew M. Kuchlingc05abb32001-02-20 22:11:24 +0000435 ""+ _LegalCharsPatt +"+?" # Any word of at least one letter, nongreedy
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000436 r")" # End of group 'key'
437 r"\s*=\s*" # Equal Sign
438 r"(?P<val>" # Start of group 'val'
439 r'"(?:[^\\"]|\\.)*"' # Any doublequoted string
440 r"|" # or
441 ""+ _LegalCharsPatt +"*" # Any word or empty string
442 r")" # End of group 'val'
443 r"\s*;?" # Probably ending in a semi-colon
Antoine Pitroufd036452008-08-19 17:56:33 +0000444 , re.ASCII) # May be removed if safe.
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000445
446
447# At long last, here is the cookie class.
448# Using this class is almost just like using a dictionary.
449# See this module's docstring for example usage.
450#
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000451class BaseCookie(dict):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000452 # A container class for a set of Morsels
453 #
Fred Drakeff5364a2000-08-24 14:40:35 +0000454
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000455 def value_decode(self, val):
456 """real_value, coded_value = value_decode(STRING)
457 Called prior to setting a cookie's value from the network
458 representation. The VALUE is the value read from HTTP
459 header.
460 Override this function to modify the behavior of cookies.
461 """
462 return val, val
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000463
464 def value_encode(self, val):
465 """real_value, coded_value = value_encode(VALUE)
466 Called prior to setting a cookie's value from the dictionary
467 representation. The VALUE is the value being assigned.
468 Override this function to modify the behavior of cookies.
469 """
470 strval = str(val)
471 return strval, strval
Fred Drakeff5364a2000-08-24 14:40:35 +0000472
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000473 def __init__(self, input=None):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000474 if input: self.load(input)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000475
476 def __set(self, key, real_value, coded_value):
477 """Private method for setting a cookie's value"""
478 M = self.get(key, Morsel())
479 M.set(key, real_value, coded_value)
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000480 dict.__setitem__(self, key, M)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000481
482 def __setitem__(self, key, value):
483 """Dictionary style assignment."""
484 rval, cval = self.value_encode(value)
485 self.__set(key, rval, cval)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000486
Georg Brandl532efab2005-08-24 22:34:21 +0000487 def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000488 """Return a string suitable for HTTP."""
489 result = []
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000490 items = sorted(self.items())
Tim Peters2f228e72001-05-13 00:19:31 +0000491 for K,V in items:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000492 result.append( V.output(attrs, header) )
Fred Draked451ec12002-04-26 02:29:55 +0000493 return sep.join(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000494
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000495 __str__ = output
496
497 def __repr__(self):
498 L = []
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. Kuchling0b29b112000-08-24 11:52:33 +0000501 L.append( '%s=%s' % (K,repr(V.value) ) )
Georg Brandl8246c432005-08-25 07:32:42 +0000502 return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
Fred Drakeff5364a2000-08-24 14:40:35 +0000503
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000504 def js_output(self, attrs=None):
505 """Return a string suitable for JavaScript."""
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.js_output(attrs) )
Fred Draked451ec12002-04-26 02:29:55 +0000510 return _nulljoin(result)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000511
512 def load(self, rawdata):
513 """Load cookies from a string (presumably HTTP_COOKIE) or
514 from a dictionary. Loading cookies from a dictionary 'd'
515 is equivalent to calling:
516 map(Cookie.__setitem__, d.keys(), d.values())
517 """
518 if type(rawdata) == type(""):
519 self.__ParseString(rawdata)
520 else:
521 self.update(rawdata)
522 return
Fred Drakeff5364a2000-08-24 14:40:35 +0000523
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000524 def __ParseString(self, str, patt=_CookiePattern):
525 i = 0 # Our starting point
526 n = len(str) # Length of string
527 M = None # current morsel
528
529 while 0 <= i < n:
530 # Start looking for a cookie
531 match = patt.search(str, i)
532 if not match: break # No more cookies
533
534 K,V = match.group("key"), match.group("val")
535 i = match.end(0)
536
537 # Parse the key, value in case it's metainfo
538 if K[0] == "$":
539 # We ignore attributes which pertain to the cookie
540 # mechanism as a whole. See RFC 2109.
541 # (Does anyone care?)
542 if M:
543 M[ K[1:] ] = V
Raymond Hettinger0a2963c2002-06-26 15:19:01 +0000544 elif K.lower() in Morsel._reserved:
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000545 if M:
Andrew M. Kuchling0b29b112000-08-24 11:52:33 +0000546 M[ K ] = _unquote(V)
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000547 else:
548 rval, cval = self.value_decode(V)
549 self.__set(K, rval, cval)
550 M = self[K]
Georg Brandl4eff9f72009-09-04 08:22:00 +0000551
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000552
553class SimpleCookie(BaseCookie):
554 """SimpleCookie
555 SimpleCookie supports strings as cookie values. When setting
556 the value using the dictionary assignment notation, SimpleCookie
557 calls the builtin str() to convert the value to a string. Values
558 received from HTTP are kept as strings.
559 """
560 def value_decode(self, val):
561 return _unquote( val ), val
562 def value_encode(self, val):
563 strval = str(val)
564 return strval, _quote( strval )
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000565
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +0000566###########################################################
567
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000568def _test():
Georg Brandl24420152008-05-26 16:32:26 +0000569 import doctest, http.cookies
570 return doctest.testmod(http.cookies)
Guido van Rossum58b6f5b2001-04-06 19:39:11 +0000571
572if __name__ == "__main__":
573 _test()