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