home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / python / Session.py < prev    next >
Encoding:
Python Source  |  2004-02-16  |  11.0 KB  |  392 lines

  1.  #
  2.  # Copyright 2004 Apache Software Foundation 
  3.  # 
  4.  # Licensed under the Apache License, Version 2.0 (the "License"); you
  5.  # may not use this file except in compliance with the License.  You
  6.  # may obtain a copy of the License at
  7.  #
  8.  #      http://www.apache.org/licenses/LICENSE-2.0
  9.  #
  10.  # Unless required by applicable law or agreed to in writing, software
  11.  # distributed under the License is distributed on an "AS IS" BASIS,
  12.  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  13.  # implied.  See the License for the specific language governing
  14.  # permissions and limitations under the License.
  15.  #
  16.  # Originally developed by Gregory Trubetskoy.
  17.  #
  18.  # $Id: Session.py,v 1.13 2004/02/16 19:47:27 grisha Exp $
  19.  
  20. import apache, Cookie
  21. import _apache
  22.  
  23. import os
  24. import time
  25. import anydbm, whichdb
  26. import random
  27. import md5
  28. import cPickle
  29. import tempfile
  30.  
  31. COOKIE_NAME="pysid"
  32. DFT_TIMEOUT=30*60 # 30 min
  33. CLEANUP_CHANCE=1000 # cleanups have 1 in CLEANUP_CHANCE chance
  34.  
  35. tempdir = tempfile.gettempdir()
  36.  
  37. def _init_rnd():
  38.     """ initialize random number generators
  39.     this is key in multithreaded env, see
  40.     python docs for random """
  41.  
  42.     # query max number of threads
  43.  
  44.     
  45.     if _apache.mpm_query(apache.AP_MPMQ_IS_THREADED):
  46.         gennum = _apache.mpm_query(apache.AP_MPMQ_MAX_SPARE_THREADS)
  47.     else:
  48.         gennum = 10
  49.  
  50.     # make generators
  51.     # this bit is from Python lib reference
  52.     g = random.Random(time.time())
  53.     result = [g]
  54.     for i in range(gennum - 1):
  55.        laststate = g.getstate()
  56.        g = random.Random()
  57.        g.setstate(laststate)
  58.        g.jumpahead(1000000)
  59.        result.append(g)
  60.  
  61.     return result
  62.  
  63. rnd_gens = _init_rnd()
  64. rnd_iter = iter(rnd_gens)
  65.  
  66. def _get_generator():
  67.     # get rnd_iter.next(), or start over
  68.     # if we reached the end of it
  69.     global rnd_iter
  70.     try:
  71.         return rnd_iter.next()
  72.     except StopIteration:
  73.         # the small potential for two threads doing this
  74.         # seems does not warrant use of a lock
  75.         rnd_iter = iter(rnd_gens)
  76.         return rnd_iter.next()
  77.  
  78. def _new_sid(req):
  79.     # Make a number based on current time, pid, remote ip
  80.     # and two random ints, then hash with md5. This should
  81.     # be fairly unique and very difficult to guess.
  82.  
  83.     t = long(time.time()*10000)
  84.     pid = os.getpid()
  85.     g = _get_generator()
  86.     rnd1 = g.randint(0, 999999999)
  87.     rnd2 = g.randint(0, 999999999)
  88.     ip = req.connection.remote_ip
  89.  
  90.     return md5.new("%d%d%d%d%s" % (t, pid, rnd1, rnd2, ip)).hexdigest()
  91.     
  92. class BaseSession(dict):
  93.  
  94.     def __init__(self, req, sid=None, secret=None, lock=1,
  95.                  timeout=0):
  96.  
  97.         self._req, self._sid, self._secret = req, sid, secret
  98.         self._lock = lock
  99.         self._new = 1
  100.         self._created = 0
  101.         self._accessed = 0
  102.         self._timeout = 0
  103.         self._locked = 0
  104.         self._invalid = 0
  105.         
  106.         dict.__init__(self)
  107.  
  108.         if not self._sid:
  109.             # check to see if cookie exists
  110.             if secret:  
  111.                 cookies = Cookie.get_cookies(req, Class=Cookie.SignedCookie,
  112.                                              secret=self._secret)
  113.             else:
  114.                 cookies = Cookie.get_cookies(req)
  115.  
  116.             if cookies.has_key(COOKIE_NAME):
  117.                 self._sid = cookies[COOKIE_NAME].value
  118.  
  119.         self.init_lock()
  120.  
  121.         if self._sid:
  122.             # attempt to load ourselves
  123.             self.lock()
  124.             if self.load():
  125.                 self._new = 0
  126.  
  127.         if self._new:
  128.             # make a new session
  129.             if self._sid: self.unlock() # unlock old sid
  130.             self._sid = _new_sid(self._req)
  131.             self.lock()                 # lock new sid
  132.             Cookie.add_cookie(self._req, self.make_cookie())
  133.             self._created = time.time()
  134.             if timeout:
  135.                 self._timeout = timeout
  136.             else:
  137.                 self._timeout = DFT_TIMEOUT
  138.  
  139.         self._accessed = time.time()
  140.  
  141.         # need cleanup?
  142.         if random.randint(1, CLEANUP_CHANCE) == 1:
  143.             self.cleanup()
  144.  
  145.     def make_cookie(self):
  146.  
  147.         if self._secret:
  148.             c = Cookie.SignedCookie(COOKIE_NAME, self._sid,
  149.                                     secret=self._secret)
  150.         else:
  151.             c = Cookie.Cookie(COOKIE_NAME, self._sid)
  152.  
  153.         config = self._req.get_options()
  154.         if config.has_key("ApplicationPath"):
  155.             c.path = config["ApplicationPath"]
  156.         else:
  157.             docroot = self._req.document_root()
  158.             # the path where *Handler directive was specified
  159.             dirpath = self._req.hlist.directory 
  160.             c.path = dirpath[len(docroot):]
  161.  
  162.             # XXX Not sure why, but on Win32 hlist.directory
  163.             # may contain a trailing \ - need to investigate,
  164.             # this value is given to us directly by httpd
  165.             if os.name == 'nt' and c.path[-1] == '\\':
  166.                 c.path = c.path[:-1]
  167.         
  168.             # Sometimes there is no path, e.g. when Location
  169.             # is used. When Alias or UserDir are used, then
  170.             # the path wouldn't match the URI. In those cases
  171.             # just default to '/'
  172.             if not c.path or not self._req.uri.startswith(c.path):
  173.                 c.path = '/'
  174.  
  175.         return c
  176.  
  177.     def invalidate(self):
  178.         c = self.make_cookie()
  179.         c.expires = 0
  180.         Cookie.add_cookie(self._req, c)
  181.         self.delete()
  182.         self._invalid = 1
  183.  
  184.     def load(self):
  185.         dict = self.do_load()
  186.         if dict == None:
  187.             return 0
  188.  
  189.         if (time.time() - dict["_accessed"]) > dict["_timeout"]:
  190.             return 0
  191.  
  192.         self._created  = dict["_created"]
  193.         self._accessed = dict["_accessed"]
  194.         self._timeout  = dict["_timeout"]
  195.         self.update(dict["_data"])
  196.         return 1
  197.  
  198.     def save(self):
  199.         if not self._invalid:
  200.             dict = {"_data"    : self.copy(), 
  201.                     "_created" : self._created, 
  202.                     "_accessed": self._accessed, 
  203.                     "_timeout" : self._timeout}
  204.             self.do_save(dict)
  205.  
  206.     def delete(self):
  207.         self.do_delete()
  208.         self.clear()
  209.  
  210.     def init_lock(self):
  211.         pass
  212.             
  213.     def lock(self):
  214.         if self._lock:
  215.             _apache._global_lock(self._req.server, self._sid)
  216.             self._locked = 1
  217.             self._req.register_cleanup(unlock_session_cleanup, self)
  218.  
  219.     def unlock(self):
  220.         if self._lock and self._locked:
  221.             _apache._global_unlock(self._req.server, self._sid)
  222.             self._locked = 0
  223.             
  224.     def is_new(self):
  225.         return not not self._new
  226.  
  227.     def id(self):
  228.         return self._sid
  229.  
  230.     def created(self):
  231.         return self._created
  232.  
  233.     def last_accessed(self):
  234.         return self._accessed
  235.  
  236.     def timeout(self):
  237.         return self._timeout
  238.  
  239.     def set_timeout(self, secs):
  240.         self._timeout = secs
  241.  
  242.     def cleanup(self):
  243.         self.do_cleanup()
  244.  
  245.     def __del__(self):
  246.         self.unlock()
  247.  
  248. def unlock_session_cleanup(sess):
  249.     sess.unlock()
  250.  
  251. def dbm_cleanup(data):
  252.     dbm, server = data
  253.     _apache._global_lock(server, None, 0)
  254.     db = anydbm.open(dbm, 'c')
  255.     try:
  256.         old = []
  257.         s = db.first()
  258.         while 1:
  259.             key, val = s
  260.             dict = cPickle.loads(val)
  261.             try:
  262.                 if (time.time() - dict["_accessed"]) > dict["_timeout"]:
  263.                     old.append(key)
  264.             except KeyError:
  265.                 old.append(key)
  266.             try:
  267.                 s = db.next()
  268.             except KeyError: break
  269.  
  270.         for key in old:
  271.             try:
  272.                 del db[key]
  273.             except: pass
  274.     finally:
  275.         db.close()
  276.         _apache._global_unlock(server, None, 0)
  277.  
  278. class DbmSession(BaseSession):
  279.  
  280.     def __init__(self, req, dbm=None, sid=0, secret=None, dbmtype=anydbm,
  281.                  timeout=0, lock=1):
  282.  
  283.         if not dbm:
  284.             opts = req.get_options()
  285.             if opts.has_key("SessionDbm"):
  286.                 dbm = opts["SessionDbm"]
  287.             else:
  288.                 dbm = os.path.join(tempdir, "mp_sess.dbm")
  289.  
  290.         self._dbmfile = dbm
  291.         self._dbmtype = dbmtype
  292.  
  293.         BaseSession.__init__(self, req, sid=sid, secret=secret,
  294.                              timeout=timeout, lock=lock)
  295.  
  296.     def _set_dbm_type(self):
  297.         module = whichdb.whichdb(self._dbmfile)
  298.         if module:
  299.             self._dbmtype = __import__(module)
  300.         
  301.     def _get_dbm(self):
  302.         result = self._dbmtype.open(self._dbmfile, 'c')
  303.         if self._dbmtype is anydbm:
  304.             self._set_dbm_type()
  305.         return result
  306.  
  307.     def do_cleanup(self):
  308.         data = [self._dbmfile, self._req.server]
  309.         self._req.register_cleanup(dbm_cleanup, data)
  310.         self._req.log_error("DbmSession: registered database cleanup.",
  311.                             apache.APLOG_NOTICE)
  312.  
  313.     def do_load(self):
  314.         _apache._global_lock(self._req.server, None, 0)
  315.         dbm = self._get_dbm()
  316.         try:
  317.             if dbm.has_key(self._sid):
  318.                 return cPickle.loads(dbm[self._sid])
  319.             else:
  320.                 return None
  321.         finally:
  322.             dbm.close()
  323.             _apache._global_unlock(self._req.server, None, 0)
  324.  
  325.     def do_save(self, dict):
  326.         _apache._global_lock(self._req.server, None, 0)
  327.         dbm = self._get_dbm()
  328.         try:
  329.             dbm[self._sid] = cPickle.dumps(dict)
  330.         finally:
  331.             dbm.close()
  332.             _apache._global_unlock(self._req.server, None, 0)
  333.  
  334.     def do_delete(self):
  335.         _apache._global_lock(self._req.server, None, 0)
  336.         dbm = self._get_dbm()
  337.         try:
  338.             try:
  339.                 del dbm[self._sid]
  340.             except KeyError: pass
  341.         finally:
  342.             dbm.close()
  343.             _apache._global_unlock(self._req.server, None, 0)
  344.  
  345. def mem_cleanup(sdict):
  346.     for sid in sdict:
  347.         dict = sdict[sid]
  348.         if (time.time() - dict["_accessed"]) > dict["_timeout"]:
  349.             del sdict[sid]
  350.  
  351. class MemorySession(BaseSession):
  352.  
  353.     sdict = {}
  354.  
  355.     def __init__(self, req, sid=0, secret=None, timeout=0, lock=1):
  356.  
  357.         BaseSession.__init__(self, req, sid=sid, secret=secret,
  358.                              timeout=timeout, lock=lock)
  359.  
  360.     def do_cleanup(self):
  361.         self._req.register_cleanup(mem_cleanup, MemorySession.sdict)
  362.         self._req.log_error("MemorySession: registered session cleanup.",
  363.                             apache.APLOG_NOTICE)
  364.  
  365.     def do_load(self):
  366.         if MemorySession.sdict.has_key(self._sid):
  367.             return MemorySession.sdict[self._sid]
  368.         return None
  369.  
  370.     def do_save(self, dict):
  371.         MemorySession.sdict[self._sid] = dict
  372.  
  373.     def do_delete(self):
  374.         try:
  375.             del MemorySession.sdict[self._sid]
  376.         except KeyError: pass
  377.  
  378. def Session(req, sid=0, secret=None, timeout=0, lock=1):
  379.  
  380.     threaded = _apache.mpm_query(apache.AP_MPMQ_IS_THREADED)
  381.     forked = _apache.mpm_query(apache.AP_MPMQ_IS_FORKED)
  382.     daemons =  _apache.mpm_query(apache.AP_MPMQ_MAX_DAEMONS)
  383.  
  384.     if (threaded and ((not forked) or (daemons == 1))):
  385.         return MemorySession(req, sid=sid, secret=secret,
  386.                              timeout=timeout, lock=lock)
  387.     else:
  388.         return DbmSession(req, sid=sid, secret=secret,
  389.                           timeout=timeout, lock=lock)
  390.  
  391.     
  392.