comparison planemo/lib/python3.7/site-packages/future/backports/http/cookies.py @ 0:d30785e31577 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:18:57 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d30785e31577
1 ####
2 # Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
3 #
4 # All Rights Reserved
5 #
6 # Permission to use, copy, modify, and distribute this software
7 # and its documentation for any purpose and without fee is hereby
8 # granted, provided that the above copyright notice appear in all
9 # copies and that both that copyright notice and this permission
10 # notice appear in supporting documentation, and that the name of
11 # Timothy O'Malley not be used in advertising or publicity
12 # pertaining to distribution of the software without specific, written
13 # prior permission.
14 #
15 # Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16 # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
17 # AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
18 # ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
21 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 # PERFORMANCE OF THIS SOFTWARE.
23 #
24 ####
25 #
26 # Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
27 # by Timothy O'Malley <timo@alum.mit.edu>
28 #
29 # Cookie.py is a Python module for the handling of HTTP
30 # cookies as a Python dictionary. See RFC 2109 for more
31 # information on cookies.
32 #
33 # The original idea to treat Cookies as a dictionary came from
34 # Dave Mitchell (davem@magnet.com) in 1995, when he released the
35 # first version of nscookie.py.
36 #
37 ####
38
39 r"""
40 http.cookies module ported to python-future from Py3.3
41
42 Here's a sample session to show how to use this module.
43 At the moment, this is the only documentation.
44
45 The Basics
46 ----------
47
48 Importing is easy...
49
50 >>> from http import cookies
51
52 Most of the time you start by creating a cookie.
53
54 >>> C = cookies.SimpleCookie()
55
56 Once you've created your Cookie, you can add values just as if it were
57 a dictionary.
58
59 >>> C = cookies.SimpleCookie()
60 >>> C["fig"] = "newton"
61 >>> C["sugar"] = "wafer"
62 >>> C.output()
63 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
64
65 Notice that the printable representation of a Cookie is the
66 appropriate format for a Set-Cookie: header. This is the
67 default behavior. You can change the header and printed
68 attributes by using the .output() function
69
70 >>> C = cookies.SimpleCookie()
71 >>> C["rocky"] = "road"
72 >>> C["rocky"]["path"] = "/cookie"
73 >>> print(C.output(header="Cookie:"))
74 Cookie: rocky=road; Path=/cookie
75 >>> print(C.output(attrs=[], header="Cookie:"))
76 Cookie: rocky=road
77
78 The load() method of a Cookie extracts cookies from a string. In a
79 CGI script, you would use this method to extract the cookies from the
80 HTTP_COOKIE environment variable.
81
82 >>> C = cookies.SimpleCookie()
83 >>> C.load("chips=ahoy; vienna=finger")
84 >>> C.output()
85 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
86
87 The load() method is darn-tootin smart about identifying cookies
88 within a string. Escaped quotation marks, nested semicolons, and other
89 such trickeries do not confuse it.
90
91 >>> C = cookies.SimpleCookie()
92 >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
93 >>> print(C)
94 Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
95
96 Each element of the Cookie also supports all of the RFC 2109
97 Cookie attributes. Here's an example which sets the Path
98 attribute.
99
100 >>> C = cookies.SimpleCookie()
101 >>> C["oreo"] = "doublestuff"
102 >>> C["oreo"]["path"] = "/"
103 >>> print(C)
104 Set-Cookie: oreo=doublestuff; Path=/
105
106 Each dictionary element has a 'value' attribute, which gives you
107 back the value associated with the key.
108
109 >>> C = cookies.SimpleCookie()
110 >>> C["twix"] = "none for you"
111 >>> C["twix"].value
112 'none for you'
113
114 The SimpleCookie expects that all values should be standard strings.
115 Just to be sure, SimpleCookie invokes the str() builtin to convert
116 the value to a string, when the values are set dictionary-style.
117
118 >>> C = cookies.SimpleCookie()
119 >>> C["number"] = 7
120 >>> C["string"] = "seven"
121 >>> C["number"].value
122 '7'
123 >>> C["string"].value
124 'seven'
125 >>> C.output()
126 'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
127
128 Finis.
129 """
130 from __future__ import unicode_literals
131 from __future__ import print_function
132 from __future__ import division
133 from __future__ import absolute_import
134 from future.builtins import chr, dict, int, str
135 from future.utils import PY2, as_native_str
136
137 #
138 # Import our required modules
139 #
140 import re
141 if PY2:
142 re.ASCII = 0 # for py2 compatibility
143 import string
144
145 __all__ = ["CookieError", "BaseCookie", "SimpleCookie"]
146
147 _nulljoin = ''.join
148 _semispacejoin = '; '.join
149 _spacejoin = ' '.join
150
151 #
152 # Define an exception visible to External modules
153 #
154 class CookieError(Exception):
155 pass
156
157
158 # These quoting routines conform to the RFC2109 specification, which in
159 # turn references the character definitions from RFC2068. They provide
160 # a two-way quoting algorithm. Any non-text character is translated
161 # into a 4 character sequence: a forward-slash followed by the
162 # three-digit octal equivalent of the character. Any '\' or '"' is
163 # quoted with a preceeding '\' slash.
164 #
165 # These are taken from RFC2068 and RFC2109.
166 # _LegalChars is the list of chars which don't require "'s
167 # _Translator hash-table for fast quoting
168 #
169 _LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~:"
170 _Translator = {
171 '\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
172 '\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
173 '\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
174 '\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
175 '\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
176 '\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
177 '\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
178 '\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
179 '\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
180 '\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
181 '\036' : '\\036', '\037' : '\\037',
182
183 # Because of the way browsers really handle cookies (as opposed
184 # to what the RFC says) we also encode , and ;
185
186 ',' : '\\054', ';' : '\\073',
187
188 '"' : '\\"', '\\' : '\\\\',
189
190 '\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
191 '\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
192 '\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
193 '\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
194 '\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
195 '\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
196 '\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
197 '\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
198 '\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
199 '\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
200 '\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
201 '\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
202 '\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
203 '\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
204 '\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
205 '\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
206 '\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
207 '\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
208 '\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
209 '\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
210 '\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
211 '\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
212 '\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
213 '\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
214 '\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
215 '\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
216 '\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
217 '\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
218 '\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
219 '\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
220 '\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
221 '\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
222 '\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
223 '\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
224 '\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
225 '\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
226 '\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
227 '\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
228 '\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
229 '\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
230 '\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
231 '\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
232 '\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
233 }
234
235 def _quote(str, LegalChars=_LegalChars):
236 r"""Quote a string for use in a cookie header.
237
238 If the string does not need to be double-quoted, then just return the
239 string. Otherwise, surround the string in doublequotes and quote
240 (with a \) special characters.
241 """
242 if all(c in LegalChars for c in str):
243 return str
244 else:
245 return '"' + _nulljoin(_Translator.get(s, s) for s in str) + '"'
246
247
248 _OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
249 _QuotePatt = re.compile(r"[\\].")
250
251 def _unquote(mystr):
252 # If there aren't any doublequotes,
253 # then there can't be any special characters. See RFC 2109.
254 if len(mystr) < 2:
255 return mystr
256 if mystr[0] != '"' or mystr[-1] != '"':
257 return mystr
258
259 # We have to assume that we must decode this string.
260 # Down to work.
261
262 # Remove the "s
263 mystr = mystr[1:-1]
264
265 # Check for special sequences. Examples:
266 # \012 --> \n
267 # \" --> "
268 #
269 i = 0
270 n = len(mystr)
271 res = []
272 while 0 <= i < n:
273 o_match = _OctalPatt.search(mystr, i)
274 q_match = _QuotePatt.search(mystr, i)
275 if not o_match and not q_match: # Neither matched
276 res.append(mystr[i:])
277 break
278 # else:
279 j = k = -1
280 if o_match:
281 j = o_match.start(0)
282 if q_match:
283 k = q_match.start(0)
284 if q_match and (not o_match or k < j): # QuotePatt matched
285 res.append(mystr[i:k])
286 res.append(mystr[k+1])
287 i = k + 2
288 else: # OctalPatt matched
289 res.append(mystr[i:j])
290 res.append(chr(int(mystr[j+1:j+4], 8)))
291 i = j + 4
292 return _nulljoin(res)
293
294 # The _getdate() routine is used to set the expiration time in the cookie's HTTP
295 # header. By default, _getdate() returns the current time in the appropriate
296 # "expires" format for a Set-Cookie header. The one optional argument is an
297 # offset from now, in seconds. For example, an offset of -3600 means "one hour
298 # ago". The offset may be a floating point number.
299 #
300
301 _weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
302
303 _monthname = [None,
304 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
305 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
306
307 def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
308 from time import gmtime, time
309 now = time()
310 year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
311 return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \
312 (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
313
314
315 class Morsel(dict):
316 """A class to hold ONE (key, value) pair.
317
318 In a cookie, each such pair may have several attributes, so this class is
319 used to keep the attributes associated with the appropriate key,value pair.
320 This class also includes a coded_value attribute, which is used to hold
321 the network representation of the value. This is most useful when Python
322 objects are pickled for network transit.
323 """
324 # RFC 2109 lists these attributes as reserved:
325 # path comment domain
326 # max-age secure version
327 #
328 # For historical reasons, these attributes are also reserved:
329 # expires
330 #
331 # This is an extension from Microsoft:
332 # httponly
333 #
334 # This dictionary provides a mapping from the lowercase
335 # variant on the left to the appropriate traditional
336 # formatting on the right.
337 _reserved = {
338 "expires" : "expires",
339 "path" : "Path",
340 "comment" : "Comment",
341 "domain" : "Domain",
342 "max-age" : "Max-Age",
343 "secure" : "secure",
344 "httponly" : "httponly",
345 "version" : "Version",
346 }
347
348 _flags = set(['secure', 'httponly'])
349
350 def __init__(self):
351 # Set defaults
352 self.key = self.value = self.coded_value = None
353
354 # Set default attributes
355 for key in self._reserved:
356 dict.__setitem__(self, key, "")
357
358 def __setitem__(self, K, V):
359 K = K.lower()
360 if not K in self._reserved:
361 raise CookieError("Invalid Attribute %s" % K)
362 dict.__setitem__(self, K, V)
363
364 def isReservedKey(self, K):
365 return K.lower() in self._reserved
366
367 def set(self, key, val, coded_val, LegalChars=_LegalChars):
368 # First we verify that the key isn't a reserved word
369 # Second we make sure it only contains legal characters
370 if key.lower() in self._reserved:
371 raise CookieError("Attempt to set a reserved key: %s" % key)
372 if any(c not in LegalChars for c in key):
373 raise CookieError("Illegal key value: %s" % key)
374
375 # It's a good key, so save it.
376 self.key = key
377 self.value = val
378 self.coded_value = coded_val
379
380 def output(self, attrs=None, header="Set-Cookie:"):
381 return "%s %s" % (header, self.OutputString(attrs))
382
383 __str__ = output
384
385 @as_native_str()
386 def __repr__(self):
387 if PY2 and isinstance(self.value, unicode):
388 val = str(self.value) # make it a newstr to remove the u prefix
389 else:
390 val = self.value
391 return '<%s: %s=%s>' % (self.__class__.__name__,
392 str(self.key), repr(val))
393
394 def js_output(self, attrs=None):
395 # Print javascript
396 return """
397 <script type="text/javascript">
398 <!-- begin hiding
399 document.cookie = \"%s\";
400 // end hiding -->
401 </script>
402 """ % (self.OutputString(attrs).replace('"', r'\"'))
403
404 def OutputString(self, attrs=None):
405 # Build up our result
406 #
407 result = []
408 append = result.append
409
410 # First, the key=value pair
411 append("%s=%s" % (self.key, self.coded_value))
412
413 # Now add any defined attributes
414 if attrs is None:
415 attrs = self._reserved
416 items = sorted(self.items())
417 for key, value in items:
418 if value == "":
419 continue
420 if key not in attrs:
421 continue
422 if key == "expires" and isinstance(value, int):
423 append("%s=%s" % (self._reserved[key], _getdate(value)))
424 elif key == "max-age" and isinstance(value, int):
425 append("%s=%d" % (self._reserved[key], value))
426 elif key == "secure":
427 append(str(self._reserved[key]))
428 elif key == "httponly":
429 append(str(self._reserved[key]))
430 else:
431 append("%s=%s" % (self._reserved[key], value))
432
433 # Return the result
434 return _semispacejoin(result)
435
436
437 #
438 # Pattern for finding cookie
439 #
440 # This used to be strict parsing based on the RFC2109 and RFC2068
441 # specifications. I have since discovered that MSIE 3.0x doesn't
442 # follow the character rules outlined in those specs. As a
443 # result, the parsing rules here are less strict.
444 #
445
446 _LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
447 _CookiePattern = re.compile(r"""
448 (?x) # This is a verbose pattern
449 (?P<key> # Start of group 'key'
450 """ + _LegalCharsPatt + r"""+? # Any word of at least one letter
451 ) # End of group 'key'
452 ( # Optional group: there may not be a value.
453 \s*=\s* # Equal Sign
454 (?P<val> # Start of group 'val'
455 "(?:[^\\"]|\\.)*" # Any doublequoted string
456 | # or
457 \w{3},\s[\w\d\s-]{9,11}\s[\d:]{8}\sGMT # Special case for "expires" attr
458 | # or
459 """ + _LegalCharsPatt + r"""* # Any word or empty string
460 ) # End of group 'val'
461 )? # End of optional value group
462 \s* # Any number of spaces.
463 (\s+|;|$) # Ending either at space, semicolon, or EOS.
464 """, re.ASCII) # May be removed if safe.
465
466
467 # At long last, here is the cookie class. Using this class is almost just like
468 # using a dictionary. See this module's docstring for example usage.
469 #
470 class BaseCookie(dict):
471 """A container class for a set of Morsels."""
472
473 def value_decode(self, val):
474 """real_value, coded_value = value_decode(STRING)
475 Called prior to setting a cookie's value from the network
476 representation. The VALUE is the value read from HTTP
477 header.
478 Override this function to modify the behavior of cookies.
479 """
480 return val, val
481
482 def value_encode(self, val):
483 """real_value, coded_value = value_encode(VALUE)
484 Called prior to setting a cookie's value from the dictionary
485 representation. The VALUE is the value being assigned.
486 Override this function to modify the behavior of cookies.
487 """
488 strval = str(val)
489 return strval, strval
490
491 def __init__(self, input=None):
492 if input:
493 self.load(input)
494
495 def __set(self, key, real_value, coded_value):
496 """Private method for setting a cookie's value"""
497 M = self.get(key, Morsel())
498 M.set(key, real_value, coded_value)
499 dict.__setitem__(self, key, M)
500
501 def __setitem__(self, key, value):
502 """Dictionary style assignment."""
503 rval, cval = self.value_encode(value)
504 self.__set(key, rval, cval)
505
506 def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
507 """Return a string suitable for HTTP."""
508 result = []
509 items = sorted(self.items())
510 for key, value in items:
511 result.append(value.output(attrs, header))
512 return sep.join(result)
513
514 __str__ = output
515
516 @as_native_str()
517 def __repr__(self):
518 l = []
519 items = sorted(self.items())
520 for key, value in items:
521 if PY2 and isinstance(value.value, unicode):
522 val = str(value.value) # make it a newstr to remove the u prefix
523 else:
524 val = value.value
525 l.append('%s=%s' % (str(key), repr(val)))
526 return '<%s: %s>' % (self.__class__.__name__, _spacejoin(l))
527
528 def js_output(self, attrs=None):
529 """Return a string suitable for JavaScript."""
530 result = []
531 items = sorted(self.items())
532 for key, value in items:
533 result.append(value.js_output(attrs))
534 return _nulljoin(result)
535
536 def load(self, rawdata):
537 """Load cookies from a string (presumably HTTP_COOKIE) or
538 from a dictionary. Loading cookies from a dictionary 'd'
539 is equivalent to calling:
540 map(Cookie.__setitem__, d.keys(), d.values())
541 """
542 if isinstance(rawdata, str):
543 self.__parse_string(rawdata)
544 else:
545 # self.update() wouldn't call our custom __setitem__
546 for key, value in rawdata.items():
547 self[key] = value
548 return
549
550 def __parse_string(self, mystr, patt=_CookiePattern):
551 i = 0 # Our starting point
552 n = len(mystr) # Length of string
553 M = None # current morsel
554
555 while 0 <= i < n:
556 # Start looking for a cookie
557 match = patt.search(mystr, i)
558 if not match:
559 # No more cookies
560 break
561
562 key, value = match.group("key"), match.group("val")
563
564 i = match.end(0)
565
566 # Parse the key, value in case it's metainfo
567 if key[0] == "$":
568 # We ignore attributes which pertain to the cookie
569 # mechanism as a whole. See RFC 2109.
570 # (Does anyone care?)
571 if M:
572 M[key[1:]] = value
573 elif key.lower() in Morsel._reserved:
574 if M:
575 if value is None:
576 if key.lower() in Morsel._flags:
577 M[key] = True
578 else:
579 M[key] = _unquote(value)
580 elif value is not None:
581 rval, cval = self.value_decode(value)
582 self.__set(key, rval, cval)
583 M = self[key]
584
585
586 class SimpleCookie(BaseCookie):
587 """
588 SimpleCookie supports strings as cookie values. When setting
589 the value using the dictionary assignment notation, SimpleCookie
590 calls the builtin str() to convert the value to a string. Values
591 received from HTTP are kept as strings.
592 """
593 def value_decode(self, val):
594 return _unquote(val), val
595
596 def value_encode(self, val):
597 strval = str(val)
598 return strval, _quote(strval)