web/lib/django/contrib/sessions/backends/base.py
changeset 0 0d40e90630ef
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/lib/django/contrib/sessions/backends/base.py	Wed Jan 20 00:34:04 2010 +0100
@@ -0,0 +1,287 @@
+import base64
+import os
+import random
+import sys
+import time
+from datetime import datetime, timedelta
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+from django.conf import settings
+from django.core.exceptions import SuspiciousOperation
+from django.utils.hashcompat import md5_constructor
+
+# Use the system (hardware-based) random number generator if it exists.
+if hasattr(random, 'SystemRandom'):
+    randrange = random.SystemRandom().randrange
+else:
+    randrange = random.randrange
+MAX_SESSION_KEY = 18446744073709551616L     # 2 << 63
+
+class CreateError(Exception):
+    """
+    Used internally as a consistent exception type to catch from save (see the
+    docstring for SessionBase.save() for details).
+    """
+    pass
+
+class SessionBase(object):
+    """
+    Base class for all Session classes.
+    """
+    TEST_COOKIE_NAME = 'testcookie'
+    TEST_COOKIE_VALUE = 'worked'
+
+    def __init__(self, session_key=None):
+        self._session_key = session_key
+        self.accessed = False
+        self.modified = False
+
+    def __contains__(self, key):
+        return key in self._session
+
+    def __getitem__(self, key):
+        return self._session[key]
+
+    def __setitem__(self, key, value):
+        self._session[key] = value
+        self.modified = True
+
+    def __delitem__(self, key):
+        del self._session[key]
+        self.modified = True
+
+    def keys(self):
+        return self._session.keys()
+
+    def items(self):
+        return self._session.items()
+
+    def get(self, key, default=None):
+        return self._session.get(key, default)
+
+    def pop(self, key, *args):
+        self.modified = self.modified or key in self._session
+        return self._session.pop(key, *args)
+
+    def setdefault(self, key, value):
+        if key in self._session:
+            return self._session[key]
+        else:
+            self.modified = True
+            self._session[key] = value
+            return value
+
+    def set_test_cookie(self):
+        self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE
+
+    def test_cookie_worked(self):
+        return self.get(self.TEST_COOKIE_NAME) == self.TEST_COOKIE_VALUE
+
+    def delete_test_cookie(self):
+        del self[self.TEST_COOKIE_NAME]
+
+    def encode(self, session_dict):
+        "Returns the given session dictionary pickled and encoded as a string."
+        pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
+        pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
+        return base64.encodestring(pickled + pickled_md5)
+
+    def decode(self, session_data):
+        encoded_data = base64.decodestring(session_data)
+        pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
+        if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
+            raise SuspiciousOperation("User tampered with session cookie.")
+        try:
+            return pickle.loads(pickled)
+        # Unpickling can cause a variety of exceptions. If something happens,
+        # just return an empty dictionary (an empty session).
+        except:
+            return {}
+
+    def update(self, dict_):
+        self._session.update(dict_)
+        self.modified = True
+
+    def has_key(self, key):
+        return self._session.has_key(key)
+
+    def values(self):
+        return self._session.values()
+
+    def iterkeys(self):
+        return self._session.iterkeys()
+
+    def itervalues(self):
+        return self._session.itervalues()
+
+    def iteritems(self):
+        return self._session.iteritems()
+
+    def clear(self):
+        # To avoid unnecessary persistent storage accesses, we set up the
+        # internals directly (loading data wastes time, since we are going to
+        # set it to an empty dict anyway).
+        self._session_cache = {}
+        self.accessed = True
+        self.modified = True
+
+    def _get_new_session_key(self):
+        "Returns session key that isn't being used."
+        # The random module is seeded when this Apache child is created.
+        # Use settings.SECRET_KEY as added salt.
+        try:
+            pid = os.getpid()
+        except AttributeError:
+            # No getpid() in Jython, for example
+            pid = 1
+        while 1:
+            session_key = md5_constructor("%s%s%s%s"
+                    % (randrange(0, MAX_SESSION_KEY), pid, time.time(),
+                       settings.SECRET_KEY)).hexdigest()
+            if not self.exists(session_key):
+                break
+        return session_key
+
+    def _get_session_key(self):
+        if self._session_key:
+            return self._session_key
+        else:
+            self._session_key = self._get_new_session_key()
+            return self._session_key
+
+    def _set_session_key(self, session_key):
+        self._session_key = session_key
+
+    session_key = property(_get_session_key, _set_session_key)
+
+    def _get_session(self, no_load=False):
+        """
+        Lazily loads session from storage (unless "no_load" is True, when only
+        an empty dict is stored) and stores it in the current instance.
+        """
+        self.accessed = True
+        try:
+            return self._session_cache
+        except AttributeError:
+            if self._session_key is None or no_load:
+                self._session_cache = {}
+            else:
+                self._session_cache = self.load()
+        return self._session_cache
+
+    _session = property(_get_session)
+
+    def get_expiry_age(self):
+        """Get the number of seconds until the session expires."""
+        expiry = self.get('_session_expiry')
+        if not expiry:   # Checks both None and 0 cases
+            return settings.SESSION_COOKIE_AGE
+        if not isinstance(expiry, datetime):
+            return expiry
+        delta = expiry - datetime.now()
+        return delta.days * 86400 + delta.seconds
+
+    def get_expiry_date(self):
+        """Get session the expiry date (as a datetime object)."""
+        expiry = self.get('_session_expiry')
+        if isinstance(expiry, datetime):
+            return expiry
+        if not expiry:   # Checks both None and 0 cases
+            expiry = settings.SESSION_COOKIE_AGE
+        return datetime.now() + timedelta(seconds=expiry)
+
+    def set_expiry(self, value):
+        """
+        Sets a custom expiration for the session. ``value`` can be an integer,
+        a Python ``datetime`` or ``timedelta`` object or ``None``.
+
+        If ``value`` is an integer, the session will expire after that many
+        seconds of inactivity. If set to ``0`` then the session will expire on
+        browser close.
+
+        If ``value`` is a ``datetime`` or ``timedelta`` object, the session
+        will expire at that specific future time.
+
+        If ``value`` is ``None``, the session uses the global session expiry
+        policy.
+        """
+        if value is None:
+            # Remove any custom expiration for this session.
+            try:
+                del self['_session_expiry']
+            except KeyError:
+                pass
+            return
+        if isinstance(value, timedelta):
+            value = datetime.now() + value
+        self['_session_expiry'] = value
+
+    def get_expire_at_browser_close(self):
+        """
+        Returns ``True`` if the session is set to expire when the browser
+        closes, and ``False`` if there's an expiry date. Use
+        ``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry
+        date/age, if there is one.
+        """
+        if self.get('_session_expiry') is None:
+            return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
+        return self.get('_session_expiry') == 0
+
+    def flush(self):
+        """
+        Removes the current session data from the database and regenerates the
+        key.
+        """
+        self.clear()
+        self.delete()
+        self.create()
+
+    def cycle_key(self):
+        """
+        Creates a new session key, whilst retaining the current session data.
+        """
+        data = self._session_cache
+        key = self.session_key
+        self.create()
+        self._session_cache = data
+        self.delete(key)
+
+    # Methods that child classes must implement.
+
+    def exists(self, session_key):
+        """
+        Returns True if the given session_key already exists.
+        """
+        raise NotImplementedError
+
+    def create(self):
+        """
+        Creates a new session instance. Guaranteed to create a new object with
+        a unique key and will have saved the result once (with empty data)
+        before the method returns.
+        """
+        raise NotImplementedError
+
+    def save(self, must_create=False):
+        """
+        Saves the session data. If 'must_create' is True, a new session object
+        is created (otherwise a CreateError exception is raised). Otherwise,
+        save() can update an existing object with the same key.
+        """
+        raise NotImplementedError
+
+    def delete(self, session_key=None):
+        """
+        Deletes the session data under this key. If the key is None, the
+        current session key value is used.
+        """
+        raise NotImplementedError
+
+    def load(self):
+        """
+        Loads the session data and returns a dictionary.
+        """
+        raise NotImplementedError