"""A server-side module for «sessions» in HTTP/HTTPS by
giving/getting a magic cookie from the browser, which
is simply a reference to a local data storage."""

# $Id: session.py,v 1.1 2003/01/18 05:29:16 stain Exp $
#
# Strongly inspired by PHP4's sessions.
# (c) Stian Soiland <stain@nvg.org> 2001
# Licence: LGPL
#
# Last changes:
# $Log: session.py,v $
# Revision 1.1  2003/01/18 05:29:16  stain
# First import from aapningstider.no
#
# Revision 1.1  2002/07/18 23:12:02  stain
# Akkai
#
# Revision 1.6  2002/04/08 01:58:54  stain
# I couldn't sleep and for some reason decided to change this module's
# ID-generation method. It's a bit easier then just lying in the dark
# thinking about girl problems.
#
#
# New method:
#
# Chooses 20 random characters from the readable part of the 7-bit ascii,
# ie, from chr(32) till chr(126) inclusive.
#
# This yields approximately 2**128 variations.  Actually it yields 95**20
# == 3584859224085422343574104404449462890625 ~= 2**131 variations.
#
# This of course is based on a 'perfect' random generator, which we don't
# have. Note that the basic idea is to have a ID that is hard to guess. If
# you have a system with 2**64 different sessions registered (unthinkable!
# Calculate the number!) - an attacker would need to do 2**64
#
# (??? Shouldn't this number be calculated in a bit trickier way?)
#
# connection attempts in order to retrieve an usable ID
# - and then he still wouldn't have any way to decide which ID to
#   retrieve.
#
#
# Other suggestions: (involves uglier programming)
#
# Calculate a number 2L**128 large by selecting four integers at random,
# putting them together by bitshifting 32, 64 and 96 bits and thereafter
# adding/and-ing them together.
#
# Then - use as a string. (you would probably need to do C-stuff to do
# this, bitshifting and adding letters with chr() would be STUPID), and
# THEN again, convert to base64.
#
# This method would use more processing power, look stupider and uglier,
# but would only use 4 random calls instead of 20. This could
#
#   a) Reduce total processing time if the randomizer is very CPU and/or
#      hardware intensive
#
#   b) Reduce possible patterns caused by imperfect random generators, and
#      thereby making the result 'more random'
#
# Revision 1.5  2001/04/10 00:11:08  stain
# Support for expiration (ie. sessions stored between browser
# instances)
#
# Revision 1.4  2001/04/06 19:36:38  stain
# Noe er endret.
#
# Revision 1.3  2001/03/13 23:45:07  stain
# Nå virker den.
#
# 

# Todo:
#   + Maybe check that the session does not exist already


import Cookie,os,random,UserDict,pickle,time

class Session(UserDict.UserDict):
  def __init__(self, identifier=None, valid=None):
    """Initializes a new session or restores the old one.
    Identifier is used as the cookie name, and any integer
    in 'valid' is the number of seconds the cookie should
    be valid"""
    UserDict.UserDict.__init__(self)

    if(identifier):
      self.identifier = identifier
    else:
      try:
        self.identifier = os.environ['HTTP_HOST']
      except:
        self.identifier = "localhost"

    self.cookie = Cookie.SimpleCookie()
    try:
      self.cookie.load(os.environ['HTTP_COOKIE'])
      self.id = self.cookie[self.identifier].value
      self.data = pickle.load(open('/tmp/py_session_' +
                     self.identifier + '_' + self.id))
    except: # Any kind of error forces a new session!
      self.new_id()
      self.new_session()
    if(valid):
      self.cookie[self.identifier]['expires'] = valid
    print self.cookie.output()    

  def __del__(self):
    pickle.dump(self.data,
          open('/tmp/py_session_' + self.identifier +
             '_' + self.id, 'w'))

  def new_id(self):
    """Forces a new session id"""
    choices = range(32,127) # the normal 7-bits ascii letters
    key = ''
    for char in range(20): # 20 chars, aprox 2**128 bits resolution
      key = key + chr(random.choice(choices))
   
    self.id = key
    self.cookie[self.identifier] = self.id

  def new_session(self):
    """Forces a blank session (reuse ID)"""
    self.data = {}
    









