Page MenuHome GnuPG

No OneTemporary

diff --git a/client/setupdialogs.cpp b/client/setupdialogs.cpp
index b3ccac4..5f278b9 100644
--- a/client/setupdialogs.cpp
+++ b/client/setupdialogs.cpp
@@ -1,395 +1,420 @@
// SPDX-FileCopyrightText: 2026 g10 code Gmbh
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-Contributor: Thomas Friedrichsmeier <thomas.friedrichsmeier@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "setupdialogs.h"
#include "config.h"
#include "connectioncontroller.h"
#include "gpgolweb_version.h"
#include "rootcagenerator/controller.h"
#include "websocketclient.h"
#include <KAssistantDialog>
#include <KLocalizedString>
#include <KTitleWidget>
#include <QApplication>
#include <QButtonGroup>
#include <QClipboard>
#include <QDialog>
#include <QDialogButtonBox>
#include <QDir>
#include <QDesktopServices>
#include <QFile>
#include <QGroupBox>
#include <QLabel>
#include <QPushButton>
#include <QRadioButton>
#include <QSaveFile>
#include <QSettings>
#include <QToolTip>
#include <QVBoxLayout>
#include "ui_confpageproxyoptions.h"
#include "ui_confpagetlscertificate.h"
using namespace Qt::StringLiterals;
PairingDialog::PairingDialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle(i18n("Pairing mode active"));
auto l = new QVBoxLayout(this);
auto lab = new QLabel(i18n("<p>Copy and paste the code shown below into the input field shown at the top of the Add-In:</p>"));
lab->setWordWrap(true);
l->addWidget(lab);
m_pairingTokenLabel = new QLabel(i18nc("status message", "<p align='center'><b>Obtaining token...</b></p>"));
l->addWidget(m_pairingTokenLabel);
auto bb = new QDialogButtonBox;
auto endButton = bb->addButton(i18nc("@button", "End pairing mode"), QDialogButtonBox::RejectRole);
connect(endButton, &QAbstractButton::clicked, this, &QDialog::reject);
m_copyButton = bb->addButton(i18nc("@button", "Copy code to clipboard"), QDialogButtonBox::ActionRole);
m_copyButton->setEnabled(false);
l->addWidget(bb);
setFixedSize(sizeHint());
connect(&WebsocketClient::self(), &WebsocketClient::pairingStatusChanged, this, &PairingDialog::pairingStatusChanged);
WebsocketClient::self().enterPairingMode();
}
void PairingDialog::pairingStatusChanged(const QString& token, bool pairingActive) {
if (!pairingActive) {
reject();
return;
}
m_pairingTokenLabel->setText(QString(u"<p align='center'><b>%1</b></p>").arg(token));
m_copyButton->setEnabled(true);
connect(m_copyButton, &QAbstractButton::clicked, [this, token]() {
qApp->clipboard()->setText(token);
});
}
class GenerateCertificateWidget : public QWidget {
Q_OBJECT
public:
GenerateCertificateWidget(bool assistant, QWidget *parent)
: QWidget(parent)
, m_controller(nullptr)
, m_installed(false)
, m_dialog(nullptr)
{
auto hbox = new QHBoxLayout(this);
hbox->setContentsMargins(0, 0, 0, 0);
m_label = new QLabel();
hbox->addWidget(m_label);
m_generateButton = new QPushButton();
m_generateButton->setVisible(!assistant);
if (!assistant) {
connect(m_generateButton, &QPushButton::clicked, this, [this]() {
KAssistantDialog dialog;
addAssistantPages(&dialog);
auto cancelButton = dialog.button(QDialogButtonBox::Cancel);
if (cancelButton) {
cancelButton->hide(); // it does not really have defined behavior
}
startGenerate();
dialog.exec();
});
}
hbox->addWidget(m_generateButton);
updateStatus();
}
void addAssistantPages(KAssistantDialog *dialog) {
m_dialog = dialog;
auto genPage = new QWidget();
auto vbox = new QVBoxLayout(genPage);
m_genProgress = new QPlainTextEdit();
vbox->addWidget(m_genProgress);
m_genDoneLabel = new QLabel();
vbox->addWidget(m_genDoneLabel);
m_genPageItem = new KPageWidgetItem(genPage);
m_genPageItem->setHeader(i18n("Generating TLS certificate"));
m_dialog->addPage(m_genPageItem);
auto installPage = new QWidget();
vbox = new QVBoxLayout(installPage);
m_installProgress = new QPlainTextEdit();
vbox->addWidget(m_installProgress);
m_installDoneLabel = new QLabel();
vbox->addWidget(m_installDoneLabel);
m_installPageItem = new KPageWidgetItem(installPage);
m_installPageItem->setHeader(i18n("Installing certificate"));
m_dialog->addPage(m_installPageItem);
connect(dialog, &KPageDialog::currentPageChanged, this,
[this](KPageWidgetItem *current, KPageWidgetItem *) {
if (current == m_genPageItem) {
startGenerate();
} else if (current == m_installPageItem) {
if (!m_installed) {
m_controller->install();
}
}
});
}
void updateStatus() {
if (Config::self()->isLocalServer()) {
setVisible(true);
if (Controller::certificateAlreadyGenerated()) {
m_label->setText(i18n("A TLS certificate has already been generated."));
m_generateButton->setText(i18n("Re-generate certificate"));
m_generateButton->setFixedSize(m_generateButton->minimumSizeHint());
//m_generateButton->setToolTip(i18n("Re-generates and installs a TLS certificate for a secure connection to the local proxy server. It is not usually necessary to repeat this step."));
} else {
m_label->setText(i18n("A TLS certificate is needed for the secure connection to the proxy."));
m_generateButton->setText(i18n("Generate and install certificate"));
if (m_dialog) {
m_label->setText(i18n("A TLS certificate is needed for the secure connection to the proxy. This will be generated in the next step."));
}
}
} else {
setVisible(false);
}
if (m_dialog) {
m_dialog->setAppropriate(m_genPageItem, Config::self()->isLocalServer() && !Controller::certificateAlreadyGenerated());
m_dialog->setAppropriate(m_installPageItem, Config::self()->isLocalServer() && !Controller::certificateAlreadyGenerated());
}
}
private:
QLabel *m_label;
QPushButton *m_generateButton;
QPlainTextEdit *m_genProgress, *m_installProgress;
QLabel *m_genDoneLabel, *m_installDoneLabel;
KPageWidgetItem *m_genPageItem, *m_installPageItem;
QPointer<Controller> m_controller; // it's a KJob and will auto-delete
bool m_installed;
KAssistantDialog *m_dialog;
void startGenerate() {
if (m_controller || m_installed) {
return;
}
m_dialog->setValid(m_genPageItem, false);
m_dialog->setValid(m_installPageItem, false);
m_controller = new Controller(this);
m_dialog->setValid(m_genPageItem, false);
connect(m_controller, &Controller::generationDone, this, [this]() {
m_genDoneLabel->setText(i18nc("@info", "Successfully generated certificate with fingerprint %1", m_controller->rootFingerprint()));
m_dialog->setValid(m_genPageItem, true);
});
connect(m_controller, &Controller::debutOutput, this, [this](const QString &output) {
m_genProgress->appendPlainText(output);
m_installProgress->appendPlainText(output);
});
connect(m_controller, &Controller::result, this, [this](KJob *) {
if (m_controller->error()) {
m_genProgress->appendPlainText(m_controller->errorText());
m_installProgress->appendPlainText(m_controller->errorText());
return;
}
m_label->setText(i18nc("@info", "Installed certificate with fingerprint: %1", m_controller->rootFingerprint()));
m_dialog->setValid(m_installPageItem, true);
m_installed = true;
});
m_controller->start();
}
};
void DialogController::doDialog(const QList<PageID> &pageIds, const bool assistant) {
KPageDialog *dialog = assistant ? new KAssistantDialog() : new KPageDialog();
auto cancelButton = dialog->button(QDialogButtonBox::Cancel);
if (cancelButton) {
cancelButton->hide(); // it does not really have defined behavior
}
if (pageIds.contains(PageProxy)) {
auto widget = new QWidget();
auto vbox = new QVBoxLayout(widget);
auto item = new KPageWidgetItem(widget, i18n("Proxy"));
item->setHeader(i18nc("@title", "Configure Proxy"));
dialog->addPage(item);
auto label = new QLabel(i18n("Choose your configuration for the proxy component:"));
vbox->addWidget(label);
label = new QLabel(i18n("Note: Changes to this setting only take effect after uploading the adjusted manifest file to Outlook on the next page!"));
auto font = label->font();
font.setItalic(true);
label->setFont(font);
label->setWordWrap(true);
vbox->addWidget(label);
auto certcontrol = new GenerateCertificateWidget(assistant, nullptr);
if (assistant) {
certcontrol->addAssistantPages(static_cast<KAssistantDialog*>(dialog));
}
auto grp = new QButtonGroup(widget);
auto localOption = new QRadioButton(i18n("Run a local proxy on this machine"));
localOption->setChecked(Config::self()->isLocalServer());
auto remoteOption = new QRadioButton(i18n("Use proxy from a remote server (EXPERIMENTAL)"));
remoteOption->setChecked(!Config::self()->isLocalServer());
grp->addButton(localOption);
vbox->addWidget(localOption);
grp->addButton(remoteOption);
vbox->addWidget(remoteOption);
auto hbox = new QHBoxLayout();
auto remoteLabel = new QLabel(i18n("Remote proxy server"));
hbox->addWidget(remoteLabel);
auto remoteServer = new QLineEdit();
remoteServer->setPlaceholderText(u"internal.company.com"_s);
remoteServer->setClearButtonEnabled(true);
hbox->addWidget(remoteServer);
vbox->addLayout(hbox);
QObject::connect(remoteOption, &QRadioButton::toggled, dialog, [remoteLabel, remoteServer, certcontrol](bool checked) {
Config::self()->setIsLocalServer(!checked);
Config::self()->save();
remoteLabel->setEnabled(!Config::self()->isLocalServer());
remoteServer->setEnabled(!Config::self()->isLocalServer());
certcontrol->updateStatus();
});
QObject::connect(remoteServer, &QLineEdit::textChanged, dialog, [remoteServer]() {
Config::self()->setRemoteAddress(QUrl::fromUserInput(remoteServer->text()));
Config::self()->save();
});
vbox->addWidget(certcontrol);
vbox->addStretch();
}
if (pageIds.contains(PageInstallAddin)) {
auto widget = new QWidget();
auto vbox = new QVBoxLayout(widget);
auto item = new KPageWidgetItem(widget);
item->setHeader(i18nc("@title", "Install Outlook Add-In"));
dialog->addPage(item);
- auto label = new QLabel(i18n("<p>Before the first use, the add-in has to be activated in Outlook:</p>"
- "<ol><li>Go to the <a href=\"%1\">Outlook Extension Manager</a> (you may be prompted to log in).<li>"
- "<li><a href=\"%2\">Click here</a> to copy the manifest file name to your clipboard.</li>"
- "<li>In Outlook, register this via <tt>My Add-Ins -> Custom Add-Ins -> Add a custom Add-In</tt></li>"
- "<li>Click on any E-Mail, and activate the add-in by clicking on the GnuPG icon shown about the email header.</li>"
- "<li>When prompted for a pairing code, <a href=\"%3\">click here to enter pairing mode</a>.</li></ol></p>",
- u"https://outlook.office.com/mail/jsmvvmdeeplink/?path=/options/manageapps&amp;bO=4"_s,
- u"copy"_s, u"pairing"_s));
- label->setWordWrap(true);
- QObject::connect(label, &QLabel::linkActivated, dialog, [label, dialog](const QString &url) {
- if (url == u"copy"_s) {
- QFile file(u":/gpgol-client/manifest.xml.in"_s);
- if (!file.open(QIODeviceBase::ReadOnly)) {
- Q_ASSERT(false);
- return;
- }
- QByteArray manifest = file.readAll();
- manifest.replace("%HOST%", ConnectionController::serverDomain().toUtf8());
- manifest.replace("%VERSION%", GPGOLWEB_VERSION_STRING);
+ auto grid = new QGridLayout();
+ vbox->addLayout(grid);
+ auto makeLabel = [](const QString &label) {
+ auto ret = new QLabel(label);
+ ret->setWordWrap(true);
+ return ret;
+ };
+ grid->addWidget(makeLabel(i18n("Before the first use, the add-in has to be activated in Outlook:")), 0, 0, 1, 3);
+ grid->addWidget(new QLabel(u"1."_s), 1, 0);
+ grid->addWidget(makeLabel(i18n("Go to the Outlook Extension Manager (you may be prompted to log in):")), 1, 1);
+ grid->addWidget(new QLabel(u"2."_s), 2, 0);
+ grid->addWidget(makeLabel(i18n("Generate a manifest file (the filename will be copied to the clipboard)")), 2, 1);
+ grid->addWidget(new QLabel(u"3."_s), 3, 0);
+ grid->addWidget(makeLabel(i18n("In Outlook, register this via <tt>My Add-Ins -> Custom Add-Ins -> Add a custom Add-In</tt>")), 3, 1, 1, 2);
+ grid->addWidget(new QLabel(u"4."_s), 4, 0);
+ grid->addWidget(makeLabel(i18n("Click on any E-Mail, and activate the add-in by clicking on the GnuPG icon shown about the email header.")), 4, 1, 1, 2);
+ grid->setColumnStretch(1, 2);
+
+ auto extMgrButton = new QPushButton(i18nc("@button", "Open Outlook Extension Manager"));
+ QObject::connect(extMgrButton, &QPushButton::clicked, dialog, []() {
+ QDesktopServices::openUrl(QUrl(u"https://outlook.office.com/mail/jsmvvmdeeplink/?path=/options/manageapps&amp;bO=4"_s));
+ });
+ grid->addWidget(extMgrButton, 1, 2);
+
+ auto generateManifestButton = new QPushButton(i18nc("@button", "Generate Manifest"));
+ QObject::connect(generateManifestButton, &QPushButton::clicked, dialog, [generateManifestButton]() {
+ QFile file(u":/gpgol-client/manifest.xml.in"_s);
+ if (!file.open(QIODeviceBase::ReadOnly)) {
+ Q_ASSERT(false);
+ return;
+ }
+ QByteArray manifest = file.readAll();
+ manifest.replace("%HOST%", ConnectionController::serverDomain().toUtf8());
+ manifest.replace("%VERSION%", GPGOLWEB_VERSION_STRING);
- const auto saveFilePath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/gpgol-web-manifest.xml"_s;
+ const auto saveFilePath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/gpgol-web-manifest.xml"_s;
- QSaveFile saveFile(saveFilePath);
- if (!saveFile.open(QIODeviceBase::WriteOnly)) {
- Q_ASSERT(false);
- return;
- }
- saveFile.write(manifest);
- saveFile.commit();
+ QSaveFile saveFile(saveFilePath);
+ if (!saveFile.open(QIODeviceBase::WriteOnly)) {
+ Q_ASSERT(false);
+ return;
+ }
+ saveFile.write(manifest);
+ saveFile.commit();
- QGuiApplication::clipboard()->setText(saveFilePath);
- QToolTip::showText(label->mapToGlobal(QPoint(10, 10)), i18n("Copied to clipboard."), label, QRect(), 2000);
- } else if (url == u"pairing"_s) {
+ QGuiApplication::clipboard()->setText(saveFilePath);
+ QToolTip::showText(generateManifestButton->mapToGlobal(QPoint(10, 10)), i18n("Copied to clipboard."), generateManifestButton, QRect(), 2000);
+ });
+ grid->addWidget(generateManifestButton, 2, 2);
+
+ //if (!Config::isLocalServer()) {
+ grid->addWidget(new QLabel(u"5."_s), 5, 0);
+ grid->addWidget(makeLabel(i18n("When prompted for a pairing code, click here to enter pairing mode:")), 5, 1);
+ auto pairingButton = new QPushButton(i18nc("@button", "Enter Pairing Mode"));
+ QObject::connect(pairingButton, &QPushButton::clicked, dialog, [dialog]() {
PairingDialog d(dialog);
d.exec();
WebsocketClient::self().quitPairingMode();
- } else {
- QDesktopServices::openUrl(QUrl(url));
- }
- });
- vbox->addWidget(label);
+ });
+ grid->addWidget(pairingButton, 5, 2);
+ //}
auto title = new KTitleWidget();
title->setText(i18n("Troubleshooting"));
vbox->addWidget(title);
- label = new QLabel(i18n("<p>If the extension is not connected:</p><ul>"
- "<li>Test for problems with the TLS-certificate installation, by opening this <a href=\"%1\">test page</a>.</li>"
- "<li>Sometimes the add-in icon is not immediately visible in the task-bar. Make sure to click on an existing message in Outlook. "
- "You may also have to click on the \"Apps\" icon. From there, you should be allowed to \"pin\" the icon for easier access.</li>"
- "<li>If you have just added the manifest, it may be necessary to reload.</li>"
- "<li>If your account is organization managed, your administrator may have to allow usage of the GPGOL/Web add-in, manually.</li>"
- "</ul></p>", u"https://"_s + ConnectionController::serverDomain() + u"/test"_s));
- label->setWordWrap(true);
- label->setOpenExternalLinks(true);
- vbox->addWidget(label);
+ grid = new QGridLayout();
+ vbox->addLayout(grid);
+ grid->addWidget(makeLabel(i18n("If the extension is not connected:")), 0, 0, 1, 2);
+ grid->addWidget(makeLabel(i18n("Test for problems with the TLS-certificate installation, by opening this test page in your browser:")), 1, 0);
+ grid->addWidget(makeLabel(i18n("Sometimes the add-in icon is not immediately visible in Outlook's menu ribbon. Make sure to select an existing message in Outlook. "
+ "You may also have to click on the \"Apps\" icon.")), 2, 0, 1, 2);
+ grid->addWidget(makeLabel(i18n("Once you see the add-in, you may want to \"pin\" it for easier access.")), 3, 0, 1, 2);
+ grid->addWidget(makeLabel(i18n("If you have just added the manifest, it may be necessary to reload / restart Outlook.")), 4, 0, 1, 2);
+ grid->addWidget(makeLabel(i18n("If your account is organization managed, your administrator may have to allow usage of the GPGOL/Web add-in, manually.")), 5, 0, 1, 2);
+ auto testPageButton = new QPushButton(i18nc("@button", "Open Test Page"));
+ QObject::connect(testPageButton, &QPushButton::clicked, dialog, []() {
+ QDesktopServices::openUrl(QUrl(u"https://"_s + ConnectionController::serverDomain() + u"/test"_s));
+ });
+ grid->addWidget(testPageButton, 1, 1);
+ grid->setColumnStretch(0, 2);
}
if (pageIds.contains(PageSettings)) {
auto widget = new QWidget();
auto vbox = new QVBoxLayout(widget);
auto featuresbox = new QGroupBox(i18n("Optional features"));
auto boxlayout = new QVBoxLayout(featuresbox);
auto reencrypt = new QCheckBox(i18n("Reencrypt email folders with new keys"), featuresbox);
reencrypt->setChecked(Config::self()->reencrypt());
QObject::connect(reencrypt, &QCheckBox::checkStateChanged, dialog, [](Qt::CheckState state) {
Config::self()->setReencrypt(state == Qt::Checked);
Config::self()->save();
});
boxlayout->addWidget(reencrypt);
vbox->addWidget(featuresbox);
auto startupbox = new QGroupBox(i18n("Startup behavior"));
boxlayout = new QVBoxLayout(startupbox);
#ifdef Q_OS_WIN
auto autoStartBox = new QCheckBox(i18n("Start GPGOL/Web automatically"));
// We intentionally don't use our own config for this: If users disable autostart via
// the Windows settings menu, we want to respect that, too.
{
QSettings winreg(u"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"_s, QSettings::NativeFormat);
autoStartBox->setChecked(!winreg.value(QCoreApplication::applicationName()).toString().isEmpty());
}
QObject::connect(autoStartBox, &QCheckBox::checkStateChanged, dialog, [](Qt::CheckState state) {
QSettings winreg(u"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"_s, QSettings::NativeFormat);
if (state) {
winreg.setValue(QCoreApplication::applicationName(),
QDir::toNativeSeparators(QCoreApplication::applicationFilePath()));
} else {
winreg.remove(QCoreApplication::applicationName());
}
});
boxlayout->addWidget(autoStartBox);
#endif
auto showOnStartup = new QCheckBox(i18n("Show status dialog when starting"));
showOnStartup->setChecked(Config::self()->showLauncher());
QObject::connect(showOnStartup, &QCheckBox::checkStateChanged, dialog, [](Qt::CheckState state) {
Config::self()->setShowLauncher(state == Qt::Checked);
Config::self()->save();
});
boxlayout->addWidget(showOnStartup);
vbox->addWidget(startupbox);
vbox->addStretch();
auto item = new KPageWidgetItem(widget);
item->setHeader(i18nc("@title", "Options"));
dialog->addPage(item);
}
dialog->exec();
}
void DialogController::doFirstTimeAssistant()
{
doDialog(QList{PageProxy, PageInstallAddin, PageSettings}, true);
}
#include "setupdialogs.moc"
diff --git a/client/setupdialogs.h b/client/setupdialogs.h
index 50472fa..189304a 100644
--- a/client/setupdialogs.h
+++ b/client/setupdialogs.h
@@ -1,33 +1,33 @@
// SPDX-FileCopyrightText: 2026 g10 code Gmbh
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-Contributor: Thomas Friedrichsmeier <thomas.friedrichsmeier@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QDialog>
class QLabel;
class QAbstractButton;
class DialogController {
public:
enum PageID {
PageProxy,
PageInstallAddin,
PageSettings,
};
+ // Encapsulate the various setup/config pages. These may be shown one by one
+ // or sequentailly (in an assistant dialog).
static void doDialog(const QList<PageID> &pageIds, const bool assistant=false);
static void doFirstTimeAssistant();
- static void checkDoFirstTimeDialog();
- static void showStatusDialog();
};
class PairingDialog : public QDialog {
public:
PairingDialog(QWidget *parent);
void pairingStatusChanged(const QString& token, bool pairingActive);
private:
QLabel* m_pairingTokenLabel;
QAbstractButton* m_copyButton;
};

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 26, 7:08 PM (22 h, 9 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
2e/10/14437b8b766def0ec9ea979e38b1

Event Timeline