Home | History | Annotate | Download | only in python2.7
      1 #!/usr/bin/env python
      2 #
      3 
      4 ####
      5 # Copyright 2000 by Timothy O'Malley <timo (at] 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 (at] 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 (at] magnet.com) in 1995, when he released the
     38 #  first version of nscookie.py.
     39 #
     40 ####
     41 
     42 r"""
     43 Here's a sample session to show how to use this module.
     44 At the moment, this is the only documentation.
     45 
     46 The Basics
     47 ----------
     48 
     49 Importing is easy..
     50 
     51    >>> import Cookie
     52 
     53 Most of the time you start by creating a cookie.  Cookies come in
     54 three flavors, each with slightly different encoding semantics, but
     55 more 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
     62 Cookie.Cookie() to create an Cookie object.  Although deprecated, it
     63 is still supported by the code.  See the Backward Compatibility notes
     64 for more information.]
     65 
     66 Once you've created your Cookie, you can add values just as if it were
     67 a 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 
     75 Notice that the printable representation of a Cookie is the
     76 appropriate format for a Set-Cookie: header.  This is the
     77 default behavior.  You can change the header and printed
     78 attributes 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 
     88 The load() method of a Cookie extracts cookies from a string.  In a
     89 CGI script, you would use this method to extract the cookies from the
     90 HTTP_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 
     97 The load() method is darn-tootin smart about identifying cookies
     98 within a string.  Escaped quotation marks, nested semicolons, and other
     99 such 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 
    106 Each element of the Cookie also supports all of the RFC 2109
    107 Cookie attributes.  Here's an example which sets the Path
    108 attribute.
    109 
    110    >>> C = Cookie.SmartCookie()
    111    >>> C["oreo"] = "doublestuff"
    112    >>> C["oreo"]["path"] = "/"
    113    >>> print C
    114    Set-Cookie: oreo=doublestuff; Path=/
    115 
    116 Each dictionary element has a 'value' attribute, which gives you
    117 back 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 
    125 A Bit More Advanced
    126 -------------------
    127 
    128 As mentioned before, there are three different flavors of Cookie
    129 objects, each with different encoding/decoding semantics.  This
    130 section briefly discusses the differences.
    131 
    132 SimpleCookie
    133 
    134 The SimpleCookie expects that all values should be standard strings.
    135 Just to be sure, SimpleCookie invokes the str() builtin to convert
    136 the 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 
    149 SerialCookie
    150 
    151 The SerialCookie expects that all values should be serialized using
    152 cPickle (or pickle, if cPickle isn't available).  As a result of
    153 serializing, SerialCookie can save almost any Python object to a
    154 value, and recover the exact same object when the cookie has been
    155 returned.  (SerialCookie can yield some strange-looking cookie
    156 values, 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 
    168 Be warned, however, if SerialCookie cannot de-serialize a value (because
    169 it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
    170 
    171 
    172 SmartCookie
    173 
    174 The SmartCookie combines aspects of each of the other two flavors.
    175 When setting a value in a dictionary-fashion, the SmartCookie will
    176 serialize (ala cPickle) the value *if and only if* it isn't a
    177 Python string.  String objects are *not* serialized.  Similarly,
    178 when the load() method parses out values, it attempts to de-serialize
    179 the value.  If it fails, then it fallsback to treating the value
    180 as 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 
    193 Backwards Compatibility
    194 -----------------------
    195 
    196 In order to keep compatibilty with earlier versions of Cookie.py,
    197 it is still possible to use Cookie.Cookie() to create a Cookie.  In
    198 fact, this simply returns a SmartCookie.
    199 
    200    >>> C = Cookie.Cookie()
    201    >>> print C.__class__.__name__
    202    SmartCookie
    203 
    204 
    205 Finis.
    206 """  #"
    207 #     ^
    208 #     |----helps out font-lock
    209 
    210 #
    211 # Import our required modules
    212 #
    213 import string
    214 
    215 try:
    216     from cPickle import dumps, loads
    217 except ImportError:
    218     from pickle import dumps, loads
    219 
    220 import 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 #
    232 class 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 
    315 def _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 
    333 def _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 
    389 def _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 
    408 class 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 #
    554 class 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 
    670 class 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 
    684 class 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 
    710 class 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()
    746 Cookie = SmartCookie
    747 
    748 #
    749 ###########################################################
    750 
    751 def _test():
    752     import doctest, Cookie
    753     return doctest.testmod(Cookie)
    754 
    755 if __name__ == "__main__":
    756     _test()
    757 
    758 
    759 #Local Variables:
    760 #tab-width: 4
    761 #end:
    762