##################################################
# 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
|