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
{
#if Quotient_VERSION_MINOR > 9
#if Quotient_VERSION_MINOR > 8
return true;
#else
return false;

View File

@@ -10,6 +10,10 @@
#include <Quotient/keyverificationsession.h>
#include <Quotient/roommember.h>
#if Quotient_VERSION_MINOR > 8
#include <Quotient/keyimport.h>
#endif
#include "controller.h"
#include "neochatconfig.h"
@@ -38,3 +42,12 @@ struct ForeignSSSSHandler {
QML_FOREIGN(Quotient::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;
}
#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"

View File

@@ -9,6 +9,10 @@
#include <QCoroTask>
#include <Quotient/connection.h>
#if Quotient_VERSION_MINOR > 8
#include <Quotient/keyimport.h>
#endif
#include "models/threepidmodel.h"
class LinkPreviewer;
@@ -169,6 +173,10 @@ public:
*/
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;
bool directChatsHaveHighlightNotifications() const;
qsizetype homeNotifications() const;

View File

@@ -40,4 +40,6 @@ ecm_add_qml_module(settings GENERATE_PLUGIN_SOURCE
PasswordSheet.qml
ThemeRadioButton.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
import QtQuick
import QtQuick.Controls
import QtQuick.Controls as QQC2
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
@@ -16,6 +17,13 @@ FormCard.FormCardPage {
title: i18nc("@title", "Security")
header: KirigamiComponents.Banner {
id: banner
showCloseButton: true
visible: false
type: Kirigami.MessageType.Error
}
FormCard.FormHeader {
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 {
id: ignoredUsersDialogComponent
IgnoredUsersDialog {