#!/usr/bin/env python
     2 #
     3 
     4 ####
     5 # Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
     6 #
     7 #                All Rights Reserved
     8 #
     9 # Permission to use, copy, modify, and distribute this software
    10 # and its documentation for any purpose and without fee is hereby
    11 # granted, provided that the above copyright notice appear in all
    12 # copies and that both that copyright notice and this permission
    13 # notice appear in supporting documentation, and that the name of
    14 # Timothy O'Malley  not be used in advertising or publicity
    15 # pertaining to distribution of the software without specific, written
    16 # prior permission.
    17 #
    18 # Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
    19 # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
    20 # AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
    21 # ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    22 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
    23 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
    24 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
    25 # PERFORMANCE OF THIS SOFTWARE.
    26 #
    27 ####
    28 #
    29 # Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
    30 #   by Timothy O'Malley <timo@alum.mit.edu>
    31 #
    32 #  Cookie.py is a Python module for the handling of HTTP
    33 #  cookies as a Python dictionary.  See RFC 2109 for more
    34 #  information on cookies.
    35 #
    36 #  The original idea to treat Cookies as a dictionary came from
    37 #  Dave Mitchell (davem@magnet.com) in 1995, when he released the
    38 #  first version of nscookie.py.
    39 #
    40 ####
    41 
    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[\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] == "{1}quot;:
   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:

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐