فهرست منبع

Merge branch 'master' of https://github.com/pwman3/pwman3

oz123 12 سال پیش
والد
کامیت
0aa6a34527
8فایلهای تغییر یافته به همراه308 افزوده شده و 116 حذف شده
  1. 24 3
      README.md
  2. 56 0
      documentation/general_notes.txt
  3. 3 4
      pwman/data/database.py
  4. 4 4
      pwman/data/factory.py
  5. 67 10
      pwman/ui/cli.py
  6. 0 1
      pwman/util/config.py
  7. 104 50
      pwman/util/crypto.py
  8. 50 44
      scripts/pwman3

+ 24 - 3
README.md

@@ -4,26 +4,47 @@ A nice command line password manager, which is smart enough to use different SQL
 Pwman3 can also copy passwords to the clipboard (on Mac and Linux) without exposing them, so you save
 Pwman3 can also copy passwords to the clipboard (on Mac and Linux) without exposing them, so you save
 some typing. 
 some typing. 
 Besides managing and storing passwords, Pwman3 can also generate passwords using different algorithms. 
 Besides managing and storing passwords, Pwman3 can also generate passwords using different algorithms. 
+## Nice Features in pwman3:
 
 
+ * copying of passwords to clipboard
+ * lauching specific uri's with default browser
+ * password generators
 
 
 ## Installing 
 ## Installing 
 
 
 Pwman3 requires the following debian packages:
 Pwman3 requires the following debian packages:
 	
 	
-when using python 2.4    
+when using python 2.4:    
+    
     python-pysqlite2
     python-pysqlite2
 	python-celementtree
 	python-celementtree
     python-crypto
     python-crypto
+
+
+
 when using python >= 2.5 
 when using python >= 2.5 
+    
     python-crypto
     python-crypto
 
 
-
 for nicer functionality:
 for nicer functionality:
     xsel - to copy password to clipboard on Linux
     xsel - to copy password to clipboard on Linux
 
 
+
+
+Pwman now uses argparse, which is only
+available in Python 2.7. Therefore, if you intend to use
+pwman3 with an older version of Python, please do so before
+installing:
+
+    $ cp scripts/pwman3 scripts/pwman3_bkup
+    $ cp scripts/pwman3_old scripts/pwman3
+
+Note that the old startup script of pwman3 might have limited
+functionality compared to the newer version. 
+
 To install:
 To install:
 
 
-$ python setup.py install
+    $ python setup.py install
 
 
 ## ikegam's function 
 ## ikegam's function 
 
 

+ 56 - 0
documentation/general_notes.txt

@@ -0,0 +1,56 @@
+# Work in progress
+
+
+enc = CryptoEngine.get() in ui/cli.py (L:940)
+
+is called before the db is even set !
+
+when priniting nodes, decrypt is first called by:
+this initializes CryptoEngine instance,
+and sets the following instance properties:
+    - _callback
+    - _instance 
+    - _keycrypted
+    - _timeout
+    - _cypher
+
+this initialization asks the user for the decryption key
+of the database. 
+
+the action that user does called the respective db function which
+returns an ENCRYPTED STRING!, 
+which is then given to decryption via nodes.py or tags.py which do the
+actual decryption on each decrypted string returned from the DB.
+
+for example print_node:
+
+initializing a _db instance, then we call _db.open()
+
+calling do_print(10) calls _db.getnodes([i]) at this point
+the database is still not decrypted ... e.g. _cypher is still empty
+
+when _db.getnodes([i]) is done node inside  print_node is containing 
+alot of encrypted string.
+
+now print_node will be called, at which point the different methods
+of node instance will decrypt their respective string:
+
+e.g. get_username(self) instide nodes.py:
+     
+     self._username -> OexYH7vT/WVpXO0ZBM93RF/l8+o8/QU8ykgDB4qY8+BxBaKylAOeJWEQ+edjpLTU\n
+     enc = CryptoEngine.get() 
+     enc.decrypt(self._username) -> nahum.oz@gmail.com
+
+
+to see this in work:
+insert 
+        import ipdb; ipdb.set_trace()
+to def getnodes(self, ids) in "pwman/data/drivers/sqlite.py.
+
+continue to pwman3/pwman/ui/cli.py(382) self.print_node(node[0])
+and then step into this function. 
+continue further into  def print_node(self, node) inside pwman3/pwman/ui/cli.py, 
+finally you should step into:
+
+    node.get_username()
+

+ 3 - 4
pwman/data/database.py

@@ -16,7 +16,6 @@
 #============================================================================
 #============================================================================
 # Copyright (C) 2012 Oz Nahum <nahumoz@gmail.com>
 # Copyright (C) 2012 Oz Nahum <nahumoz@gmail.com>
 #============================================================================
 #============================================================================
-#============================================================================
 # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
 # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
 #============================================================================
 #============================================================================
 
 
@@ -58,9 +57,9 @@ class Database:
         return self._filtertags
         return self._filtertags
     
     
     def filter(self, tags):
     def filter(self, tags):
-        for t in tags:
-            if not (t in self._filtertags):
-                self._filtertags.append(t)
+        for tag in tags:
+            if not (tag in self._filtertags):
+                self._filtertags.append(tag)
 
 
     def clearfilter(self):
     def clearfilter(self):
         self._filtertags = []
         self._filtertags = []

+ 4 - 4
pwman/data/factory.py

@@ -16,15 +16,15 @@
 #============================================================================
 #============================================================================
 # Copyright (C) 2012 Oz Nahum <nahumoz@gmail.com>
 # Copyright (C) 2012 Oz Nahum <nahumoz@gmail.com>
 #============================================================================
 #============================================================================
-#============================================================================
 # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
 # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
 #============================================================================
 #============================================================================
 
 
-"""Factory to create Database instances
-
+"""
+Factory to create Database instances
+A Generic interface for all DB engines.
 Usage:
 Usage:
 
 
-import pwlib.db.DatabaseFactory as DBFactory
+import pwman.data.factory as DBFactory
 
 
 db = DBFactory.create(params)
 db = DBFactory.create(params)
 db.open()
 db.open()

+ 67 - 10
pwman/ui/cli.py

@@ -512,10 +512,10 @@ class PwmanCli(cmd.Cmd):
         if self.hasxsel:
         if self.hasxsel:
             ids= self.get_ids(args)
             ids= self.get_ids(args)
             if len(ids) > 1:
             if len(ids) > 1:
-                print "Can only 1 password at a time..."
+                print "Can copy only 1 password at a time..."
+                return None
             try:
             try:
                 node = self._db.getnodes(ids)
                 node = self._db.getnodes(ids)
-                node[0].get_password()
                 text_to_clipboards(node[0].get_password())
                 text_to_clipboards(node[0].get_password())
                 print """copied password for %s@%s clipboard... erasing in 10 sec...""" %\
                 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()) 
@@ -528,13 +528,35 @@ class PwmanCli(cmd.Cmd):
 
 
     def do_cp(self,args):
     def do_cp(self,args):
         self.do_copy(args)
         self.do_copy(args)
-        
+    
+    def do_open(self, args):
+        ids = self.get_ids(args)
+        if len(ids) > 1:
+            print "Can open only 1 link at a time ..."
+            return None
+        try:
+            node = self._db.getnodes(ids)
+            url = node[0].get_url()
+            open_url(url)
+        except Exception, e:
+            self.error(e)
+
+    def do_o(self, args):
+        self.do_open(args)
     ##
     ##
     ## Help functions
     ## Help functions
     ##
     ##
     def usage(self, string):
     def usage(self, string):
         print "Usage: %s" % (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()
+
     def help_copy(self):
     def help_copy(self):
         self.usage("copy <ID>")
         self.usage("copy <ID>")
         print "Copy password to X clipboard (xsel required)"
         print "Copy password to X clipboard (xsel required)"
@@ -696,15 +718,35 @@ class PwmanCliMac(PwmanCli):
     def do_cp(self,args):
     def do_cp(self,args):
         self.do_copy(args)
         self.do_copy(args)
         
         
+    def do_open(self, args):
+        ids = self.get_ids(args)
+        if len(ids) > 1:
+            print "Can open only 1 link at a time ..."
+            return None
+        try:
+            node = self._db.getnodes(ids)
+            url = node[0].get_url()
+            open_url(url,MacOS=True)
+        except Exception, e:
+            self.error(e)
+
+    def do_o(self, args):
+        self.do_open(args)
+    
     ##
     ##
     ## Help functions
     ## Help functions
     ##
     ##
-    def usage(self, string):
-        print "Usage: %s" % (string)
-   
+    def help_open(self):
+        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()
+
     def help_copy(self):
     def help_copy(self):
         self.usage("copy <ID>")
         self.usage("copy <ID>")
-        print "Copy password to X clipboard (xsel required)"
+        print "Copy password to Cocoa clipboard using pbcopy)"
 
 
     def help_cp(self):
     def help_cp(self):
         self.help_copy()
         self.help_copy()
@@ -771,7 +813,6 @@ def getpassword(question, width=_defaultwidth, echo=False):
         print question.ljust(width),
         print question.ljust(width),
         return sys.stdin.readline().rstrip()
         return sys.stdin.readline().rstrip()
     else:
     else:
-        
         while 1:
         while 1:
             a1 = getpass.getpass(question.ljust(width))
             a1 = getpass.getpass(question.ljust(width))
             if len(a1) == 0:
             if len(a1) == 0:
@@ -807,6 +848,7 @@ def select(question, possible):
 
 
 def text_to_clipboards(text):
 def text_to_clipboards(text):
     """
     """
+    copy text to clipboard
     credit:
     credit:
     https://pythonadventures.wordpress.com/tag/xclip/
     https://pythonadventures.wordpress.com/tag/xclip/
     """
     """
@@ -818,11 +860,13 @@ def text_to_clipboards(text):
         xsel_proc = sp.Popen(['xsel', '-bi'], stdin=sp.PIPE)
         xsel_proc = sp.Popen(['xsel', '-bi'], stdin=sp.PIPE)
         xsel_proc.communicate(text) 
         xsel_proc.communicate(text) 
     except OSError, e:
     except OSError, e:
-        print e, "\nExecuting xsel failed, is it installed ?"
+        print e, "\nExecuting xsel failed, is it installed ?\n \
+please check your configuration file ... "
         
         
         
         
 def text_to_mcclipboards(text):
 def text_to_mcclipboards(text):
     """
     """
+    copy text to mac os x clip board
     credit:
     credit:
     https://pythonadventures.wordpress.com/tag/xclip/
     https://pythonadventures.wordpress.com/tag/xclip/
     """
     """
@@ -834,6 +878,19 @@ def text_to_mcclipboards(text):
         print e, "\nExecuting pbcoy failed..."
         print e, "\nExecuting pbcoy failed..."
 
 
 
 
+def open_url(link, MacOS=False):
+    """
+    launch xdg-open or open in MacOSX with url
+    """
+    uopen="xdg-open"
+    if MacOS:
+        uopen="open"
+    try:
+       sp.Popen([uopen, link], stdin=sp.PIPE)
+    except OSError, e:
+        print "Executing open_url failed with:\n", e
+    
+
 class CliMenu(object):
 class CliMenu(object):
     def __init__(self):
     def __init__(self):
         self.items = []
         self.items = []

+ 0 - 1
pwman/util/config.py

@@ -16,7 +16,6 @@
 #============================================================================
 #============================================================================
 # Copyright (C) 2012 Oz Nahum <nahumoz@gmail.com>
 # Copyright (C) 2012 Oz Nahum <nahumoz@gmail.com>
 #============================================================================
 #============================================================================
-#============================================================================
 # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
 # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
 #============================================================================
 #============================================================================
 
 

+ 104 - 50
pwman/util/crypto.py

@@ -1,31 +1,32 @@
 #============================================================================
 #============================================================================
 # This file is part of Pwman3.
 # This file is part of Pwman3.
-# 
+#
 # Pwman3 is free software; you can redistribute it and/or modify
 # Pwman3 is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License, version 2
 # 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,
 # Pwman3 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 # GNU General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU General Public License
 # You should have received a copy of the GNU General Public License
 # along with Pwman3; if not, write to the Free Software
 # along with Pwman3; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #============================================================================
 #============================================================================
 # Copyright (C) 2012 Oz Nahum <nahumoz@gmail.com>
 # Copyright (C) 2012 Oz Nahum <nahumoz@gmail.com>
 #============================================================================
 #============================================================================
-#============================================================================
 # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
 # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
 #============================================================================
 #============================================================================
 
 
-"""Encryption Module used by PwmanDatabase
+"""
+Encryption Module used by PwmanDatabase
 
 
 Supports AES, ARC2, Blowfish, CAST, DES, DES3, IDEA, RC5.
 Supports AES, ARC2, Blowfish, CAST, DES, DES3, IDEA, RC5.
 
 
 Usage:
 Usage:
 import pwman.util.crypto.CryptoEngine as CryptoEngine
 import pwman.util.crypto.CryptoEngine as CryptoEngine
+from pwman.util.crypto import CryptoEngine
 
 
 class myCallback(CryptoEngine.Callback):
 class myCallback(CryptoEngine.Callback):
     def execute(self):
     def execute(self):
@@ -41,14 +42,22 @@ ciphertext = crypto.encrypt("plaintext")
 plaintext = cyypto.decrypt(ciphertext)
 plaintext = cyypto.decrypt(ciphertext)
 
 
 """
 """
-from Crypto.Cipher import *
+from Crypto.Cipher import Blowfish as cBlowfish
+from Crypto.Cipher import AES as cAES
+from Crypto.Cipher import ARC2 as cARC2
+from Crypto.Cipher import ARC4 as cARC2
+from Crypto.Cipher import CAST as cCAST
+from Crypto.Cipher import DES as cDES
+from Crypto.Cipher import DES3 as cDES3
+
 from Crypto.Util.randpool import RandomPool
 from Crypto.Util.randpool import RandomPool
+
 from pwman.util.callback import Callback
 from pwman.util.callback import Callback
 import pwman.util.config as config
 import pwman.util.config as config
 import cPickle
 import cPickle
 import time
 import time
 
 
-_instance = None
+_INSTANCE = None
 
 
 # Use this to tell if crypto is successful or not
 # Use this to tell if crypto is successful or not
 _TAG = "PWMANCRYPTO"
 _TAG = "PWMANCRYPTO"
@@ -84,14 +93,15 @@ class CryptoPasswordMismatchException(CryptoException):
     """Entered passwords do not match."""
     """Entered passwords do not match."""
     def __str__(self):
     def __str__(self):
         return "CryptoPasswordMismatchException: " + self.message
         return "CryptoPasswordMismatchException: " + self.message
-    
+
 
 
 class CryptoEngine:
 class CryptoEngine:
     """Cryptographic Engine"""
     """Cryptographic Engine"""
     _timeoutcount = 0
     _timeoutcount = 0
     _instance = None
     _instance = None
     _callback = None
     _callback = None
-    
+
+    @classmethod
     def get(cls):
     def get(cls):
         """
         """
         get() -> CryptoEngine
         get() -> CryptoEngine
@@ -105,7 +115,7 @@ class CryptoEngine:
             else:
             else:
                 CryptoEngine._instance = CryptoEngine()
                 CryptoEngine._instance = CryptoEngine()
         return CryptoEngine._instance
         return CryptoEngine._instance
-    get = classmethod(get)
+    #get = classmethod(get)
 
 
     def __init__(self):
     def __init__(self):
         """Initialise the Cryptographic Engine
         """Initialise the Cryptographic Engine
@@ -121,27 +131,27 @@ class CryptoEngine:
         if len(algo) > 0:
         if len(algo) > 0:
             self._algo = algo
             self._algo = algo
         else:
         else:
-            raise CryptoException("Parameters missing [%s]" % (e) )
+            raise CryptoException("Parameters missing, no algorithm given")
 
 
         callback = config.get_value("Encryption", "callback")
         callback = config.get_value("Encryption", "callback")
         if isinstance(callback, Callback):
         if isinstance(callback, Callback):
             self._callback = callback
             self._callback = callback
         else:
         else:
             self._callback = None
             self._callback = None
-        
+
         keycrypted = config.get_value("Encryption", "keycrypted")
         keycrypted = config.get_value("Encryption", "keycrypted")
         if len(keycrypted) > 0:
         if len(keycrypted) > 0:
             self._keycrypted = keycrypted
             self._keycrypted = keycrypted
         else:
         else:
             self._keycrypted = None
             self._keycrypted = None
-            
+
         timeout = config.get_value("Encryption", "timeout")
         timeout = config.get_value("Encryption", "timeout")
         if timeout.isdigit():
         if timeout.isdigit():
             self._timeout = timeout
             self._timeout = timeout
         else:
         else:
             self._timeout = -1
             self._timeout = -1
         self._cipher = None
         self._cipher = None
-    
+
     def encrypt(self, obj):
     def encrypt(self, obj):
         """
         """
         encrypt(obj) -> ciphertext
         encrypt(obj) -> ciphertext
@@ -150,31 +160,48 @@ class CryptoEngine:
         cipher = self._getcipher()
         cipher = self._getcipher()
         plaintext = self._preparedata(obj, cipher.block_size)
         plaintext = self._preparedata(obj, cipher.block_size)
         ciphertext = cipher.encrypt(plaintext)
         ciphertext = cipher.encrypt(plaintext)
+
         return str(ciphertext).encode('base64')
         return str(ciphertext).encode('base64')
-        
+
     def decrypt(self, ciphertext):
     def decrypt(self, ciphertext):
         """
         """
         decrypt(ciphertext) -> obj
         decrypt(ciphertext) -> obj
         Decrypt ciphertext and returns the obj that was encrypted.
         Decrypt ciphertext and returns the obj that was encrypted.
         If key is bad, a CryptoBadKeyException is raised
         If key is bad, a CryptoBadKeyException is raised
         Can also raise a CryptoException and CryptoUnsupportedException"""
         Can also raise a CryptoException and CryptoUnsupportedException"""
+        print ciphertext
         cipher = self._getcipher()
         cipher = self._getcipher()
         ciphertext = str(ciphertext).decode('base64')
         ciphertext = str(ciphertext).decode('base64')
         plaintext = cipher.decrypt(ciphertext)
         plaintext = cipher.decrypt(ciphertext)
+        print plaintext
+        #import pdb
+        #pdb.set_trace()
         return self._retrievedata(plaintext)
         return self._retrievedata(plaintext)
 
 
     def set_cryptedkey(self, key):
     def set_cryptedkey(self, key):
+        """
+        hold _keycrypted
+        """
         self._keycrypted = key
         self._keycrypted = key
 
 
     def get_cryptedkey(self):
     def get_cryptedkey(self):
+        """
+        return _keycrypted
+        """
         return self._keycrypted
         return self._keycrypted
 
 
     def set_callback(self, callback):
     def set_callback(self, callback):
+        """
+        set the callback function
+        """
         self._callback = callback
         self._callback = callback
 
 
     def get_callback(self):
     def get_callback(self):
+        """
+        return call back function
+        """
         return self._callback
         return self._callback
-    
+
     def changepassword(self):
     def changepassword(self):
         """
         """
         Creates a new key. The key itself is actually stored in
         Creates a new key. The key itself is actually stored in
@@ -183,106 +210,127 @@ class CryptoEngine:
         password for the database.
         password for the database.
         If oldKeyCrypted is none, then a new password is generated."""
         If oldKeyCrypted is none, then a new password is generated."""
         if (self._callback == None):
         if (self._callback == None):
-            raise CryptoNoCallbackException("No call back class has been specified")
+            raise CryptoNoCallbackException("No call back class has been \
+specified")
         if (self._keycrypted == None):
         if (self._keycrypted == None):
             # Generate a new key, 32 bits in length, if that's
             # Generate a new key, 32 bits in length, if that's
             # too long for the Cipher, _getCipherReal will sort it out
             # too long for the Cipher, _getCipherReal will sort it out
             random = RandomPool()
             random = RandomPool()
             key = str(random.get_bytes(32)).encode('base64')
             key = str(random.get_bytes(32)).encode('base64')
         else:
         else:
-            password = self._callback.getsecret("Please enter your current password")
+            password = self._callback.getsecret("Please enter your current \
+password")
             cipher = self._getcipher_real(password, self._algo)
             cipher = self._getcipher_real(password, self._algo)
             plainkey = cipher.decrypt(str(self._keycrypted).decode('base64'))
             plainkey = cipher.decrypt(str(self._keycrypted).decode('base64'))
             key = self._retrievedata(plainkey)
             key = self._retrievedata(plainkey)
-        newpassword1 = self._callback.getsecret("Please enter your new password");
-        newpassword2 = self._callback.getsecret("Please enter your new password again");
+        newpassword1 = self._callback.getsecret("Please enter your new \
+password")
+        newpassword2 = self._callback.getsecret("Please enter your new \
+password again")
         if (newpassword1 != newpassword2):
         if (newpassword1 != newpassword2):
             raise CryptoPasswordMismatchException("Passwords do not match")
             raise CryptoPasswordMismatchException("Passwords do not match")
         newcipher = self._getcipher_real(newpassword1, self._algo)
         newcipher = self._getcipher_real(newpassword1, self._algo)
-        self._keycrypted = str(newcipher.encrypt(self._preparedata(key, newcipher.block_size))).encode('base64')
-        
+        self._keycrypted = str(newcipher.encrypt(self._preparedata(key,
+                           newcipher.block_size))).encode('base64')
+
         # we also want to create the cipher if there isn't one already
         # we also want to create the cipher if there isn't one already
         # so this CryptoEngine can be used from now on
         # so this CryptoEngine can be used from now on
         if (self._cipher == None):
         if (self._cipher == None):
-            self._cipher = self._getcipher_real(str(key).decode('base64'), self._algo)
-            CryptoEngine._timeoutcount = time.time()        
-            
+            self._cipher = self._getcipher_real(str(key).decode('base64'),
+                                                self._algo)
+            CryptoEngine._timeoutcount = time.time()
+
         return self._keycrypted
         return self._keycrypted
 
 
     def alive(self):
     def alive(self):
+        """
+        check if we have cipher
+        """
         if (self._cipher != None):
         if (self._cipher != None):
             return True
             return True
         else:
         else:
             return False
             return False
 
 
     def forget(self):
     def forget(self):
+        """
+        discard cipher
+        """
         self._cipher = None
         self._cipher = None
-        
+
     def _getcipher(self):
     def _getcipher(self):
+        """
+        get cypher from user, to decrypt DB
+        """
         if (self._cipher != None
         if (self._cipher != None
             and (self._timeout == -1
             and (self._timeout == -1
-                 or (time.time() - CryptoEngine._timeoutcount) < self._timeout)):
+            or (time.time() - CryptoEngine._timeoutcount) < self._timeout)):
             return self._cipher
             return self._cipher
         if (self._callback == None):
         if (self._callback == None):
             raise CryptoNoCallbackException("No Callback exception")
             raise CryptoNoCallbackException("No Callback exception")
         if (self._keycrypted == None):
         if (self._keycrypted == None):
             raise CryptoNoKeyException("Encryption key has not been generated")
             raise CryptoNoKeyException("Encryption key has not been generated")
 
 
-        maxTries = 5
+        max_tries = 5
         tries = 0
         tries = 0
 
 
         key = None
         key = None
 
 
-        while tries < maxTries:
+        while tries < max_tries:
             try:
             try:
-                password = self._callback.getsecret("Please enter your password")
+                password = self._callback.getsecret("Please enter your\
+password")
                 tmpcipher = self._getcipher_real(password, self._algo)
                 tmpcipher = self._getcipher_real(password, self._algo)
-                plainkey = tmpcipher.decrypt(str(self._keycrypted).decode('base64'))
+                plainkey = tmpcipher.decrypt(str(self._keycrypted).decode(
+                    'base64'))
                 key = self._retrievedata(plainkey)
                 key = self._retrievedata(plainkey)
                 break
                 break
-            except CryptoBadKeyException, e:
+            except CryptoBadKeyException:
                 print "Wrong password."
                 print "Wrong password."
                 tries += 1
                 tries += 1
 
 
         if not key:
         if not key:
-            raise Exception("Wrong password entered %s times; giving up" % maxTries)
-        
-        self._cipher = self._getcipher_real(str(key).decode('base64'), self._algo)
+            raise Exception("Wrong password entered %s times; giving up" \
+                    % max_tries)
+
+        self._cipher = self._getcipher_real(str(key).decode('base64'),
+                self._algo)
 
 
         CryptoEngine._timeoutcount = time.time()
         CryptoEngine._timeoutcount = time.time()
         return self._cipher
         return self._cipher
-        
+
 
 
     def _getcipher_real(self, key, algo):
     def _getcipher_real(self, key, algo):
+        """
+        do the real job of decrypting using functions
+        form PyCrypto
+        """
         if (algo == "AES"):
         if (algo == "AES"):
             key = self._padkey(key, [16, 24, 32])
             key = self._padkey(key, [16, 24, 32])
-            cipher = AES.new(key, AES.MODE_ECB)
+            cipher = cAES.new(key, cAES.MODE_ECB)
         elif (algo == 'ARC2'):
         elif (algo == 'ARC2'):
-            cipher = ARC2.new(key, ARC2.MODE_ECB)
+            cipher = cARC2.new(key, cARC2.MODE_ECB)
         elif (algo == 'ARC4'):
         elif (algo == 'ARC4'):
             raise CryptoUnsupportedException("ARC4 is currently unsupported")
             raise CryptoUnsupportedException("ARC4 is currently unsupported")
         elif (algo == 'Blowfish'):
         elif (algo == 'Blowfish'):
-            cipher = Blowfish.new(key, Blowfish.MODE_ECB)
+            cipher = cBlowfish.new(key, cBlowfish.MODE_ECB)
         elif (algo == 'CAST'):
         elif (algo == 'CAST'):
-            cipher = CAST.new(key, CAST.MODE_ECB)
+            cipher = cCAST.new(key, cCAST.MODE_ECB)
         elif (algo == 'DES'):
         elif (algo == 'DES'):
             self._padkey(key, [8])
             self._padkey(key, [8])
-            cipher = DES.new(key, DES.MODE_ECB)
+            cipher = cDES.new(key, cDES.MODE_ECB)
         elif (algo == 'DES3'):
         elif (algo == 'DES3'):
             key = self._padkey(key, [16, 24])
             key = self._padkey(key, [16, 24])
-            cipher = DES3.new(key, DES3.MODE_ECB)
-        elif (algo == 'IDEA'):
-            key = self._padkey(key, [16])
-            cipher = IDEA.new(key, IDEA.MODE_ECB)
-        elif (algo == 'RC5'):
-            cipher = RC5.new(key, RC5.MODE_ECB)
+            cipher =  cDES3.new(key, cDES3.MODE_ECB)
         elif (algo == 'XOR'):
         elif (algo == 'XOR'):
             raise CryptoUnsupportedException("XOR is currently unsupported")
             raise CryptoUnsupportedException("XOR is currently unsupported")
         else:
         else:
-            raise CryptoException("Invalid algorithm specified")        
+            raise CryptoException("Invalid algorithm specified")
         return cipher
         return cipher
 
 
     def _padkey(self, key, acceptable_lengths):
     def _padkey(self, key, acceptable_lengths):
+        """
+        pad key with extra string
+        """
         maxlen = max(acceptable_lengths)
         maxlen = max(acceptable_lengths)
         keylen = len(key)
         keylen = len(key)
         if (keylen > maxlen):
         if (keylen > maxlen):
@@ -297,6 +345,9 @@ class CryptoEngine:
         return key.ljust(newkeylen)
         return key.ljust(newkeylen)
 
 
     def _preparedata(self, obj, blocksize):
     def _preparedata(self, obj, blocksize):
+        """
+        prepare data before encrypting
+        """
         plaintext = cPickle.dumps(obj)
         plaintext = cPickle.dumps(obj)
         plaintext = _TAG + plaintext
         plaintext = _TAG + plaintext
         numblocks = (len(plaintext)/blocksize) + 1
         numblocks = (len(plaintext)/blocksize) + 1
@@ -304,6 +355,9 @@ class CryptoEngine:
         return plaintext.ljust(newdatasize)
         return plaintext.ljust(newdatasize)
 
 
     def _retrievedata(self, plaintext):
     def _retrievedata(self, plaintext):
+        """
+        retrieve encrypted data
+        """
         if (plaintext.startswith(_TAG)):
         if (plaintext.startswith(_TAG)):
             plaintext = plaintext[len(_TAG):]
             plaintext = plaintext[len(_TAG):]
         else:
         else:
@@ -316,7 +370,7 @@ class DummyCryptoEngine(CryptoEngine):
     Only for testing and debugging the DB drivers really."""
     Only for testing and debugging the DB drivers really."""
     def __init__(self):
     def __init__(self):
         pass
         pass
-    
+
     def encrypt(self, obj):
     def encrypt(self, obj):
         """Return the object pickled."""
         """Return the object pickled."""
         return cPickle.dumps(obj)
         return cPickle.dumps(obj)

+ 50 - 44
scripts/pwman3

@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python
 #============================================================================
 #============================================================================
 # This file is part of Pwman3.
 # This file is part of Pwman3.
 #
 #
@@ -15,37 +15,54 @@
 # along with Pwman3; if not, write to the Free Software
 # along with Pwman3; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 # 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>
 # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
 #============================================================================
 #============================================================================
+import os
+import os.path
+import subprocess as sp
+
+_saveconfig = True
+
+import argparse
+
+
+parser = argparse.ArgumentParser(description='pwman3 - a command line password'\
++'manager.')
+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',
+                    default=os.path.expanduser("~/.pwman/pwman.db"))
+
+parser.add_argument('-e', '--encryption', dest="algo", default="Blowfish",
+                help="Possible options are: AES, ARC2, ARC4, "\
+                +"Blowfish(default) CAST, DES, DES3, IDEA, RC5")
+
+parser.add_argument('-t','--test',  help="Run pwman from current directory \
+without installation", action="store_true")
+args = parser.parse_args()
+
+if args.test:
+    import sys
+    sys.path.insert(0,os.getcwd())
+
 from pwman.util.crypto import CryptoEngine
 from pwman.util.crypto import CryptoEngine
 import getopt
 import getopt
 import sys
 import sys
 if 'darwin' in sys.platform:
 if 'darwin' in sys.platform:
-    from pwman.ui.cli import PwmanCliMac
+    from pwman.ui.cli import PwmanCliMac as PwmanCli
+    OSX=True
 else:
 else:
     from pwman.ui.cli import PwmanCli
     from pwman.ui.cli import PwmanCli
 
 
 import pwman.util.config as config
 import pwman.util.config as config
 import pwman.data.factory
 import pwman.data.factory
-import os
-import os.path
-import subprocess as sp
-_saveconfig = True
-
 
 
-def print_help():
-    print """Syntax: %s [options]
+config_file=args.cfile
 
 
- -c, --config FILE      Read configuration from FILE
- -d, --database FILE    Use FILE as database
- -e, --encryption ALGO  Use ALGO to encrypt data
-                        Possible options are:
-                         AES, ARC2, ARC4, Blowfish(default),
-                         CAST, DES, DES3, IDEA, RC5
- -h, --help             Display this help and exit
-
-Please report bugs at https://github.com/pwman3/pwman3
-""" % (sys.argv[0])
 
 
 
 
 def which(cmd):
 def which(cmd):
@@ -75,42 +92,31 @@ try:
                       }
                       }
     config.set_defaults(default_config)
     config.set_defaults(default_config)
 
 
-    opts, args = getopt.getopt(sys.argv[1:], 'c:d:e:h',
-                               ["config=", "database=",
-                               "encryption=", "help"])
-
-    for o in opts:
-        if o[0] == '-c' or o[0] == "--config":
-            config_file = os.path.expanduser(o[1])
-
-        if o[0] == '-h' or o[0] == "--help":
-            print_help()
-            sys.exit(0)
-    # if no config exists yet, look for xsel
     if os.path.exists(config_file):
     if os.path.exists(config_file):
         config.load(config_file)
         config.load(config_file)
         xselpath = config.get_value("Global", "xselpath")
         xselpath = config.get_value("Global", "xselpath")
-    else:
+    elif not OSX :
         xselpath = which("xsel")
         xselpath = which("xsel")
         config.set_value("Global", "xsel", xselpath)
         config.set_value("Global", "xsel", xselpath)
-
-    for o in opts:
-        if o[0] == '-d' or o[0] == "--database":
-            config.set_value("Database", "filename",
-                             os.path.expanduser(o[1]))
-            _saveconfig = False
-        if o[0] == '-e' or o[0] == "--encryption":
-            config.set_value("Encryption", "algorithm", o[1])
+    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
+    if args.algo != "Blowfish":
+            config.set_value("Encryption", "algorithm", args.algo)
             _saveconfig = False
             _saveconfig = False
 
 
     # set umask before creating/opening any files
     # set umask before creating/opening any files
     umask = int(config.get_value("Global", "umask"))
     umask = int(config.get_value("Global", "umask"))
     os.umask(umask)
     os.umask(umask)
-
+    
     enc = CryptoEngine.get()
     enc = CryptoEngine.get()
 
 
-    type = config.get_value("Database", "type")
-    db = pwman.data.factory.create(type)
+    dbtype = config.get_value("Database", "type")
+    db = pwman.data.factory.create(dbtype)
     cli = PwmanCli(db, xselpath)
     cli = PwmanCli(db, xselpath)
 except SystemExit, e:
 except SystemExit, e:
     sys.exit(e)
     sys.exit(e)
@@ -122,7 +128,7 @@ try:
     try:
     try:
         cli.cmdloop()
         cli.cmdloop()
     except KeyboardInterrupt, e:
     except KeyboardInterrupt, e:
-        print
+        print e
 finally:
 finally:
     try:
     try:
         if _saveconfig:
         if _saveconfig: