Code

1087967b6096a6d8d0e8e311238c19c378a488e0
[roundup.git] / roundup / backends / sessions_dbm.py
1 #$Id: sessions_dbm.py,v 1.10 2008-08-18 05:04:01 richard Exp $
2 """This module defines a very basic store that's used by the CGI interface
3 to store session and one-time-key information.
5 Yes, it's called "sessions" - because originally it only defined a session
6 class. It's now also used for One Time Key handling too.
7 """
8 __docformat__ = 'restructuredtext'
10 import anydbm, whichdb, os, marshal, time
11 from roundup import hyperdb
12 from roundup.i18n import _
14 class BasicDatabase:
15     ''' Provide a nice encapsulation of an anydbm store.
17         Keys are id strings, values are automatically marshalled data.
18     '''
19     _db_type = None
21     def __init__(self, db):
22         self.config = db.config
23         self.dir = db.config.DATABASE
24         os.umask(db.config.UMASK)
26     def exists(self, infoid):
27         db = self.opendb('c')
28         try:
29             return db.has_key(infoid)
30         finally:
31             db.close()
33     def clear(self):
34         path = os.path.join(self.dir, self.name)
35         if os.path.exists(path):
36             os.remove(path)
37         elif os.path.exists(path+'.db'):    # dbm appends .db
38             os.remove(path+'.db')
40     def cache_db_type(self, path):
41         ''' determine which DB wrote the class file, and cache it as an
42             attribute of __class__ (to allow for subclassed DBs to be
43             different sorts)
44         '''
45         db_type = ''
46         if os.path.exists(path):
47             db_type = whichdb.whichdb(path)
48             if not db_type:
49                 raise hyperdb.DatabaseError, \
50                     _("Couldn't identify database type")
51         elif os.path.exists(path+'.db'):
52             # if the path ends in '.db', it's a dbm database, whether
53             # anydbm says it's dbhash or not!
54             db_type = 'dbm'
55         self.__class__._db_type = db_type
57     _marker = []
58     def get(self, infoid, value, default=_marker):
59         db = self.opendb('c')
60         try:
61             if db.has_key(infoid):
62                 values = marshal.loads(db[infoid])
63             else:
64                 if default != self._marker:
65                     return default
66                 raise KeyError, 'No such %s "%s"'%(self.name, infoid)
67             return values.get(value, None)
68         finally:
69             db.close()
71     def getall(self, infoid):
72         db = self.opendb('c')
73         try:
74             try:
75                 d = marshal.loads(db[infoid])
76                 del d['__timestamp']
77                 return d
78             except KeyError:
79                 raise KeyError, 'No such %s "%s"'%(self.name, infoid)
80         finally:
81             db.close()
83     def set(self, infoid, **newvalues):
84         db = self.opendb('c')
85         try:
86             if db.has_key(infoid):
87                 values = marshal.loads(db[infoid])
88             else:
89                 values = {'__timestamp': time.time()}
90             values.update(newvalues)
91             db[infoid] = marshal.dumps(values)
92         finally:
93             db.close()
95     def list(self):
96         db = self.opendb('r')
97         try:
98             return db.keys()
99         finally:
100             db.close()
102     def destroy(self, infoid):
103         db = self.opendb('c')
104         try:
105             if db.has_key(infoid):
106                 del db[infoid]
107         finally:
108             db.close()
110     def opendb(self, mode):
111         '''Low-level database opener that gets around anydbm/dbm
112            eccentricities.
113         '''
114         # figure the class db type
115         path = os.path.join(os.getcwd(), self.dir, self.name)
116         if self._db_type is None:
117             self.cache_db_type(path)
119         db_type = self._db_type
121         # new database? let anydbm pick the best dbm
122         if not db_type:
123             return anydbm.open(path, 'c')
125         # open the database with the correct module
126         dbm = __import__(db_type)
127         return dbm.open(path, mode)
129     def commit(self):
130         pass
132     def close(self):
133         pass
135     def updateTimestamp(self, sessid):
136         ''' don't update every hit - once a minute should be OK '''
137         sess = self.get(sessid, '__timestamp', None)
138         now = time.time()
139         if sess is None or now > sess + 60:
140             self.set(sessid, __timestamp=now)
142     def clean(self):
143         ''' Remove session records that haven't been used for a week. '''
144         now = time.time()
145         week = 60*60*24*7
146         for sessid in self.list():
147             sess = self.get(sessid, '__timestamp', None)
148             if sess is None:
149                 self.updateTimestamp(sessid)
150                 continue
151             interval = now - sess
152             if interval > week:
153                 self.destroy(sessid)
155 class Sessions(BasicDatabase):
156     name = 'sessions'
158 class OneTimeKeys(BasicDatabase):
159     name = 'otks'
161 # vim: set sts ts=4 sw=4 et si :