*.py: Hack around the mapping change from `itervalues' to just `values', etc.
[catacomb-python] / pwsafe
diff --git a/pwsafe b/pwsafe
index 896d63a..e622e53 100644 (file)
--- a/pwsafe
+++ b/pwsafe
@@ -39,6 +39,14 @@ import catacomb as C
 from catacomb.pwsafe import *
 
 ###--------------------------------------------------------------------------
+### Python version portability.
+
+def _text(bin): return bin
+def _bin(text): return text
+
+def _excval(): return SYS.exc_info()[1]
+
+###--------------------------------------------------------------------------
 ### Utilities.
 
 ## The program name.
@@ -46,7 +54,7 @@ prog = re.sub(r'^.*[/\\]', '', argv[0])
 
 def moan(msg):
   """Issue a warning message MSG."""
-  print >>stderr, '%s: %s' % (prog, msg)
+  stderr.write('%s: %s\n' % (prog, msg))
 
 def die(msg):
   """Report MSG as a fatal error, and exit."""
@@ -59,8 +67,8 @@ def die(msg):
 def cmd_create(av):
 
   ## Default crypto-primitive selections.
-  cipher = 'blowfish-cbc'
-  hash = 'rmd160'
+  cipher = 'rijndael-cbc'
+  hash = 'sha256'
   mac = None
 
   ## Parse the options.
@@ -75,7 +83,7 @@ def cmd_create(av):
     elif o in ('-m', '--mac'): mac = a
     elif o in ('-h', '--hash'): hash = a
     elif o in ('-d', '--database'): dbty = a
-    else: raise 'Barf!'
+    else: raise Exception("barf")
   if len(args) > 2: return 1
   if len(args) >= 1: tag = args[0]
   else: tag = 'pwsafe'
@@ -94,8 +102,8 @@ def cmd_changepp(av):
 def cmd_find(av):
   if len(av) != 1: return 1
   with PW(file) as pw:
-    try: print pw[av[0]]
-    except KeyError, exc: die("Password `%s' not found" % exc.args[0])
+    try: print(_text(pw[_bin(av[0])]))
+    except KeyError: die("Password `%s' not found" % _excval().args[0])
 
 def cmd_store(av):
   if len(av) < 1 or len(av) > 2: return 1
@@ -106,10 +114,10 @@ def cmd_store(av):
       vpp = C.getpass("Confirm passphrase `%s': " % tag)
       if pp != vpp: die("passphrases don't match")
     elif av[1] == '-':
-      pp = stdin.readline().rstrip('\n')
+      pp = _bin(stdin.readline().rstrip('\n'))
     else:
-      pp = av[1]
-    pw[av[0]] = pp
+      pp = _bin(av[1])
+    pw[_bin(av[0])] = pp
 
 def cmd_copy(av):
   if len(av) < 1 or len(av) > 2: return 1
@@ -118,7 +126,8 @@ def cmd_copy(av):
       if len(av) >= 3: pat = av[1]
       else: pat = None
       for k in pw_in:
-        if pat is None or fnmatch(k, pat): pw_out[k] = pw_in[k]
+        ktext = _text(k)
+        if pat is None or fnmatch(ktext, pat): pw_out[k] = pw_in[k]
 
 def cmd_list(av):
   if len(av) > 1: return 1
@@ -126,16 +135,17 @@ def cmd_list(av):
     if len(av) >= 1: pat = av[0]
     else: pat = None
     for k in pw:
-      if pat is None or fnmatch(k, pat): print k
+      ktext = _text(k)
+      if pat is None or fnmatch(ktext, pat): print(ktext)
 
 def cmd_topixie(av):
   if len(av) > 2: return 1
   with PW(file) as pw:
     pix = C.Pixie()
     if len(av) == 0:
-      for tag in pw: pix.set(tag, pw[tag])
+      for tag in pw: pix.set(tag, pw[_bin(tag)])
     else:
-      tag = av[0]
+      tag = _bin(av[0])
       if len(av) >= 2: pptag = av[1]
       else: pptag = av[0]
       pix.set(pptag, pw[tag])
@@ -143,9 +153,28 @@ def cmd_topixie(av):
 def cmd_del(av):
   if len(av) != 1: return 1
   with PW(file, writep = True) as pw:
-    tag = av[0]
+    tag = _bin(av[0])
     try: del pw[tag]
-    except KeyError, exc: die("Password `%s' not found" % exc.args[0])
+    except KeyError: die("Password `%s' not found" % _excval().args[0])
+
+def cmd_xfer(av):
+
+  ## Parse the command line.
+  try: opts, args = getopt(av, 'd:', ['database='])
+  except GetoptError: return 1
+  dbty = 'flat'
+  for o, a in opts:
+    if o in ('-d', '--database'): dbty = a
+    else: raise Exception("barf")
+  if len(args) != 1: return 1
+  try: dbcls = StorageBackend.byname(dbty)
+  except KeyError: die("Unknown database backend `%s'" % dbty)
+
+  ## Create the target database.
+  with StorageBackend.open(file) as db_in:
+    with dbcls.create(args[0]) as db_out:
+      for k, v in db_in.iter_meta(): db_out.put_meta(k, v)
+      for k, v in db_in.iter_passwds(): db_out.put_passwd(k, v)
 
 commands = { 'create': [cmd_create,
                         '[-c CIPHER] [-d DBTYPE] [-h HASH] [-m MAC] [PP-TAG]'],
@@ -155,24 +184,25 @@ commands = { 'create': [cmd_create,
              'changepp' : [cmd_changepp, ''],
              'copy' : [cmd_copy, 'DEST-FILE [GLOB-PATTERN]'],
              'to-pixie' : [cmd_topixie, '[TAG [PIXIE-TAG]]'],
-             'delete' : [cmd_del, 'TAG']}
+             'delete' : [cmd_del, 'TAG'],
+             'xfer': [cmd_xfer, '[-d DBTYPE] DEST-FILE'] }
 
 ###--------------------------------------------------------------------------
 ### Command-line handling and dispatch.
 
 def version():
-  print '%s 1.0.0' % prog
-  print 'Backend types: %s' % \
-      ' '.join([c.NAME for c in StorageBackend.classes()])
+  print('%s 1.0.0' % prog)
+  print('Backend types: %s' %
+        ' '.join([c.NAME for c in StorageBackend.classes()]))
 
 def usage(fp):
-  print >>fp, 'Usage: %s COMMAND [ARGS...]' % prog
+  fp.write('Usage: %s COMMAND [ARGS...]\n' % prog)
 
 def help():
   version()
-  print
+  print('')
   usage(stdout)
-  print '''
+  print('''
 Maintains passwords or other short secrets securely.
 
 Options:
@@ -184,9 +214,9 @@ Options:
 -f, --file=FILE                Where to find the password-safe file.
 
 Commands provided:
-'''
+''')
   for c in sorted(commands):
-    print '%s %s' % (c, commands[c][1])
+    print('%s %s' % (c, commands[c][1]))
 
 ## Choose a default database file.
 if 'PWSAFE' in environ:
@@ -214,7 +244,7 @@ for o, a in opts:
   elif o in ('-f', '--file'):
     file = a
   else:
-    raise 'Barf!'
+    raise Exception("barf")
 if len(argv) < 1:
   usage(stderr)
   exit(1)
@@ -227,7 +257,7 @@ else:
   c = 'find'
 try:
   if commands[c][0](argv):
-    print >>stderr, 'Usage: %s %s %s' % (prog, c, commands[c][1])
+    stderr.write('Usage: %s %s %s\n' % (prog, c, commands[c][1]))
     exit(1)
 except DecryptError:
   die("decryption failure")