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.
8 __docformat__
= 'restructuredtext'
10 import os
, marshal
, time
12 from roundup
import hyperdb
13 from roundup
.i18n
import _
14 from roundup
.anypy
.dbm_
import anydbm
, whichdb
17 ''' Provide a nice encapsulation of an anydbm store.
19 Keys are id strings, values are automatically marshalled data.
23 def __init__(self
, db
):
24 self
.config
= db
.config
25 self
.dir = db
.config
.DATABASE
26 os
.umask(db
.config
.UMASK
)
28 def exists(self
, infoid
):
36 path
= os
.path
.join(self
.dir, self
.name
)
37 if os
.path
.exists(path
):
39 elif os
.path
.exists(path
+'.db'): # dbm appends .db
42 def cache_db_type(self
, path
):
43 ''' determine which DB wrote the class file, and cache it as an
44 attribute of __class__ (to allow for subclassed DBs to be
48 if os
.path
.exists(path
):
49 db_type
= whichdb(path
)
51 raise hyperdb
.DatabaseError(
52 _("Couldn't identify database type"))
53 elif os
.path
.exists(path
+'.db'):
54 # if the path ends in '.db', it's a dbm database, whether
55 # anydbm says it's dbhash or not!
57 self
.__class__
._db_type
= db_type
60 def get(self
, infoid
, value
, default
=_marker
):
64 values
= marshal
.loads(db
[infoid
])
66 if default
!= self
._marker
:
68 raise KeyError('No such %s "%s"'%(self
.name
, infoid
))
69 return values
.get(value
, None)
73 def getall(self
, infoid
):
77 d
= marshal
.loads(db
[infoid
])
81 raise KeyError('No such %s "%s"'%(self
.name
, infoid
))
85 def set(self
, infoid
, **newvalues
):
89 values
= marshal
.loads(db
[infoid
])
91 values
= {'__timestamp': time
.time()}
92 values
.update(newvalues
)
93 db
[infoid
] = marshal
.dumps(values
)
104 def destroy(self
, infoid
):
105 db
= self
.opendb('c')
112 def opendb(self
, mode
):
113 '''Low-level database opener that gets around anydbm/dbm
116 # figure the class db type
117 path
= os
.path
.join(os
.getcwd(), self
.dir, self
.name
)
118 if self
._db_type
is None:
119 self
.cache_db_type(path
)
121 db_type
= self
._db_type
123 # new database? let anydbm pick the best dbm
125 return anydbm
.open(path
, 'c')
127 # open the database with the correct module
128 dbm
= __import__(db_type
)
129 return dbm
.open(path
, mode
)
137 def updateTimestamp(self
, sessid
):
138 ''' don't update every hit - once a minute should be OK '''
139 sess
= self
.get(sessid
, '__timestamp', None)
141 if sess
is None or now
> sess
+ 60:
142 self
.set(sessid
, __timestamp
=now
)
145 ''' Remove session records that haven't been used for a week. '''
148 for sessid
in self
.list():
149 sess
= self
.get(sessid
, '__timestamp', None)
151 self
.updateTimestamp(sessid
)
153 interval
= now
- sess
157 class Sessions(BasicDatabase
):
160 class OneTimeKeys(BasicDatabase
):
163 # vim: set sts ts=4 sw=4 et si :