Bläddra i källkod

release v0.2.1

prepare release of v0.2.1
oz123 12 år sedan
förälder
incheckning
c46ba7570a
8 ändrade filer med 107 tillägg och 92 borttagningar
  1. 9 1
      ChangeLog
  2. 1 1
      PKG-INFO
  3. 8 7
      pwman/__init__.py
  4. 23 19
      pwman/data/drivers/sqlite.py
  5. 56 52
      pwman/ui/cli.py
  6. 3 7
      pwman/util/crypto.py
  7. 4 5
      scripts/pwman3
  8. 3 0
      setup.py

+ 9 - 1
ChangeLog

@@ -1,4 +1,12 @@
-2012-10-06 Oz Nahumn <nahumoz@gmail.com>
+2012-12-14 Oz Nahum <nahumoz@gmail.com>
+	* Version 0.2.1 
+	* implement open uri function
+	* better MacOSX logic
+	* implement test mode, so code can run locally
+	* use ArgParse 
+	* some improvements on documentation
+
+2012-10-06 Oz Nahum <nahumoz@gmail.com>
 	* added Debian patches.
     * Merge pull request from David North.
 	* Add copy to clipboard on Mac OSX

+ 1 - 1
PKG-INFO

@@ -1,7 +1,7 @@
 Metadata-Version: 1.0
 Name: Pwman3
 Summary: Pwman is a password management application.
-Home-page: https://github.com/oz123/pwman3
+Home-page: https://github.com/pwman3/pwman3
 Author: Oz Nahum
 Author-email: nahumoz@gmail.com
 License: GNU GPL

+ 8 - 7
pwman/__init__.py

@@ -1,15 +1,15 @@
 #============================================================================
 # This file is part of Pwman3.
-# 
+#
 # Pwman3 is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License, version 2
-# as published by the Free Software Foundation; 
-# 
+# as published by the Free Software Foundation;
+#
 # Pwman3 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU General Public License
 # along with Pwman3; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
@@ -20,10 +20,11 @@
 #============================================================================
 
 appname = "Pwman3"
-version = "0.1.0"
-website = "http://github.com/oz123/pwman3"
+version = "0.2.1"
+website = "http://github.com/pwman3/pwman3"
 author = "Oz Nahum"
 authoremail = "nahumoz@gmail.com"
-description = "Pwman is a password management application."
+description = "Pwman is a lightweight command line password management\
+application."
 keywords = "password management sqlite crypto"
 

+ 23 - 19
pwman/data/drivers/sqlite.py

@@ -1,15 +1,15 @@
 #============================================================================
 # This file is part of Pwman3.
-# 
+#
 # Pwman3 is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License, version 2
-# as published by the Free Software Foundation; 
-# 
+# as published by the Free Software Foundation;
+#
 # Pwman3 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU General Public License
 # along with Pwman3; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
@@ -39,7 +39,7 @@ import cPickle
 
 class SQLiteDatabase(Database):
     """SQLite Database implementation"""
-    
+
     def __init__(self):
         """Initialise SQLitePwmanDatabase instance."""
         Database.__init__(self)
@@ -77,7 +77,7 @@ class SQLiteDatabase(Database):
                     sql += " INTERSECT "
                 else:
                     first = False
-                    
+
                 sql += ("SELECT NODE FROM LOOKUP LEFT JOIN TAGS ON TAG = TAGS.ID "
                         + " WHERE TAGS.DATA = ?")
                 params.append(cPickle.dumps(t))
@@ -102,7 +102,7 @@ class SQLiteDatabase(Database):
             return tags
         except sqlite.DatabaseError, e:
             raise DatabaseException("SQLite: %s" % (e))
-        
+
     def getnodes(self, ids):
         nodes = []
         for i in ids:
@@ -125,7 +125,7 @@ class SQLiteDatabase(Database):
         try:
             sql = "UPDATE NODES SET DATA = ? WHERE ID = ?";
             self._cur.execute(sql, [cPickle.dumps(node), id])
-            
+
         except sqlite.DatabaseError, e:
             raise DatabaseException("SQLite: %s" % (e))
         self._setnodetags(node)
@@ -147,7 +147,7 @@ class SQLiteDatabase(Database):
 
             self._setnodetags(n)
             self._commit()
-            
+
     def removenodes(self, nodes):
         for n in nodes:
             if not isinstance(n, Node): raise DatabaseException(
@@ -155,7 +155,7 @@ class SQLiteDatabase(Database):
             try:
                 sql = "DELETE FROM NODES WHERE ID = ?";
                 self._cur.execute(sql, [n.get_id()])
-                
+
             except sqlite.DatabaseError, e:
                 raise DatabaseException("SQLite: %s" % (e))
             self._deletenodetags(n)
@@ -206,7 +206,7 @@ class SQLiteDatabase(Database):
             if not isinstance(t, Tag): raise DatabaseException(
                 "Tried to insert foreign object into database [%s]", t)
             data = cPickle.dumps(t)
-            
+
             try:
                 self._cur.execute(sql, [data])
                 row = self._cur.fetchone()
@@ -224,19 +224,19 @@ class SQLiteDatabase(Database):
         try:
             sql = "DELETE FROM LOOKUP WHERE NODE = ?"
             self._cur.execute(sql, [node.get_id()])
-            
+
         except sqlite.DatabaseError, e:
             raise DatabaseException("SQLite: %s" % (e))
         self._commit()
-        
+
     def _setnodetags(self, node):
         self._deletenodetags(node)
         ids = self._tagids(node.get_tags())
-        
+
         for i in ids:
             sql = "INSERT OR REPLACE INTO LOOKUP VALUES(?, ?)"
             params = [node.get_id(), i]
-            
+
             try:
                 self._cur.execute(sql, params)
             except sqlite.DatabaseError, e:
@@ -250,7 +250,7 @@ class SQLiteDatabase(Database):
         except sqlite.DatabaseError, e:
             raise DatabaseException("SQLite: %s" % (e))
         self._commit()
-        
+
     def _checktables(self):
         """ Check if the Pwman tables exist """
         self._cur.execute("PRAGMA TABLE_INFO(NODES)")
@@ -271,7 +271,7 @@ class SQLiteDatabase(Database):
             self._cur.execute("CREATE TABLE KEY"
                               + "(THEKEY TEXT NOT NULL DEFAULT '')");
             self._cur.execute("INSERT INTO KEY VALUES('')");
-            
+
             try:
                 self._con.commit()
             except DatabaseError, e:
@@ -288,9 +288,13 @@ class SQLiteDatabase(Database):
             self._con.rollback()
             raise DatabaseException(
                 "SQLite: Error saving key [%s]" % (e))
-        
-        
+
+
     def loadkey(self):
+        """
+        fetch the key to database. the key is also stored
+        encrypted.
+        """
         self._cur.execute("SELECT THEKEY FROM KEY");
         keyrow = self._cur.fetchone()
         if (keyrow[0] == ''):

+ 56 - 52
pwman/ui/cli.py

@@ -1,15 +1,15 @@
 #============================================================================
 # This file is part of Pwman3.
-# 
+#
 # Pwman3 is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License, version 2
-# as published by the Free Software Foundation; 
-# 
+# as published by the Free Software Foundation;
+#
 # Pwman3 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU General Public License
 # along with Pwman3; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
@@ -54,7 +54,7 @@ except ImportError, e:
 class CLICallback(Callback):
     def getinput(self, question):
         return raw_input(question)
-    
+
     def getsecret(self, question):
         return getpass.getpass(question + ":")
 
@@ -79,7 +79,7 @@ class PwmanCli(cmd.Cmd):
         else:
 #            traceback.print_exc()
             print "Error: %s " % (exception)
-    
+
     def do_EOF(self, args):
         return self.do_exit(args)
 
@@ -109,10 +109,10 @@ class PwmanCli(cmd.Cmd):
                 ids += range(int(m.group(1)),
                              int(m.group(2))+1)
         return ids
-    
+
     def get_filesystem_path(self, default=""):
         return getinput("Enter filename: ", default)
-    
+
     def get_username(self, default=""):
         return getinput("Username: ", default)
 
@@ -170,7 +170,7 @@ class PwmanCli(cmd.Cmd):
         for tn in tagstrings:
             tags.append(Tag(tn))
         return tags
-        
+
     def print_node(self, node):
         width = str(_defaultwidth)
         print "Node %d." % (node.get_id())
@@ -184,7 +184,7 @@ class PwmanCli(cmd.Cmd):
                                     node.get_notes())
         print typeset("Tags: ", ANSI.Red),
         for t in node.get_tags():
-            print "%s " % t.get_name(),
+            print "rr %s " % t.get_name(),
         print
 
         def heardEnter():
@@ -203,7 +203,7 @@ class PwmanCli(cmd.Cmd):
                 if ret is not None:
                     return True
             return False
-        
+
         def waituntil_enter(somepredicate,timeout, period=0.25):
             mustend = time.time() + timeout
             while time.time() < mustend:
@@ -212,13 +212,13 @@ class PwmanCli(cmd.Cmd):
                     break
                 time.sleep(period)
             self.do_cls('')
-        
+
         if sys.platform != 'win32':
             print "Type Enter to flush screen (autoflash in 5 sec.)"
             waituntil_enter(heardEnter, 5)
         else:
             print "Press any key to flush screen (autoflash in 5 sec.)"
-            waituntil_enter(heardEnterWin, 5) 
+            waituntil_enter(heardEnterWin, 5)
 
     def do_tags(self, arg):
         tags = self._db.listtags()
@@ -237,7 +237,7 @@ class PwmanCli(cmd.Cmd):
         enc = CryptoEngine.get()
         if not enc.alive():
             return strings
-        
+
         tags = self._db.listtags()
         for t in tags:
             name = t.get_name()
@@ -325,7 +325,7 @@ class PwmanCli(cmd.Cmd):
     def do_export(self, arg):
         try:
             nodes = self.get_ids(arg)
-                
+
             types = exporter.Exporter.types()
             type = select("Select filetype:", types)
             exp = exporter.Exporter.get(type)
@@ -385,7 +385,7 @@ class PwmanCli(cmd.Cmd):
 
     def do_rm(self, arg):
         self.do_delete(arg)
-        
+
     def do_delete(self, arg):
         ids = self.get_ids(arg)
         try:
@@ -404,9 +404,9 @@ class PwmanCli(cmd.Cmd):
 
     def do_ls(self, args):
         self.do_list(args)
-        
+
     def do_list(self, args):
-        
+
         if len(args.split()) > 0:
             self.do_clear('')
             self.do_filter(args)
@@ -447,7 +447,7 @@ class PwmanCli(cmd.Cmd):
                     c = getonechar("Press <Space> for more, or 'Q' to cancel")
                     if c == 'q':
                         break
-        
+
         except Exception, e:
             self.error(e)
 
@@ -457,7 +457,7 @@ class PwmanCli(cmd.Cmd):
             enc.forget()
         except Exception,e:
             self.error(e)
-            
+
     def do_passwd(self, args):
         try:
             self._db.changepassword()
@@ -504,7 +504,7 @@ class PwmanCli(cmd.Cmd):
             print "Config saved."
         except Exception, e:
             self.error(e)
-    
+
     def do_cls(self,args):
         os.system('clear')
 
@@ -518,7 +518,7 @@ class PwmanCli(cmd.Cmd):
                 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()) 
+                (node[0].get_username(), node[0].get_url())
                 time.sleep(10)
                 text_to_clipboards("")
             except Exception, e:
@@ -528,7 +528,7 @@ class PwmanCli(cmd.Cmd):
 
     def do_cp(self,args):
         self.do_copy(args)
-    
+
     def do_open(self, args):
         ids = self.get_ids(args)
         if len(ids) > 1:
@@ -548,12 +548,12 @@ class PwmanCli(cmd.Cmd):
     ##
     def usage(self, string):
         print "Usage: %s" % (string)
-    
+
     def help_open(self):
         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()
 
@@ -570,7 +570,7 @@ the url must contain http:// or https://."
 
     def help_ls(self):
         self.help_list()
-        
+
     def help_list(self):
         self.usage("list <tag> ...")
         print "List nodes that match current or specified filter. ls is an alias."
@@ -606,7 +606,7 @@ the url must contain http:// or https://."
         self.usage("edit <ID|tag> ... ")
         print "Edits a nodes."
         self._mult_id_help()
-    
+
     def help_import(self):
         self.usage("import [filename] ...")
         print "Imports a nodes from a file."
@@ -615,14 +615,14 @@ the url must contain http:// or https://."
         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."
         self._mult_id_help()
-        
+
     def help_new(self):
         self.usage("new")
         print "Creates a new node."
-    
+
     def help_rm(self):
         self.help_delete()
-    
+
     def help_print(self):
         self.usage("print <ID|tag> ...")
         print "Displays a node. ",
@@ -630,7 +630,7 @@ the url must contain http:// or https://."
 
     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'."
-    
+
     def help_exit(self):
         self.usage("exit")
         print "Exits the application."
@@ -642,10 +642,10 @@ the url must contain http:// or https://."
     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>"
-        
+
     def help_ls(self):
         self.help_list()
-    
+
     def help_passwd(self):
         self.usage("passwd")
         print "Changes the password on the database. "
@@ -665,14 +665,18 @@ the url must contain http:// or https://."
     def help_tags(self):
         self.usage("tags")
         print "Displays all tags in used in the database."
-        
+
     def postloop(self):
         try:
             readline.write_history_file(self._historyfile)
         except Exception, e:
             pass
-    
+
     def __init__(self, db, hasxsel):
+        """
+        initialize CLI interface, set up the DB
+        connecion, see if we have xsel ...
+        """
         cmd.Cmd.__init__(self)
         self.intro = "%s %s (c) %s <%s>" % (pwman.appname, pwman.version,
                                             pwman.author, pwman.authoremail)
@@ -709,7 +713,7 @@ class PwmanCliMac(PwmanCli):
             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()) 
+            (node[0].get_username(), node[0].get_url())
             time.sleep(10)
             text_to_clipboards("")
         except Exception, e:
@@ -717,7 +721,7 @@ class PwmanCliMac(PwmanCli):
 
     def do_cp(self,args):
         self.do_copy(args)
-        
+
     def do_open(self, args):
         ids = self.get_ids(args)
         if len(ids) > 1:
@@ -732,7 +736,7 @@ class PwmanCliMac(PwmanCli):
 
     def do_o(self, args):
         self.do_open(args)
-    
+
     ##
     ## Help functions
     ##
@@ -740,7 +744,7 @@ class PwmanCliMac(PwmanCli):
         self.usage("open <ID>")
         print "Launch default browser with 'open url',\n\
 the url must contain http:// or https://."
-    
+
     def help_o(self):
         self.help_open()
 
@@ -757,7 +761,7 @@ def getonechar(question, width=_defaultwidth):
     question = "%s " % (question)
     print question.ljust(width),
     sys.stdout.flush()
-        
+
     fd = sys.stdin.fileno()
     tty_mode = tty.tcgetattr(fd)
     tty.setcbreak(fd)
@@ -767,7 +771,7 @@ def getonechar(question, width=_defaultwidth):
         tty.tcsetattr(fd, tty.TCSAFLUSH, tty_mode)
     print ch
     return ch
-    
+
 def getyesno(question, defaultyes=False, width=_defaultwidth):
     if (defaultyes):
         default = "[Y/n]"
@@ -801,7 +805,7 @@ def getinput(question, default="", completer=None, width=_defaultwidth):
         readline.set_startup_hook(defaulter)
         oldcompleter = readline.get_completer()
         readline.set_completer(completer)
-        
+
         x = raw_input(question.ljust(width))
 
         readline.set_completer(oldcompleter)
@@ -822,8 +826,8 @@ def getpassword(question, width=_defaultwidth, echo=False):
                 return a1
             else:
                 print "Passwords don't match. Try again."
-        
-        
+
+
 def typeset(text, color, bold=False, underline=False):
     if not config.get_value("Global", "colors") == 'yes':
         return text
@@ -858,12 +862,12 @@ def text_to_clipboards(text):
         xsel_proc.communicate(text)
         # "clipboard":
         xsel_proc = sp.Popen(['xsel', '-bi'], stdin=sp.PIPE)
-        xsel_proc.communicate(text) 
+        xsel_proc.communicate(text)
     except OSError, e:
         print e, "\nExecuting xsel failed, is it installed ?\n \
 please check your configuration file ... "
-        
-        
+
+
 def text_to_mcclipboards(text):
     """
     copy text to mac os x clip board
@@ -889,12 +893,12 @@ def open_url(link, MacOS=False):
        sp.Popen([uopen, link], stdin=sp.PIPE)
     except OSError, e:
         print "Executing open_url failed with:\n", e
-    
+
 
 class CliMenu(object):
     def __init__(self):
         self.items = []
-        
+
     def add(self, item):
         if (isinstance(item, CliMenuItem)):
             self.items.append(item)
@@ -913,21 +917,21 @@ class CliMenu(object):
                         currentstr += ("%s " % (c))
                 else:
                     currentstr = current
-                    
+
                 print ("%d - %-"+str(_defaultwidth)+"s %s") % (i, x.name+":",
                                                                currentstr)
             print "%c - Finish editing" % ('X')
             option = getonechar("Enter your choice:")
             try:
                 # substract 1 because array subscripts start at 1
-                selection = int(option) - 1 
+                selection = int(option) - 1
                 value = self.items[selection].editor(self.items[selection].getter())
                 self.items[selection].setter(value)
             except (ValueError,IndexError):
                 if (option.upper() == 'X'):
                     break
                 print "Invalid selection"
-                
+
 class CliMenuItem(object):
     def __init__(self, name, editor, getter, setter):
         self.name = name

+ 3 - 7
pwman/util/crypto.py

@@ -40,8 +40,8 @@ CryptoEngine.init(params)
 crypto = CryptoEngine.get()
 ciphertext = crypto.encrypt("plaintext")
 plaintext = cyypto.decrypt(ciphertext)
-
 """
+
 from Crypto.Cipher import Blowfish as cBlowfish
 from Crypto.Cipher import AES as cAES
 from Crypto.Cipher import ARC2 as cARC2
@@ -104,7 +104,7 @@ class CryptoEngine:
     @classmethod
     def get(cls):
         """
-        get() -> CryptoEngine
+        CryptoEngine.get() -> CryptoEngine
         Return an instance of CryptoEngine.
         If no instance is found, a CryptoException is raised.
         """
@@ -169,13 +169,9 @@ class CryptoEngine:
         Decrypt ciphertext and returns the obj that was encrypted.
         If key is bad, a CryptoBadKeyException is raised
         Can also raise a CryptoException and CryptoUnsupportedException"""
-        print ciphertext
         cipher = self._getcipher()
         ciphertext = str(ciphertext).decode('base64')
         plaintext = cipher.decrypt(ciphertext)
-        print plaintext
-        #import pdb
-        #pdb.set_trace()
         return self._retrievedata(plaintext)
 
     def set_cryptedkey(self, key):
@@ -277,7 +273,7 @@ password again")
 
         while tries < max_tries:
             try:
-                password = self._callback.getsecret("Please enter your\
+                password = self._callback.getsecret("Please enter your \
 password")
                 tmpcipher = self._getcipher_real(password, self._algo)
                 plainkey = tmpcipher.decrypt(str(self._keycrypted).decode(

+ 4 - 5
scripts/pwman3

@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 #============================================================================
 # This file is part of Pwman3.
 #
@@ -15,7 +15,6 @@
 # along with Pwman3; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #============================================================================
-#============================================================================
 # Copyright (C) 2012 Oz Nahum <nahumoz@gmail.com>
 #============================================================================
 # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
@@ -31,7 +30,7 @@ import argparse
 
 parser = argparse.ArgumentParser(description='pwman3 - a command line password'\
 +'manager.')
-parser.add_argument('-c','--config', dest='cfile', 
+parser.add_argument('-c','--config', dest='cfile',
                     default=os.path.expanduser("~/.pwman/config"),
                     help='cofiguretion file to read')
 parser.add_argument('-d', '--database', dest='dbase',
@@ -101,7 +100,7 @@ try:
     elif OSX:
         pbcopypath = which("pbcopy")
         config.set_value("Global", "xsel", pbcopypath)
-        
+
     if args.dbase !=  config.get_value('Database', "filename"):
         config.set_value("Database", "filename", args.dbase)
         _saveconfig = False
@@ -112,7 +111,7 @@ try:
     # set umask before creating/opening any files
     umask = int(config.get_value("Global", "umask"))
     os.umask(umask)
-    
+
     enc = CryptoEngine.get()
 
     dbtype = config.get_value("Database", "type")

+ 3 - 0
setup.py

@@ -1,4 +1,7 @@
 #!/usr/bin/env python
+"""
+script to install pwman3
+"""
 
 from distutils.core import setup
 import pwman