PageRenderTime 24ms CodeModel.GetById 12ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/django/contrib/sessions/backends/file.py

https://code.google.com/p/mango-py/
Python | 149 lines | 97 code | 21 blank | 31 comment | 17 complexity | 05184dc68d4a0cad58eb8d5553cde7b6 MD5 | raw file
  1import errno
  2import os
  3import tempfile
  4
  5from django.conf import settings
  6from django.contrib.sessions.backends.base import SessionBase, CreateError
  7from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
  8
  9
 10class SessionStore(SessionBase):
 11    """
 12    Implements a file based session store.
 13    """
 14    def __init__(self, session_key=None):
 15        self.storage_path = getattr(settings, "SESSION_FILE_PATH", None)
 16        if not self.storage_path:
 17            self.storage_path = tempfile.gettempdir()
 18
 19        # Make sure the storage path is valid.
 20        if not os.path.isdir(self.storage_path):
 21            raise ImproperlyConfigured(
 22                "The session storage path %r doesn't exist. Please set your"
 23                " SESSION_FILE_PATH setting to an existing directory in which"
 24                " Django can store session data." % self.storage_path)
 25
 26        self.file_prefix = settings.SESSION_COOKIE_NAME
 27        super(SessionStore, self).__init__(session_key)
 28
 29    VALID_KEY_CHARS = set("abcdef0123456789")
 30
 31    def _key_to_file(self, session_key=None):
 32        """
 33        Get the file associated with this session key.
 34        """
 35        if session_key is None:
 36            session_key = self.session_key
 37
 38        # Make sure we're not vulnerable to directory traversal. Session keys
 39        # should always be md5s, so they should never contain directory
 40        # components.
 41        if not set(session_key).issubset(self.VALID_KEY_CHARS):
 42            raise SuspiciousOperation(
 43                "Invalid characters in session key")
 44
 45        return os.path.join(self.storage_path, self.file_prefix + session_key)
 46
 47    def load(self):
 48        session_data = {}
 49        try:
 50            session_file = open(self._key_to_file(), "rb")
 51            try:
 52                file_data = session_file.read()
 53                # Don't fail if there is no data in the session file.
 54                # We may have opened the empty placeholder file.
 55                if file_data:
 56                    try:
 57                        session_data = self.decode(file_data)
 58                    except (EOFError, SuspiciousOperation):
 59                        self.create()
 60            finally:
 61                session_file.close()
 62        except IOError:
 63            self.create()
 64        return session_data
 65
 66    def create(self):
 67        while True:
 68            self._session_key = self._get_new_session_key()
 69            try:
 70                self.save(must_create=True)
 71            except CreateError:
 72                continue
 73            self.modified = True
 74            self._session_cache = {}
 75            return
 76
 77    def save(self, must_create=False):
 78        # Get the session data now, before we start messing
 79        # with the file it is stored within.
 80        session_data = self._get_session(no_load=must_create)
 81
 82        session_file_name = self._key_to_file()
 83
 84        try:
 85            # Make sure the file exists.  If it does not already exist, an
 86            # empty placeholder file is created.
 87            flags = os.O_WRONLY | os.O_CREAT | getattr(os, 'O_BINARY', 0)
 88            if must_create:
 89                flags |= os.O_EXCL
 90            fd = os.open(session_file_name, flags)
 91            os.close(fd)
 92
 93        except OSError, e:
 94            if must_create and e.errno == errno.EEXIST:
 95                raise CreateError
 96            raise
 97
 98        # Write the session file without interfering with other threads
 99        # or processes.  By writing to an atomically generated temporary
100        # file and then using the atomic os.rename() to make the complete
101        # file visible, we avoid having to lock the session file, while
102        # still maintaining its integrity.
103        #
104        # Note: Locking the session file was explored, but rejected in part
105        # because in order to be atomic and cross-platform, it required a
106        # long-lived lock file for each session, doubling the number of
107        # files in the session storage directory at any given time.  This
108        # rename solution is cleaner and avoids any additional overhead
109        # when reading the session data, which is the more common case
110        # unless SESSION_SAVE_EVERY_REQUEST = True.
111        #
112        # See ticket #8616.
113        dir, prefix = os.path.split(session_file_name)
114
115        try:
116            output_file_fd, output_file_name = tempfile.mkstemp(dir=dir,
117                prefix=prefix + '_out_')
118            renamed = False
119            try:
120                try:
121                    os.write(output_file_fd, self.encode(session_data))
122                finally:
123                    os.close(output_file_fd)
124                os.rename(output_file_name, session_file_name)
125                renamed = True
126            finally:
127                if not renamed:
128                    os.unlink(output_file_name)
129
130        except (OSError, IOError, EOFError):
131            pass
132
133    def exists(self, session_key):
134        if os.path.exists(self._key_to_file(session_key)):
135            return True
136        return False
137
138    def delete(self, session_key=None):
139        if session_key is None:
140            if self._session_key is None:
141                return
142            session_key = self._session_key
143        try:
144            os.unlink(self._key_to_file(session_key))
145        except OSError:
146            pass
147
148    def clean(self):
149        pass