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