|
@@ -41,7 +41,7 @@ import os
|
|
|
import struct
|
|
|
import getpass
|
|
|
import cmd
|
|
|
-#import traceback
|
|
|
+# import traceback
|
|
|
import time
|
|
|
import select as uselect
|
|
|
import subprocess as sp
|
|
@@ -58,6 +58,7 @@ try:
|
|
|
except ImportError, e:
|
|
|
_readline_available = False
|
|
|
|
|
|
+
|
|
|
class CLICallback(Callback):
|
|
|
def getinput(self, question):
|
|
|
return raw_input(question)
|
|
@@ -65,6 +66,7 @@ class CLICallback(Callback):
|
|
|
def getsecret(self, question):
|
|
|
return getpass.getpass(question + ":")
|
|
|
|
|
|
+
|
|
|
class ANSI(object):
|
|
|
Reset = 0
|
|
|
Bold = 1
|
|
@@ -79,6 +81,7 @@ class ANSI(object):
|
|
|
Cyan = 36
|
|
|
White = 37
|
|
|
|
|
|
+
|
|
|
class PwmanCli(cmd.Cmd):
|
|
|
def error(self, exception):
|
|
|
if (isinstance(exception, KeyboardInterrupt)):
|
|
@@ -92,10 +95,10 @@ class PwmanCli(cmd.Cmd):
|
|
|
|
|
|
def do_exit(self, args):
|
|
|
print
|
|
|
- #try:
|
|
|
+ # try:
|
|
|
# print "goodbye"
|
|
|
self._db.close()
|
|
|
- #except DatabaseException, e:
|
|
|
+ # except DatabaseException, e:
|
|
|
# self.error(e)
|
|
|
return True
|
|
|
|
|
@@ -105,7 +108,7 @@ class PwmanCli(cmd.Cmd):
|
|
|
idstrs = args.split()
|
|
|
for i in idstrs:
|
|
|
m = rex.match(i)
|
|
|
- if m == None:
|
|
|
+ if m is None:
|
|
|
try:
|
|
|
ids.append(int(i))
|
|
|
except ValueError:
|
|
@@ -123,8 +126,8 @@ class PwmanCli(cmd.Cmd):
|
|
|
def get_username(self, default=""):
|
|
|
return getinput("Username: ", default)
|
|
|
|
|
|
- def get_password(self, argsgiven, numerics=False,leetify=False, symbols=False,
|
|
|
- special_signs=False):
|
|
|
+ def get_password(self, argsgiven, numerics=False, leetify=False,
|
|
|
+ symbols=False, special_signs=False):
|
|
|
"""
|
|
|
in the config file:
|
|
|
numerics -> numerics
|
|
@@ -134,19 +137,23 @@ class PwmanCli(cmd.Cmd):
|
|
|
if argsgiven == 1:
|
|
|
length = getinput("Password length (default 7): ", "7")
|
|
|
length = int(length)
|
|
|
- (password, dumpme) = generator.generate_password(length, length, \
|
|
|
- True, leetify, numerics, special_signs)
|
|
|
+ password, dumpme = generator.generate_password(length, length,
|
|
|
+ True, leetify,
|
|
|
+ numerics,
|
|
|
+ special_signs)
|
|
|
print "New password: %s" % (password)
|
|
|
return password
|
|
|
# no args given
|
|
|
- password = getpassword("Password (Blank to generate): ", _defaultwidth, \
|
|
|
- False)
|
|
|
+ password = getpassword("Password (Blank to generate): ",
|
|
|
+ _defaultwidth, False)
|
|
|
if len(password) == 0:
|
|
|
length = getinput("Password length (default 7): ", "7")
|
|
|
length = int(length)
|
|
|
|
|
|
- (password, dumpme) = generator.generate_password(length, length, \
|
|
|
- True, leetify, numerics, special_signs)
|
|
|
+ (password, dumpme) = generator.generate_password(length, length,
|
|
|
+ True, leetify,
|
|
|
+ numerics,
|
|
|
+ special_signs)
|
|
|
print "New password: %s" % (password)
|
|
|
|
|
|
return password
|
|
@@ -243,7 +250,7 @@ class PwmanCli(cmd.Cmd):
|
|
|
def do_tags(self, arg):
|
|
|
tags = self._db.listtags()
|
|
|
if len(tags) > 0:
|
|
|
- tags[0].get_name() # hack to get password request before output
|
|
|
+ tags[0].get_name() # hack to get password request before output
|
|
|
print "Tags: ",
|
|
|
if len(tags) == 0:
|
|
|
print "None",
|
|
@@ -251,7 +258,6 @@ class PwmanCli(cmd.Cmd):
|
|
|
print "%s " % (t.get_name()),
|
|
|
print
|
|
|
|
|
|
-
|
|
|
def complete_filter(self, text, line, begidx, endidx):
|
|
|
strings = []
|
|
|
enc = CryptoEngine.get()
|
|
@@ -265,7 +271,6 @@ class PwmanCli(cmd.Cmd):
|
|
|
strings.append(t.get_name())
|
|
|
return strings
|
|
|
|
|
|
-
|
|
|
def do_filter(self, args):
|
|
|
tagstrings = args.split()
|
|
|
|
|
@@ -323,7 +328,6 @@ class PwmanCli(cmd.Cmd):
|
|
|
except Exception, e:
|
|
|
self.error(e)
|
|
|
|
|
|
-
|
|
|
def do_import(self, arg):
|
|
|
try:
|
|
|
args = arg.split()
|
|
@@ -386,8 +390,8 @@ class PwmanCli(cmd.Cmd):
|
|
|
Password (Blank to generate):
|
|
|
"""
|
|
|
errmsg = """could not parse config override, please input some"""\
|
|
|
- +""" kind of dictionary, e.g.: n {'leetify':False, """\
|
|
|
- +"""'numerics':True, 'special_chars':True}"""
|
|
|
+ + """ kind of dictionary, e.g.: n {'leetify':False, """\
|
|
|
+ + """'numerics':True, 'special_chars':True}"""
|
|
|
try:
|
|
|
username = self.get_username()
|
|
|
if args:
|
|
@@ -399,10 +403,14 @@ class PwmanCli(cmd.Cmd):
|
|
|
raise Exception(errmsg)
|
|
|
password = self.get_password(1, **args)
|
|
|
else:
|
|
|
- numerics = config.get_value("Generator", "numerics").lower() == 'true'
|
|
|
+ numerics = config.get_value("Generator",
|
|
|
+ "numerics").lower() == 'true'
|
|
|
# TODO: allow custom leetifying through the config
|
|
|
- leetify = config.get_value("Generator", "leetify").lower() == 'true'
|
|
|
- special_chars = config.get_value("Generator", "special_chars").lower() == 'true'
|
|
|
+ leetify = config.get_value("Generator",
|
|
|
+ "leetify").lower() == 'true'
|
|
|
+ special_chars = config.get_value("Generator",
|
|
|
+ "special_chars").lower() == \
|
|
|
+ 'true'
|
|
|
password = self.get_password(0,
|
|
|
numerics=numerics,
|
|
|
symbols=leetify,
|
|
@@ -437,8 +445,8 @@ class PwmanCli(cmd.Cmd):
|
|
|
nodes = self._db.getnodes(ids)
|
|
|
for n in nodes:
|
|
|
b = getyesno("Are you sure you want to delete '%s@%s'?"
|
|
|
- % (n.get_username(), n.get_url()), False)
|
|
|
- if b == True:
|
|
|
+ % (n.get_username(), n.get_url()), False)
|
|
|
+ if b is True:
|
|
|
self._db.removenodes([n])
|
|
|
print "%s@%s deleted" % (n.get_username(), n.get_url())
|
|
|
except Exception, e:
|
|
@@ -459,7 +467,7 @@ class PwmanCli(cmd.Cmd):
|
|
|
if sys.platform != 'win32':
|
|
|
rows, cols = gettermsize()
|
|
|
else:
|
|
|
- rows, cols = 18, 80 # fix this !
|
|
|
+ rows, cols = 18, 80
|
|
|
nodeids = self._db.listnodes()
|
|
|
nodes = self._db.getnodes(nodeids)
|
|
|
cols -= 8
|
|
@@ -562,8 +570,9 @@ class PwmanCli(cmd.Cmd):
|
|
|
try:
|
|
|
node = self._db.getnodes(ids)
|
|
|
text_to_clipboards(node[0].get_password())
|
|
|
- print "copied password for %s@%s clipboard... erasing in 10 sec..." % \
|
|
|
- (node[0].get_username(), node[0].get_url())
|
|
|
+ print "copied password for %s@%s clipboard... " \
|
|
|
+ + "erasing in 10 sec..." % (node[0].get_username(),
|
|
|
+ node[0].get_url())
|
|
|
time.sleep(10)
|
|
|
text_to_clipboards("")
|
|
|
except Exception, e:
|
|
@@ -594,6 +603,7 @@ class PwmanCli(cmd.Cmd):
|
|
|
##
|
|
|
## Help functions
|
|
|
##
|
|
|
+
|
|
|
def usage(self, string):
|
|
|
print "Usage: %s" % (string)
|
|
|
|
|
@@ -601,6 +611,7 @@ class PwmanCli(cmd.Cmd):
|
|
|
self.usage("open <ID>")
|
|
|
print "Launch default browser with 'xdg-open url',\n" \
|
|
|
+ "the url must contain http:// or https://."
|
|
|
+
|
|
|
def help_o(self):
|
|
|
self.help_open()
|
|
|
|
|
@@ -617,7 +628,8 @@ class PwmanCli(cmd.Cmd):
|
|
|
|
|
|
def help_list(self):
|
|
|
self.usage("list <tag> ...")
|
|
|
- print "List nodes that match current or specified filter. ls is an alias."
|
|
|
+ print "List nodes that match current or specified filter."
|
|
|
+ + "ls is an alias."
|
|
|
|
|
|
def help_EOF(self):
|
|
|
self.help_exit()
|
|
@@ -657,7 +669,9 @@ class PwmanCli(cmd.Cmd):
|
|
|
|
|
|
def help_export(self):
|
|
|
self.usage("export <ID|tag> ... ")
|
|
|
- print "Exports a list of ids to an external format. If no IDs or tags are specified, then all nodes under the current filter are exported."
|
|
|
+ print "Exports a list of ids to an external format. If no IDs or" \
|
|
|
+ + " tags are specified, then all nodes under the current" \
|
|
|
+ + " filter are exported."
|
|
|
self._mult_id_help()
|
|
|
|
|
|
def help_new(self):
|
|
@@ -675,7 +689,12 @@ pwman> n {'leetify':False, 'numerics':True}"""
|
|
|
self._mult_id_help()
|
|
|
|
|
|
def _mult_id_help(self):
|
|
|
- print "Multiple ids and nodes can be specified, separated by a space. A range of ids can be specified in the format n-N. e.g. '10-20' would specify all nodes having ids from 10 to 20 inclusive. Tags are considered one-by-one. e.g. 'foo 2 bar' would yield to all nodes with tag 'foo', node 2 and all nodes with tag 'bar'."
|
|
|
+ print "Multiple ids and nodes can be specified, separated by a space."\
|
|
|
+ + "A range of ids can be specified in the format n-N. e.g. " \
|
|
|
+ + "'10-20' would specify all nodes having ids from 10 to 20 " \
|
|
|
+ + "inclusive. Tags are considered one-by-one. e.g. 'foo 2 bar'" \
|
|
|
+ + " would yield to all nodes with tag 'foo', node 2 and all "\
|
|
|
+ + "nodes with tag 'bar'."
|
|
|
|
|
|
def help_exit(self):
|
|
|
self.usage("exit")
|
|
@@ -683,11 +702,17 @@ pwman> n {'leetify':False, 'numerics':True}"""
|
|
|
|
|
|
def help_save(self):
|
|
|
self.usage("save [filename]")
|
|
|
- print "Saves the current configuration to [filename]. If no filename is given, the configuration is saved to the file from which the initial configuration was loaded."
|
|
|
+ print "Saves the current configuration to [filename]. If no filename "
|
|
|
+ + "is given, the configuration is saved to the file from which the "
|
|
|
+ + "initial configuration was loaded."
|
|
|
|
|
|
def help_set(self):
|
|
|
self.usage("set [configoption] [value]")
|
|
|
- print "Sets a configuration option. If no value is specified, the current value for [configoption] is output. If neither [configoption] nor [value] are specified, the whole current configuration is output. [configoption] must be of the format <section>.<option>"
|
|
|
+ print "Sets a configuration option. If no value is specified, the "
|
|
|
+ + "current value for [configoption] is output. If neither "
|
|
|
+ + "[configoption] nor [value] are specified, the whole current "
|
|
|
+ + "configuration is output. [configoption] must be of the "
|
|
|
+ + "format <section>.<option>"
|
|
|
|
|
|
def help_passwd(self):
|
|
|
self.usage("passwd")
|
|
@@ -695,7 +720,8 @@ pwman> n {'leetify':False, 'numerics':True}"""
|
|
|
|
|
|
def help_forget(self):
|
|
|
self.usage("forget")
|
|
|
- print "Forgets the database password. Your password will need to be reentered before accessing the database again."
|
|
|
+ print "Forgets the database password. Your password will need to "
|
|
|
+ + "be reentered before accessing the database again."
|
|
|
|
|
|
def help_clear(self):
|
|
|
self.usage("clear")
|
|
@@ -703,7 +729,8 @@ pwman> n {'leetify':False, 'numerics':True}"""
|
|
|
|
|
|
def help_filter(self):
|
|
|
self.usage("filter <tag> ...")
|
|
|
- print "Filters nodes on tag. Arguments can be zero or more tags. Displays current tags if called without arguments."
|
|
|
+ print "Filters nodes on tag. Arguments can be zero or more tags. "
|
|
|
+ + "Displays current tags if called without arguments."
|
|
|
|
|
|
def help_tags(self):
|
|
|
self.usage("tags")
|
|
@@ -758,7 +785,7 @@ class PwmanCliNew(PwmanCli):
|
|
|
"""
|
|
|
cmd.Cmd.__init__(self)
|
|
|
self.intro = "%s %s (c) visit: %s" % (pwman.appname, pwman.version,
|
|
|
- pwman.website)
|
|
|
+ pwman.website)
|
|
|
self._historyfile = config.get_value("Readline", "history")
|
|
|
self.hasxsel = hasxsel
|
|
|
try:
|
|
@@ -776,6 +803,7 @@ class PwmanCliNew(PwmanCli):
|
|
|
pass
|
|
|
|
|
|
self.prompt = "pwman> "
|
|
|
+
|
|
|
def print_node(self, node):
|
|
|
width = str(_defaultwidth)
|
|
|
print "Node %d." % (node.get_id())
|
|
@@ -852,7 +880,8 @@ class PwmanCliNew(PwmanCli):
|
|
|
strings = []
|
|
|
tags = self._db.listtags(True)
|
|
|
for t in tags:
|
|
|
- strings.append(t.get_name())
|
|
|
+ # strings.append(t.get_name())
|
|
|
+ strings.append(t)
|
|
|
|
|
|
def complete(text, state):
|
|
|
count = 0
|
|
@@ -925,8 +954,8 @@ class PwmanCliNew(PwmanCli):
|
|
|
Password (Blank to generate):
|
|
|
"""
|
|
|
errmsg = """could not parse config override, please input some"""\
|
|
|
- +""" kind of dictionary, e.g.: n {'leetify':False, """\
|
|
|
- +"""'numerics':True, 'special_chars':True}"""
|
|
|
+ + """ kind of dictionary, e.g.: n {'leetify':False, """\
|
|
|
+ + """'numerics':True, 'special_chars':True}"""
|
|
|
try:
|
|
|
username = self.get_username()
|
|
|
if args:
|
|
@@ -938,10 +967,13 @@ class PwmanCliNew(PwmanCli):
|
|
|
raise Exception(errmsg)
|
|
|
password = self.get_password(1, **args)
|
|
|
else:
|
|
|
- numerics = config.get_value("Generator", "numerics").lower() == 'true'
|
|
|
+ numerics = config.get_value(
|
|
|
+ "Generator", "numerics").lower() == 'true'
|
|
|
# TODO: allow custom leetifying through the config
|
|
|
- leetify = config.get_value("Generator", "leetify").lower() == 'true'
|
|
|
- special_chars = config.get_value("Generator", "special_chars").lower() == 'true'
|
|
|
+ leetify = config.get_value(
|
|
|
+ "Generator", "leetify").lower() == 'true'
|
|
|
+ special_chars = config.get_value(
|
|
|
+ "Generator", "special_chars").lower() == 'true'
|
|
|
password = self.get_password(0,
|
|
|
numerics=numerics,
|
|
|
symbols=leetify,
|
|
@@ -979,8 +1011,9 @@ class PwmanCliMac(PwmanCli):
|
|
|
node = self._db.getnodes(ids)
|
|
|
node[0].get_password()
|
|
|
text_to_mcclipboard(node[0].get_password())
|
|
|
- print "copied password for %s@%s clipboard... erasing in 10 sec..." % \
|
|
|
- (node[0].get_username(), node[0].get_url())
|
|
|
+ print "copied password for %s@%s clipboard... " \
|
|
|
+ + "erasing in 10 sec..." % \
|
|
|
+ (node[0].get_username(), node[0].get_url())
|
|
|
time.sleep(10)
|
|
|
text_to_clipboards("")
|
|
|
except Exception, e:
|
|
@@ -1025,11 +1058,13 @@ class PwmanCliMac(PwmanCli):
|
|
|
def help_cp(self):
|
|
|
self.help_copy()
|
|
|
|
|
|
+
|
|
|
class PwmanCliMacNew(PwmanCliMac):
|
|
|
pass
|
|
|
|
|
|
_defaultwidth = 10
|
|
|
|
|
|
+
|
|
|
def getonechar(question, width=_defaultwidth):
|
|
|
question = "%s " % (question)
|
|
|
print question.ljust(width),
|
|
@@ -1045,6 +1080,7 @@ def getonechar(question, width=_defaultwidth):
|
|
|
print ch
|
|
|
return ch
|
|
|
|
|
|
+
|
|
|
def getyesno(question, defaultyes=False, width=_defaultwidth):
|
|
|
if (defaultyes):
|
|
|
default = "[Y/n]"
|
|
@@ -1063,6 +1099,7 @@ def getyesno(question, defaultyes=False, width=_defaultwidth):
|
|
|
else:
|
|
|
return getyesno(question, defaultyes, width)
|
|
|
|
|
|
+
|
|
|
def gettermsize():
|
|
|
s = struct.pack("HHHH", 0, 0, 0, 0)
|
|
|
f = sys.stdout.fileno()
|
|
@@ -1070,6 +1107,7 @@ def gettermsize():
|
|
|
rows, cols, width, height = struct.unpack("HHHH", x)
|
|
|
return rows, cols
|
|
|
|
|
|
+
|
|
|
def getinput(question, default="", completer=None, width=_defaultwidth):
|
|
|
if (not _readline_available):
|
|
|
return raw_input(question.ljust(width))
|
|
@@ -1088,6 +1126,7 @@ def getinput(question, default="", completer=None, width=_defaultwidth):
|
|
|
readline.set_startup_hook()
|
|
|
return x
|
|
|
|
|
|
+
|
|
|
def getpassword(question, width=_defaultwidth, echo=False):
|
|
|
if echo:
|
|
|
print question.ljust(width),
|
|
@@ -1118,6 +1157,7 @@ def typeset(text, color, bold=False, underline=False):
|
|
|
return "\033[%s%s%sm%s\033[%sm" % (bold, underline, color,
|
|
|
text, ANSI.Reset)
|
|
|
|
|
|
+
|
|
|
def select(question, possible):
|
|
|
for i in range(0, len(possible)):
|
|
|
print ("%d - %-"+str(_defaultwidth)+"s") % (i+1, possible[i])
|
|
@@ -1126,6 +1166,7 @@ def select(question, possible):
|
|
|
if uinput.isdigit() and int(uinput) in range(1, len(possible)+1):
|
|
|
return possible[int(uinput)-1]
|
|
|
|
|
|
+
|
|
|
def text_to_clipboards(text):
|
|
|
"""
|
|
|
copy text to clipboard
|
|
@@ -1199,17 +1240,18 @@ class CliMenu(object):
|
|
|
print "%c - Finish editing" % ('X')
|
|
|
option = getonechar("Enter your choice:")
|
|
|
try:
|
|
|
+ print "selection, ", option
|
|
|
# substract 1 because array subscripts start at 0
|
|
|
selection = int(option) - 1
|
|
|
- print "selection, ", selection
|
|
|
# new value is created by calling the editor with the
|
|
|
# previous value as a parameter
|
|
|
# TODO: enable overriding password policy as if new node
|
|
|
# is created.
|
|
|
- if selection == 1: # for password
|
|
|
+ if selection == 1: # for password
|
|
|
value = self.items[selection].editor(0)
|
|
|
else:
|
|
|
- value = self.items[selection].editor(self.items[selection].getter())
|
|
|
+ edit = self.items[selection].getter()
|
|
|
+ value = self.items[selection].editor(edit)
|
|
|
|
|
|
self.items[selection].setter(value)
|
|
|
except (ValueError, IndexError):
|
|
@@ -1217,6 +1259,7 @@ class CliMenu(object):
|
|
|
break
|
|
|
print "Invalid selection"
|
|
|
|
|
|
+
|
|
|
class CliMenuItem(object):
|
|
|
def __init__(self, name, editor, getter, setter):
|
|
|
self.name = name
|