Add UI for importing and exporting megolm keys
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
71
src/settings/ExportKeysDialog.qml
Normal file
71
src/settings/ExportKeysDialog.qml
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/settings/ImportKeysDialog.qml
Normal file
83
src/settings/ImportKeysDialog.qml
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user