Add UI for importing and exporting megolm keys

This commit is contained in:
Tobias Fella
2024-07-06 18:48:42 +02:00
parent 37d6033df4
commit 5170854a2c
8 changed files with 241 additions and 2 deletions

View File

@@ -409,7 +409,7 @@ void Controller::removeConnection(const QString &userId)
bool Controller::csSupported() const bool Controller::csSupported() const
{ {
#if Quotient_VERSION_MINOR > 9 #if Quotient_VERSION_MINOR > 8
return true; return true;
#else #else
return false; return false;

View File

@@ -10,6 +10,10 @@
#include <Quotient/keyverificationsession.h> #include <Quotient/keyverificationsession.h>
#include <Quotient/roommember.h> #include <Quotient/roommember.h>
#if Quotient_VERSION_MINOR > 8
#include <Quotient/keyimport.h>
#endif
#include "controller.h" #include "controller.h"
#include "neochatconfig.h" #include "neochatconfig.h"
@@ -38,3 +42,12 @@ struct ForeignSSSSHandler {
QML_FOREIGN(Quotient::SSSSHandler) QML_FOREIGN(Quotient::SSSSHandler)
QML_NAMED_ELEMENT(SSSSHandler) QML_NAMED_ELEMENT(SSSSHandler)
}; };
#if Quotient_VERSION_MINOR > 8
struct ForeignKeyImport {
Q_GADGET
QML_SINGLETON
QML_FOREIGN(Quotient::KeyImport)
QML_NAMED_ELEMENT(KeyImport)
};
#endif

View File

@@ -574,4 +574,21 @@ LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
return previewer; return previewer;
} }
#if Quotient_VERSION_MINOR > 8
KeyImport::Error NeoChatConnection::exportMegolmSessions(const QString &passphrase, const QString &path)
{
KeyImport keyImport;
auto result = keyImport.exportKeys(passphrase, this);
if (!result.has_value()) {
return result.error();
}
QUrl url(path);
QFile file(url.toLocalFile());
file.open(QFile::WriteOnly);
file.write(result.value());
file.close();
return KeyImport::Success;
}
#endif
#include "moc_neochatconnection.cpp" #include "moc_neochatconnection.cpp"

View File

@@ -9,6 +9,10 @@
#include <QCoroTask> #include <QCoroTask>
#include <Quotient/connection.h> #include <Quotient/connection.h>
#if Quotient_VERSION_MINOR > 8
#include <Quotient/keyimport.h>
#endif
#include "models/threepidmodel.h" #include "models/threepidmodel.h"
class LinkPreviewer; class LinkPreviewer;
@@ -169,6 +173,10 @@ public:
*/ */
Q_INVOKABLE QString accountDataJsonString(const QString &type) const; Q_INVOKABLE QString accountDataJsonString(const QString &type) const;
#if Quotient_VERSION_MINOR > 8
Q_INVOKABLE Quotient::KeyImport::Error exportMegolmSessions(const QString &passphrase, const QString &path);
#endif
qsizetype directChatNotifications() const; qsizetype directChatNotifications() const;
bool directChatsHaveHighlightNotifications() const; bool directChatsHaveHighlightNotifications() const;
qsizetype homeNotifications() const; qsizetype homeNotifications() const;

View File

@@ -40,4 +40,6 @@ ecm_add_qml_module(settings GENERATE_PLUGIN_SOURCE
PasswordSheet.qml PasswordSheet.qml
ThemeRadioButton.qml ThemeRadioButton.qml
ThreePIdCard.qml ThreePIdCard.qml
ImportKeysDialog.qml
ExportKeysDialog.qml
) )

View File

@@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import QtQuick.Dialogs
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat
FormCard.FormCardPage {
id: root
title: i18nc("@title", "Export Keys")
required property NeoChatConnection connection
header: KirigamiComponents.Banner {
id: banner
showCloseButton: true
visible: false
type: Kirigami.MessageType.Error
}
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
FormCard.FormTextFieldDelegate {
id: passphraseField
label: i18nc("@label:textbox", "Passphrase:")
echoMode: TextInput.Password
}
FormCard.FormTextDelegate {
text: i18n("A passphrase to secure your key backup. It should not be your account password.")
}
FormCard.FormDelegateSeparator {}
FormCard.FormButtonDelegate {
enabled: passphraseField.text.length > 0
text: i18nc("@action:button", "Export keys")
onClicked: {
let dialog = saveDialog.createObject(root);
dialog.accepted.connect(() => {
banner.visible = false;
let error = root.connection.exportMegolmSessions(passphraseField.text, dialog.selectedFile);
passphraseField.text = "";
if (error === KeyImport.Success) {
banner.text = i18nc("@info", "Keys exported successfully");
banner.type = Kirigami.MessageType.Positive;
banner.visible = true;
} else {
banner.text = i18nc("@info", "Unknown error");
banner.type = Kirigami.MessageType.Error;
banner.visible = true;
}
});
dialog.open();
}
}
}
Component {
id: saveDialog
FileDialog {
fileMode: FileDialog.SaveFile
currentFolder: Config.lastSaveDirectory.length > 0 ? Config.lastSaveDirectory : StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
}
}
}

View File

@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Dialogs
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat
FormCard.FormCardPage {
id: root
required property NeoChatConnection connection
signal success
title: i18nc("@title", "Import Keys")
header: KirigamiComponents.Banner {
id: banner
showCloseButton: true
visible: false
type: Kirigami.MessageType.Error
}
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
FormCard.FormButtonDelegate {
id: fileButton
text: i18nc("@action:button", "Choose backup file")
description: ""
icon.name: "cloud-upload"
onClicked: {
let dialog = Qt.createComponent("QtQuick.Dialogs", "FileDialog").createObject(root, {
fileMode: FileDialog.OpenFile
});
dialog.accepted.connect(() => {
fileButton.description = dialog.selectedFile.toString().substring(7);
});
dialog.open();
}
}
FormCard.FormDelegateSeparator {}
FormCard.FormTextFieldDelegate {
id: passphraseField
label: i18nc("@label:textbox", "Passphrase:")
echoMode: TextInput.Password
}
FormCard.FormDelegateSeparator {}
FormCard.FormButtonDelegate {
text: i18nc("@action:button", "Import keys")
enabled: fileButton.description.length > 0 && passphraseField.text.length > 0
onClicked: {
banner.visible = false;
let error = KeyImport.importKeys(Controller.loadFileContent(fileButton.description), passphraseField.text, root.connection);
passphraseField.text = "";
if (error === KeyImport.Success) {
root.success();
root.closeDialog();
} else if (error === KeyImport.InvalidPassphrase) {
banner.text = i18nc("@info", "Invalid passphrase");
banner.type = Kirigami.MessageType.Error;
banner.visible = true;
} else if (error === KeyImport.InvalidData) {
banner.text = i18nc("@info", "Invalid key backup data");
banner.type = Kirigami.MessageType.Error;
banner.visible = true;
} else {
banner.text = i18nc("@info", "Unknown error");
banner.type = Kirigami.MessageType.Error;
banner.visible = true;
}
}
}
}
}

View File

@@ -2,10 +2,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls as QQC2
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat import org.kde.neochat
@@ -16,6 +17,13 @@ FormCard.FormCardPage {
title: i18nc("@title", "Security") title: i18nc("@title", "Security")
header: KirigamiComponents.Banner {
id: banner
showCloseButton: true
visible: false
type: Kirigami.MessageType.Error
}
FormCard.FormHeader { FormCard.FormHeader {
title: i18nc("@title:group", "Invitations") title: i18nc("@title:group", "Invitations")
} }
@@ -60,6 +68,43 @@ FormCard.FormCardPage {
} }
} }
FormCard.FormHeader {
visible: Controller.csSupported
title: i18nc("@title", "Encryption Keys")
}
FormCard.FormCard {
visible: Controller.csSupported
FormCard.FormButtonDelegate {
text: i18nc("@action:button", "Import Encryption Keys")
icon.name: "document-import"
onClicked: {
let dialog = root.QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat.settings", "ImportKeysDialog"), {
connection: root.connection
}, {
title: i18nc("@title", "Import Keys"),
});
dialog.success.connect(() => {
banner.text = i18nc("@info", "Keys imported successfully");
banner.type = Kirigami.MessageType.Positive;
banner.visible = true;
});
banner.visible = false;
}
}
FormCard.FormButtonDelegate {
text: i18nc("@action:button", "Export Encryption Keys")
icon.name: "document-export"
onClicked: {
root.QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat.settings", "ExportKeysDialog"), {
connection: root.connection
}, {
title: i18nc("@title", "Export Keys")
});
banner.visible = false;
}
}
}
Component { Component {
id: ignoredUsersDialogComponent id: ignoredUsersDialogComponent
IgnoredUsersDialog { IgnoredUsersDialog {