Skip to content

Commit

Permalink
Proof of concept: Remote sync with scp (keepassxreboot#1775)
Browse files Browse the repository at this point in the history
Provide extensible implementation for further programs
  • Loading branch information
t-h-e committed Nov 6, 2022
1 parent 3cbe4df commit 40d0df9
Show file tree
Hide file tree
Showing 28 changed files with 1,174 additions and 12 deletions.
78 changes: 72 additions & 6 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2262,6 +2262,26 @@ This is definitely a bug, please report it to the developers.</source>
<source>Writing the XML file failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No URL set. Cannot sync database.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 with command `%2` exited with status code: %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 with command `%2` did not finish in time. Process was killed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Failed to upload merged database. %1 with command `%2` exited with status code: %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Failed to upload merged database. %1 with command `%2` did not finish in time. Process was killed.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DatabaseWidget</name>
Expand Down Expand Up @@ -2904,7 +2924,7 @@ Would you like to correct it?</source>
</message>
<message>
<source>URL:</source>
<translation type="unfinished"></translation>
<translation type="unfinished">URL:</translation>
</message>
<message>
<source>Url field</source>
Expand Down Expand Up @@ -3611,11 +3631,6 @@ This may cause the affected plugins to malfunction.</source>
<source>Confirm Overwrite Attachment</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Attachment &quot;%1&quot; already exists.
Would you like to overwrite the existing attachment?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm Attachment</source>
<translation type="unfinished"></translation>
Expand Down Expand Up @@ -3645,6 +3660,11 @@ Do you want to save the changes to your database?</source>
Error: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Attachment &quot;%1&quot; already exists.
Would you like to overwrite the existing attachment?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EntryAttributesModel</name>
Expand Down Expand Up @@ -5431,6 +5451,14 @@ We recommend you use the AppImage available on our downloads page.</source>
<source>You must restart the application to apply this setting. Would you like to restart now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Remote Sync…</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remote sync</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Tags</source>
<translation type="unfinished"></translation>
Expand Down Expand Up @@ -7959,6 +7987,44 @@ This options is deprecated, use --set-key-file instead.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RemoteSettingsDialog</name>
<message>
<source>scp</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RemoteSettingsWidgetScp</name>
<message>
<source>Connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>URL:</source>
<translation type="unfinished">URL:</translation>
</message>
<message>
<source>URL field</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Port:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Port field</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Key file:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Key File field</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ReportsWidgetBrowserStatistics</name>
<message>
Expand Down
5 changes: 5 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ set(keepassx_SOURCES
gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp
gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp
gui/dbsettings/DatabaseSettingsWidgetDatabaseKey.cpp
gui/remote/RemoteProcess.cpp
gui/remote/RemoteSettingsDialog.cpp
gui/remote/RemoteSettingsWidget.cpp
gui/remote/RemoteSettingsWidgetScp.cpp
gui/remote/ScpParams.cpp
gui/reports/ReportsWidget.cpp
gui/reports/ReportsDialog.cpp
gui/reports/ReportsWidgetHealthcheck.cpp
Expand Down
1 change: 1 addition & 0 deletions src/gui/DatabaseOpenDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class DatabaseOpenDialog : public QDialog
None,
AutoType,
Merge,
RemoteSync,
Browser
};

Expand Down
88 changes: 87 additions & 1 deletion src/gui/DatabaseTabWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
#include "DatabaseTabWidget.h"

#include <QFileInfo>
#include <QProcess>
#include <QTabBar>
#include <QTemporaryDir>
#include <utility>

#include "autotype/AutoType.h"
#include "core/Tools.h"
Expand All @@ -41,12 +44,14 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
, m_dbWidgetStateSync(new DatabaseWidgetStateSync(this))
, m_dbWidgetPendingLock(nullptr)
, m_databaseOpenDialog(new DatabaseOpenDialog(this))
, m_remoteProgramParams(nullptr)
{
auto* tabBar = new QTabBar(this);
tabBar->setAcceptDrops(true);
tabBar->setChangeCurrentOnDrag(true);
setTabBar(tabBar);
setDocumentMode(true);
setCreateRemoteProcessFunc([this]() { return new RemoteProcess(this); });

// clang-format off
connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabaseTab(int)));
Expand Down Expand Up @@ -248,6 +253,10 @@ void DatabaseTabWidget::addDatabaseTab(DatabaseWidget* dbWidget, bool inBackgrou
connect(dbWidget, SIGNAL(databaseUnlocked()), SLOT(emitDatabaseLockChanged()));
connect(dbWidget, SIGNAL(databaseLocked()), SLOT(updateTabName()));
connect(dbWidget, SIGNAL(databaseLocked()), SLOT(emitDatabaseLockChanged()));
connect(dbWidget,
SIGNAL(databaseSynced(QSharedPointer<Database>)),
SLOT(handleSyncedDatabaseRemote(QSharedPointer<Database>)));
connect(dbWidget, SIGNAL(syncWithRemote(RemoteProgramParams*)), SLOT(syncDatabaseWithRemote(RemoteProgramParams*)));
}

void DatabaseTabWidget::importCsv()
Expand Down Expand Up @@ -284,11 +293,78 @@ void DatabaseTabWidget::mergeDatabase()
}
}

void DatabaseTabWidget::syncDatabaseWithRemote(RemoteProgramParams* remoteProgramParams)
{
if (remoteProgramParams->getUrl().isEmpty()) {
currentDatabaseWidget()->showErrorMessage(tr("No URL set. Cannot sync database."));
return;
}

auto* remoteProcess = m_createRemoteProcess();
QString destination = remoteProcess->getTempFileLocation();
m_remoteProgramParams = remoteProgramParams;
remoteProcess->start(remoteProgramParams->getProgram(), remoteProgramParams->getArgumentsForDownload(destination));
bool finished = remoteProcess->waitForFinished(10000);
int statusCode = remoteProcess->exitCode();
if (finished && statusCode == 0) {
remoteSyncDatabase(destination);
} else {
QStringList command;
command << remoteProgramParams->getProgram() << remoteProgramParams->getArgumentsForDownload(destination);
if (finished) {
currentDatabaseWidget()->showErrorMessage(tr("%1 with command `%2` exited with status code: %3")
.arg(remoteProgramParams->getProgram())
.arg(command.join(" "))
.arg(statusCode));
} else {
remoteProcess->kill();
currentDatabaseWidget()->showErrorMessage(
tr("%1 with command `%2` did not finish in time. Process was killed.")
.arg(remoteProgramParams->getProgram())
.arg(command.join(" ")));
}
}
}

void DatabaseTabWidget::handleSyncedDatabaseRemote(const QSharedPointer<Database>& remoteSyncedDb)
{
auto* remoteProcess = m_createRemoteProcess();
remoteProcess->start(m_remoteProgramParams->getProgram(),
m_remoteProgramParams->getArgumentsForUpload(remoteSyncedDb->filePath()));
bool finished = remoteProcess->waitForFinished(10000);
int statusCode = remoteProcess->exitCode();
if (!finished || statusCode != 0) {
QStringList command;
command << m_remoteProgramParams->getProgram()
<< m_remoteProgramParams->getArgumentsForUpload(remoteSyncedDb->filePath());
if (finished) {
currentDatabaseWidget()->showErrorMessage(
tr("Failed to upload merged database. %1 with command `%2` exited with status code: %3")
.arg(m_remoteProgramParams->getProgram())
.arg(command.join(" "))
.arg(statusCode));
} else {
remoteProcess->kill();
currentDatabaseWidget()->showErrorMessage(
tr("Failed to upload merged database. %1 with command `%2` did not finish in time. Process was killed.")
.arg(m_remoteProgramParams->getProgram())
.arg(command.join(" "))
.arg(statusCode));
}
}
m_remoteProgramParams = nullptr;
}

void DatabaseTabWidget::mergeDatabase(const QString& filePath)
{
unlockDatabaseInDialog(currentDatabaseWidget(), DatabaseOpenDialog::Intent::Merge, filePath);
}

void DatabaseTabWidget::remoteSyncDatabase(const QString& filePath)
{
unlockDatabaseInDialog(currentDatabaseWidget(), DatabaseOpenDialog::Intent::RemoteSync, filePath);
}

void DatabaseTabWidget::importKeePass1Database()
{
auto filter = QString("%1 (*.kdb);;%2 (*)").arg(tr("KeePass 1 database"), tr("All files"));
Expand Down Expand Up @@ -556,6 +632,11 @@ void DatabaseTabWidget::showDatabaseSettings()
currentDatabaseWidget()->switchToDatabaseSettings();
}

void DatabaseTabWidget::showRemoteSettings()
{
currentDatabaseWidget()->switchToRemoteSettings();
}

bool DatabaseTabWidget::isModified(int index) const
{
if (count() == 0) {
Expand Down Expand Up @@ -778,7 +859,7 @@ void DatabaseTabWidget::handleDatabaseUnlockDialogFinished(bool accepted, Databa
{
// change the active tab to the database that was just unlocked in the dialog
auto intent = m_databaseOpenDialog->intent();
if (accepted && intent != DatabaseOpenDialog::Intent::Merge) {
if (accepted && intent != DatabaseOpenDialog::Intent::Merge && intent != DatabaseOpenDialog::Intent::RemoteSync) {
int index = indexOf(dbWidget);
if (index != -1) {
setCurrentIndex(index);
Expand Down Expand Up @@ -883,3 +964,8 @@ void DatabaseTabWidget::performBrowserUnlock()
unlockAnyDatabaseInDialog(DatabaseOpenDialog::Intent::Browser);
}
}

void DatabaseTabWidget::setCreateRemoteProcessFunc(std::function<RemoteProcess*()> createRemoteProcessFunc)
{
m_createRemoteProcess = std::move(createRemoteProcessFunc);
}
11 changes: 11 additions & 0 deletions src/gui/DatabaseTabWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#include "DatabaseOpenDialog.h"
#include "gui/MessageWidget.h"
#include "gui/remote/RemoteProcess.h"
#include "gui/remote/RemoteProgramParams.h"

#include <QTabWidget>
#include <QTimer>
Expand All @@ -37,6 +39,7 @@ class DatabaseTabWidget : public QTabWidget
explicit DatabaseTabWidget(QWidget* parent = nullptr);
~DatabaseTabWidget() override;
void mergeDatabase(const QString& filePath);
void remoteSyncDatabase(const QString& filePath);

QString tabName(int index);
DatabaseWidget* currentDatabaseWidget();
Expand All @@ -46,6 +49,8 @@ class DatabaseTabWidget : public QTabWidget
bool isModified(int index = -1) const;
bool hasLockableDatabases() const;

void setCreateRemoteProcessFunc(std::function<RemoteProcess*()> createRemoteProcessFunc); // used for testing only

public slots:
void lockAndSwitchToFirstUnlockedDatabase(int index = -1);
void addDatabaseTab(const QString& filePath,
Expand All @@ -63,6 +68,8 @@ public slots:
DatabaseWidget* newDatabase();
void openDatabase();
void mergeDatabase();
void syncDatabaseWithRemote(RemoteProgramParams* RemoteProgramParams);
void handleSyncedDatabaseRemote(const QSharedPointer<Database>&);
void importCsv();
void importKeePass1Database();
void importOpVaultDatabase();
Expand All @@ -84,6 +91,7 @@ public slots:
void showDatabaseSecurity();
void showDatabaseReports();
void showDatabaseSettings();
void showRemoteSettings();
void performGlobalAutoType(const QString& search);
void performBrowserUnlock();

Expand Down Expand Up @@ -116,6 +124,9 @@ private slots:
QPointer<DatabaseWidget> m_dbWidgetPendingLock;
QPointer<DatabaseOpenDialog> m_databaseOpenDialog;
QTimer m_lockDelayTimer;

RemoteProgramParams* m_remoteProgramParams;
std::function<RemoteProcess*()> m_createRemoteProcess;
};

#endif // KEEPASSX_DATABASETABWIDGET_H
Loading

0 comments on commit 40d0df9

Please sign in to comment.