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