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
39r"""
40Here's a sample session to show how to use this module.
41At the moment, this is the only documentation.
42
43The Basics
44----------
45
46Importing is easy..
47
48   >>> import Cookie
49
50Most of the time you start by creating a cookie.  Cookies come in
51three flavors, each with slightly different encoding semantics, but
52more 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
59Cookie.Cookie() to create a Cookie object.  Although deprecated, it
60is still supported by the code.  See the Backward Compatibility notes
61for more information.]
62
63Once you've created your Cookie, you can add values just as if it were
64a 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
72Notice that the printable representation of a Cookie is the
73appropriate format for a Set-Cookie: header.  This is the
74default behavior.  You can change the header and printed
75attributes 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
85The load() method of a Cookie extracts cookies from a string.  In a
86CGI script, you would use this method to extract the cookies from the
87HTTP_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
94The load() method is darn-tootin smart about identifying cookies
95within a string.  Escaped quotation marks, nested semicolons, and other
96such 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
103Each element of the Cookie also supports all of the RFC 2109
104Cookie attributes.  Here's an example which sets the Path
105attribute.
106
107   >>> C = Cookie.SmartCookie()
108   >>> C["oreo"] = "doublestuff"
109   >>> C["oreo"]["path"] = "/"
110   >>> print C
111   Set-Cookie: oreo=doublestuff; Path=/
112
113Each dictionary element has a 'value' attribute, which gives you
114back 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
122A Bit More Advanced
123-------------------
124
125As mentioned before, there are three different flavors of Cookie
126objects, each with different encoding/decoding semantics.  This
127section briefly discusses the differences.
128
129SimpleCookie
130
131The SimpleCookie expects that all values should be standard strings.
132Just to be sure, SimpleCookie invokes the str() builtin to convert
133the 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
146SerialCookie
147
148The SerialCookie expects that all values should be serialized using
149cPickle (or pickle, if cPickle isn't available).  As a result of
150serializing, SerialCookie can save almost any Python object to a
151value, and recover the exact same object when the cookie has been
152returned.  (SerialCookie can yield some strange-looking cookie
153values, 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
165Be warned, however, if SerialCookie cannot de-serialize a value (because
166it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
167
168
169SmartCookie
170
171The SmartCookie combines aspects of each of the other two flavors.
172When setting a value in a dictionary-fashion, the SmartCookie will
173serialize (ala cPickle) the value *if and only if* it isn't a
174Python string.  String objects are *not* serialized.  Similarly,
175when the load() method parses out values, it attempts to de-serialize
176the value.  If it fails, then it fallsback to treating the value
177as 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
190Backwards Compatibility
191-----------------------
192
193In order to keep compatibility with earlier versions of Cookie.py,
194it is still possible to use Cookie.Cookie() to create a Cookie.  In
195fact, this simply returns a SmartCookie.
196
197   >>> C = Cookie.Cookie()
198   >>> print C.__class__.__name__
199   SmartCookie
200
201
202Finis.
203"""  #"
204#     ^
205#     |----helps out font-lock
206
207#
208# Import our required modules
209#
210import string
211
212try:
213    from cPickle import dumps, loads
214except ImportError:
215    from pickle import dumps, loads
216
217import 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#
229class 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
312def _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
330def _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
386def _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
405class 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#
558class 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
682class 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
696class 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
722class 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()
758Cookie = SmartCookie
759
760#
761###########################################################
762
763def _test():
764    import doctest, Cookie
765    return doctest.testmod(Cookie)
766
767if __name__ == "__main__":
768    _test()
769
770
771#Local Variables:
772#tab-width: 4
773#end:
774