OSDN Git Service

Add the ability to encrypt & decrypt the database more easily.
authorRandy Baumgarte <randy@fbn.cx>
Tue, 28 Sep 2010 22:18:18 +0000 (18:18 -0400)
committerRandy Baumgarte <randy@fbn.cx>
Fri, 1 Oct 2010 16:43:37 +0000 (12:43 -0400)
src/cx/fbn/nevernote/Global.java
src/cx/fbn/nevernote/NeverNote.java
src/cx/fbn/nevernote/dialog/DBEncryptDialog.java [new file with mode: 0644]
src/cx/fbn/nevernote/gui/MainMenuBar.java

index 0375cde..8a082dc 100644 (file)
@@ -860,6 +860,11 @@ public class Global {
                        val = "jdbc:h2:"+Global.getFileManager().getDbDirPath(Global.databaseName);\r
                return val;\r
     }\r
+    public static void setDatabaseUrl(String value) {\r
+               settings.beginGroup("General");\r
+               settings.setValue("DatabaseURL", value);\r
+               settings.endGroup();\r
+    }\r
     public static String getDatabaseUserid() {\r
                settings.beginGroup("General");\r
                String val  = (String)settings.value("databaseUserid", "");\r
index 5a5a956..30a5cc7 100644 (file)
@@ -26,6 +26,7 @@ import java.security.NoSuchAlgorithmException;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -39,6 +40,7 @@ import java.util.SortedMap;
 import java.util.Vector;
 
 import org.apache.thrift.TException;
+import org.h2.tools.ChangeFileEncryption;
 
 import com.evernote.edam.error.EDAMNotFoundException;
 import com.evernote.edam.error.EDAMSystemException;
@@ -127,6 +129,7 @@ import cx.fbn.nevernote.config.InitializationException;
 import cx.fbn.nevernote.config.StartupConfig;
 import cx.fbn.nevernote.dialog.AccountDialog;
 import cx.fbn.nevernote.dialog.ConfigDialog;
+import cx.fbn.nevernote.dialog.DBEncryptDialog;
 import cx.fbn.nevernote.dialog.DatabaseLoginDialog;
 import cx.fbn.nevernote.dialog.DatabaseStatus;
 import cx.fbn.nevernote.dialog.FindDialog;
@@ -280,6 +283,9 @@ public class NeverNote extends QMainWindow{
     String                             trashNoteGuid;                          // Guid to restore / set into or out of trash to save position
     Thumbnailer                        preview;                                        // generate preview image
     ThumbnailViewer            thumbnailViewer;                        // View preview thumbnail; 
+    boolean                            encryptOnShutdown;                      // should I encrypt when I close?
+    boolean                            decryptOnShutdown;                      // should I decrypt on shutdown;
+    String                             encryptCipher;                          // What cipher should I use?
     
     String iconPath = new String("classpath:cx/fbn/nevernote/icons/");
        
@@ -297,9 +303,13 @@ public class NeverNote extends QMainWindow{
                
                logger = new ApplicationLogger("nevernote.log");
                logger.log(logger.HIGH, "Starting Application");
-
+               
+               decryptOnShutdown = false;
+               encryptOnShutdown = false;
                conn.checkDatabaseVersion();
                
+               
+               
                // Start building the invalid XML tables
                Global.invalidElements = conn.getInvalidXMLTable().getInvalidElements();
                List<String> elements = conn.getInvalidXMLTable().getInvalidAttributeElements();
@@ -644,8 +654,12 @@ public class NeverNote extends QMainWindow{
      */
     private static DatabaseConnection setupDatabaseConnection() throws InitializationException {
        ApplicationLogger logger = new ApplicationLogger("nevernote-database.log");
-       DatabaseConnection dbConn = new DatabaseConnection(logger,Global.getDatabaseUrl(), Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
-
+       
+       File f = Global.getFileManager().getDbDirFile(Global.databaseName + ".h2.db");
+               boolean dbExists = f.exists(); 
+               if (!dbExists)
+                       Global.setDatabaseUrl("");
+       
         if (Global.getDatabaseUrl().toUpperCase().indexOf("CIPHER=") > -1) {
             boolean goodCheck = false;
             while (!goodCheck) {
@@ -658,24 +672,103 @@ public class NeverNote extends QMainWindow{
                         Global.getDatabaseUserPassword(), Global.cipherPassword);
             }
         }
-        return dbConn;
+               DatabaseConnection dbConn = new DatabaseConnection(logger,Global.getDatabaseUrl(), Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
+       return dbConn;
+    }
+    
+    // Encrypt the database upon shutdown
+    private void encryptOnShutdown() {
+        String dbPath= Global.getFileManager().getDbDirPath("");
+        String dbName = "NeverNote";
+        try {
+               Statement st = conn.getConnection().createStatement();  
+               st.execute("shutdown");
+               QMessageBox box = new QMessageBox();
+                       box.setText("Encrypting Database");
+                       box.show();
+                       ChangeFileEncryption.execute(dbPath, dbName, encryptCipher, null, Global.cipherPassword.toCharArray(), true);
+                       Global.setDatabaseUrl(Global.getDatabaseUrl() + ";CIPHER="+encryptCipher);
+                       box.setText("Encryption Complete");
+                       box.close();
+        } catch (SQLException e) {
+                       e.printStackTrace();
+               }       
+    }
+    
+    // Decrypt the database upon shutdown
+    private void decryptOnShutdown() {
+        String dbPath= Global.getFileManager().getDbDirPath("");
+        String dbName = "NeverNote";
+        try {
+               Statement st = conn.getConnection().createStatement();  
+               st.execute("shutdown");
+               if (Global.getDatabaseUrl().toUpperCase().indexOf(";CIPHER=AES") > -1)
+                       encryptCipher = "AES";
+               else
+                       encryptCipher = "XTEA";
+               QMessageBox box = new QMessageBox();
+                       box.setText("Decrypting Database");
+                       box.show();
+                       ChangeFileEncryption.execute(dbPath, dbName, encryptCipher, Global.cipherPassword.toCharArray(), null, true);
+                       Global.setDatabaseUrl("");
+                       box.setText("Decryption Complete");
+                       box.close();
+               } catch (SQLException e) {
+                       e.printStackTrace();
+               }       
+    }
+    /**
+     * Encrypt/Decrypt the local database
+     **/
+    public void doDatabaseEncrypt() {
+       // The database is not currently encrypted
+        if (Global.getDatabaseUrl().toUpperCase().indexOf("CIPHER=") == -1) {
+               if (QMessageBox.question(this, tr("Confirmation"), tr("Encrypting the database is used" +
+                               "to enhance security and is performed\nupon shutdown, but please be aware that if"+
+                               " you lose the password your\nis lost forever.\n\nIt is highly recommended you " +
+                               "perform a backup and/or fully synchronize\n prior to executing this funtction.\n\n" +
+                               "Do you wish to proceed?"),
+                               QMessageBox.StandardButton.Yes, 
+                               QMessageBox.StandardButton.No)==StandardButton.No.value()) {
+                               return;
+               }
+               DBEncryptDialog dialog = new DBEncryptDialog();
+               dialog.exec();
+               if (dialog.okPressed()) {
+                       Global.cipherPassword = dialog.getPassword();
+                       encryptOnShutdown  = true;
+                       encryptCipher = "AES";
+               }
+        } else {
+            DBEncryptDialog dialog = new DBEncryptDialog();
+            dialog.setWindowTitle("Database Decryption");
+            dialog.exec();
+            if (dialog.okPressed()) {
+               if (!dialog.getPassword().equals(Global.cipherPassword)) {
+                       QMessageBox.critical(null, "Incorrect Password", "Incorrect Password");
+                       return;
+               }
+               decryptOnShutdown  = true;
+               encryptCipher = "";
+            }
+        }
+        return;
     }
 
        private static void initializeGlobalSettings(String[] args) throws InitializationException {
-                StartupConfig startupConfig = new StartupConfig();
+               StartupConfig   startupConfig = new StartupConfig();
 
-                for (String arg : args) {
-                        String lower = arg.toLowerCase();
-                        if (lower.startsWith("--name="))
-                                startupConfig.setName(arg.substring(arg.indexOf('=') + 1));
-                        if (lower.startsWith("--home="))
-                                startupConfig.setHomeDirPath(arg.substring(arg.indexOf('=') + 1));
-                        if (lower.startsWith("--disable-viewing"))
-                                startupConfig.setDisableViewing(true);
-                }
-
-                Global.setup(startupConfig);
+        for (String arg : args) {
+            String lower = arg.toLowerCase();
+            if (lower.startsWith("--name="))
+               startupConfig.setName(arg.substring(arg.indexOf('=') + 1));
+            if (lower.startsWith("--home="))
+               startupConfig.setHomeDirPath(arg.substring(arg.indexOf('=') + 1));
+            if (lower.startsWith("--disable-viewing"))
+               startupConfig.setDisableViewing(true);
         }
+                Global.setup(startupConfig);
+    }
 
     // Exit point
        @Override
@@ -774,6 +867,12 @@ public class NeverNote extends QMainWindow{
                        }
                }
 
+               if (encryptOnShutdown) {
+                       encryptOnShutdown();
+               }
+               if (decryptOnShutdown) {
+                       decryptOnShutdown();
+               }
                logger.log(logger.HIGH, "Leaving NeverNote.closeEvent");
        }
 
diff --git a/src/cx/fbn/nevernote/dialog/DBEncryptDialog.java b/src/cx/fbn/nevernote/dialog/DBEncryptDialog.java
new file mode 100644 (file)
index 0000000..d9d4c04
--- /dev/null
@@ -0,0 +1,110 @@
+/*\r
+ * This file is part of NeverNote \r
+ * Copyright 2009 Randy Baumgarte\r
+ * \r
+ * This file may be licensed under the terms of of the\r
+ * GNU General Public License Version 2 (the ``GPL'').\r
+ *\r
+ * Software distributed under the License is distributed\r
+ * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either\r
+ * express or implied. See the GPL for the specific language\r
+ * governing rights and limitations.\r
+ *\r
+ * You should have received a copy of the GPL along with this\r
+ * program. If not, go to http://www.gnu.org/licenses/gpl.html\r
+ * or write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
+ *\r
+*/\r
+\r
+package cx.fbn.nevernote.dialog;\r
+\r
+import com.trolltech.qt.gui.QDialog;\r
+import com.trolltech.qt.gui.QGridLayout;\r
+import com.trolltech.qt.gui.QLabel;\r
+import com.trolltech.qt.gui.QLineEdit;\r
+import com.trolltech.qt.gui.QPushButton;\r
+\r
+public class DBEncryptDialog extends QDialog {\r
+\r
+       private boolean         okPressed;\r
+       private final QLineEdit password1;\r
+       private final QLineEdit password2;\r
+       private final QPushButton ok;\r
+       \r
+       \r
+       // Constructor\r
+       public DBEncryptDialog() {\r
+               okPressed = false;\r
+               setWindowTitle(tr("Database Encryption"));\r
+               QGridLayout grid = new QGridLayout();\r
+               setLayout(grid);\r
+               QGridLayout passwordGrid = new QGridLayout();\r
+               QGridLayout buttonGrid = new QGridLayout();\r
+                               \r
+               password1 = new QLineEdit();\r
+               password1.setEchoMode(QLineEdit.EchoMode.Password);\r
+               password2 = new QLineEdit();\r
+               password2.setEchoMode(QLineEdit.EchoMode.Password);\r
+               \r
+               password1.textChanged.connect(this, "validateInput()");\r
+               password2.textChanged.connect(this, "validateInput()");\r
+               \r
+               passwordGrid.addWidget(new QLabel(tr("Password")), 1,1);\r
+               passwordGrid.addWidget(password1, 1, 2);\r
+               passwordGrid.addWidget(new QLabel(tr("Verify Password")), 2,1);\r
+               passwordGrid.addWidget(password2, 2, 2);\r
+               passwordGrid.setContentsMargins(10, 10,  -10, -10);\r
+               grid.addLayout(passwordGrid,1,1);\r
+               \r
+               ok = new QPushButton(tr("OK"));\r
+               ok.setEnabled(false);\r
+               ok.clicked.connect(this, "okButtonPressed()");\r
+               QPushButton cancel = new QPushButton(tr("Cancel"));\r
+               cancel.clicked.connect(this, "cancelButtonPressed()");\r
+               buttonGrid.addWidget(ok, 1, 1);\r
+               buttonGrid.addWidget(cancel, 1,2);\r
+               grid.addLayout(buttonGrid,2,1);\r
+       }\r
+       \r
+       // The OK button was pressed\r
+       @SuppressWarnings("unused")\r
+       private void okButtonPressed() {\r
+               okPressed = true;\r
+               close();\r
+       }\r
+       \r
+       // The CANCEL button was pressed\r
+       @SuppressWarnings("unused")\r
+       private void cancelButtonPressed() {\r
+               okPressed = false;\r
+               close();\r
+       }\r
+       \r
+       // Get the userid from the field\r
+       public String getPassword() {\r
+               return password1.text();\r
+       }\r
+       \r
+       // Check if the OK button was pressed\r
+       public boolean okPressed() {\r
+               return okPressed;\r
+       }\r
+       \r
+       // Validate user input\r
+       public void validateInput() {\r
+               ok.setEnabled(true);\r
+               if (password1.text().trim().equals("")) {\r
+                       ok.setEnabled(false);\r
+                       return;\r
+               }       \r
+               if (password1.text().length() < 4) {\r
+                       ok.setEnabled(false);\r
+                       return;\r
+               }\r
+               if (!password1.text().equals(password2.text())) {\r
+                       ok.setEnabled(false);\r
+                       return;\r
+               }\r
+       }\r
+}\r
index ffb1513..97cbaaf 100644 (file)
@@ -94,6 +94,7 @@ public class MainMenuBar extends QMenuBar {
        public QAction                  databaseStatusAction;           // Current database status\r
        public QAction                  folderImportAction;                     // Automatically import files \r
        public QAction                  spellCheckAction;                       // Spell checker\r
+       public QAction                  encryptDatabaseAction;          // Encrypt the local database\r
        \r
        public QAction                  notebookEditAction;                     // Edit the selected notebook\r
        public QAction                  notebookAddAction;                      // Add a new notebook\r
@@ -525,6 +526,15 @@ public class MainMenuBar extends QMenuBar {
                spellCheckAction.triggered.connect(parent.browserWindow, "doSpellCheck()");\r
                setupShortcut(spellCheckAction, "Tools_Spell_Check");\r
 \r
+               encryptDatabaseAction = new QAction(tr("Encrypt Database"), this);\r
+               encryptDatabaseAction.setToolTip("Encrypt the database upon shutdown");\r
+               encryptDatabaseAction.triggered.connect(parent, "doDatabaseEncrypt()");\r
+               setupShortcut(encryptDatabaseAction, "Tools_Database_Encrypt");\r
+               if (Global.cipherPassword != null && Global.cipherPassword != "") {\r
+                       encryptDatabaseAction.setText("Decrypt Database");\r
+                       encryptDatabaseAction.setToolTip("Decrypt the database upon shutdown");\r
+               }\r
+               \r
                loggerAction = new QAction(tr("Log"), this);\r
                loggerAction.setToolTip("Show the detailed application log");\r
                loggerAction.triggered.connect(parent, "logger()");\r
@@ -653,6 +663,8 @@ public class MainMenuBar extends QMenuBar {
                toolsMenu.addAction(fullReindexAction);\r
                toolsMenu.addAction(disableIndexing);\r
 //             toolsMenu.addAction(compactAction);\r
+               toolsMenu.addSeparator();\r
+               toolsMenu.addAction(encryptDatabaseAction);\r
                toolsMenu.addAction(databaseStatusAction);\r
                toolsMenu.addSeparator();\r
                toolsMenu.addAction(folderImportAction);\r