# session.py - Python Service Objects # # Author: Thanos Vassilakis thanos@0x01.com # # Copyright (c) thanos vassilakis 2000,2001, 2002 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; either version 2.1 of the # License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU Lesser General Public License for more details. # # See terms of license at gnu.org. # # $Id: session.py,v 1.15 2004/07/12 03:52:06 thanos Exp $ # __version__="$Revision: 1.15 $" from weakref import ref from time import time, strftime, gmtime from cPickle import load, dump, dumps, loads from tempfile import mktemp from util import MixIn, log import sys, os try: mapClass = dict except: import UserDict mapClass = UserDict.UserDict class SessionImpl: "abstract implementaion class" DEFAULTServiceIdKey = "PSOServiceId" DEFAULTSessionIdKey = "PSOSessionId" DEFAULTServiceIdValue = "SESSION_ID" def getServiceId(self, reqHandler): """session.getServiceId(requestHandler) -> String ~ The service id, set in the HTTP directive, is returned. This will default to the script's name""" return reqHandler.getEnviron(self.DEFAULTServiceIdKey, self.getDefaultServiceId()) def getDefaultServiceId(self): return self.DEFAULTServiceIdValue def getSessionId(self, reqHandler): """session.getSessionId(requestHandler) -> String | None ~ The current session id returned. If none return None. By default will look for a HTTP directive ServiceId""" return reqHandler.getEnviron(self.DEFAULTSessonIdKey) def load(self, reqHandler, session): "session.load(requestHandler) -> Session" def save(self, reqHandler, session): "session.session(requestHandler, self) -> None" def revert(self, reqHandler): "session.revert(requestHandler, self) -> None ~reverts the session to last saved copy." def expire(self, reqHandler, when): """ session.expire(requestHandler, when) -> None ~ if when is evals to a number will expire the session in when seconds, otherwise will try and parse when as a date. For more on this format see RFC2068 section 3.3.1 [also RCF822 and RCF1123] """ def newSessionId(self): """ session.newSessionId(self.requestHandler) -> String ~ returns a new sessionId, preferably unique.""" class Session(mapClass): """Session Bridge""" def __init__(self, reqHandler, impl=None): """session = Session(requestHandler, SessionImplmentor()) ~ ctor's a session with the given request handler and session implementation instance""" mapClass.__init__(self) self.init(reqHandler, impl) self.setup() def init(self, reqHandler, impl): """ self.init(reqHandler, impl)-> None ~ template init method called by CTOR""" self.sessionId=None self.serviceId=None self.status='new' self.impl = impl #self.impl.session = ref(self) self.reqHandler= ref(reqHandler) #self.reqHandler= reqHandler self.expiresWhen=None def setup(self): """ self.setup()-> None ~ template method used to define relationship between session and requestHandler""" self.serviceId= self.getServiceId() self.sessionId= self.getSessionId() if not self.isNew(): self.load() def isNew(self): """ session.new()-> 1|0 ~ returns 1 if session was created on this request, otherwise 0 used to define relationship between session and requestHandler""" return self.status=="new" def getServiceId(self, reqHandler=None): """ session.getServiceId()-> String ~ returns serviceId, request passed on to the implementor.""" if reqHandler is None: reqHandler = self.reqHandler() if not self.serviceId: self.serviceId = self.impl.getServiceId(reqHandler) return self.serviceId def getSessionId(self, reqHandler=None): """ session.getSessionId()-> String ~ returns either the current sessionId or a new session id.""" if reqHandler is None: reqHandler = self.reqHandler() if not self.sessionId: self.sessionId = reqHandler.getEnviron(self.getServiceId(reqHandler)) if not self.sessionId: self.sessionId = self.impl.getSessionId(reqHandler) if not self.sessionId: self.status = 'new' self.sessionId = self.impl.newSessionId(reqHandler) self.purge(reqHandler) else: self.status='open' return self.sessionId def purge(self, reqHandler=None): self.impl.purge(reqHandler) def load(self, reqHandler=None): """ session.load()-> None ~ loads a session into its self.""" if reqHandler is None: reqHandler = self.reqHandler() try: session = self.impl.load(reqHandler, self) if session: expires = session.getExpire() if expires is not None and expires < time(): self.status = "new" return self.update(session) else: self.status = "new" except Exception, e: import traceback traceback.print_exc() self.status = "new" return {} def save(self, reqHandler=None): """ session.save()-> None ~ saves self.""" if reqHandler is None: reqHandler = self.reqHandler() self.reqHandler = None self.status = 'open' self.impl.save(reqHandler, self) self.reqHandler= reqHandler def getExpire(self): """ session.getExpire()-> float ~ returns when it will expire in secs since epoche.""" return self.expiresWhen def expire(self, when = None): #def expire(self, when = None): """ session.expire(when)-> None ~ expires session at time.time() + when, if eval(when) evalutes, otherwise tries to parse when and expire session at when. For more on this format see RFC2068 section 3.3.1 [also RCF822 and RCF1123] """ self.expireWhen = when self.impl.expire(self, self.reqHandler(), when) #self.impl.expire(self, self.req(), when) def setSession(self, reqHandler=None): """ session.setSession() -> None ~ adds session to request handler. """ if reqHandler is None: reqHandler = self.reqHandler() self.impl.setSession(self, reqHandler) class BrowserLoader(MixIn): def purge(self, reqHandler=None): pass def load(self, reqHandler, session): return loads(session.getSessionId(reqHandler)) def save(self, reqHandler, session): print 'saving' try: print '
saving', reqHandler, session.getServiceId(reqHandler), dumps(session) reqHandler.setCookie(session.getServiceId(reqHandler), dumps(session)) print '
saved', reqHandler.getCookie(session.getServiceId(reqHandler)) except: import traceback traceback.print_exc(file=sys.stdout) def newSessionId(self, reqHandler): return {} class FileLoader(MixIn): DIRECTIVEtAG='PSOSessionFileLoader_' DIRECTIVES=('Path',) def getFileName(self, reqHandler, session): """session.getFileName(requestHandler) -> String ~ returns a fully qualified session file name.""" path = self.getPath(reqHandler) file = session.getSessionId(reqHandler) filename = os.path.join(path, file) return filename def getPath(self, reqHandler): """ session.getPath(requestHandler) -> String ~ returns the path where sessions will be stored.""" return self.getDirectives(FileLoader, reqHandler).get('Path') def purge(self, reqHandler=None): pass def newSessionId(self, reqHandler): """session.newSessionId(requestHandler) -> String ~ returns a new sessionId, preferably unique.""" id = os.path.basename(mktemp(self.getServiceId( reqHandler))) return id def load(self, reqHandler, session): """session.load(requestHandler) -> session ~ returns session. All thrown expections passed on.""" return load(open(self.getFileName(reqHandler, session),'r+b')) def save(self, reqHandler, session): """ session.save(requestHandler) -> None ~ saves session. All thrown expections passed on.""" file = self.getFileName(reqHandler, session) f = open(file,'w+b') dump( session, f) class CookieSession(MixIn): DIRECTIVEtAG='PSOCookieSession_' DIRECTIVES=('expires', 'Path','Comment','Domain','Max-Age','secure','Version') def getCookie(self, reqHandler): """ self.getCookie(requestHandler) -> String | None ~ returns the session cookie if found otherwise none""" cookie = reqHandler.getEnviron(reqHandler.getCookieKey()) if cookie: try: start=0 look4 = self.getServiceId(reqHandler)+'=' start = cookie.index(look4) start += len(look4) end = cookie.find(';', start) if end == -1: cookie = cookie[start:] else: cookie = cookie[start:end] return cookie.strip() except Exception, e: return None def getSessionId(self, reqHandler): """self.getSessionId(requestHandler) -> String | None ~ The current session id returned. If none return None.""" return self.getCookie(reqHandler) def when(self, when): """self.when(when) -> None ~ utility method to build the correct cookie expire attributes.""" if not hasattr(self, 'attrs'): self.attrs = {} if when is not None: try: when = eval(str(when)) when = time() + when #self.attrs['Max-Age']= str(long(when)) self.attrs['expires'] = strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime(when)) except: self.attrs['expires'] = when if self.attrs.has_key('Max-Age'): del self.attrs['Max-Age'] def expire(self, session, reqHandler, when = None): """ self.expire(session, requestHandler, when)-> None ~ expires session at time.time() + when, if eval(when) evalutes, otherwise tries to parse when and expire session at when. For more on this format see RFC2068 section 3.3.1 [also RCF822 and RCF1123] """ self.when(when) reqHandler.setSession(session) def getAttrs(self): """ self.getAttrs() -> Dictionary of cookie attributes ~ utility method for setting up the session cookie""" if hasattr(self, 'attrs'): return self.attrs else: return {} def setSession(self, session, reqHandler): """ self.setSession(session, reqHandler) -> None ~ adds session to request handler. Handles setting the cookie when the session is new. """ self.attrs = self.getAttrs() self.attrs.update(self.getDirectives(CookieSession, reqHandler)) value = reqHandler.getEnviron('PSOSessionExpires') if value: self.when(value) reqHandler.setCookie(session.getServiceId(), session.getSessionId(), **self.getAttrs()) class CookieFileImpl(CookieSession, FileLoader, SessionImpl): """ Default session implementation, using temporary files to store the session, and using a browser cookie to pass the session id accross requests.""" class FileSession(Session ): """ Default session bridge, using CookieFileImpl as the implementaion class""" def init(self, reqHandler,imp): Session.init(self, reqHandler, CookieFileImpl()) class CookieBrowserImpl(CookieSession, BrowserLoader, SessionImpl): """ session implementation, using actual browser cookie to store the session. This is not a first choice."""