"""
cgisession.py 0.1.1 - server side web session handling
(C) 2000 by Michael Stroeder <michael@stroeder.com>

This module implements server side session handling stored in
arbitrary string-keyed dictionary objects
"""

__version__ = '0.1.1'

import random,time,thread


##############################################################################
# Exception classes
##############################################################################

class SessionException(Exception):
  def __init__(self, *args):
      self.args = args

class GenerateIDError(SessionException):
  def __init__(self, maxtry):
    self.maxtry = maxtry
  def __str__(self):
    return "Could not create new session id. Tried %d times." % (self.maxtry)

class SessionExpired(SessionException):
  def __init__(self, timestamp):
    self.timestamp = timestamp
  def __str__(self):
    return "Session expired %s." % (time.strftime('%Y-%m-%d %H:%M:%S',time.gmtime(self.timestamp)))

class KeyError(SessionException):
  def __init__(self, sessionid):
    self.sessionid = sessionid
  def __str__(self):
    return "No session with key %s." % (self.sessionid)


##############################################################################
# The session class which handles storing and retrieving of session data
##############################################################################

class CGISession:

  # dictobj has to be a instance of a dictionary-like
  # object (e.g. derived from UserDict)
  def __init__(self,dictobj={},expire=0,lockwait=0):

    self.sessiondict = dictobj
    self.expire = expire
    self.lockwait = lockwait
    self.lock = thread.allocate_lock() 


  # Generate a new random and unique session id string
  def GenerateSessionID(self,maxtry=0):

    newid = "%08x" % random.randint(0,2147483646L)
    tried = 0
    while self.sessiondict.has_key(newid) and (not maxtry or tried<maxtry):
      newid = "%08x" % random.randint(0,2147483646L)
      tried = tried+1
    if maxtry and tried>=maxtry:
      raise GenerateIDError(maxtry)
    return newid


  # Store session data under session id
  def StoreSession(self,sessiondata=None,sessionid=''):

    self.lock.acquire(self.lockwait)
    if not sessionid:
      # generate completely new session data entry
      sessionid=self.GenerateSessionID()

    # Store session data with timestamp
    self.sessiondict[sessionid] = (time.time(),sessiondata)

    self.lock.release()
    return sessionid


  def RetrieveSession(self,sessionid):

    self.lock.acquire(self.lockwait)
    # Check if session id exists    
    if not self.sessiondict.has_key(sessionid):
      raise KeyError(sessionid)

    # Read the timestamped session data
    timestamp,sessiondata = self.sessiondict[sessionid]

    # Check if session data is already expired
    currenttime = time.time()
    if self.expire and (currenttime>timestamp+self.expire):
      # Remove session entry
      del self.sessiondict[sessionid]
      raise SessionExpired(timestamp)
    self.lock.release()
    return sessiondata


  # Store session data under session id
  def NewSession(self):
    return self.StoreSession('','')

