home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / python / Cookie.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2004-03-11  |  12.6 KB  |  307 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.3)
  3.  
  4. """
  5.  
  6. This module contains classes to support HTTP State Management
  7. Mechanism, also known as Cookies. The classes provide simple
  8. ways for creating, parsing and digitally signing cookies, as
  9. well as the ability to store simple Python objects in Cookies
  10. (using marshalling).
  11.  
  12. The behaviour of the classes is designed to be most useful
  13. within mod_python applications.
  14.  
  15. The current state of HTTP State Management standardization is
  16. rather unclear. It appears that the de-facto standard is the
  17. original Netscape specification, even though already two RFC's
  18. have been put out (RFC2109 (1997) and RFC2965 (2000)). The
  19. RFC's add a couple of useful features (e.g. using Max-Age instead
  20. of Expires, but my limited tests show that Max-Age is ignored
  21. by the two browsers tested (IE and Safari). As a result of this,
  22. perhaps trying to be RFC-compliant (by automatically providing
  23. Max-Age and Version) could be a waste of cookie space...
  24.  
  25. """
  26. import time
  27. import re
  28. import hmac
  29. import marshal
  30. import base64
  31. import apache
  32.  
  33. class CookieError(Exception):
  34.     pass
  35.  
  36.  
  37. class metaCookie(type):
  38.     
  39.     def __new__(cls, clsname, bases, clsdict):
  40.         _valid_attr = ('version', 'path', 'domain', 'secure', 'comment', 'expires', 'max_age', 'commentURL', 'discard', 'port')
  41.         __slots__ = _valid_attr + ('name', 'value', '_value', '_expires', '__data__')
  42.         clsdict['_valid_attr'] = _valid_attr
  43.         clsdict['__slots__'] = __slots__
  44.         
  45.         def set_expires(self, value):
  46.             if type(value) == type(''):
  47.                 
  48.                 try:
  49.                     t = time.strptime(value, '%a, %d-%b-%Y %H:%M:%S GMT')
  50.                 except ValueError:
  51.                     raise ValueError, 'Invalid expires time: %s' % value
  52.  
  53.                 t = time.mktime(t)
  54.             else:
  55.                 t = value
  56.                 value = time.strftime('%a, %d-%b-%Y %H:%M:%S GMT', time.gmtime(t))
  57.             self._expires = '%s' % value
  58.  
  59.         
  60.         def get_expires(self):
  61.             return self._expires
  62.  
  63.         clsdict['expires'] = property(fget = get_expires, fset = set_expires)
  64.         return type.__new__(cls, clsname, bases, clsdict)
  65.  
  66.  
  67.  
  68. class Cookie(object):
  69.     '''
  70.     This class implements the basic Cookie functionality. Note that
  71.     unlike the Python Standard Library Cookie class, this class represents
  72.     a single cookie (not a list of Morsels).
  73.     '''
  74.     __metaclass__ = metaCookie
  75.     
  76.     def parse(Class, str):
  77.         '''
  78.         Parse a Cookie or Set-Cookie header value, and return
  79.         a dict of Cookies. Note: the string should NOT include the
  80.         header name, only the value.
  81.         '''
  82.         dict = _parse_cookie(str, Class)
  83.         return dict
  84.  
  85.     parse = classmethod(parse)
  86.     
  87.     def __init__(self, name, value, **kw):
  88.         '''
  89.         This constructor takes at least a name and value as the
  90.         arguments, as well as optionally any of allowed cookie attributes
  91.         as defined in the existing cookie standards. 
  92.         '''
  93.         (self.name, self.value) = (name, value)
  94.         for k in kw:
  95.             setattr(self, k.lower(), kw[k])
  96.         
  97.         self.__data__ = { }
  98.  
  99.     
  100.     def __str__(self):
  101.         """
  102.         Provides the string representation of the Cookie suitable for
  103.         sending to the browser. Note that the actual header name will
  104.         not be part of the string.
  105.  
  106.         This method makes no attempt to automatically double-quote
  107.         strings that contain special characters, even though the RFC's
  108.         dictate this. This is because doing so seems to confuse most
  109.         browsers out there.
  110.         """
  111.         result = [
  112.             '%s=%s' % (self.name, self.value)]
  113.         for name in self._valid_attr:
  114.             if hasattr(self, name):
  115.                 if name in ('secure', 'discard'):
  116.                     result.append(name)
  117.                 else:
  118.                     result.append('%s=%s' % (name, getattr(self, name)))
  119.             name in ('secure', 'discard')
  120.         
  121.         return '; '.join(result)
  122.  
  123.     
  124.     def __repr__(self):
  125.         return '<%s: %s>' % (self.__class__.__name__, str(self))
  126.  
  127.  
  128.  
  129. class SignedCookie(Cookie):
  130.     '''
  131.     This is a variation of Cookie that provides automatic
  132.     cryptographic signing of cookies and verification. It uses
  133.     the HMAC support in the Python standard library. This ensures
  134.     that the cookie has not been tamprered with on the client side.
  135.  
  136.     Note that this class does not encrypt cookie data, thus it
  137.     is still plainly visible as part of the cookie.
  138.     '''
  139.     
  140.     def parse(Class, s, secret):
  141.         dict = _parse_cookie(s, Class)
  142.         for k in dict:
  143.             c = dict[k]
  144.             
  145.             try:
  146.                 c.unsign(secret)
  147.             continue
  148.             except CookieError:
  149.                 dict[k] = Cookie.parse(Cookie.__str__(c))[k]
  150.                 continue
  151.             
  152.  
  153.         
  154.         return dict
  155.  
  156.     parse = classmethod(parse)
  157.     
  158.     def __init__(self, name, value, secret = None, **kw):
  159.         Cookie.__init__(self, name, value, **kw)
  160.         self.__data__['secret'] = secret
  161.  
  162.     
  163.     def hexdigest(self, str):
  164.         if not self.__data__['secret']:
  165.             raise CookieError, 'Cannot sign without a secret'
  166.         
  167.         _hmac = hmac.new(self.__data__['secret'], self.name)
  168.         _hmac.update(str)
  169.         return _hmac.hexdigest()
  170.  
  171.     
  172.     def __str__(self):
  173.         result = [
  174.             '%s=%s%s' % (self.name, self.hexdigest(self.value), self.value)]
  175.         for name in self._valid_attr:
  176.             if hasattr(self, name):
  177.                 if name in ('secure', 'discard'):
  178.                     result.append(name)
  179.                 else:
  180.                     result.append('%s=%s' % (name, getattr(self, name)))
  181.             name in ('secure', 'discard')
  182.         
  183.         return '; '.join(result)
  184.  
  185.     
  186.     def unsign(self, secret):
  187.         (sig, val) = (self.value[:32], self.value[32:])
  188.         mac = hmac.new(secret, self.name)
  189.         mac.update(val)
  190.         if mac.hexdigest() == sig:
  191.             self.value = val
  192.             self.__data__['secret'] = secret
  193.         else:
  194.             raise CookieError, 'Incorrectly Signed Cookie: %s=%s' % (self.name, self.value)
  195.  
  196.  
  197.  
  198. class MarshalCookie(SignedCookie):
  199.     '''
  200.     This is a variation of SignedCookie that can store more than
  201.     just strings. It will automatically marshal the cookie value,
  202.     therefore any marshallable object can be used as value.
  203.  
  204.     The standard library Cookie module provides the ability to pickle
  205.     data, which is a major security problem. It is believed that unmarshalling
  206.     (as opposed to unpickling) is safe, yet we still err on the side of caution
  207.     which is why this class is a subclass of SignedCooke making sure what
  208.     we are about to unmarshal passes the digital signature test.
  209.  
  210.     Here is a link to a sugesstion that marshalling is safer than unpickling
  211.     http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=7xn0hcugmy.fsf%40ruckus.brouhaha.com
  212.     '''
  213.     
  214.     def parse(Class, s, secret):
  215.         dict = _parse_cookie(s, Class)
  216.         for k in dict:
  217.             c = dict[k]
  218.             
  219.             try:
  220.                 c.unmarshal(secret)
  221.             continue
  222.             except (CookieError, ValueError):
  223.                 dict[k] = Cookie.parse(Cookie.__str__(c))[k]
  224.                 continue
  225.             
  226.  
  227.         
  228.         return dict
  229.  
  230.     parse = classmethod(parse)
  231.     
  232.     def __str__(self):
  233.         m = base64.encodestring(marshal.dumps(self.value))[:-1]
  234.         result = [
  235.             '%s=%s%s' % (self.name, self.hexdigest(m), m)]
  236.         for name in self._valid_attr:
  237.             if hasattr(self, name):
  238.                 if name in ('secure', 'discard'):
  239.                     result.append(name)
  240.                 else:
  241.                     result.append('%s=%s' % (name, getattr(self, name)))
  242.             name in ('secure', 'discard')
  243.         
  244.         return '; '.join(result)
  245.  
  246.     
  247.     def unmarshal(self, secret):
  248.         self.unsign(secret)
  249.         self.value = marshal.loads(base64.decodestring(self.value))
  250.  
  251.  
  252. _cookiePattern = re.compile('(?x)[,\\ ]*(?P<key>[^;\\ =]+)\\ *(=\\ *)?(?P<val>"(?:[^\\\\"]|\\\\.)*"|[^;]*)\\s*;?')
  253.  
  254. def _parse_cookie(str, Class):
  255.     result = { }
  256.     valid = Cookie._valid_attr + ('max-age',)
  257.     c = None
  258.     matchIter = _cookiePattern.finditer(str)
  259.     for match in matchIter:
  260.         (key, val) = (match.group('key'), match.group('val'))
  261.         if not c:
  262.             c = Class(key, val)
  263.             result[key] = c
  264.         
  265.         l_key = key.lower()
  266.         if l_key in valid or key[0] == '$':
  267.             if l_key == 'max-age':
  268.                 l_key = 'max_age'
  269.             
  270.             setattr(c, l_key, val)
  271.             continue
  272.         c = Class(l_key, val)
  273.         result[l_key] = c
  274.     
  275.     return result
  276.  
  277.  
  278. def add_cookie(req, cookie, value = '', **kw):
  279.     """
  280.     Sets a cookie in outgoing headers and adds a cache
  281.     directive so that caches don't cache the cookie.
  282.     """
  283.     if not isinstance(cookie, Cookie):
  284.         cookie = Cookie(cookie, value, **kw)
  285.     
  286.     if not req.headers_out.has_key('Set-Cookie'):
  287.         req.headers_out.add('Cache-Control', 'no-cache="set-cookie"')
  288.     
  289.     req.headers_out.add('Set-Cookie', str(cookie))
  290.  
  291.  
  292. def get_cookies(req, Class = Cookie, **kw):
  293.     '''
  294.     A shorthand for retrieveing and parsing cookies given
  295.     a Cookie class. The class must be one of the classes from
  296.     this module.
  297.     '''
  298.     if not req.headers_in.has_key('cookie'):
  299.         return { }
  300.     
  301.     cookies = req.headers_in['cookie']
  302.     if type(cookies) == type([]):
  303.         cookies = '; '.join(cookies)
  304.     
  305.     return Class.parse(cookies, **kw)
  306.  
  307.