svn-gvsig-desktop / tags / v1_1_Build_1013 / extensions / extScripting / scripts / jython / Lib / Cookie.py @ 13521
History | View | Annotate | Download (24.3 KB)
1 | 5782 | jmvivo | #!/usr/bin/env python
|
---|---|---|---|
2 | #
|
||
3 | |||
4 | ####
|
||
5 | # Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
|
||
6 | #
|
||
7 | # All Rights Reserved
|
||
8 | #
|
||
9 | # 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
|
||
16 | # prior permission.
|
||
17 | #
|
||
18 | # 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
|
||
25 | # PERFORMANCE OF THIS SOFTWARE.
|
||
26 | #
|
||
27 | ####
|
||
28 | #
|
||
29 | # Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
|
||
30 | # 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
|
||
37 | # Dave Mitchell (davem@magnet.com) in 1995, when he released the
|
||
38 | # first version of nscookie.py.
|
||
39 | #
|
||
40 | ####
|
||
41 | |||
42 | r"""
|
||
43 | Here's a sample session to show how to use this module.
|
||
44 | At the moment, this is the only documentation.
|
||
45 |
|
||
46 | The Basics
|
||
47 | ----------
|
||
48 |
|
||
49 | Importing is easy..
|
||
50 |
|
||
51 | >>> import Cookie
|
||
52 |
|
||
53 | Most of the time you start by creating a cookie. Cookies come in
|
||
54 | three flavors, each with slighly different encoding semanitcs, but
|
||
55 | more on that later.
|
||
56 |
|
||
57 | >>> C = Cookie.SimpleCookie()
|
||
58 | >>> C = Cookie.SerialCookie()
|
||
59 | >>> C = Cookie.SmartCookie()
|
||
60 |
|
||
61 | [Note: Long-time users of Cookie.py will remember using
|
||
62 | Cookie.Cookie() to create an Cookie object. Although deprecated, it
|
||
63 | is still supported by the code. See the Backward Compatibility notes
|
||
64 | for more information.]
|
||
65 |
|
||
66 | Once you've created your Cookie, you can add values just as if it were
|
||
67 | a dictionary.
|
||
68 |
|
||
69 | >>> C = Cookie.SmartCookie()
|
||
70 | >>> C["fig"] = "newton"
|
||
71 | >>> C["sugar"] = "wafer"
|
||
72 | >>> print C
|
||
73 | Set-Cookie: sugar=wafer;
|
||
74 | Set-Cookie: fig=newton;
|
||
75 |
|
||
76 | Notice that the printable representation of a Cookie is the
|
||
77 | appropriate format for a Set-Cookie: header. This is the
|
||
78 | default behavior. You can change the header and printed
|
||
79 | attributes by using the the .output() function
|
||
80 |
|
||
81 | >>> C = Cookie.SmartCookie()
|
||
82 | >>> C["rocky"] = "road"
|
||
83 | >>> C["rocky"]["path"] = "/cookie"
|
||
84 | >>> print C.output(header="Cookie:")
|
||
85 | Cookie: rocky=road; Path=/cookie;
|
||
86 | >>> print C.output(attrs=[], header="Cookie:")
|
||
87 | Cookie: rocky=road;
|
||
88 |
|
||
89 | The load() method of a Cookie extracts cookies from a string. In a
|
||
90 | CGI script, you would use this method to extract the cookies from the
|
||
91 | HTTP_COOKIE environment variable.
|
||
92 |
|
||
93 | >>> C = Cookie.SmartCookie()
|
||
94 | >>> C.load("chips=ahoy; vienna=finger")
|
||
95 | >>> print C
|
||
96 | Set-Cookie: vienna=finger;
|
||
97 | Set-Cookie: chips=ahoy;
|
||
98 |
|
||
99 | The load() method is darn-tootin smart about identifying cookies
|
||
100 | within a string. Escaped quotation marks, nested semicolons, and other
|
||
101 | such trickeries do not confuse it.
|
||
102 |
|
||
103 | >>> C = Cookie.SmartCookie()
|
||
104 | >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
|
||
105 | >>> print C
|
||
106 | Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;";
|
||
107 |
|
||
108 | Each element of the Cookie also supports all of the RFC 2109
|
||
109 | Cookie attributes. Here's an example which sets the Path
|
||
110 | attribute.
|
||
111 |
|
||
112 | >>> C = Cookie.SmartCookie()
|
||
113 | >>> C["oreo"] = "doublestuff"
|
||
114 | >>> C["oreo"]["path"] = "/"
|
||
115 | >>> print C
|
||
116 | Set-Cookie: oreo=doublestuff; Path=/;
|
||
117 |
|
||
118 | Each dictionary element has a 'value' attribute, which gives you
|
||
119 | back the value associated with the key.
|
||
120 |
|
||
121 | >>> C = Cookie.SmartCookie()
|
||
122 | >>> C["twix"] = "none for you"
|
||
123 | >>> C["twix"].value
|
||
124 | 'none for you'
|
||
125 |
|
||
126 |
|
||
127 | A Bit More Advanced
|
||
128 | -------------------
|
||
129 |
|
||
130 | As mentioned before, there are three different flavors of Cookie
|
||
131 | objects, each with different encoding/decoding semantics. This
|
||
132 | section briefly discusses the differences.
|
||
133 |
|
||
134 | SimpleCookie
|
||
135 |
|
||
136 | The SimpleCookie expects that all values should be standard strings.
|
||
137 | Just to be sure, SimpleCookie invokes the str() builtin to convert
|
||
138 | the value to a string, when the values are set dictionary-style.
|
||
139 |
|
||
140 | >>> C = Cookie.SimpleCookie()
|
||
141 | >>> C["number"] = 7
|
||
142 | >>> C["string"] = "seven"
|
||
143 | >>> C["number"].value
|
||
144 | '7'
|
||
145 | >>> C["string"].value
|
||
146 | 'seven'
|
||
147 | >>> print C
|
||
148 | Set-Cookie: number=7;
|
||
149 | Set-Cookie: string=seven;
|
||
150 |
|
||
151 |
|
||
152 | SerialCookie
|
||
153 |
|
||
154 | The SerialCookie expects that all values should be serialized using
|
||
155 | cPickle (or pickle, if cPickle isn't available). As a result of
|
||
156 | serializing, SerialCookie can save almost any Python object to a
|
||
157 | value, and recover the exact same object when the cookie has been
|
||
158 | returned. (SerialCookie can yield some strange-looking cookie
|
||
159 | values, however.)
|
||
160 |
|
||
161 | >>> C = Cookie.SerialCookie()
|
||
162 | >>> C["number"] = 7
|
||
163 | >>> C["string"] = "seven"
|
||
164 | >>> C["number"].value
|
||
165 | 7
|
||
166 | >>> C["string"].value
|
||
167 | 'seven'
|
||
168 | >>> print C
|
||
169 | Set-Cookie: number="I7\012.";
|
||
170 | Set-Cookie: string="S'seven'\012p1\012.";
|
||
171 |
|
||
172 | Be warned, however, if SerialCookie cannot de-serialize a value (because
|
||
173 | it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
|
||
174 |
|
||
175 |
|
||
176 | SmartCookie
|
||
177 |
|
||
178 | The SmartCookie combines aspects of each of the other two flavors.
|
||
179 | When setting a value in a dictionary-fashion, the SmartCookie will
|
||
180 | serialize (ala cPickle) the value *if and only if* it isn't a
|
||
181 | Python string. String objects are *not* serialized. Similarly,
|
||
182 | when the load() method parses out values, it attempts to de-serialize
|
||
183 | the value. If it fails, then it fallsback to treating the value
|
||
184 | as a string.
|
||
185 |
|
||
186 | >>> C = Cookie.SmartCookie()
|
||
187 | >>> C["number"] = 7
|
||
188 | >>> C["string"] = "seven"
|
||
189 | >>> C["number"].value
|
||
190 | 7
|
||
191 | >>> C["string"].value
|
||
192 | 'seven'
|
||
193 | >>> print C
|
||
194 | Set-Cookie: number="I7\012.";
|
||
195 | Set-Cookie: string=seven;
|
||
196 |
|
||
197 |
|
||
198 | Backwards Compatibility
|
||
199 | -----------------------
|
||
200 |
|
||
201 | In order to keep compatibilty with earlier versions of Cookie.py,
|
||
202 | it is still possible to use Cookie.Cookie() to create a Cookie. In
|
||
203 | fact, this simply returns a SmartCookie.
|
||
204 |
|
||
205 | >>> C = Cookie.Cookie()
|
||
206 | >>> print C.__class__.__name__
|
||
207 | SmartCookie
|
||
208 |
|
||
209 |
|
||
210 | Finis.
|
||
211 | """ #" |
||
212 | # ^
|
||
213 | # |----helps out font-lock
|
||
214 | |||
215 | #
|
||
216 | # Import our required modules
|
||
217 | #
|
||
218 | import string, sys |
||
219 | from UserDict import UserDict |
||
220 | |||
221 | try:
|
||
222 | from cPickle import dumps, loads |
||
223 | except ImportError: |
||
224 | from pickle import dumps, loads |
||
225 | |||
226 | try:
|
||
227 | import re |
||
228 | except ImportError: |
||
229 | raise ImportError, "Cookie.py requires 're' from Python 1.5 or later" |
||
230 | |||
231 | __all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie", |
||
232 | "SmartCookie","Cookie"] |
||
233 | |||
234 | #
|
||
235 | # Define an exception visible to External modules
|
||
236 | #
|
||
237 | class CookieError(Exception): |
||
238 | pass
|
||
239 | |||
240 | |||
241 | # These quoting routines conform to the RFC2109 specification, which in
|
||
242 | # turn references the character definitions from RFC2068. They provide
|
||
243 | # a two-way quoting algorithm. Any non-text character is translated
|
||
244 | # into a 4 character sequence: a forward-slash followed by the
|
||
245 | # three-digit octal equivalent of the character. Any '\' or '"' is
|
||
246 | # quoted with a preceeding '\' slash.
|
||
247 | #
|
||
248 | # These are taken from RFC2068 and RFC2109.
|
||
249 | # _LegalChars is the list of chars which don't require "'s
|
||
250 | # _Translator hash-table for fast quoting
|
||
251 | #
|
||
252 | _LegalChars = string.letters + string.digits + "!#$%&'*+-.^_`|~"
|
||
253 | _Translator = { |
||
254 | '\000' : '\\000', '\001' : '\\001', '\002' : '\\002', |
||
255 | '\003' : '\\003', '\004' : '\\004', '\005' : '\\005', |
||
256 | '\006' : '\\006', '\007' : '\\007', '\010' : '\\010', |
||
257 | '\011' : '\\011', '\012' : '\\012', '\013' : '\\013', |
||
258 | '\014' : '\\014', '\015' : '\\015', '\016' : '\\016', |
||
259 | '\017' : '\\017', '\020' : '\\020', '\021' : '\\021', |
||
260 | '\022' : '\\022', '\023' : '\\023', '\024' : '\\024', |
||
261 | '\025' : '\\025', '\026' : '\\026', '\027' : '\\027', |
||
262 | '\030' : '\\030', '\031' : '\\031', '\032' : '\\032', |
||
263 | '\033' : '\\033', '\034' : '\\034', '\035' : '\\035', |
||
264 | '\036' : '\\036', '\037' : '\\037', |
||
265 | |||
266 | '"' : '\\"', '\\' : '\\\\', |
||
267 | |||
268 | '\177' : '\\177', '\200' : '\\200', '\201' : '\\201', |
||
269 | '\202' : '\\202', '\203' : '\\203', '\204' : '\\204', |
||
270 | '\205' : '\\205', '\206' : '\\206', '\207' : '\\207', |
||
271 | '\210' : '\\210', '\211' : '\\211', '\212' : '\\212', |
||
272 | '\213' : '\\213', '\214' : '\\214', '\215' : '\\215', |
||
273 | '\216' : '\\216', '\217' : '\\217', '\220' : '\\220', |
||
274 | '\221' : '\\221', '\222' : '\\222', '\223' : '\\223', |
||
275 | '\224' : '\\224', '\225' : '\\225', '\226' : '\\226', |
||
276 | '\227' : '\\227', '\230' : '\\230', '\231' : '\\231', |
||
277 | '\232' : '\\232', '\233' : '\\233', '\234' : '\\234', |
||
278 | '\235' : '\\235', '\236' : '\\236', '\237' : '\\237', |
||
279 | '\240' : '\\240', '\241' : '\\241', '\242' : '\\242', |
||
280 | '\243' : '\\243', '\244' : '\\244', '\245' : '\\245', |
||
281 | '\246' : '\\246', '\247' : '\\247', '\250' : '\\250', |
||
282 | '\251' : '\\251', '\252' : '\\252', '\253' : '\\253', |
||
283 | '\254' : '\\254', '\255' : '\\255', '\256' : '\\256', |
||
284 | '\257' : '\\257', '\260' : '\\260', '\261' : '\\261', |
||
285 | '\262' : '\\262', '\263' : '\\263', '\264' : '\\264', |
||
286 | '\265' : '\\265', '\266' : '\\266', '\267' : '\\267', |
||
287 | '\270' : '\\270', '\271' : '\\271', '\272' : '\\272', |
||
288 | '\273' : '\\273', '\274' : '\\274', '\275' : '\\275', |
||
289 | '\276' : '\\276', '\277' : '\\277', '\300' : '\\300', |
||
290 | '\301' : '\\301', '\302' : '\\302', '\303' : '\\303', |
||
291 | '\304' : '\\304', '\305' : '\\305', '\306' : '\\306', |
||
292 | '\307' : '\\307', '\310' : '\\310', '\311' : '\\311', |
||
293 | '\312' : '\\312', '\313' : '\\313', '\314' : '\\314', |
||
294 | '\315' : '\\315', '\316' : '\\316', '\317' : '\\317', |
||
295 | '\320' : '\\320', '\321' : '\\321', '\322' : '\\322', |
||
296 | '\323' : '\\323', '\324' : '\\324', '\325' : '\\325', |
||
297 | '\326' : '\\326', '\327' : '\\327', '\330' : '\\330', |
||
298 | '\331' : '\\331', '\332' : '\\332', '\333' : '\\333', |
||
299 | '\334' : '\\334', '\335' : '\\335', '\336' : '\\336', |
||
300 | '\337' : '\\337', '\340' : '\\340', '\341' : '\\341', |
||
301 | '\342' : '\\342', '\343' : '\\343', '\344' : '\\344', |
||
302 | '\345' : '\\345', '\346' : '\\346', '\347' : '\\347', |
||
303 | '\350' : '\\350', '\351' : '\\351', '\352' : '\\352', |
||
304 | '\353' : '\\353', '\354' : '\\354', '\355' : '\\355', |
||
305 | '\356' : '\\356', '\357' : '\\357', '\360' : '\\360', |
||
306 | '\361' : '\\361', '\362' : '\\362', '\363' : '\\363', |
||
307 | '\364' : '\\364', '\365' : '\\365', '\366' : '\\366', |
||
308 | '\367' : '\\367', '\370' : '\\370', '\371' : '\\371', |
||
309 | '\372' : '\\372', '\373' : '\\373', '\374' : '\\374', |
||
310 | '\375' : '\\375', '\376' : '\\376', '\377' : '\\377' |
||
311 | } |
||
312 | |||
313 | def _quote(str, LegalChars=_LegalChars, |
||
314 | join=string.join, idmap=string._idmap, translate=string.translate): |
||
315 | #
|
||
316 | # If the string does not need to be double-quoted,
|
||
317 | # then just return the string. Otherwise, surround
|
||
318 | # the string in doublequotes and precede quote (with a \)
|
||
319 | # special characters.
|
||
320 | #
|
||
321 | if "" == translate(str, idmap, LegalChars): |
||
322 | return str |
||
323 | else:
|
||
324 | return '"' + join( map(_Translator.get, str, str), "" ) + '"' |
||
325 | # end _quote
|
||
326 | |||
327 | |||
328 | _OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
|
||
329 | _QuotePatt = re.compile(r"[\\].")
|
||
330 | |||
331 | def _unquote(str, join=string.join, atoi=string.atoi): |
||
332 | # If there aren't any doublequotes,
|
||
333 | # then there can't be any special characters. See RFC 2109.
|
||
334 | if len(str) < 2: |
||
335 | return str |
||
336 | if str[0] != '"' or str[-1] != '"': |
||
337 | return str |
||
338 | |||
339 | # We have to assume that we must decode this string.
|
||
340 | # Down to work.
|
||
341 | |||
342 | # Remove the "s
|
||
343 | str = str[1:-1] |
||
344 | |||
345 | # Check for special sequences. Examples:
|
||
346 | # \012 --> \n
|
||
347 | # \" --> "
|
||
348 | #
|
||
349 | i = 0
|
||
350 | n = len(str) |
||
351 | res = [] |
||
352 | while 0 <= i < n: |
||
353 | Omatch = _OctalPatt.search(str, i)
|
||
354 | Qmatch = _QuotePatt.search(str, i)
|
||
355 | if not Omatch and not Qmatch: # Neither matched |
||
356 | res.append(str[i:])
|
||
357 | break
|
||
358 | # else:
|
||
359 | j = k = -1
|
||
360 | if Omatch: j = Omatch.start(0) |
||
361 | if Qmatch: k = Qmatch.start(0) |
||
362 | if Qmatch and ( not Omatch or k < j ): # QuotePatt matched |
||
363 | res.append(str[i:k])
|
||
364 | res.append(str[k+1]) |
||
365 | i = k+2
|
||
366 | else: # OctalPatt matched |
||
367 | res.append(str[i:j])
|
||
368 | res.append( chr( atoi(str[j+1:j+4], 8) ) ) |
||
369 | i = j+4
|
||
370 | return join(res, "") |
||
371 | # end _unquote
|
||
372 | |||
373 | # The _getdate() routine is used to set the expiration time in
|
||
374 | # the cookie's HTTP header. By default, _getdate() returns the
|
||
375 | # current time in the appropriate "expires" format for a
|
||
376 | # Set-Cookie header. The one optional argument is an offset from
|
||
377 | # now, in seconds. For example, an offset of -3600 means "one hour ago".
|
||
378 | # The offset may be a floating point number.
|
||
379 | #
|
||
380 | |||
381 | _weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] |
||
382 | |||
383 | _monthname = [None,
|
||
384 | 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', |
||
385 | 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] |
||
386 | |||
387 | def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname): |
||
388 | from time import gmtime, time |
||
389 | now = time() |
||
390 | year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future) |
||
391 | return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \ |
||
392 | (weekdayname[wd], day, monthname[month], year, hh, mm, ss) |
||
393 | |||
394 | |||
395 | #
|
||
396 | # A class to hold ONE key,value pair.
|
||
397 | # In a cookie, each such pair may have several attributes.
|
||
398 | # so this class is used to keep the attributes associated
|
||
399 | # with the appropriate key,value pair.
|
||
400 | # This class also includes a coded_value attribute, which
|
||
401 | # is used to hold the network representation of the
|
||
402 | # value. This is most useful when Python objects are
|
||
403 | # pickled for network transit.
|
||
404 | #
|
||
405 | |||
406 | class Morsel(UserDict): |
||
407 | # RFC 2109 lists these attributes as reserved:
|
||
408 | # path comment domain
|
||
409 | # max-age secure version
|
||
410 | #
|
||
411 | # For historical reasons, these attributes are also reserved:
|
||
412 | # expires
|
||
413 | #
|
||
414 | # This dictionary provides a mapping from the lowercase
|
||
415 | # variant on the left to the appropriate traditional
|
||
416 | # formatting on the right.
|
||
417 | _reserved = { "expires" : "expires", |
||
418 | "path" : "Path", |
||
419 | "comment" : "Comment", |
||
420 | "domain" : "Domain", |
||
421 | "max-age" : "Max-Age", |
||
422 | "secure" : "secure", |
||
423 | "version" : "Version", |
||
424 | } |
||
425 | _reserved_keys = _reserved.keys() |
||
426 | |||
427 | def __init__(self): |
||
428 | # Set defaults
|
||
429 | self.key = self.value = self.coded_value = None |
||
430 | UserDict.__init__(self)
|
||
431 | |||
432 | # Set default attributes
|
||
433 | for K in self._reserved_keys: |
||
434 | UserDict.__setitem__(self, K, "") |
||
435 | # end __init__
|
||
436 | |||
437 | def __setitem__(self, K, V): |
||
438 | K = string.lower(K) |
||
439 | if not K in self._reserved_keys: |
||
440 | raise CookieError("Invalid Attribute %s" % K) |
||
441 | UserDict.__setitem__(self, K, V)
|
||
442 | # end __setitem__
|
||
443 | |||
444 | def isReservedKey(self, K): |
||
445 | return string.lower(K) in self._reserved_keys |
||
446 | # end isReservedKey
|
||
447 | |||
448 | def set(self, key, val, coded_val, |
||
449 | LegalChars=_LegalChars, |
||
450 | idmap=string._idmap, translate=string.translate ): |
||
451 | # First we verify that the key isn't a reserved word
|
||
452 | # Second we make sure it only contains legal characters
|
||
453 | if string.lower(key) in self._reserved_keys: |
||
454 | raise CookieError("Attempt to set a reserved key: %s" % key) |
||
455 | if "" != translate(key, idmap, LegalChars): |
||
456 | raise CookieError("Illegal key value: %s" % key) |
||
457 | |||
458 | # It's a good key, so save it.
|
||
459 | self.key = key
|
||
460 | self.value = val
|
||
461 | self.coded_value = coded_val
|
||
462 | # end set
|
||
463 | |||
464 | def output(self, attrs=None, header = "Set-Cookie:"): |
||
465 | return "%s %s" % ( header, self.OutputString(attrs) ) |
||
466 | |||
467 | __str__ = output |
||
468 | |||
469 | def __repr__(self): |
||
470 | return '<%s: %s=%s>' % (self.__class__.__name__, |
||
471 | self.key, repr(self.value) ) |
||
472 | |||
473 | def js_output(self, attrs=None): |
||
474 | # Print javascript
|
||
475 | return """ |
||
476 | <SCRIPT LANGUAGE="JavaScript">
|
||
477 | <!-- begin hiding
|
||
478 | document.cookie = \"%s\"
|
||
479 | // end hiding -->
|
||
480 | </script>
|
||
481 | """ % ( self.OutputString(attrs), ) |
||
482 | # end js_output()
|
||
483 | |||
484 | def OutputString(self, attrs=None): |
||
485 | # Build up our result
|
||
486 | #
|
||
487 | result = [] |
||
488 | RA = result.append |
||
489 | |||
490 | # First, the key=value pair
|
||
491 | RA("%s=%s;" % (self.key, self.coded_value)) |
||
492 | |||
493 | # Now add any defined attributes
|
||
494 | if attrs is None: |
||
495 | attrs = self._reserved_keys
|
||
496 | for K,V in self.items(): |
||
497 | if V == "": continue |
||
498 | if K not in attrs: continue |
||
499 | if K == "expires" and type(V) == type(1): |
||
500 | RA("%s=%s;" % (self._reserved[K], _getdate(V))) |
||
501 | elif K == "max-age" and type(V) == type(1): |
||
502 | RA("%s=%d;" % (self._reserved[K], V)) |
||
503 | elif K == "secure": |
||
504 | RA("%s;" % self._reserved[K]) |
||
505 | else:
|
||
506 | RA("%s=%s;" % (self._reserved[K], V)) |
||
507 | |||
508 | # Return the result
|
||
509 | return string.join(result, " ") |
||
510 | # end OutputString
|
||
511 | # end Morsel class
|
||
512 | |||
513 | |||
514 | |||
515 | #
|
||
516 | # Pattern for finding cookie
|
||
517 | #
|
||
518 | # This used to be strict parsing based on the RFC2109 and RFC2068
|
||
519 | # specifications. I have since discovered that MSIE 3.0x doesn't
|
||
520 | # follow the character rules outlined in those specs. As a
|
||
521 | # result, the parsing rules here are less strict.
|
||
522 | #
|
||
523 | |||
524 | _LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
|
||
525 | _CookiePattern = re.compile( |
||
526 | r"(?x)" # This is a Verbose pattern |
||
527 | r"(?P<key>" # Start of group 'key' |
||
528 | ""+ _LegalCharsPatt +"+?" # Any word of at least one letter, nongreedy |
||
529 | r")" # End of group 'key' |
||
530 | r"\s*=\s*" # Equal Sign |
||
531 | r"(?P<val>" # Start of group 'val' |
||
532 | r'"(?:[^\\"]|\\.)*"' # Any doublequoted string |
||
533 | r"|" # or |
||
534 | ""+ _LegalCharsPatt +"*" # Any word or empty string |
||
535 | r")" # End of group 'val' |
||
536 | r"\s*;?" # Probably ending in a semi-colon |
||
537 | ) |
||
538 | |||
539 | |||
540 | # At long last, here is the cookie class.
|
||
541 | # Using this class is almost just like using a dictionary.
|
||
542 | # See this module's docstring for example usage.
|
||
543 | #
|
||
544 | class BaseCookie(UserDict): |
||
545 | # A container class for a set of Morsels
|
||
546 | #
|
||
547 | |||
548 | def value_decode(self, val): |
||
549 | """real_value, coded_value = value_decode(STRING)
|
||
550 | Called prior to setting a cookie's value from the network
|
||
551 | representation. The VALUE is the value read from HTTP
|
||
552 | header.
|
||
553 | Override this function to modify the behavior of cookies.
|
||
554 | """
|
||
555 | return val, val
|
||
556 | # end value_encode
|
||
557 | |||
558 | def value_encode(self, val): |
||
559 | """real_value, coded_value = value_encode(VALUE)
|
||
560 | Called prior to setting a cookie's value from the dictionary
|
||
561 | representation. The VALUE is the value being assigned.
|
||
562 | Override this function to modify the behavior of cookies.
|
||
563 | """
|
||
564 | strval = str(val)
|
||
565 | return strval, strval
|
||
566 | # end value_encode
|
||
567 | |||
568 | def __init__(self, input=None): |
||
569 | UserDict.__init__(self)
|
||
570 | if input: self.load(input) |
||
571 | # end __init__
|
||
572 | |||
573 | def __set(self, key, real_value, coded_value): |
||
574 | """Private method for setting a cookie's value"""
|
||
575 | M = self.get(key, Morsel())
|
||
576 | M.set(key, real_value, coded_value) |
||
577 | UserDict.__setitem__(self, key, M)
|
||
578 | # end __set
|
||
579 | |||
580 | def __setitem__(self, key, value): |
||
581 | """Dictionary style assignment."""
|
||
582 | rval, cval = self.value_encode(value)
|
||
583 | self.__set(key, rval, cval)
|
||
584 | # end __setitem__
|
||
585 | |||
586 | def output(self, attrs=None, header="Set-Cookie:", sep="\n"): |
||
587 | """Return a string suitable for HTTP."""
|
||
588 | result = [] |
||
589 | for K,V in self.items(): |
||
590 | result.append( V.output(attrs, header) ) |
||
591 | return string.join(result, sep)
|
||
592 | # end output
|
||
593 | |||
594 | __str__ = output |
||
595 | |||
596 | def __repr__(self): |
||
597 | L = [] |
||
598 | for K,V in self.items(): |
||
599 | L.append( '%s=%s' % (K,repr(V.value) ) ) |
||
600 | return '<%s: %s>' % (self.__class__.__name__, string.join(L)) |
||
601 | |||
602 | def js_output(self, attrs=None): |
||
603 | """Return a string suitable for JavaScript."""
|
||
604 | result = [] |
||
605 | for K,V in self.items(): |
||
606 | result.append( V.js_output(attrs) ) |
||
607 | return string.join(result, "") |
||
608 | # end js_output
|
||
609 | |||
610 | def load(self, rawdata): |
||
611 | """Load cookies from a string (presumably HTTP_COOKIE) or
|
||
612 | from a dictionary. Loading cookies from a dictionary 'd'
|
||
613 | is equivalent to calling:
|
||
614 | map(Cookie.__setitem__, d.keys(), d.values())
|
||
615 | """
|
||
616 | if type(rawdata) == type(""): |
||
617 | self.__ParseString(rawdata)
|
||
618 | else:
|
||
619 | self.update(rawdata)
|
||
620 | return
|
||
621 | # end load()
|
||
622 | |||
623 | def __ParseString(self, str, patt=_CookiePattern): |
||
624 | i = 0 # Our starting point |
||
625 | n = len(str) # Length of string |
||
626 | M = None # current morsel |
||
627 | |||
628 | while 0 <= i < n: |
||
629 | # Start looking for a cookie
|
||
630 | match = patt.search(str, i)
|
||
631 | if not match: break # No more cookies |
||
632 | |||
633 | K,V = match.group("key"), match.group("val") |
||
634 | i = match.end(0)
|
||
635 | |||
636 | # Parse the key, value in case it's metainfo
|
||
637 | if K[0] == "$": |
||
638 | # We ignore attributes which pertain to the cookie
|
||
639 | # mechanism as a whole. See RFC 2109.
|
||
640 | # (Does anyone care?)
|
||
641 | if M:
|
||
642 | M[ K[1:] ] = V
|
||
643 | elif string.lower(K) in Morsel._reserved_keys: |
||
644 | if M:
|
||
645 | M[ K ] = _unquote(V) |
||
646 | else:
|
||
647 | rval, cval = self.value_decode(V)
|
||
648 | self.__set(K, rval, cval)
|
||
649 | M = self[K]
|
||
650 | # end __ParseString
|
||
651 | # end BaseCookie class
|
||
652 | |||
653 | class SimpleCookie(BaseCookie): |
||
654 | """SimpleCookie
|
||
655 | SimpleCookie supports strings as cookie values. When setting
|
||
656 | the value using the dictionary assignment notation, SimpleCookie
|
||
657 | calls the builtin str() to convert the value to a string. Values
|
||
658 | received from HTTP are kept as strings.
|
||
659 | """
|
||
660 | def value_decode(self, val): |
||
661 | return _unquote( val ), val
|
||
662 | def value_encode(self, val): |
||
663 | strval = str(val)
|
||
664 | return strval, _quote( strval )
|
||
665 | # end SimpleCookie
|
||
666 | |||
667 | class SerialCookie(BaseCookie): |
||
668 | """SerialCookie
|
||
669 | SerialCookie supports arbitrary objects as cookie values. All
|
||
670 | values are serialized (using cPickle) before being sent to the
|
||
671 | client. All incoming values are assumed to be valid Pickle
|
||
672 | representations. IF AN INCOMING VALUE IS NOT IN A VALID PICKLE
|
||
673 | FORMAT, THEN AN EXCEPTION WILL BE RAISED.
|
||
674 |
|
||
675 | Note: Large cookie values add overhead because they must be
|
||
676 | retransmitted on every HTTP transaction.
|
||
677 |
|
||
678 | Note: HTTP has a 2k limit on the size of a cookie. This class
|
||
679 | does not check for this limit, so be careful!!!
|
||
680 | """
|
||
681 | def value_decode(self, val): |
||
682 | # This could raise an exception!
|
||
683 | return loads( _unquote(val) ), val
|
||
684 | def value_encode(self, val): |
||
685 | return val, _quote( dumps(val) )
|
||
686 | # end SerialCookie
|
||
687 | |||
688 | class SmartCookie(BaseCookie): |
||
689 | """SmartCookie
|
||
690 | SmartCookie supports arbitrary objects as cookie values. If the
|
||
691 | object is a string, then it is quoted. If the object is not a
|
||
692 | string, however, then SmartCookie will use cPickle to serialize
|
||
693 | the object into a string representation.
|
||
694 |
|
||
695 | Note: Large cookie values add overhead because they must be
|
||
696 | retransmitted on every HTTP transaction.
|
||
697 |
|
||
698 | Note: HTTP has a 2k limit on the size of a cookie. This class
|
||
699 | does not check for this limit, so be careful!!!
|
||
700 | """
|
||
701 | def value_decode(self, val): |
||
702 | strval = _unquote(val) |
||
703 | try:
|
||
704 | return loads(strval), val
|
||
705 | except:
|
||
706 | return strval, val
|
||
707 | def value_encode(self, val): |
||
708 | if type(val) == type(""): |
||
709 | return val, _quote(val)
|
||
710 | else:
|
||
711 | return val, _quote( dumps(val) )
|
||
712 | # end SmartCookie
|
||
713 | |||
714 | |||
715 | ###########################################################
|
||
716 | # Backwards Compatibility: Don't break any existing code!
|
||
717 | |||
718 | # We provide Cookie() as an alias for SmartCookie()
|
||
719 | Cookie = SmartCookie |
||
720 | |||
721 | #
|
||
722 | ###########################################################
|
||
723 | |||
724 | def _test(): |
||
725 | import doctest, Cookie |
||
726 | return doctest.testmod(Cookie)
|
||
727 | |||
728 | if __name__ == "__main__": |
||
729 | _test() |
||
730 | |||
731 | |||
732 | #Local Variables:
|
||
733 | #tab-width: 4
|
||
734 | #end: |