diff --git a/CMakeLists.txt b/CMakeLists.txt index 19ee9532..5ae40fa6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ endif() set(SQLB_HDR src/gen_version.h src/sqlitetypes.h - src/csvparser.h + src/csvparser.h src/grammar/sqlite3TokenTypes.hpp src/grammar/Sqlite3Lexer.hpp src/grammar/Sqlite3Parser.hpp @@ -54,6 +54,7 @@ set(SQLB_MOC_HDR src/sqltextedit.h src/DbStructureModel.h src/Application.h + src/sqlite.h ) set(SQLB_SRC @@ -179,13 +180,21 @@ if(APPLE) add_definitions(-DCHECKNEWVERSION) endif(APPLE) +# SQLCipher option +if(sqlcipher) + add_definitions(-DSQLCIPHER) + set(LIBSQLITE_NAME sqlcipher) +else(sqlcipher) + set(LIBSQLITE_NAME sqlite3) +endif(sqlcipher) + # add extra library path for MacOS and FreeBSD set(EXTRAPATH APPLE OR ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") if(EXTRAPATH) - find_library(LIBSQLITE sqlite3 HINTS /usr/local/lib /usr/local/opt/sqlite/lib) + find_library(LIBSQLITE ${LIBSQLITE_NAME} HINTS /usr/local/lib /usr/local/opt/sqlite/lib) set(ADDITIONAL_INCLUDE_PATHS /usr/local/include /usr/local/opt/sqlite/include) else(EXTRAPATH) - find_library(LIBSQLITE sqlite3) + find_library(LIBSQLITE ${LIBSQLITE_NAME}) endif(EXTRAPATH) include_directories( diff --git a/src/AboutDialog.cpp b/src/AboutDialog.cpp index d41519a0..39e4300b 100644 --- a/src/AboutDialog.cpp +++ b/src/AboutDialog.cpp @@ -1,7 +1,7 @@ #include "AboutDialog.h" #include "ui_AboutDialog.h" #include "gen_version.h" -#include +#include "sqlite.h" AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), @@ -12,7 +12,11 @@ AboutDialog::AboutDialog(QWidget *parent) : ui->label_version->setText(ui->label_version->text() + " " + APP_VERSION); ui->label_versionqt->setText(ui->label_versionqt->text() + " " + QT_VERSION_STR); +#ifdef SQLCIPHER + ui->label_versionsqlite->setText(ui->label_versionsqlite->text().replace("SQLite", "SQLCipher") + " " + SQLITE_VERSION); +#else ui->label_versionsqlite->setText(ui->label_versionsqlite->text() + " " + SQLITE_VERSION); +#endif } AboutDialog::~AboutDialog() diff --git a/src/ExportCsvDialog.cpp b/src/ExportCsvDialog.cpp index 03a8e67f..7ed1a8ed 100644 --- a/src/ExportCsvDialog.cpp +++ b/src/ExportCsvDialog.cpp @@ -3,12 +3,12 @@ #include "sqlitedb.h" #include "PreferencesDialog.h" #include "sqlitetablemodel.h" +#include "sqlite.h" #include #include #include #include -#include ExportCsvDialog::ExportCsvDialog(DBBrowserDB* db, QWidget* parent, const QString& query, const QString& selection) : QDialog(parent), diff --git a/src/ImportCsvDialog.cpp b/src/ImportCsvDialog.cpp index 5d442ac8..fc07dc2b 100644 --- a/src/ImportCsvDialog.cpp +++ b/src/ImportCsvDialog.cpp @@ -2,6 +2,7 @@ #include "ui_ImportCsvDialog.h" #include "sqlitedb.h" #include "csvparser.h" +#include "sqlite.h" #include #include @@ -10,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 4917acae..3c028a13 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -15,6 +15,7 @@ #include "VacuumDialog.h" #include "DbStructureModel.h" #include "gen_version.h" +#include "sqlite.h" #include #include @@ -28,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -195,23 +195,30 @@ bool MainWindow::fileOpen(const QString& fileName, bool dontAddToRecentFiles) if(QFile::exists(wFile) ) { fileClose(); - if(db.open(wFile)) + // Try opening it as a project file first + if(loadProject(wFile)) { - statusEncodingLabel->setText(db.getPragma("encoding")); - setCurrentFile(wFile); - if(!dontAddToRecentFiles) - addToRecentFilesMenu(wFile); retval = true; } else { - // Failed opening file; so it might be a project file instead - return loadProject(wFile); + // No project file; so it should be a database file + if(db.open(wFile)) + { + statusEncodingLabel->setText(db.getPragma("encoding")); + setCurrentFile(wFile); + if(!dontAddToRecentFiles) + addToRecentFilesMenu(wFile); + openSqlTab(true); + retval = true; + } else { + QMessageBox::warning(this, qApp->applicationName(), tr("Invalid file format.")); + return false; + } } loadExtensionsFromSettings(); populateStructure(); resetBrowser(); if(ui->mainTab->currentIndex() == 2) loadPragmas(); - openSqlTab(true); } return retval; @@ -1749,10 +1756,7 @@ bool MainWindow::loadProject(QString filename) xml.readNext(); // token == QXmlStreamReader::StartDocument xml.readNext(); // name == sqlb_project if(xml.name() != "sqlb_project") - { - QMessageBox::warning(this, qApp->applicationName(), tr("Invalid file format.")); return false; - } addToRecentFilesMenu(filename); diff --git a/src/sqlite.h b/src/sqlite.h new file mode 100644 index 00000000..d4945f85 --- /dev/null +++ b/src/sqlite.h @@ -0,0 +1,12 @@ +#ifndef SQLITE_H +#define SQLITE_H + +#ifdef SQLCIPHER + #define SQLITE_TEMP_STORE 2 + #define SQLITE_HAS_CODEC + #include +#else + #include +#endif + +#endif diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 95ec31bc..2a82d56b 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -1,5 +1,6 @@ #include "sqlitedb.h" #include "sqlitetablemodel.h" +#include "sqlite.h" #include #include @@ -7,7 +8,7 @@ #include #include #include -#include +#include // collation callbacks int collCompare(void* /*pArg*/, int /*eTextRepA*/, const void* sA, int /*eTextRepB*/, const void* sB) @@ -43,32 +44,62 @@ bool DBBrowserDB::getDirty() const return !savepointList.empty(); } -bool DBBrowserDB::open ( const QString & db) +bool DBBrowserDB::open(const QString& db) { bool ok=false; int err; if (isOpen()) close(); - //try to verify the SQLite version 3 file header - QFile dbfile(db); - if ( dbfile.open( QIODevice::ReadOnly ) ) { - char buffer[16+1]; - dbfile.readLine(buffer, 16); - QString contents = QString(buffer); - dbfile.close(); - if (!contents.startsWith("SQLite format 3")) { - lastErrorMessage = QObject::tr("File is not a SQLite 3 database"); - return false; - } - } else { - lastErrorMessage = QObject::tr("File could not be read"); + lastErrorMessage = QObject::tr("no error"); + + // Open database file + if(sqlite3_open_v2(db.toUtf8(), &_db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) + { + lastErrorMessage = QString::fromUtf8((const char*)sqlite3_errmsg(_db)); return false; } - lastErrorMessage = QObject::tr("no error"); - - err = sqlite3_open_v2(db.toUtf8(), &_db, SQLITE_OPEN_READWRITE, NULL); + // Try reading from database + bool done = false; + do + { + QString statement = "SELECT COUNT(*) FROM sqlite_master;"; + QByteArray utf8Statement = statement.toUtf8(); + sqlite3_stmt* vm; + const char* tail; + err = sqlite3_prepare_v2(_db, utf8Statement, utf8Statement.length(), &vm, &tail); + if(sqlite3_step(vm) != SQLITE_ROW) + { +#ifdef SQLCIPHER + QString pass = QInputDialog::getText(0, qApp->applicationName(), + QObject::tr("Couldn't read from database file. This means it is either not a valid SQLite3 " + "database or it is encrypted.\nIn the latter case you can provide a passphrase to open the file. Note" + "that only databases encrypted using SQLCipher are supported.")); + if(pass.isEmpty()) + { + sqlite3_close(_db); + _db = 0; + return false; + } else { + int pagesize = QInputDialog::getInt(0, qApp->applicationName(), + QObject::tr("If the database has been encrypted using a different page size than normally (i.e. 1024 bytes) this " + "value is required, too."), + 1024, 0); + if(pagesize == 0) pagesize = 1024; + sqlite3_key(_db, pass.toUtf8(), pass.toUtf8().length()); + sqlite3_exec(_db, QString("PRAGMA cipher_page_size = %1;").arg(pagesize).toUtf8(), NULL, NULL, NULL); + } +#else + sqlite3_close(_db); + _db = 0; + return false; +#endif + } else { + done = true; + } + sqlite3_finalize(vm); + } while(!done); // register collation callback sqlite3_collation_needed(_db, NULL, collation_needed); diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 26ced406..2af27dd8 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -1,10 +1,10 @@ #include "sqlitetablemodel.h" #include "sqlitedb.h" +#include "sqlite.h" #include #include #include -#include SqliteTableModel::SqliteTableModel(QObject* parent, DBBrowserDB* db, size_t chunkSize) : QAbstractTableModel(parent) diff --git a/src/src.pro b/src/src.pro index e14f6b9e..ee481803 100644 --- a/src/src.pro +++ b/src/src.pro @@ -42,7 +42,8 @@ HEADERS += \ SqlExecutionArea.h \ VacuumDialog.h \ DbStructureModel.h \ - Application.h + Application.h \ + sqlite.h SOURCES += \ sqlitedb.cpp \ @@ -88,6 +89,13 @@ TRANSLATIONS += \ translations/sqlb_fr.ts \ translations/sqlb_ru.ts +CONFIG(sqlcipher) { + QMAKE_CXXFLAGS += -DENABLE_SQLCIPHER + LIBS += -lsqlcipher +} else { + LIBS += -lsqlite3 +} + LIBPATH_QHEXEDIT=$$PWD/../libs/qhexedit LIBPATH_ANTLR=$$PWD/../libs/antlr-2.7.7 LIBPATH_QCUSTOMPLOT=$$PWD/../libs/qcustomplot-source @@ -121,7 +129,7 @@ mac { UI_DIR = .ui INCLUDEPATH += $$PWD/../libs/antlr-2.7.7 $$PWD/../libs/qhexedit $$PWD/../libs/qcustomplot-source $$PWD/.. -LIBS += -L$$LIBPATH_QHEXEDIT -L$$LIBPATH_ANTLR -L$$LIBPATH_QCUSTOMPLOT -lantlr -lqhexedit -lqcustomplot -lsqlite3 +LIBS += -L$$LIBPATH_QHEXEDIT -L$$LIBPATH_ANTLR -L$$LIBPATH_QCUSTOMPLOT -lantlr -lqhexedit -lqcustomplot DEPENDPATH += $$PWD/../libs/antlr-2.7.7 $$PWD/../libs/qhexedit $$PWD/../libs/qcustomplot-source # Rules for creating/updating {ts|qm}-files