PageRenderTime 91ms CodeModel.GetById 15ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 0ms

/Lib/Cookie.py

http://unladen-swallow.googlecode.com/
Python | 752 lines | 638 code | 22 blank | 92 comment | 19 complexity | 524cdb2ebe2a4671091c4f8c27005dfd MD5 | raw file
  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\'\\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    '"' : '\\"',       '\\' : '\\\\',
262
263    '\177' : '\\177',  '\200' : '\\200',  '\201' : '\\201',
264    '\202' : '\\202',  '\203' : '\\203',  '\204' : '\\204',
265    '\205' : '\\205',  '\206' : '\\206',  '\207' : '\\207',
266    '\210' : '\\210',  '\211' : '\\211',  '\212' : '\\212',
267    '\213' : '\\213',  '\214' : '\\214',  '\215' : '\\215',
268    '\216' : '\\216',  '\217' : '\\217',  '\220' : '\\220',
269    '\221' : '\\221',  '\222' : '\\222',  '\223' : '\\223',
270    '\224' : '\\224',  '\225' : '\\225',  '\226' : '\\226',
271    '\227' : '\\227',  '\230' : '\\230',  '\231' : '\\231',
272    '\232' : '\\232',  '\233' : '\\233',  '\234' : '\\234',
273    '\235' : '\\235',  '\236' : '\\236',  '\237' : '\\237',
274    '\240' : '\\240',  '\241' : '\\241',  '\242' : '\\242',
275    '\243' : '\\243',  '\244' : '\\244',  '\245' : '\\245',
276    '\246' : '\\246',  '\247' : '\\247',  '\250' : '\\250',
277    '\251' : '\\251',  '\252' : '\\252',  '\253' : '\\253',
278    '\254' : '\\254',  '\255' : '\\255',  '\256' : '\\256',
279    '\257' : '\\257',  '\260' : '\\260',  '\261' : '\\261',
280    '\262' : '\\262',  '\263' : '\\263',  '\264' : '\\264',
281    '\265' : '\\265',  '\266' : '\\266',  '\267' : '\\267',
282    '\270' : '\\270',  '\271' : '\\271',  '\272' : '\\272',
283    '\273' : '\\273',  '\274' : '\\274',  '\275' : '\\275',
284    '\276' : '\\276',  '\277' : '\\277',  '\300' : '\\300',
285    '\301' : '\\301',  '\302' : '\\302',  '\303' : '\\303',
286    '\304' : '\\304',  '\305' : '\\305',  '\306' : '\\306',
287    '\307' : '\\307',  '\310' : '\\310',  '\311' : '\\311',
288    '\312' : '\\312',  '\313' : '\\313',  '\314' : '\\314',
289    '\315' : '\\315',  '\316' : '\\316',  '\317' : '\\317',
290    '\320' : '\\320',  '\321' : '\\321',  '\322' : '\\322',
291    '\323' : '\\323',  '\324' : '\\324',  '\325' : '\\325',
292    '\326' : '\\326',  '\327' : '\\327',  '\330' : '\\330',
293    '\331' : '\\331',  '\332' : '\\332',  '\333' : '\\333',
294    '\334' : '\\334',  '\335' : '\\335',  '\336' : '\\336',
295    '\337' : '\\337',  '\340' : '\\340',  '\341' : '\\341',
296    '\342' : '\\342',  '\343' : '\\343',  '\344' : '\\344',
297    '\345' : '\\345',  '\346' : '\\346',  '\347' : '\\347',
298    '\350' : '\\350',  '\351' : '\\351',  '\352' : '\\352',
299    '\353' : '\\353',  '\354' : '\\354',  '\355' : '\\355',
300    '\356' : '\\356',  '\357' : '\\357',  '\360' : '\\360',
301    '\361' : '\\361',  '\362' : '\\362',  '\363' : '\\363',
302    '\364' : '\\364',  '\365' : '\\365',  '\366' : '\\366',
303    '\367' : '\\367',  '\370' : '\\370',  '\371' : '\\371',
304    '\372' : '\\372',  '\373' : '\\373',  '\374' : '\\374',
305    '\375' : '\\375',  '\376' : '\\376',  '\377' : '\\377'
306    }
307
308_idmap = ''.join(chr(x) for x in xrange(256))
309
310def _quote(str, LegalChars=_LegalChars,
311           idmap=_idmap, translate=string.translate):
312    #
313    # If the string does not need to be double-quoted,
314    # then just return the string.  Otherwise, surround
315    # the string in doublequotes and precede quote (with a \)
316    # special characters.
317    #
318    if "" == translate(str, idmap, LegalChars):
319        return str
320    else:
321        return '"' + _nulljoin( map(_Translator.get, str, str) ) + '"'
322# end _quote
323
324
325_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
326_QuotePatt = re.compile(r"[\\].")
327
328def _unquote(str):
329    # If there aren't any doublequotes,
330    # then there can't be any special characters.  See RFC 2109.
331    if  len(str) < 2:
332        return str
333    if str[0] != '"' or str[-1] != '"':
334        return str
335
336    # We have to assume that we must decode this string.
337    # Down to work.
338
339    # Remove the "s
340    str = str[1:-1]
341
342    # Check for special sequences.  Examples:
343    #    \012 --> \n
344    #    \"   --> "
345    #
346    i = 0
347    n = len(str)
348    res = []
349    while 0 <= i < n:
350        Omatch = _OctalPatt.search(str, i)
351        Qmatch = _QuotePatt.search(str, i)
352        if not Omatch and not Qmatch:              # Neither matched
353            res.append(str[i:])
354            break
355        # else:
356        j = k = -1
357        if Omatch: j = Omatch.start(0)
358        if Qmatch: k = Qmatch.start(0)
359        if Qmatch and ( not Omatch or k < j ):     # QuotePatt matched
360            res.append(str[i:k])
361            res.append(str[k+1])
362            i = k+2
363        else:                                      # OctalPatt matched
364            res.append(str[i:j])
365            res.append( chr( int(str[j+1:j+4], 8) ) )
366            i = j+4
367    return _nulljoin(res)
368# end _unquote
369
370# The _getdate() routine is used to set the expiration time in
371# the cookie's HTTP header.      By default, _getdate() returns the
372# current time in the appropriate "expires" format for a
373# Set-Cookie header.     The one optional argument is an offset from
374# now, in seconds.      For example, an offset of -3600 means "one hour ago".
375# The offset may be a floating point number.
376#
377
378_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
379
380_monthname = [None,
381              'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
382              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
383
384def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
385    from time import gmtime, time
386    now = time()
387    year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
388    return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
389           (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
390
391
392#
393# A class to hold ONE key,value pair.
394# In a cookie, each such pair may have several attributes.
395#       so this class is used to keep the attributes associated
396#       with the appropriate key,value pair.
397# This class also includes a coded_value attribute, which
398#       is used to hold the network representation of the
399#       value.  This is most useful when Python objects are
400#       pickled for network transit.
401#
402
403class Morsel(dict):
404    # RFC 2109 lists these attributes as reserved:
405    #   path       comment         domain
406    #   max-age    secure      version
407    #
408    # For historical reasons, these attributes are also reserved:
409    #   expires
410    #
411    # This is an extension from Microsoft:
412    #   httponly
413    #
414    # This dictionary provides a mapping from the lowercase
415    # variant on the left to the appropriate traditional
416    # formatting on the right.
417    _reserved = { "expires" : "expires",
418                   "path"        : "Path",
419                   "comment" : "Comment",
420                   "domain"      : "Domain",
421                   "max-age" : "Max-Age",
422                   "secure"      : "secure",
423                   "httponly"  : "httponly",
424                   "version" : "Version",
425                   }
426
427    def __init__(self):
428        # Set defaults
429        self.key = self.value = self.coded_value = None
430
431        # Set default attributes
432        for K in self._reserved:
433            dict.__setitem__(self, K, "")
434    # end __init__
435
436    def __setitem__(self, K, V):
437        K = K.lower()
438        if not K in self._reserved:
439            raise CookieError("Invalid Attribute %s" % K)
440        dict.__setitem__(self, K, V)
441    # end __setitem__
442
443    def isReservedKey(self, K):
444        return K.lower() in self._reserved
445    # end isReservedKey
446
447    def set(self, key, val, coded_val,
448            LegalChars=_LegalChars,
449            idmap=_idmap, translate=string.translate):
450        # First we verify that the key isn't a reserved word
451        # Second we make sure it only contains legal characters
452        if key.lower() in self._reserved:
453            raise CookieError("Attempt to set a reserved key: %s" % key)
454        if "" != translate(key, idmap, LegalChars):
455            raise CookieError("Illegal key value: %s" % key)
456
457        # It's a good key, so save it.
458        self.key                 = key
459        self.value               = val
460        self.coded_value         = coded_val
461    # end set
462
463    def output(self, attrs=None, header = "Set-Cookie:"):
464        return "%s %s" % ( header, self.OutputString(attrs) )
465
466    __str__ = output
467
468    def __repr__(self):
469        return '<%s: %s=%s>' % (self.__class__.__name__,
470                                self.key, repr(self.value) )
471
472    def js_output(self, attrs=None):
473        # Print javascript
474        return """
475        <script type="text/javascript">
476        <!-- begin hiding
477        document.cookie = \"%s\";
478        // end hiding -->
479        </script>
480        """ % ( self.OutputString(attrs), )
481    # end js_output()
482
483    def OutputString(self, attrs=None):
484        # Build up our result
485        #
486        result = []
487        RA = result.append
488
489        # First, the key=value pair
490        RA("%s=%s" % (self.key, self.coded_value))
491
492        # Now add any defined attributes
493        if attrs is None:
494            attrs = self._reserved
495        items = self.items()
496        items.sort()
497        for K,V in items:
498            if V == "": continue
499            if K not in attrs: continue
500            if K == "expires" and type(V) == type(1):
501                RA("%s=%s" % (self._reserved[K], _getdate(V)))
502            elif K == "max-age" and type(V) == type(1):
503                RA("%s=%d" % (self._reserved[K], V))
504            elif K == "secure":
505                RA(str(self._reserved[K]))
506            elif K == "httponly":
507                RA(str(self._reserved[K]))
508            else:
509                RA("%s=%s" % (self._reserved[K], V))
510
511        # Return the result
512        return _semispacejoin(result)
513    # end OutputString
514# end Morsel class
515
516
517
518#
519# Pattern for finding cookie
520#
521# This used to be strict parsing based on the RFC2109 and RFC2068
522# specifications.  I have since discovered that MSIE 3.0x doesn't
523# follow the character rules outlined in those specs.  As a
524# result, the parsing rules here are less strict.
525#
526
527_LegalCharsPatt  = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
528_CookiePattern = re.compile(
529    r"(?x)"                       # This is a Verbose pattern
530    r"(?P<key>"                   # Start of group 'key'
531    ""+ _LegalCharsPatt +"+?"     # Any word of at least one letter, nongreedy
532    r")"                          # End of group 'key'
533    r"\s*=\s*"                    # Equal Sign
534    r"(?P<val>"                   # Start of group 'val'
535    r'"(?:[^\\"]|\\.)*"'            # Any doublequoted string
536    r"|"                            # or
537    ""+ _LegalCharsPatt +"*"        # Any word or empty string
538    r")"                          # End of group 'val'
539    r"\s*;?"                      # Probably ending in a semi-colon
540    )
541
542
543# At long last, here is the cookie class.
544#   Using this class is almost just like using a dictionary.
545# See this module's docstring for example usage.
546#
547class BaseCookie(dict):
548    # A container class for a set of Morsels
549    #
550
551    def value_decode(self, val):
552        """real_value, coded_value = value_decode(STRING)
553        Called prior to setting a cookie's value from the network
554        representation.  The VALUE is the value read from HTTP
555        header.
556        Override this function to modify the behavior of cookies.
557        """
558        return val, val
559    # end value_encode
560
561    def value_encode(self, val):
562        """real_value, coded_value = value_encode(VALUE)
563        Called prior to setting a cookie's value from the dictionary
564        representation.  The VALUE is the value being assigned.
565        Override this function to modify the behavior of cookies.
566        """
567        strval = str(val)
568        return strval, strval
569    # end value_encode
570
571    def __init__(self, input=None):
572        if input: self.load(input)
573    # end __init__
574
575    def __set(self, key, real_value, coded_value):
576        """Private method for setting a cookie's value"""
577        M = self.get(key, Morsel())
578        M.set(key, real_value, coded_value)
579        dict.__setitem__(self, key, M)
580    # end __set
581
582    def __setitem__(self, key, value):
583        """Dictionary style assignment."""
584        rval, cval = self.value_encode(value)
585        self.__set(key, rval, cval)
586    # end __setitem__
587
588    def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
589        """Return a string suitable for HTTP."""
590        result = []
591        items = self.items()
592        items.sort()
593        for K,V in items:
594            result.append( V.output(attrs, header) )
595        return sep.join(result)
596    # end output
597
598    __str__ = output
599
600    def __repr__(self):
601        L = []
602        items = self.items()
603        items.sort()
604        for K,V in items:
605            L.append( '%s=%s' % (K,repr(V.value) ) )
606        return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
607
608    def js_output(self, attrs=None):
609        """Return a string suitable for JavaScript."""
610        result = []
611        items = self.items()
612        items.sort()
613        for K,V in items:
614            result.append( V.js_output(attrs) )
615        return _nulljoin(result)
616    # end js_output
617
618    def load(self, rawdata):
619        """Load cookies from a string (presumably HTTP_COOKIE) or
620        from a dictionary.  Loading cookies from a dictionary 'd'
621        is equivalent to calling:
622            map(Cookie.__setitem__, d.keys(), d.values())
623        """
624        if type(rawdata) == type(""):
625            self.__ParseString(rawdata)
626        else:
627            self.update(rawdata)
628        return
629    # end load()
630
631    def __ParseString(self, str, patt=_CookiePattern):
632        i = 0            # Our starting point
633        n = len(str)     # Length of string
634        M = None         # current morsel
635
636        while 0 <= i < n:
637            # Start looking for a cookie
638            match = patt.search(str, i)
639            if not match: break          # No more cookies
640
641            K,V = match.group("key"), match.group("val")
642            i = match.end(0)
643
644            # Parse the key, value in case it's metainfo
645            if K[0] == "$":
646                # We ignore attributes which pertain to the cookie
647                # mechanism as a whole.  See RFC 2109.
648                # (Does anyone care?)
649                if M:
650                    M[ K[1:] ] = V
651            elif K.lower() in Morsel._reserved:
652                if M:
653                    M[ K ] = _unquote(V)
654            else:
655                rval, cval = self.value_decode(V)
656                self.__set(K, rval, cval)
657                M = self[K]
658    # end __ParseString
659# end BaseCookie class
660
661class SimpleCookie(BaseCookie):
662    """SimpleCookie
663    SimpleCookie supports strings as cookie values.  When setting
664    the value using the dictionary assignment notation, SimpleCookie
665    calls the builtin str() to convert the value to a string.  Values
666    received from HTTP are kept as strings.
667    """
668    def value_decode(self, val):
669        return _unquote( val ), val
670    def value_encode(self, val):
671        strval = str(val)
672        return strval, _quote( strval )
673# end SimpleCookie
674
675class SerialCookie(BaseCookie):
676    """SerialCookie
677    SerialCookie supports arbitrary objects as cookie values. All
678    values are serialized (using cPickle) before being sent to the
679    client.  All incoming values are assumed to be valid Pickle
680    representations.  IF AN INCOMING VALUE IS NOT IN A VALID PICKLE
681    FORMAT, THEN AN EXCEPTION WILL BE RAISED.
682
683    Note: Large cookie values add overhead because they must be
684    retransmitted on every HTTP transaction.
685
686    Note: HTTP has a 2k limit on the size of a cookie.  This class
687    does not check for this limit, so be careful!!!
688    """
689    def __init__(self, input=None):
690        warnings.warn("SerialCookie class is insecure; do not use it",
691                      DeprecationWarning)
692        BaseCookie.__init__(self, input)
693    # end __init__
694    def value_decode(self, val):
695        # This could raise an exception!
696        return loads( _unquote(val) ), val
697    def value_encode(self, val):
698        return val, _quote( dumps(val) )
699# end SerialCookie
700
701class SmartCookie(BaseCookie):
702    """SmartCookie
703    SmartCookie supports arbitrary objects as cookie values.  If the
704    object is a string, then it is quoted.  If the object is not a
705    string, however, then SmartCookie will use cPickle to serialize
706    the object into a string representation.
707
708    Note: Large cookie values add overhead because they must be
709    retransmitted on every HTTP transaction.
710
711    Note: HTTP has a 2k limit on the size of a cookie.  This class
712    does not check for this limit, so be careful!!!
713    """
714    def __init__(self, input=None):
715        warnings.warn("Cookie/SmartCookie class is insecure; do not use it",
716                      DeprecationWarning)
717        BaseCookie.__init__(self, input)
718    # end __init__
719    def value_decode(self, val):
720        strval = _unquote(val)
721        try:
722            return loads(strval), val
723        except:
724            return strval, val
725    def value_encode(self, val):
726        if type(val) == type(""):
727            return val, _quote(val)
728        else:
729            return val, _quote( dumps(val) )
730# end SmartCookie
731
732
733###########################################################
734# Backwards Compatibility:  Don't break any existing code!
735
736# We provide Cookie() as an alias for SmartCookie()
737Cookie = SmartCookie
738
739#
740###########################################################
741
742def _test():
743    import doctest, Cookie
744    return doctest.testmod(Cookie)
745
746if __name__ == "__main__":
747    _test()
748
749
750#Local Variables:
751#tab-width: 4
752#end: