~inkscape.dev/inkscape-devlibs64/trunk

« back to all changes in this revision

Viewing changes to python/Lib/Cookie.py

  • Committer: Eduard Braun
  • Date: 2016-10-22 16:51:19 UTC
  • Revision ID: eduard.braun2@gmx.de-20161022165119-9eosgy6lp8j1kzli
Update Python to version 2.7.12

Included modules:
  coverage 4.2
  lxml 3.6.4
  numpy 1.11.2
  scour 0.35
  six 1.10.0

Show diffs side-by-side

added added

removed removed

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