commit b8436fc874ef501a5f16b6e31271706236856eb9
Author: dsorber <david.sorber@gmail.com>
Date:   Sun Sep 17 11:01:46 2017 -0400

    pwmgr v0.5.0 -- Added change detection so textview buffer contents will only be storred if they are changed.

diff --git a/software/pwmgr/pwmgr.py b/software/pwmgr/pwmgr.py
index 871ffc6..39ff16f 100755
--- a/software/pwmgr/pwmgr.py
+++ b/software/pwmgr/pwmgr.py
@@ -1,10 +1,10 @@
 #!/usr/bin/python3
 import glob
+import hashlib
 import os
 import shutil
 import subprocess
 import sys
-import time
 import threading
 
 import gi
@@ -19,7 +19,7 @@ SCRATCH_FILE_PATH = '/mnt/pw_ramdisk/chicken_soup.txt'
 ENC_TMP_FILE_PATH = '/home/dsorber/Documents/chicken_soup.enc.tmp'
 KEY_FILE_PATH = '/mnt/pw_ramdisk/.f05d11ef-000c-45c2-ae14-56b56c23e1c5'
 
-VERSION = '0.0.4'
+VERSION = '0.0.5'
 
 class SearchDialog(Gtk.Dialog):
 
@@ -153,6 +153,7 @@ class TextViewWindow(Gtk.Window):
         self.mounted = False
         self.pass_data_loaded = False
         self.pwd_file_fd = None
+        self.original_hash = None
         self.pbar_win = None
         self.should_quit = False
     
@@ -283,6 +284,9 @@ class TextViewWindow(Gtk.Window):
             self.textview.set_editable(True)
             self.textview.set_cursor_visible(True)
             
+            # Store hash of the original content which is used to detect changes
+            self.original_hash = self._hash_textview()
+            
             # Mark state that pasword data is loaded
             self.pass_data_loaded = True
         
@@ -292,26 +296,31 @@ class TextViewWindow(Gtk.Window):
         """
         
         if self.mounted and self.pass_data_loaded:
-            
-            # Write the password store datat to the sratch file
-            with open(SCRATCH_FILE_PATH, 'w') as pwd_file_fd:
-                pwd_file_fd.write(
-                    self.textbuffer.get_text(self.textbuffer.get_start_iter(),
-                                             self.textbuffer.get_end_iter(), 
-                                             True))
-            print('Write complete')
-            
-            # Encrypt the password store
-            cmd = 'openssl enc -aes-256-cbc -salt -in {:s} -out {:s} -pass file:{:s}'
-            cmd = cmd.format(SCRATCH_FILE_PATH, ENC_TMP_FILE_PATH, KEY_FILE_PATH)
-            subprocess.call(cmd, shell=True, timeout=5)
-            print('Enc complete')
-            
-            # Remove the key file, then copy over the encrypted file with the
-            # just created temp one
-            os.remove(KEY_FILE_PATH)
-            os.remove(ENC_FILE_PATH)
-            os.rename(ENC_TMP_FILE_PATH, ENC_FILE_PATH)
+            # Check if changes have been made
+            current_hash = self._hash_textview()
+            if self.original_hash.hexdigest() != current_hash.hexdigest():
+                # Changes were made, must save data
+                # Write the password store data to the scratch file
+                with open(SCRATCH_FILE_PATH, 'w') as pwd_file_fd:
+                    pwd_file_fd.write(
+                        self.textbuffer.get_text(self.textbuffer.get_start_iter(),
+                                                 self.textbuffer.get_end_iter(), 
+                                                 True))
+                print('Write complete')
+                
+                # Encrypt the password store
+                cmd = 'openssl enc -aes-256-cbc -salt -in {:s} -out {:s} -pass'\
+                      ' file:{:s}'
+                cmd = cmd.format(SCRATCH_FILE_PATH, ENC_TMP_FILE_PATH, 
+                                 KEY_FILE_PATH)
+                subprocess.call(cmd, shell=True, timeout=5)
+                print('Enc complete')
+                
+                # Remove the key file, then copy over the encrypted file with 
+                # the just created temp one
+                os.remove(KEY_FILE_PATH)
+                os.remove(ENC_FILE_PATH)
+                os.rename(ENC_TMP_FILE_PATH, ENC_FILE_PATH)
             
             # Clear the buffer and disable the textview
             self.textbuffer.set_text('')
@@ -326,6 +335,12 @@ class TextViewWindow(Gtk.Window):
         for file_ in glob.glob('{:s}/*'.format(MOUNT_PATH)):
             os.remove(file_)
     
+    def _hash_textview(self):
+        passwd_text = self.textbuffer.get_text(self.textbuffer.get_start_iter(),
+                                               self.textbuffer.get_end_iter(), 
+                                               True)
+        return hashlib.sha256(bytearray(passwd_text, 'utf-8'))
+    
     def do_mount_cleanup(self):
         """
         Clean up ramdisk by overwriting, then unmount
@@ -366,16 +381,19 @@ class TextViewWindow(Gtk.Window):
         """
         Close handler
         """
-        
-        # If data is loaded ask to save before closing
+                
         if self.pass_data_loaded:
-            dialog = WarningDialog(self, 'Save changes?', 
-                                   'Save changes to password data?')
-            response = dialog.run()
-            dialog.destroy()
+            # Hash the current text to see if any changes have been made
+            current_hash = self._hash_textview()
+            if current_hash.hexdigest() != self.original_hash.hexdigest():
+                # Changes have been made, ask the user to save
+                dialog = WarningDialog(self, 'Save changes?', 
+                                       'Save changes to password data?')
+                response = dialog.run()
+                dialog.destroy()
         
-            if response == Gtk.ResponseType.OK:
-                self.save_pwd_file(widget)
+                if response == Gtk.ResponseType.OK:
+                    self.save_pwd_file(widget)
         
         # Clear the buffer and disable the textview
         self.textbuffer.set_text('')
