(SELECT attrset FROM expiry WHERE time < ?)''',
[now])
cur.execute('DELETE FROM expiry WHERE time < ?', [now])
+ cur.execute('''DELETE FROM expiry WHERE attrset IN
+ (SELECT attrset
+ FROM expiry LEFT JOIN attrset
+ ON expiry.attrset = attrset.id
+ WHERE attrset.id ISNULL)''')
AttrDB.cleanup(me)
- def expiredp(me, id):
+ def expiry(me, id):
for t, in me.select('SELECT time FROM expiry WHERE attrset = ?', [id]):
- if t < time_format():
- return True
- return False
+ return t
+ return None
+ def expiredp(me, id):
+ t = me.expiry(id)
+ if t is not None and t < time_format():
+ return True
+ else:
+ return False
def setexpire(me, id, when):
if when != C.KEXP_FOREVER:
cur = me.db.cursor()
keyfile = 'db/keyring'
tag = 'cryptomail'
dbfile = 'db/cryptomail.db'
+user = None
commands = {}
def timecmp(x, y):
def cmd_generate(argv):
try:
- opts, argv = getopt(argv, 't:c:f:',
- ['expire=', 'timeout=', 'constraint=', 'format='])
+ opts, argv = getopt(argv, 't:c:f:i:',
+ ['expire=', 'timeout=', 'constraint=',
+ 'info=', 'format='])
except GetoptError:
return 1
kr = C.KeyFile(keyfile, C.KOPEN_WRITE)
if c not in constraints:
die("unknown constraint `%s'", c)
map.setdefault(c, []).append(v)
- elif o in ('f', '--format'):
+ elif o in ('-f', '--format'):
format = a
+ elif o in ('-i', '--info'):
+ map['info'] = [a]
else:
raise 'Barf!'
if timecmp(expwhen, k.deltime) > 0:
a = AttrMultiMap(db)
a.update(map)
a['addr'] = [addr]
+ if user is not None:
+ a['user'] = [user]
c = Crypto(k).encrypt(a.id)
db.setexpire(a.id, expwhen)
print format.replace('%', M.base32_encode(Crypto(k).encrypt(a.id)).
(cmd_initdb, '', """
Initialize an attribute database.""")
+def getid(local):
+ k = C.KeyFile(keyfile, C.KOPEN_READ)[tag]
+ id = Crypto(k).decrypt(M.base32_decode(local))
+ if id is None:
+ raise Reject, 'decrypt failed'
+ return id
+
def cmd_addrcheck(argv):
try:
opts, argv = getopt(argv, '', [])
except GetoptError:
return 1
local, sender = (lambda addr, sender = None, *hunoz: (addr, sender))(*argv)
- k = C.KeyFile(keyfile, C.KOPEN_READ)[tag]
db = CMDB(dbfile)
try:
- id = Crypto(k).decrypt(M.base32_decode(local))
- if id is None:
- raise Reject, 'decrypt failed'
+ id = getid(local)
addr = check(db, id, sender)
except Reject, msg:
print '-%s' % msg
opts, argv = getopt(argv, '', [])
except GetoptError:
return 1
- local, sender = (lambda addr, sender = None, *hunoz: (addr, sender))(*argv)
- k = C.KeyFile(keyfile, C.KOPEN_READ)[tag]
+ if len(argv) not in (1, 2):
+ return 1
+ local, sender = (lambda addr, sender = None: (addr, sender))(*argv)
db = CMDB(dbfile)
try:
- id = Crypto(k).decrypt(M.base32_decode(local))
+ id = getid(local)
if id is None:
raise Reject, 'decrypt failed'
addr = check(db, id, sender, stdin)
stdin.seek(0)
print addr
commands['fwaddr'] = \
- (cmd_fwaddr, 'LOCAL [SENDER [IGNORED ...]]', """
+ (cmd_fwaddr, 'LOCAL [SENDER]', """
Check address token LOCAL. On failure, report reason to stderr and exit
111. On success, write forwarding address to stdout and exit 0. Expects
the message on standard input, as a seekable file.""")
+def cmd_info(argv):
+ try:
+ opts, argv = getopt(argv, '', [])
+ except GetoptError:
+ return 1
+ if len(argv) != 1:
+ return 1
+ local = argv[0]
+ db = CMDB(dbfile)
+ try:
+ id = getid(local)
+ a = AttrMultiMap(db, id)
+ if user is not None and user != a.get('user', [None])[0]:
+ raise Reject, 'not your token'
+ if 'addr' not in a:
+ die('unknown token (expired?)')
+ keys = a.keys()
+ keys.sort()
+ for k in keys:
+ for v in a[k]:
+ print '%s: %s' % (k, v)
+ expwhen = db.expiry(id)
+ if expwhen:
+ print 'expires: %s'
+ else:
+ print 'no-expiry'
+ except Reject, msg:
+ die('invalid token')
+commands['info'] = \
+ (cmd_info, 'LOCAL', """
+Exaimne the address token LOCAL, and print information about it to standard
+output.""")
+
+def cmd_revoke(argv):
+ try:
+ opts, argv = getopt(argv, '', [])
+ except GetoptError:
+ return 1
+ if len(argv) != 1:
+ return 1
+ local = argv[0]
+ db = CMDB(dbfile)
+ try:
+ id = getid(local)
+ a = AttrMultiMap(db, id)
+ if user is not None and user != a.get('user', [None])[0]:
+ raise Reject, 'not your token'
+ if 'addr' not in a:
+ die('unknown token (expired?)')
+ a.clear()
+ db.cleanup()
+ db.commit()
+ except Reject, msg:
+ die('invalid token')
+commands['revoke'] = \
+ (cmd_revoke, 'LOCAL', """
+Revoke the token LOCAL.""")
+
def cmd_cleanup(argv):
try:
opts, argv = getopt(argv, '', [])
-d, --database=FILE Use FILE as the attribute database.
-k, --keyring=KEYRING Use KEYRING as the keyring.
-t, --tag=TAG Use TAG as the key tag.
+ -U, --user=USER Claim to be USER.
"""
cmds = commands.keys()
cmds.sort()
cmd_help()
def main():
- global argv
+ global argv, user, keyfile, dbfile, tag
try:
opts, argv = getopt(argv[1:],
- 'hvud:k:t:',
+ 'hvud:k:t:U:',
['help', 'version', 'usage',
- 'database=', 'keyring=', 'tag='])
+ 'database=', 'keyring=', 'tag=', 'user='])
except GetoptError:
usage(stderr)
exit(111)
keyfile = a
elif o in ('-t', '--tag'):
tag = a
+ elif o in ('-U', '--user'):
+ user = a
else:
raise 'Barf!'
if len(argv) < 1:
try:
main()
-except Exception:
+except SystemExit:
+ raise
+except:
ty, exc, tb = exc_info()
moan('unhandled %s exception' % ty.__name__)
for file, line, func, text in TB.extract_tb(tb):