################################################## # SPYCE - Python-based HTML Scripting # Copyright (c) 2002 Rimon Barr. ################################################## # This is the old session module. It is provided for backwards compatibility, # but not recommended for new development. from spyceModule import spyceModule import re, time, string, random import spyceLock try: import cPickle pickle = cPickle except: import pickle __doc__ = '''Session module provides support for session management - the storage of variables on the server between requests under some short identifier. A user must call setHandler() to determine how the sessions are stored, before using the other session methods. The get(), set() and delete() methods provide access to the session information. The autoSession() method will turn on the automatic session management (loading and saving the session). When automatic session management is turned on the session information, identifier, parameter name and browser method are stored in the variables called auto, autoID, autoName and autoMethod, respectively.''' class session1(spyceModule): def start(self): "Initialise the session module variables." self._serverobject = self._api.getServerObject() if 'session' not in dir(self._serverobject): self._serverobject.session = sessionHandlerRegistry() self._handler = None self._clearAutoSession() def finish(self, theError=None): "Save the session, if automatic session management is turned on." if self.autoID: self.set(self.auto, self.autoExpire, self.autoID) if self.autoMethod=='cookie': self._api.getModule('cookie').set(self.autoName, self.autoID) sessionCleanup(self._serverobject.session) def init(self, handler=None, *args, **kwargs): if handler: session = apply(self.setHandler, (handler,)+args) if kwargs.has_key('auto') and kwargs['auto']: auto = kwargs['auto'] if type(auto) != type(()): auto = (auto,) apply(session.autoSession, auto) def setHandler(self, file_name, *params): "Select a session handler." file_name = string.split(file_name, ':') if len(file_name)==1: file, name = None, file_name[0] else: file, name = file_name[:2] if file: handler = self._api.loadModule(name, file, self._api.getFilename()) else: handler = eval(name) self._handler = apply(handler, (self,)+params) self._serverobject.session.add(self._handler) return self def get(self, id): # method deletes session, if stale "Retrieve session information." if not self._handler: raise 'call setHandler to initialise' return self._handler.get(id) def delete(self, id=None): "Delete session information." if not self._handler: raise 'call setHandler to initialise' if not id: id = self.autoID self._clearAutoSession() return self._handler.delete(id) def set(self, state, expire, id=None): "Set session information." if not self._handler: raise 'call setHandler to initialise' return self._handler.set(state, expire, id) def clear(self): "Clear all session information in current handler." if not self._handler: raise 'call setHandler to initialise' return self._handler.clear() def autoSession(self, expire, method='cookie', name='spyceSession'): "Turn on automatic session management." if not self._handler: raise 'call setHandler to initialise' method = string.lower(method) if method=='cookie': self.autoID = self._api.getModule('cookie').get(name) elif method=='post': self.autoID = self._api.getModule('request').post1(name) elif method=='get': self.autoID = self._api.getModule('request').get1(name) else: raise 'runtime error: invalid autosession method' self.autoMethod = method self.autoName = name self.autoExpire = expire self.auto = None if self.autoID: self.auto = self.get(self.autoID) if not self.auto: self.autoID = None if not self.autoID: # generate a sessionid self.autoID = self.set(None, self.autoExpire) def _clearAutoSession(self): self.auto = None self.autoID = None self.autoMethod = None self.autoName = None self.autoExpire = None ################################################## # Cleanup # # expire sessions every n requests in expectation SESSION_EXPIRE_CHECK = 50 class sessionHandlerRegistry: "Registry of all used session handlers." def __init__(self): self.handlers = {} def add(self, handler): self.handlers[handler.getHandlerID()] = handler def list(self): return self.handlers.values() def remove(self, handler): del self.handlers[handler.getHandlerID()] def sessionCleanup(registry): """Iterates through all session handlers and sessions to perform session cleanup""" if random.randrange(SESSION_EXPIRE_CHECK): return for handler in registry.list(): try: sessions = handler.keys() for s in sessions: handler.get(s) # will delete stale sessions except: registry.remove(handler) ################################################## # Session handlers # class sessionHandler: '''All session handlers should subclass this, and implement the methods marked: 'not implemented'.''' def __init__(self, sessionModule): self.childnum = sessionModule._api.getServerID() def getHandlerID(self): raise 'not implemented' def get(self, id): # method should delete, if session is stale raise 'not implemented' def delete(self, id): raise 'not implemented' def clear(self): raise 'not implemented' def set(self, state, expire, id=None): raise 'not implemented' def keys(self): raise 'not implemented' def __getitem__(self, key): return self.get(key) def __delitem__(self, key): return self.delete(key) ################################################## # File-based session handler # class session_dir(sessionHandler): def __init__(self, sessionModule, dir=None): sessionHandler.__init__(self, sessionModule) if not dir: import spyce dir = spyce.getServer().config.tmp if not os.path.exists(dir): raise "session directory '%s' does not exist" % dir self.dir = dir self.prefix = 'spy' def getHandlerID(self): return 'session_dir', self.childnum, self.dir def get(self, id): if not id: return None filename = os.path.join(self.dir, self.prefix+id) f=None sessionInfo = None try: f=open(filename, 'rb') sessionInfo = pickle.load(f) f.close() except: try: if f: f.close() os.unlink(filename) except: pass if sessionInfo: if time.time() > sessionInfo['expire']: self.delete(id) return None else: return sessionInfo['state'] else: return None def delete(self, id): try: filename = os.path.join(self.dir, self.prefix+id) os.remove(filename) except: pass def clear(self): for id in self.keys(): self.delete(id) def set(self, state, expire, id=None): f=None try: if id: filename = os.path.join(self.dir, self.prefix+id) f=open(filename, 'wb') else: filename, f, id = openUniqueFile(self.dir, self.prefix, ('%d_' % self.childnum)) sessionInfo = {} sessionInfo['expire'] = int(time.time())+expire sessionInfo['state'] = state pickle.dump(sessionInfo, f, -1) f.close() except: try: if f: f.close() except: pass raise return id def keys(self): sessions = os.listdir(self.dir) sessions = filter(lambda s, p=self.prefix: s[:len(p)]==p, sessions) sessions = map(lambda s, self=self: s[len(self.prefix):], sessions) return sessions # requires unique (dir, prefix) def openUniqueFile(dir, prefix, unique, mode='w', max=1000000): filelock = spyceLock.fileLock(os.path.join(dir, prefix)) filelock.acquire() try: id = "%06d"%random.randrange(max) filename = os.path.join(dir, prefix+unique+id) while os.path.exists(filename): id = str(random.randrange(max)) filename = os.path.join(dir, prefix+unique+id) f = None f = open(filename, mode) return filename, f, unique+id finally: filelock.release() ################################################## # Hash file session handlers # class sessionHandlerDBM(sessionHandler): def __init__(self, sessionModule, filename): sessionHandler.__init__(self, sessionModule) self.filename = filename self.dbm = None self.BINARY_MODE = 1 self.dbm_type = None # redefine in subclass def getHandlerID(self): return 'session_'+self.dbm_type, self.childnum, self.filename def _open(self): raise 'need to implement' def _close(self): if self.dbm: self.dbm.close() self.dbm = None def get(self, id): if not id: return None self._open() try: expire, state = None, None if self.dbm.has_key(id): expire, state = pickle.loads(self.dbm[id]) if expire!=None and time.time() > expire: self.delete(id) state = None return state finally: self._close() def delete(self, id): self._open() try: if self.dbm.has_key(id): del self.dbm[id] finally: self._close() def clear(self): if os.path.exists(self.filename): os.unlink(self.filename) def set(self, state, expire, id=None): self._open() try: if not id: id = generateKey(self.dbm, self.childnum) value = pickle.dumps( (int(time.time())+expire, state), self.BINARY_MODE) self.dbm[id] = value return id finally: self._close() def keys(self): self._open() try: return self.dbm.keys() finally: self._close() def opendb(dbm_session_handler, module, filename, flags): mod = __import__(module) if not dbm_session_handler.dbm: dbm_session_handler.dbm = mod.open(filename, flags) class session_gdbm(sessionHandlerDBM): def __init__(self, sessionModule, filename): sessionHandlerDBM.__init__(self, sessionModule, filename) self.dbm_type = 'gdbm' def _open(self): opendb(self, self.dbm_type, self.filename, 'cu') class session_bsddb(sessionHandlerDBM): def __init__(self, sessionModule, filename): sessionHandlerDBM.__init__(self, sessionModule, filename) self.dbm_type = 'bsddb' def _open(self): opendb(self, 'dbhash', self.filename, 'c') def generateKey(hash, prefix, max = 1000000): prefix = str(prefix)+'_' key = random.randrange(max) while hash.has_key(prefix+str(key)): key = random.randrange(max) key = prefix+str(key) hash[key] = pickle.dumps(None, 1) return key ################################################## # User callback session handlers # class session_user(sessionHandler): '''User-callback session handler''' def __init__(self, sessionModule, getf, setf, delf, idsf, info=None): self.serverID = sessionModule._api.getServerID() self.info = info self.getf = getf self.setf = setf self.delf = delf self.idsf = idsf def getHandlerID(self): return 'session_user', self.serverID, self.info def get(self, id): # method should delete, if session is stale return self.getf(self.info, id) def set(self, state, expire, id): return self.setf(self.info, state, expire, self.serverID, id) def delete(self, id): return self.delf(self.info, id) def keys(self): return self.idsf(self.info) def clear(self): for id in self.keys(): self.delete(id) ################################################## # database-based session handlers # # rimtodo: database-based session handler