"""Track pending actions which require confirmation."""
import os
import sha
import time
import errno
import random
import cPickle
from Mailman import mm_cfg
SUBSCRIPTION = 'S'
UNSUBSCRIPTION = 'U'
CHANGE_OF_ADDRESS = 'C'
HELD_MESSAGE = 'H'
RE_ENABLE = 'E'
PROBE_BOUNCE = 'P'
_ALLKEYS = (SUBSCRIPTION, UNSUBSCRIPTION,
CHANGE_OF_ADDRESS, HELD_MESSAGE,
RE_ENABLE, PROBE_BOUNCE,
)
try:
True, False
except NameError:
True = 1
False = 0
_missing = []
class Pending:
def InitTempVars(self):
self.__pendfile = os.path.join(self.fullpath(), 'pending.pck')
def pend_new(self, op, *content, **kws):
"""Create a new entry in the pending database, returning cookie for it.
"""
assert op in _ALLKEYS, 'op: %s' % op
lifetime = kws.get('lifetime', mm_cfg.PENDING_REQUEST_LIFE)
assert self.Locked()
db = self.__load()
while True:
now = time.time()
x = random.random() + now % 1.0 + time.clock() % 1.0
cookie = sha.new(repr(x)).hexdigest()
if not db.has_key(cookie):
break
db[cookie] = (op,) + content
evictions = db.setdefault('evictions', {})
evictions[cookie] = now + lifetime
self.__save(db)
return cookie
def __load(self):
try:
fp = open(self.__pendfile)
except IOError, e:
if e.errno <> errno.ENOENT: raise
return {'evictions': {}}
try:
return cPickle.load(fp)
finally:
fp.close()
def __save(self, db):
evictions = db['evictions']
now = time.time()
for cookie, data in db.items():
if cookie in ('evictions', 'version'):
continue
timestamp = evictions[cookie]
if now > timestamp:
del db[cookie]
del evictions[cookie]
for cookie in evictions.keys():
if not db.has_key(cookie):
del evictions[cookie]
db['version'] = mm_cfg.PENDING_FILE_SCHEMA_VERSION
tmpfile = '%s.tmp.%d.%d' % (self.__pendfile, os.getpid(), now)
omask = os.umask(007)
try:
fp = open(tmpfile, 'w')
try:
cPickle.dump(db, fp)
fp.flush()
os.fsync(fp.fileno())
finally:
fp.close()
os.rename(tmpfile, self.__pendfile)
finally:
os.umask(omask)
def pend_confirm(self, cookie, expunge=True):
"""Return data for cookie, or None if not found.
If optional expunge is True (the default), the record is also removed
from the database.
"""
db = self.__load()
if not expunge:
return db.get(cookie)
assert self.Locked()
content = db.get(cookie, _missing)
if content is _missing:
return None
del db[cookie]
del db['evictions'][cookie]
self.__save(db)
return content
def pend_repend(self, cookie, data, lifetime=mm_cfg.PENDING_REQUEST_LIFE):
assert self.Locked()
db = self.__load()
db[cookie] = data
db['evictions'][cookie] = time.time() + lifetime
self.__save(db)
def _update(olddb):
db = {}
if olddb.has_key('lastculltime'):
del olddb['lastculltime']
evictions = db.setdefault('evictions', {})
for cookie, data in olddb.items():
cookie = str(cookie)
db[cookie] = (SUBSCRIPTION,) + data[:-1] + \
(mm_cfg.DEFAULT_SERVER_LANGUAGE,)
evictions[cookie] = data[-1] + mm_cfg.PENDING_REQUEST_LIFE
return db