diff options
| author | saturneric <[email protected]> | 2025-12-11 22:59:09 +0100 |
|---|---|---|
| committer | saturneric <[email protected]> | 2025-12-11 22:59:09 +0100 |
| commit | 562e92a148402b98938c7bfb1f91b2a0e99e53d5 (patch) | |
| tree | 5a2e65977368bab55da94cf277022994535c3a33 /src/m_key_server_sync/KeyServerSyncModule.cpp | |
| parent | feat(ui): add `GnuPG` info dialog to help menu (diff) | |
| download | modules-562e92a148402b98938c7bfb1f91b2a0e99e53d5.tar.gz modules-562e92a148402b98938c7bfb1f91b2a0e99e53d5.zip | |
feat(key-server): add public key search and import dialog
* Introduce UI dialog for searching and importing public keys from
key servers using multiple search criteria (key ID, email,
fingerprint).
* Implement backend integration for querying and importing keys from
standard PKS-compatible servers.
* Add logic to parse, display, and import key details with status
indicators and user feedback.
* Enhance menu integration and key operation options in the UI for
improved usability and workflow.
Relates to user-requested feature for easier key server integration.
Diffstat (limited to 'src/m_key_server_sync/KeyServerSyncModule.cpp')
| -rw-r--r-- | src/m_key_server_sync/KeyServerSyncModule.cpp | 240 |
1 files changed, 236 insertions, 4 deletions
diff --git a/src/m_key_server_sync/KeyServerSyncModule.cpp b/src/m_key_server_sync/KeyServerSyncModule.cpp index ad56d2d..65153c3 100644 --- a/src/m_key_server_sync/KeyServerSyncModule.cpp +++ b/src/m_key_server_sync/KeyServerSyncModule.cpp @@ -28,9 +28,13 @@ #include "KeyServerSyncModule.h" +#include <GFSDKGpg.h> + #include <QtCore> +#include <QtWidgets> #include "GFModuleDefine.h" +#include "SearchKeyDialog.h" #include "VKSInterface.h" GF_MODULE_API_DEFINE_V2("com.bktus.gpgfrontend.module.key_server_sync", @@ -48,9 +52,241 @@ auto GFActiveModule() -> int { LISTEN("REQUEST_GET_PUBLIC_KEY_BY_FINGERPRINT"); LISTEN("REQUEST_GET_PUBLIC_KEY_BY_KEY_ID"); LISTEN("REQUEST_UPLOAD_PUBLIC_KEY"); + LISTEN("MAINWINDOW_MENU_MOUNTED"); + LISTEN("KEY_PAIR_OPERA_MENU_CREATED"); return 0; } +namespace { + +auto UploadKeyToServer(QWidget* parent, int channel, const QString& key_id) + -> int { + char* key_data = nullptr; + int size = 0; + auto ret = GFGpgExportKey(channel, QDUP(key_id), 1, &key_data, &size); + if (ret != 0 || key_data == nullptr || size <= 0) { + return -1; + } + + auto data = UDUP(key_data); + + if (data.size() != size) { + FLOG_ERROR("uploaded key data size mismatch: expected %1, got %2", size, + data.size()); + return -1; + } + + QByteArray key_data_array = data.toUtf8(); + + auto* vks = new VKSInterface(); + QObject::connect( + vks, &VKSInterface::SignalKeyUploaded, QThread::currentThread(), + [parent](const QString& fpr, const QJsonObject& status, + const QString& token) { + // Handle successful response + QString status_message = QCoreApplication::translate( + "GTrC", "The following email addresses have status:\n"); + QStringList email_list; + if (!status.isEmpty()) { + for (auto it = status.constBegin(); it != status.constEnd(); ++it) { + status_message += + QString("%1: %2\n").arg(it.key(), it.value().toString()); + email_list.append(it.key()); + } + } else { + status_message += QCoreApplication::translate( + "GTrC", "Could not parse status information."); + } + + // Notify user of successful upload and status details + QMessageBox::information( + parent, + QCoreApplication::translate("GTrC", "Public Key Upload Successful"), + QCoreApplication::translate( + "GTrC", + "The public key was successfully uploaded to the " + "key server keys.openpgp.org.\n" + "Fingerprint: %1\n\n" + "%2\n" + "Please check your email (%3) for further " + "verification from keys.openpgp.org.\n\n" + "Note: For verification, you can find more " + "information here: " + "https://keys.openpgp.org/about") + .arg(fpr, status_message, email_list.join(", "))); + }); + + QObject::connect( + vks, &VKSInterface::SignalErrorOccurred, QThread::currentThread(), + [parent, key_id](const QString& error, const QString& data) { + QMessageBox::critical( + parent, QCoreApplication::translate("GTrC", "Key Upload Failed"), + QCoreApplication::translate( + "GTrC", + "Failed to upload public key to the server.\n" + "Fingerprint: %1\n" + "Error: %2") + .arg(key_id, error)); + }); + + QObject::connect(vks, &VKSInterface::SignalKeyRetrieved, vks, + &VKSInterface::deleteLater); + + vks->UploadKey(key_data); + + return 0; +} + +auto UpdateKeyFromKeyServer(QWidget* parent, int channel, const QString& fpr) + -> int { + auto* vks = new VKSInterface(); + + QObject::connect(vks, &VKSInterface::SignalKeyRetrieved, + QThread::currentThread(), + [parent, channel](const QString& key_data) { + auto data = key_data.toUtf8(); + GFGpgImportKeys(channel, parent, data.constData(), + static_cast<int>(data.size())); + }); + + QObject::connect( + vks, &VKSInterface::SignalErrorOccurred, QThread::currentThread(), + [parent, fpr](const QString& error, const QString& data) { + QMessageBox::critical( + parent, QCoreApplication::translate("GTrC", "Key Update Failed"), + QCoreApplication::translate( + "GTrC", + "Failed to retrieve public key from the server.\n" + "Key ID: %1\n" + "Error: %2") + .arg(fpr, error)); + }); + QObject::connect(vks, &VKSInterface::SignalKeyRetrieved, vks, + &VKSInterface::deleteLater); + vks->GetByFingerprint(fpr); + return 0; +} + +} // namespace + +REGISTER_EVENT_HANDLER(MAINWINDOW_MENU_MOUNTED, [](const MEvent& event) -> int { + LOG_DEBUG("main window menu mounted event: processing"); + + if (!event.contains("main_window")) { + LOG_DEBUG("main window menu mounted event: no main_window found"); + CB_ERR(event, -1, "no main_window found"); + } + + auto* main_window = GFUIGetGUIObjectAs<QMainWindow>(event["main_window"]); + if (!main_window) { + LOG_ERROR( + "main window menu mounted: main_window handle invalid or not " + "QMainWindow"); + CB_ERR(event, -1, "main_window handle invalid or not QMainWindow"); + } + + if (!event.contains("import_key_menu")) { + LOG_DEBUG("main window menu mounted event: no import_key_menu found"); + CB_ERR(event, -1, "no import_key_menu found"); + } + + auto* import_key_menu = GFUIGetGUIObjectAs<QMenu>(event["import_key_menu"]); + if (!import_key_menu) { + LOG_ERROR( + "main window menu mounted: import_key_menu handle invalid or not " + "QMenu"); + CB_ERR(event, -1, "import_key_menu handle invalid or not QMenu"); + } + + LOG_DEBUG("adding key server sync actions to import key menu"); + + QMetaObject::invokeMethod( + QApplication::instance(), + [&]() -> void { + QWidget* parent = + qobject_cast<QWidget*>(static_cast<QObject*>(main_window)); + auto* action = new QAction( + QCoreApplication::translate("GTrC", "Key Server"), nullptr); + action->setToolTip(QCoreApplication::translate( + "GTrC", "Import public keys from a trusted key server.")); + action->setIcon(QIcon(":/icons/import_key_from_server.png")); + QObject::connect(action, &QAction::triggered, parent, [=]() { + auto* dialog = new SearchKeyDialog(parent); + dialog->show(); + }); + import_key_menu->addAction(action); + }, + Qt::BlockingQueuedConnection); + CB_SUCC(event); +}); + +REGISTER_EVENT_HANDLER( + KEY_PAIR_OPERA_MENU_CREATED, [](const MEvent& event) -> int { + auto* tab = GFUIGetGUIObjectAs<QWidget>(event["tab"]); + if (!tab) { + LOG_ERROR( + "key pair opera menu created: tab handle " + "invalid or not KeyPairOperaTab"); + CB_ERR(event, -1, "tab handle invalid or not KeyPairOperaTab"); + } + + auto* layout = GFUIGetGUIObjectAs<QVBoxLayout>(event["opera_layout"]); + if (!layout) { + LOG_ERROR( + "key pair opera menu created: opera_menu handle " + "invalid or not QMenu"); + CB_ERR(event, -1, "opera_menu handle invalid or not QMenu"); + } + + auto is_private_key = event["is_private_key"].toInt() != 0; + auto has_master_key = event["has_master_key"].toInt() != 0; + + auto channel = event["channel"].toInt(); + auto key_id = event["key_id"]; + auto fpr = event["fpr"]; + + FLOG_DEBUG( + "adding key server sync actions: key id: %1, channel: %2, is " + "private key: %3, has master key: %4", + key_id, channel, static_cast<int>(is_private_key), + static_cast<int>(has_master_key)); + + QMetaObject::invokeMethod(QApplication::instance(), [=]() -> void { + auto* menu = new QMenu(tab); + + auto* key_server_opera_button = new QPushButton( + QCoreApplication::translate("GTrC", "Key Server Operations")); + key_server_opera_button->setStyleSheet("text-align:center;"); + key_server_opera_button->setMenu(menu); + + // add upload / update key actions + auto* upload_key_pair = new QAction(QCoreApplication::translate( + "GTrC", "Publish Public Key to Key Server")); + QObject::connect(upload_key_pair, &QAction::triggered, tab, + [=]() { UploadKeyToServer(tab, channel, key_id); }); + if (!(is_private_key && has_master_key)) { + upload_key_pair->setDisabled(true); + } + + auto* update_key_pair = new QAction(QCoreApplication::translate( + "GTrC", "Refresh Public Key From Key Server")); + QObject::connect(update_key_pair, &QAction::triggered, tab, + [=]() { UpdateKeyFromKeyServer(tab, channel, fpr); }); + + // when a key has primary key, it should always upload to keyserver. + if (has_master_key) { + update_key_pair->setDisabled(true); + } + + menu->addAction(upload_key_pair); + menu->addAction(update_key_pair); + + layout->addWidget(key_server_opera_button); + }); + + CB_SUCC(event); + }); + REGISTER_EVENT_HANDLER( REQUEST_GET_PUBLIC_KEY_BY_FINGERPRINT, [](const MEvent& event) -> int { if (event["fingerprint"].isEmpty()) @@ -105,8 +341,6 @@ REGISTER_EVENT_HANDLER( {"key_data", key}, }); }); - QObject::connect(vks, &VKSInterface::SignalKeyRetrieved, vks, - &VKSInterface::deleteLater); QObject::connect(vks, &VKSInterface::SignalErrorOccurred, QThread::currentThread(), [event](const QString& error, const QString& data) { @@ -145,8 +379,6 @@ REGISTER_EVENT_HANDLER( {"token", token}, }); }); - QObject::connect(vks, &VKSInterface::SignalKeyRetrieved, vks, - &VKSInterface::deleteLater); QObject::connect(vks, &VKSInterface::SignalErrorOccurred, QThread::currentThread(), [event](const QString& error, const QString& data) { |
