Skip to content
2 changes: 2 additions & 0 deletions src/PreferencesDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ void PreferencesDialog::loadSettings()

ui->spinStructureFontSize->setValue(Settings::getValue("db", "fontsize").toInt());
ui->watcherCheckBox->setChecked(Settings::getValue("db", "watcher").toBool());
ui->checkSQLCipherParentDotenvLookup->setChecked(Settings::getValue("db", "sqlcipherparentdotenvlookup").toBool());

// Gracefully handle the preferred Data Browser font not being available
int matchingFont = ui->comboDataBrowserFont->findText(Settings::getValue("databrowser", "font").toString(), Qt::MatchExactly);
Expand Down Expand Up @@ -199,6 +200,7 @@ void PreferencesDialog::saveSettings(bool accept)
Settings::setValue("db", "defaultfieldtype", ui->defaultFieldTypeComboBox->currentIndex());
Settings::setValue("db", "fontsize", ui->spinStructureFontSize->value());
Settings::setValue("db", "watcher", ui->watcherCheckBox->isChecked());
Settings::setValue("db", "sqlcipherparentdotenvlookup", ui->checkSQLCipherParentDotenvLookup->isChecked());

Settings::setValue("checkversion", "enabled", ui->checkUpdates->isChecked());

Expand Down
40 changes: 30 additions & 10 deletions src/PreferencesDialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -752,16 +752,36 @@ in new project file</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="watcherCheckBox">
<property name="text">
<string>enabled</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="watcherCheckBox">
<property name="text">
<string>enabled</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="labelSQLCipherParentDotenvLookup">
<property name="toolTip">
<string>When enabled, DB4S searches parent folders for .db4s.env files, which can slow down opening encrypted databases.</string>
</property>
<property name="text">
<string>Search parent folders for .db4s.env SQLCipher passwords</string>
</property>
<property name="buddy">
<cstring>checkSQLCipherParentDotenvLookup</cstring>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="checkSQLCipherParentDotenvLookup">
<property name="text">
<string>enabled</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
Expand Down
4 changes: 4 additions & 0 deletions src/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ QVariant Settings::getDefaultValue(const std::string& group, const std::string&
if(group == "db" && name == "watcher")
return false;

// db/sqlcipherparentdotenvlookup?
if(group == "db" && name == "sqlcipherparentdotenvlookup")
return false;

// exportcsv/firstrowheader?
if(group == "exportcsv" && name == "firstrowheader")
return true;
Expand Down
67 changes: 48 additions & 19 deletions src/sqlitedb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <QDebug>
#include <QThread>
#include <QRegularExpression>
#include <QSettings>

#include <algorithm>
#include <array>
Expand Down Expand Up @@ -478,32 +479,31 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted

// Being in a while loop, we don't want to check the same file multiple times
if (!isDotenvChecked) {
QFile databaseFile(filePath);
QFileInfo databaseFileInfo(databaseFile);
const QFileInfo databaseFileInfo(filePath);

QString databaseDirectoryPath = databaseFileInfo.dir().path();
QString databaseFileName(databaseFileInfo.fileName());
const QDir databaseDirectory(databaseFileInfo.absoluteDir());
const QString databaseFileName(databaseFileInfo.fileName());

QString dotenvFilePath = databaseDirectoryPath + "/.env";
QSettings dotenv(dotenvFilePath, QSettings::IniFormat);
auto tryLoadDotenv = [&](const QString& dotenvFilePath) -> bool {
if (!QFile::exists(dotenvFilePath))
return false;

QVariant passwordValue = dotenv.value(databaseFileName);
const QSettings dotenv(dotenvFilePath, QSettings::IniFormat);
const QVariant passwordValue = dotenv.value(databaseFileName);

foundDotenvPassword = !passwordValue.isNull();
isDotenvChecked = true;
if (passwordValue.isNull())
return false;

if (foundDotenvPassword)
{
std::string password = passwordValue.toString().toStdString();
const std::string password = passwordValue.toString().toStdString();

QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase));
CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt());
const QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase));
const CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt());

int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt();
int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt();
int plaintextHeaderSize = dotenv.value(databaseFileName + "_plaintextHeaderSize", enc_default_plaintext_header_size).toInt();
std::string hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", QString::fromStdString(enc_default_hmac_algorithm)).toString().toStdString();
std::string kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", QString::fromStdString(enc_default_kdf_algorithm)).toString().toStdString();
const int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt();
const int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt();
const int plaintextHeaderSize = dotenv.value(databaseFileName + "_plaintextHeaderSize", enc_default_plaintext_header_size).toInt();
const std::string hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", QString::fromStdString(enc_default_hmac_algorithm)).toString().toStdString();
const std::string kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", QString::fromStdString(enc_default_kdf_algorithm)).toString().toStdString();

cipherSettings->setKeyFormat(keyFormat);
cipherSettings->setPassword(password);
Expand All @@ -512,7 +512,36 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted
cipherSettings->setHmacAlgorithm("HMAC_" + hmacAlgorithm);
cipherSettings->setKdfAlgorithm("PBKDF2_HMAC_" + kdfAlgorithm);
cipherSettings->setPlaintextHeaderSize(plaintextHeaderSize);

return true;
};

const QString db4sEnvPath = databaseDirectory.filePath(".db4s.env");
const bool hasDB4SEnv = QFile::exists(db4sEnvPath);

if (hasDB4SEnv)
{
foundDotenvPassword = tryLoadDotenv(db4sEnvPath);
} else {
// Deprecated: legacy .env in the database directory only. Remove once .db4s.env is adopted.
foundDotenvPassword = tryLoadDotenv(databaseDirectory.filePath(".env"));

// Parent folder search is opt-in because it can slow down opening encrypted databases.
if (!foundDotenvPassword && Settings::getValue("db", "sqlcipherparentdotenvlookup").toBool())
{
QDir searchDirectory(databaseDirectory);
while (searchDirectory.cdUp())
{
if (tryLoadDotenv(searchDirectory.filePath(".db4s.env")))
{
foundDotenvPassword = true;
break;
}
}
}
}
Comment thread
revolter marked this conversation as resolved.

isDotenvChecked = true;
}

if(foundDotenvPassword)
Expand Down
Loading