File indexing completed on 2024-04-21 05:50:47

0001 /*
0002     SPDX-FileCopyrightText: 2002 Jean-Baptiste Mardelle <bj@altern.org>
0003     SPDX-FileCopyrightText: 2007-2022 Rolf Eike Beer <kde@opensource.sf-tec.de>
0004     SPDX-FileCopyrightText: 2011 Luis Ángel Fernández Fernández <laffdez@gmail.com>
0005     SPDX-FileCopyrightText: 2016 Andrius Štikonas <andrius@stikonas.eu>
0006     SPDX-FileCopyrightText: 2022 Dmitrii Fomchenkov <fomchenkovda@basealt.ru>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "keysmanager.h"
0011 
0012 #include "caff.h"
0013 #include "core/images.h"
0014 #include "detailedconsole.h"
0015 #include "gpgproc.h"
0016 #include "groupedit.h"
0017 #include "keyadaptor.h"
0018 #include "keyexport.h"
0019 #include "keyinfodialog.h"
0020 #include "keyservers.h"
0021 #include "keytreeview.h"
0022 #include "kgpg_general_debug.h"
0023 #include "kgpg.h"
0024 #include "kgpgchangekey.h"
0025 #include "kgpgkeygenerate.h"
0026 #include "kgpgoptions.h"
0027 #include "kgpgrevokewidget.h"
0028 #include "kgpgsettings.h"
0029 #include "newkey.h"
0030 #include "selectpublickeydialog.h"
0031 #include "selectsecretkey.h"
0032 #include "sourceselect.h"
0033 #include "editor/kgpgeditor.h"
0034 #include "editor/kgpgtextedit.h"
0035 #include "model/keylistproxymodel.h"
0036 #include "transactions/kgpgaddphoto.h"
0037 #include "transactions/kgpgadduid.h"
0038 #include "transactions/kgpgdecrypt.h"
0039 #include "transactions/kgpgdelkey.h"
0040 #include "transactions/kgpgdelsign.h"
0041 #include "transactions/kgpgdeluid.h"
0042 #include "transactions/kgpgencrypt.h"
0043 #include "transactions/kgpgexport.h"
0044 #include "transactions/kgpggeneratekey.h"
0045 #include "transactions/kgpggeneraterevoke.h"
0046 #include "transactions/kgpgimport.h"
0047 #include "transactions/kgpgkeyservergettransaction.h"
0048 #include "transactions/kgpgprimaryuid.h"
0049 #include "transactions/kgpgsignkey.h"
0050 #include "transactions/kgpgsignuid.h"
0051 #include "transactions/kgpgtransactionjob.h"
0052 #include "transactions/customstyleditemdelegate.h"
0053 
0054 #include <algorithm>
0055 #include <Akonadi/ContactEditor>
0056 #include <Akonadi/ContactEditorDialog>
0057 #include <Akonadi/ContactSearchJob>
0058 #include <KActionCollection>
0059 #include <KContacts/AddresseeList>
0060 // #include <KContacts/Key> TODO
0061 #include <kmessagebox.h>
0062 #include <kwidgetsaddons_version.h>
0063 #include <KIO/Global>
0064 #include <KIO/ApplicationLauncherJob>
0065 #include <KIO/OpenUrlJob>
0066 #include <KIO/JobTracker>
0067 #include <kio_version.h>
0068 #include <KIO/JobUiDelegateFactory>
0069 #include <KJobTrackerInterface>
0070 #include <KLocalizedString>
0071 #include <KMessageBox>
0072 #include <KProcess>
0073 #include <KSelectAction>
0074 #include <KService>
0075 #include <KSharedConfig>
0076 #include <KStandardAction>
0077 #include <KStandardGuiItem>
0078 #include <KStandardShortcut>
0079 #include <KStatusNotifierItem>
0080 #include <KToggleAction>
0081 
0082 #include <QApplication>
0083 #include <QDBusConnection>
0084 
0085 #include <QDesktopServices>
0086 #include <QDir>
0087 #include <QEvent>
0088 #include <QFileDialog>
0089 #include <QIcon>
0090 #include <QInputDialog>
0091 #include <QKeySequence>
0092 #include <QLabel>
0093 #include <QLineEdit>
0094 #include <QList>
0095 #include <QMenu>
0096 #include <QMetaObject>
0097 #include <QNetworkInformation>
0098 #include <QPainter>
0099 #include <QPrintDialog>
0100 #include <QPrinter>
0101 #include <QProcess>
0102 #include <QRegularExpression>
0103 #include <QStatusBar>
0104 #include <QUrl>
0105 #include <QWidget>
0106 #include <QWidgetAction>
0107 
0108 using namespace Qt::Literals::StringLiterals;
0109 
0110 using namespace KgpgCore;
0111 
0112 KeysManager::KeysManager(QWidget *parent)
0113            : KXmlGuiWindow(parent),
0114        imodel(new KGpgItemModel(this)),
0115        m_adduid(nullptr),
0116        m_genkey(nullptr),
0117        m_delkey(nullptr),
0118        terminalkey(nullptr),
0119        m_trayicon(nullptr)
0120 {
0121     new KeyAdaptor(this);
0122     QDBusConnection::sessionBus().registerObject(QLatin1String( "/KeyInterface" ), this);
0123 
0124     setAttribute(Qt::WA_DeleteOnClose, false);
0125     setWindowTitle(i18n("Key Management"));
0126 
0127     KStandardAction::quit(this, &KeysManager::quitApp, actionCollection());
0128     actionCollection()->addAction(KStandardAction::Preferences, QLatin1String( "options_configure" ), this, SLOT(showOptions()));
0129 
0130     openEditor = actionCollection()->addAction(QLatin1String("kgpg_editor"), this, &KeysManager::slotOpenEditor);
0131     openEditor->setIcon(QIcon::fromTheme( QLatin1String( "accessories-text-editor" )));
0132     openEditor->setText(i18n("&Open Editor"));
0133 
0134     kserver = actionCollection()->addAction( QLatin1String("key_server"), this, &KeysManager::showKeyServer);
0135     kserver->setText( i18n("&Key Server Dialog") );
0136     kserver->setIcon( QIcon::fromTheme( QLatin1String( "network-server" )) );
0137 
0138     goToDefaultKey = actionCollection()->addAction(QLatin1String("go_default_key"), this, &KeysManager::slotGotoDefaultKey);
0139     goToDefaultKey->setIcon(QIcon::fromTheme( QLatin1String( "go-home" )));
0140     goToDefaultKey->setText(i18n("&Go to Default Key"));
0141     actionCollection()->setDefaultShortcut(goToDefaultKey, QKeySequence(Qt::CTRL | Qt::Key_Home));
0142 
0143     s_kgpgEditor = new KgpgEditor(this, imodel, Qt::Dialog);
0144     s_kgpgEditor->setAttribute(Qt::WA_DeleteOnClose, false);
0145 
0146     // this must come after kserver, preferences, and openEditor are created
0147     // because they are used to set up the tray icon context menu
0148     readOptions();
0149 
0150     QAction *action;
0151 
0152     action = actionCollection()->addAction(QLatin1String("gpg_man"), this, &KeysManager::slotManpage);
0153     action->setText( i18n("View GnuPG Manual") );
0154     action->setIcon( QIcon::fromTheme( QLatin1String( "help-contents" )) );
0155 
0156     action = actionCollection()->addAction(QLatin1String("key_refresh"), this, &KeysManager::refreshkey);
0157     action->setIcon(QIcon::fromTheme( QLatin1String( "view-refresh" )));
0158     action->setText(i18n("&Refresh List"));
0159     actionCollection()->setDefaultShortcuts(action, KStandardShortcut::reload());
0160 
0161     longId = actionCollection()->add<KToggleAction>(QLatin1String("show_long_keyid"), this, &KeysManager::slotShowLongId);
0162     longId->setText(i18n("Show &Long Key Id"));
0163     longId->setChecked(KGpgSettings::showLongKeyId());
0164 
0165     QAction *infoKey = actionCollection()->addAction(QLatin1String("key_info"), this, &KeysManager::keyproperties);
0166     infoKey->setIcon(QIcon::fromTheme( QLatin1String( "document-properties-key" )));
0167     infoKey->setText(i18n("K&ey Properties"));
0168 
0169     QAction *openKeyUrl = actionCollection()->addAction(QLatin1String("key_url"), this, &KeysManager::slotOpenKeyUrl);
0170     openKeyUrl->setIcon(QIcon::fromTheme(QLatin1String("internet-services")));
0171     openKeyUrl->setText(i18n("&Open Key URL"));
0172 
0173     editKey = actionCollection()->addAction(QLatin1String("key_edit"), this, &KeysManager::slotedit);
0174     editKey->setIcon(QIcon::fromTheme( QLatin1String( "utilities-terminal" )));
0175     editKey->setText(i18n("Edit Key in &Terminal"));
0176     actionCollection()->setDefaultShortcut(editKey, QKeySequence(Qt::ALT | Qt::Key_Return));
0177 
0178     QAction *generateKey = actionCollection()->addAction(QLatin1String("key_gener"), this, &KeysManager::slotGenerateKey);
0179     generateKey->setIcon(QIcon::fromTheme( QLatin1String( "key-generate-pair" )));
0180     generateKey->setText(i18n("&Generate Key Pair..."));
0181     actionCollection()->setDefaultShortcuts(generateKey, KStandardShortcut::shortcut(KStandardShortcut::New));
0182 
0183     exportPublicKey = actionCollection()->addAction(QLatin1String("key_export"), this, &KeysManager::slotexport);
0184     exportPublicKey->setIcon(QIcon::fromTheme( QLatin1String( "document-export-key" )));
0185     actionCollection()->setDefaultShortcuts(exportPublicKey, KStandardShortcut::shortcut(KStandardShortcut::Copy));
0186 
0187     QAction *importKey = actionCollection()->addAction(QLatin1String("key_import"), this, &KeysManager::slotPreImportKey);
0188     importKey->setIcon(QIcon::fromTheme( QLatin1String( "document-import-key" )));
0189     importKey->setText(i18n("&Import Key..."));
0190     actionCollection()->setDefaultShortcuts(importKey, KStandardShortcut::shortcut(KStandardShortcut::Paste));
0191 
0192     m_sendEmail = actionCollection()->addAction(QLatin1String("send_mail"), this, &KeysManager::slotSendEmail);
0193     m_sendEmail->setIcon(QIcon::fromTheme(QLatin1String("mail-send")));
0194     m_sendEmail->setText(i18n("Send Ema&il"));
0195 
0196     QAction *newContact = actionCollection()->addAction(QLatin1String("add_kab"), this, &KeysManager::addToKAB);
0197     newContact->setIcon(QIcon::fromTheme( QLatin1String( "contact-new" )));
0198     newContact->setText(i18n("&Create New Contact in Address Book"));
0199 
0200     createGroup = actionCollection()->addAction(QLatin1String("create_group"), this, &KeysManager::createNewGroup);
0201     createGroup->setIcon(Images::group());
0202 
0203     editCurrentGroup = actionCollection()->addAction(QLatin1String("edit_group"), this, &KeysManager::editGroup);
0204     editCurrentGroup->setText(i18n("&Edit Group..."));
0205 
0206     delGroup = actionCollection()->addAction(QLatin1String("delete_group"), this, &KeysManager::deleteGroup);
0207     delGroup->setText(i18n("&Delete Group"));
0208     delGroup->setIcon(QIcon::fromTheme( QLatin1String( "edit-delete" )));
0209 
0210     m_groupRename = actionCollection()->addAction(QLatin1String("rename_group"), this, &KeysManager::renameGroup);
0211     m_groupRename->setText(i18n("&Rename Group"));
0212     m_groupRename->setIcon(QIcon::fromTheme( QLatin1String( "edit-rename" )));
0213     actionCollection()->setDefaultShortcut(m_groupRename, QKeySequence(Qt::Key_F2));
0214 
0215     deleteKey = actionCollection()->addAction(QLatin1String("key_delete"), this, &KeysManager::confirmdeletekey);
0216     deleteKey->setIcon(QIcon::fromTheme( QLatin1String( "edit-delete" )));
0217     actionCollection()->setDefaultShortcut(deleteKey, QKeySequence(Qt::Key_Delete));
0218 
0219     setDefaultKey = actionCollection()->addAction(QLatin1String("key_default"), this, &KeysManager::slotSetDefKey);
0220     setDefaultKey->setText(i18n("Set as De&fault Key"));
0221 
0222     QAction *addPhoto = actionCollection()->addAction(QLatin1String("add_photo"), this, &KeysManager::slotAddPhoto);
0223     addPhoto->setText(i18n("&Add Photo..."));
0224 
0225     QAction *addUid = actionCollection()->addAction(QLatin1String("add_uid"), this, &KeysManager::slotAddUid);
0226     addUid->setText(i18n("&Add User Id..."));
0227 
0228     QAction *exportSecretKey = actionCollection()->addAction(QLatin1String("key_sexport"), this, &KeysManager::slotexportsec);
0229     exportSecretKey->setText(i18n("Export Secret Key..."));
0230 
0231     QAction *deleteKeyPair = actionCollection()->addAction(QLatin1String("key_pdelete"), this, &KeysManager::deleteseckey);
0232     deleteKeyPair->setText(i18n("Delete Key Pair"));
0233     deleteKeyPair->setIcon(QIcon::fromTheme( QLatin1String( "edit-delete" )));
0234 
0235     m_revokeKey = actionCollection()->addAction(QLatin1String("key_revoke"), this, &KeysManager::revokeWidget);
0236     m_revokeKey->setText(i18n("Revoke Key..."));
0237 
0238     QAction *regeneratePublic = actionCollection()->addAction(QLatin1String("key_regener"), this, &KeysManager::slotregenerate);
0239     regeneratePublic->setText(i18n("&Regenerate Public Key"));
0240 
0241     delUid = actionCollection()->addAction(QLatin1String("del_uid"), this, &KeysManager::slotDelUid);
0242     delUid->setIcon(QIcon::fromTheme( QLatin1String( "edit-delete" )));
0243 
0244     setPrimUid = actionCollection()->addAction(QLatin1String("prim_uid"), this, &KeysManager::slotPrimUid);
0245     setPrimUid->setText(i18n("Set User Id as &Primary"));
0246 
0247     QAction *openPhoto = actionCollection()->addAction(QLatin1String("key_photo"), this, &KeysManager::slotShowPhoto);
0248     openPhoto->setIcon(QIcon::fromTheme( QLatin1String( "image-x-generic" )));
0249     openPhoto->setText(i18n("&Open Photo"));
0250 
0251     QAction *deletePhoto = actionCollection()->addAction(QLatin1String("delete_photo"), this, &KeysManager::slotDeletePhoto);
0252     deletePhoto->setIcon(QIcon::fromTheme( QLatin1String( "edit-delete" )));
0253     deletePhoto->setText(i18n("&Delete Photo"));
0254 
0255     delSignKey = actionCollection()->addAction(QLatin1String("key_delsign"), this, &KeysManager::delsignkey);
0256     delSignKey->setIcon(QIcon::fromTheme( QLatin1String( "edit-delete" )));
0257     delSignKey->setEnabled(false);
0258 
0259     importAllSignKeys = actionCollection()->addAction(QLatin1String("key_importallsign"), this, &KeysManager::importallsignkey);
0260     importAllSignKeys->setIcon(QIcon::fromTheme( QLatin1String( "document-import" )));
0261     importAllSignKeys->setText(i18n("Import &Missing Signatures From Keyserver"));
0262 
0263     refreshKey = actionCollection()->addAction(QLatin1String("key_server_refresh"), this, &KeysManager::refreshKeyFromServer);
0264     refreshKey->setIcon(QIcon::fromTheme( QLatin1String( "view-refresh" )));
0265 
0266     signKey = actionCollection()->addAction(QLatin1String("key_sign"), this, &KeysManager::signkey);
0267     signKey->setIcon(QIcon::fromTheme( QLatin1String( "document-sign-key" )));
0268 
0269     signUid = actionCollection()->addAction(QLatin1String("key_sign_uid"), this, &KeysManager::signuid);
0270     signUid->setIcon(QIcon::fromTheme( QLatin1String( "document-sign-key" )));
0271 
0272     signMailUid = actionCollection()->addAction(QLatin1String("key_sign_mail_uid"), this, &KeysManager::caff);
0273     signMailUid->setIcon(QIcon::fromTheme( QLatin1String( "document-sign-key" )));
0274 
0275     importSignatureKey = actionCollection()->addAction(QLatin1String("key_importsign"), this, &KeysManager::preimportsignkey);
0276     importSignatureKey->setIcon(QIcon::fromTheme( QLatin1String( "document-import-key" )));
0277 
0278     sTrust = actionCollection()->add<KToggleAction>(QLatin1String("show_trust"), this, &KeysManager::slotShowTrust);
0279     sTrust->setText(i18n("Trust"));
0280 
0281     sSize = actionCollection()->add<KToggleAction>(QLatin1String("show_size"), this, &KeysManager::slotShowSize);
0282     sSize->setText(i18n("Size"));
0283 
0284     sCreat = actionCollection()->add<KToggleAction>(QLatin1String("show_creat"), this, &KeysManager::slotShowCreation);
0285     sCreat->setText(i18n("Creation"));
0286 
0287     sExpi = actionCollection()->add<KToggleAction>(QLatin1String("show_expi"), this, &KeysManager::slotShowExpiration);
0288     sExpi->setText(i18n("Expiration"));
0289 
0290     photoProps = actionCollection()->add<KSelectAction>(QLatin1String( "photo_settings" ));
0291     photoProps->setIcon(QIcon::fromTheme( QLatin1String( "image-x-generic" )));
0292     photoProps->setText(i18n("&Photo ID's"));
0293 
0294     // Keep the list in kgpg.kcfg in sync with this one!
0295     QStringList list;
0296     list.append(i18n("Disable"));
0297     list.append(i18nc("small picture", "Small"));
0298     list.append(i18nc("medium picture", "Medium"));
0299     list.append(i18nc("large picture", "Large"));
0300     photoProps->setItems(list);
0301 
0302     trustProps = actionCollection()->add<KSelectAction>(QLatin1String( "trust_filter_settings" ));
0303     trustProps->setText(i18n("Minimum &Trust"));
0304 
0305     QStringList tlist;
0306     tlist.append(i18nc("no filter: show all keys", "&None"));
0307     tlist.append(i18nc("show only active keys", "&Active"));
0308     tlist.append(i18nc("show only keys with at least marginal trust", "&Marginal"));
0309     tlist.append(i18nc("show only keys with at least full trust", "&Full"));
0310     tlist.append(i18nc("show only ultimately trusted keys", "&Ultimate"));
0311 
0312     trustProps->setItems(tlist);
0313 
0314     iproxy = new KeyListProxyModel(this);
0315     iproxy->setKeyModel(imodel);
0316     connect(this, &KeysManager::readAgainOptions, iproxy, &KeyListProxyModel::settingsChanged);
0317 
0318     iview = new KeyTreeView(this, iproxy);
0319     iview->setItemDelegate(new CustomStyledItemDelegate(iview));
0320     connect(iview, &KeyTreeView::doubleClicked, this, QOverload<const QModelIndex &>::of(&KeysManager::defaultAction));
0321     connect(iview, &KeyTreeView::importDrop, this, QOverload<const QList<QUrl> &>::of(&KeysManager::slotImport));
0322     iview->setSelectionMode(QAbstractItemView::ExtendedSelection);
0323     setCentralWidget(iview);
0324     iview->resizeColumnsToContents();
0325     iview->setAlternatingRowColors(true);
0326     iview->setSortingEnabled(true);
0327     connect(iview, &KeyTreeView::customContextMenuRequested, this, &KeysManager::slotMenu);
0328     iview->setContextMenuPolicy(Qt::CustomContextMenu);
0329     connect(iview->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KeysManager::checkList);
0330 
0331     connect(iview, &KeyTreeView::returnPressed, this, &KeysManager::slotDefaultAction);
0332 
0333     hPublic = actionCollection()->add<KToggleAction>(QLatin1String("show_secret"), iproxy, &KeyListProxyModel::setOnlySecret);
0334     hPublic->setIcon(QIcon::fromTheme( QLatin1String( "view-key-secret" )));
0335     hPublic->setText(i18n("&Show Only Secret Keys"));
0336     hPublic->setChecked(KGpgSettings::showSecret());
0337 
0338     int psize = KGpgSettings::photoProperties();
0339     photoProps->setCurrentItem(psize);
0340     slotSetPhotoSize(psize);
0341     psize = KGpgSettings::trustLevel();
0342     trustProps->setCurrentItem(psize);
0343     slotSetTrustFilter(psize);
0344     slotShowLongId(KGpgSettings::showLongKeyId());
0345 
0346     m_popuppub = new QMenu(this);
0347     m_popuppub->addAction(exportPublicKey);
0348     m_popuppub->addAction(m_sendEmail);
0349     m_popuppub->addAction(signMailUid);
0350     m_popuppub->addAction(signKey);
0351     m_popuppub->addAction(signUid);
0352     m_popuppub->addAction(deleteKey);
0353     m_popuppub->addAction(infoKey);
0354     m_popuppub->addAction(openKeyUrl);
0355     m_popuppub->addAction(editKey);
0356     m_popuppub->addAction(refreshKey);
0357     m_popuppub->addAction(createGroup);
0358     m_popuppub->addSeparator();
0359     m_popuppub->addAction(importAllSignKeys);
0360 
0361     m_popupsec = new QMenu(this);
0362     m_popupsec->addAction(exportPublicKey);
0363     m_popupsec->addAction(m_sendEmail);
0364     m_popupsec->addAction(signKey);
0365     m_popupsec->addAction(signUid);
0366     m_popupsec->addAction(signMailUid);
0367     m_popupsec->addAction(infoKey);
0368     m_popupsec->addAction(openKeyUrl);
0369     m_popupsec->addAction(editKey);
0370     m_popupsec->addAction(refreshKey);
0371     m_popupsec->addAction(setDefaultKey);
0372     m_popupsec->addSeparator();
0373     m_popupsec->addAction(importAllSignKeys);
0374     m_popupsec->addSeparator();
0375     m_popupsec->addAction(addPhoto);
0376     m_popupsec->addAction(addUid);
0377     m_popupsec->addAction(exportSecretKey);
0378     m_popupsec->addAction(deleteKeyPair);
0379 
0380     m_popupgroup = new QMenu(this);
0381     m_popupgroup->addAction(editCurrentGroup);
0382     m_popupgroup->addAction(m_groupRename);
0383     m_popupgroup->addAction(delGroup);
0384     m_popupgroup->addAction(refreshKey);
0385 
0386     m_popupout = new QMenu(this);
0387     m_popupout->addAction(importKey);
0388 
0389     m_popupsig = new QMenu(this);
0390     m_popupsig->addAction(importSignatureKey);
0391     m_popupsig->addAction(delSignKey);
0392 
0393     m_popupphoto = new QMenu(this);
0394     m_popupphoto->addAction(openPhoto);
0395     m_popupphoto->addAction(signUid);
0396     m_popupphoto->addAction(signMailUid);
0397     m_popupphoto->addAction(deletePhoto);
0398 
0399     m_popupuid = new QMenu(this);
0400     m_popupuid->addAction(m_sendEmail);
0401     m_popupuid->addAction(signMailUid);
0402     m_popupuid->addAction(signUid);
0403     m_popupuid->addAction(delUid);
0404     m_popupuid->addAction(setPrimUid);
0405 
0406     m_popuporphan = new QMenu(this);
0407     m_popuporphan->addAction(regeneratePublic);
0408     m_popuporphan->addAction(deleteKeyPair);
0409 
0410     exportPublicKey->setEnabled(false);
0411 
0412     KConfigGroup cg = KConfigGroup(KSharedConfig::openConfig().data(), u"KeyView"_s);
0413     iview->restoreLayout(cg);
0414 
0415     connect(photoProps, &KSelectAction::indexTriggered, this, &KeysManager::slotSetPhotoSize);
0416     connect(trustProps, &KSelectAction::indexTriggered, this, &KeysManager::slotSetTrustFilter);
0417 
0418     QLabel *searchLabel = new QLabel(i18n("Search:"), this);
0419     m_listviewsearch = new QLineEdit(this);
0420     m_listviewsearch->setClearButtonEnabled(true);
0421 
0422     QWidget *searchWidget = new QWidget(this);
0423     QHBoxLayout *searchLayout = new QHBoxLayout(searchWidget);
0424     searchLayout->setContentsMargins(0, 0, 0, 0);
0425     searchLayout->addWidget(searchLabel);
0426     searchLayout->addWidget(m_listviewsearch);
0427     searchLayout->addStretch();
0428 
0429     QWidgetAction *searchLineAction = new QWidgetAction(/*i18nc("Name of the action that is a search line, shown for example in the toolbar configuration dialog",
0430             "Search Line"), */this);
0431     actionCollection()->addAction(QLatin1String( "search_line" ), searchLineAction);
0432     searchLineAction->setDefaultWidget(searchWidget);
0433 
0434     action = actionCollection()->addAction(QLatin1String("search_focus"), m_listviewsearch, QOverload<>::of(&QWidget::setFocus));
0435     action->setText(i18nc("Name of the action that gives the focus to the search line", "Focus Search Line"));
0436     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_F6));
0437     connect(m_listviewsearch, &QLineEdit::textChanged, iproxy, &KeyListProxyModel::setFilterFixedString);
0438 
0439     setActionDescriptions(1);
0440 
0441     // get all keys data
0442     setupGUI(KXmlGuiWindow::Create | Save | ToolBar | StatusBar | Keys, QLatin1String( "keysmanager.rc" ));
0443 
0444     sTrust->setChecked(KGpgSettings::showTrust());
0445     iview->setColumnHidden(2, !KGpgSettings::showTrust());
0446     sSize->setChecked(KGpgSettings::showSize());
0447     iview->setColumnHidden(3, !KGpgSettings::showSize());
0448     sCreat->setChecked(KGpgSettings::showCreat());
0449     iview->setColumnHidden(4, !KGpgSettings::showCreat());
0450     sExpi->setChecked(KGpgSettings::showExpi());
0451     iview->setColumnHidden(5, !KGpgSettings::showExpi());
0452     iproxy->setOnlySecret(KGpgSettings::showSecret());
0453 
0454     m_statusBarLabel.setAlignment(Qt::AlignCenter);
0455     statusBar()->addPermanentWidget(&m_statusBarLabel);
0456 
0457     cg = KConfigGroup(KSharedConfig::openConfig().data(), u"MainWindow"_s);
0458     setAutoSaveSettings(cg, true);
0459     applyMainWindowSettings(cg);
0460 
0461     connect(this, &KeysManager::fontChanged, s_kgpgEditor, &KgpgEditor::slotSetFont);
0462     QNetworkInformation::loadDefaultBackend();
0463     auto networkInformation = QNetworkInformation::instance();
0464     if (networkInformation) {
0465         Q_ASSERT(networkInformation);
0466         connect(networkInformation, &QNetworkInformation::reachabilityChanged, this, [this](QNetworkInformation::Reachability reachability) {
0467             toggleNetworkActions(reachability == QNetworkInformation::Reachability::Online);
0468         });
0469 
0470         toggleNetworkActions(networkInformation->reachability() == QNetworkInformation::Reachability::Online);
0471     }
0472     importSignatureKey->setEnabled(false);
0473 
0474     stateChanged(QStringLiteral("empty_list"));
0475 
0476     QMetaObject::invokeMethod(this, "refreshkey", Qt::QueuedConnection);
0477 }
0478 
0479 void KeysManager::slotGenerateKey()
0480 {
0481     if (m_genkey) {
0482         KMessageBox::error(this,
0483                 i18n("Another key generation operation is still in progress.\nPlease wait a moment until this operation is complete."),
0484                 i18n("Generating new key pair"));
0485         return;
0486     }
0487 
0488     QPointer<KgpgKeyGenerate> kg = new KgpgKeyGenerate(this);
0489     if (kg->exec() == QDialog::Accepted) {
0490         if (!kg->isExpertMode()) {
0491             KGpgGenerateKey *genkey = new KGpgGenerateKey(this, kg->name(), kg->email(),
0492                     kg->comment(), kg->algo(), kg->size(), kg->days(), kg->expiration(),
0493                     kg->caps());
0494 
0495             m_genkey = new KGpgTransactionJob(genkey);
0496             connect(m_genkey, &KGpgTransactionJob::result, this, &KeysManager::slotGenerateKeyDone);
0497 
0498             KIO::getJobTracker()->registerJob(m_genkey);
0499             m_genkey->start();
0500             QApplication::setOverrideCursor(Qt::BusyCursor);
0501         } else {
0502             KConfigGroup config(KSharedConfig::openConfig(), u"General"_s);
0503 
0504             QString terminalApp(config.readPathEntry("TerminalApplication", QLatin1String( "konsole" )));
0505 
0506             const QString gpgbin = KGpgSettings::gpgBinaryPath();
0507             QString gpg_args = gpgbin + QLatin1String(" --expert");
0508             if (GPGProc::gpgVersion(GPGProc::gpgVersionString(gpgbin)) > 0x20100)
0509                 gpg_args += QLatin1String(" --full-gen-key ");
0510             else
0511                 gpg_args += QLatin1String(" --gen-key ");
0512 
0513             gpg_args += GPGProc::getGpgHomeArguments(gpgbin).join(QLatin1Char(' '));
0514 
0515             QStringList args;
0516             args << QLatin1String( "-e" )
0517                  << gpg_args;
0518 
0519             QProcess *genKeyProc = new QProcess(this);
0520             genKeyProc->start(terminalApp, args);
0521             if (!genKeyProc->waitForStarted(-1)) {
0522                 KMessageBox::error(this, i18n("Generating new key pair"),
0523                         i18n("Can not start \"konsole\" application for expert mode."));
0524             } else {
0525                 genKeyProc->waitForFinished(-1);
0526                 refreshkey();
0527             }
0528         }
0529     }
0530 
0531     delete kg;
0532 }
0533 
0534 void KeysManager::showKeyManager()
0535 {
0536     show();
0537 }
0538 
0539 void KeysManager::slotOpenEditor()
0540 {
0541     KgpgEditor *kgpgtxtedit = new KgpgEditor(this, imodel, Qt::Window);
0542 
0543     connect(this, &KeysManager::fontChanged, kgpgtxtedit, &KgpgEditor::slotSetFont);
0544 
0545     kgpgtxtedit->show();
0546 }
0547 
0548 void KeysManager::changeMessage(const QString &msg, const bool keep)
0549 {
0550     int timeout = keep ? 0 : 10000;
0551 
0552     statusBar()->showMessage(msg, timeout);
0553 }
0554 
0555 void KeysManager::updateStatusCounter()
0556 {
0557     m_statusBarLabel.setText(imodel->statusCountMessage());
0558 }
0559 
0560 void KeysManager::slotGenerateKeyDone(KJob *job)
0561 {
0562     changeMessage(i18nc("Application ready for user input", "Ready"));
0563     QApplication::restoreOverrideCursor();
0564 
0565     KGpgTransactionJob *tjob = qobject_cast<KGpgTransactionJob *>(job);
0566 
0567     const KGpgGenerateKey * const genkey = qobject_cast<const KGpgGenerateKey *>(tjob->getTransaction());
0568     int res = tjob->getResultCode();
0569 
0570     const QString infomessage(i18n("Generating new key pair"));
0571 
0572     switch (res) {
0573     case KGpgTransaction::TS_BAD_PASSPHRASE:
0574         KMessageBox::error(this, i18n("Bad passphrase. Cannot generate a new key pair."), infomessage);
0575         break;
0576     case KGpgTransaction::TS_USER_ABORTED:
0577         KMessageBox::error(this, i18n("Aborted by the user. Cannot generate a new key pair."), infomessage);
0578         break;
0579     case KGpgTransaction::TS_INVALID_EMAIL:
0580         KMessageBox::error(this, i18n("The email address is not valid. Cannot generate a new key pair."), infomessage);
0581         break;
0582     case KGpgGenerateKey::TS_INVALID_NAME:
0583         KMessageBox::error(this, i18n("The name is not accepted by gpg. Cannot generate a new key pair."), infomessage);
0584         break;
0585     case KGpgTransaction::TS_OK: {
0586         updateStatusCounter();
0587 
0588         QPointer<QDialog> keyCreated = new QDialog(this);
0589         keyCreated->setWindowTitle(i18n("New Key Pair Created"));
0590 
0591         QVBoxLayout *mainLayout = new QVBoxLayout(keyCreated);
0592         keyCreated->setLayout(mainLayout);
0593 
0594         newKey *page = new newKey(keyCreated);
0595         page->TLname->setText(QLatin1String( "<b>" ) + genkey->getName() + QLatin1String( "</b>" ));
0596 
0597         const QString email(genkey->getEmail());
0598         page->TLemail->setText(QLatin1String( "<b>" ) + email + QLatin1String( "</b>" ));
0599 
0600         page->kURLRequester1->setUrl(KGpgRevokeDialog::revokeUrl(genkey->getName(), email));
0601 
0602         const QString fingerprint(genkey->getFingerprint());
0603         page->TLid->setText(QLatin1String( "<b>" ) % fingerprint.right(8) % QLatin1String( "</b>" ));
0604         page->LEfinger->setText(fingerprint);
0605         page->CBdefault->setChecked(true);
0606         page->show();
0607         mainLayout->addWidget(page);
0608         page->buttonBox->button(QDialogButtonBox::Ok)->setShortcut(Qt::CTRL | Qt::Key_Return);
0609         connect(page->buttonBox, &QDialogButtonBox::accepted, keyCreated.data(), &QDialog::accept);
0610 
0611         keyCreated->exec();
0612         if (keyCreated.isNull())
0613             return;
0614 
0615         imodel->refreshKey(fingerprint);
0616         KGpgKeyNode *knode = imodel->getRootNode()->findKey(fingerprint);
0617         if (page->CBdefault->isChecked())
0618             imodel->setDefaultKey(knode);
0619 
0620         iview->selectNode(knode);
0621 
0622         if (page->CBsave->isChecked() || page->CBprint->isChecked()) {
0623             QUrl revurl;
0624             if (page->CBsave->isChecked())
0625                 revurl = page->kURLRequester1->url();
0626 
0627             KGpgGenerateRevoke *genRev = new KGpgGenerateRevoke(this, fingerprint, revurl,
0628                     0, i18n("backup copy"));
0629 
0630             connect(genRev, &KGpgGenerateRevoke::done, this, &KeysManager::slotRevokeGenerated);
0631 
0632             if (page->CBprint->isChecked())
0633                 connect(genRev, &KGpgGenerateRevoke::revokeCertificate, this, &KeysManager::doPrint);
0634 
0635             genRev->start();
0636         }
0637         delete keyCreated;
0638         break;
0639     }
0640     default:
0641         KMessageBox::detailedError(this,
0642                 i18n("gpg process did not finish. Cannot generate a new key pair."),
0643                 genkey->gpgErrorMessage(), infomessage);
0644     }
0645 
0646     m_genkey = nullptr;
0647 }
0648 
0649 void KeysManager::slotShowTrust()
0650 {
0651     bool b = !sTrust->isChecked();
0652     iview->setColumnHidden(KEYCOLUMN_TRUST, b);
0653     if (!b && (iview->columnWidth(KEYCOLUMN_TRUST) == 0))
0654         iview->resizeColumnToContents(KEYCOLUMN_TRUST);
0655 }
0656 
0657 void KeysManager::slotShowExpiration()
0658 {
0659     bool b = !sExpi->isChecked();
0660     iview->setColumnHidden(KEYCOLUMN_EXPIR, b);
0661     if (!b && (iview->columnWidth(KEYCOLUMN_EXPIR) == 0))
0662         iview->resizeColumnToContents(KEYCOLUMN_EXPIR);
0663 }
0664 
0665 void KeysManager::slotShowSize()
0666 {
0667     bool b = !sSize->isChecked();
0668     iview->setColumnHidden(KEYCOLUMN_SIZE, b);
0669     if (!b && (iview->columnWidth(KEYCOLUMN_SIZE) == 0))
0670         iview->resizeColumnToContents(KEYCOLUMN_SIZE);
0671 }
0672 
0673 void KeysManager::slotShowCreation()
0674 {
0675     bool b = !sCreat->isChecked();
0676     iview->setColumnHidden(KEYCOLUMN_CREAT, b);
0677     if (!b && (iview->columnWidth(KEYCOLUMN_CREAT) == 0))
0678         iview->resizeColumnToContents(KEYCOLUMN_CREAT);
0679 }
0680 
0681 void KeysManager::slotShowLongId(bool b)
0682 {
0683     iproxy->setIdLength(b ? 16 : 8);
0684 }
0685 
0686 void KeysManager::slotSetTrustFilter(int i)
0687 {
0688     KgpgCore::KgpgKeyTrustFlag t;
0689 
0690     Q_ASSERT((i >= 0) && (i < 5));
0691     switch (i) {
0692     case 0:
0693         t = TRUST_MINIMUM;
0694         break;
0695     case 1:
0696         t = TRUST_UNDEFINED;
0697         break;
0698     case 2:
0699         t = TRUST_MARGINAL;
0700         break;
0701     case 3:
0702         t = TRUST_FULL;
0703         break;
0704     default:
0705         t = TRUST_ULTIMATE;
0706     }
0707 
0708     iproxy->setTrustFilter(t);
0709 }
0710 
0711 void KeysManager::slotGotoDefaultKey()
0712 {
0713     iview->selectNode(imodel->getRootNode()->findKey(KGpgSettings::defaultKey()));
0714 }
0715 
0716 void KeysManager::refreshKeyFromServer()
0717 {
0718     const auto keysList = iview->selectedNodes();
0719     if (keysList.empty())
0720         return;
0721 
0722     QStringList keyIDS;
0723 
0724     for (KGpgNode *item : keysList) {
0725         if (item->getType() == ITYPE_GROUP)
0726         {
0727             for (int j = 0; j < item->getChildCount(); j++)
0728                 keyIDS << item->getChild(j)->getId();
0729 
0730             continue;
0731         }
0732 
0733         if (item->getType() & ITYPE_PAIR) {
0734             keyIDS << item->getId();
0735         } else {
0736             KMessageBox::error(this, i18n("You can only refresh primary keys. Please check your selection."));
0737             return;
0738         }
0739     }
0740 
0741     QString proxy;
0742     if (KGpgSettings::useProxy())
0743         proxy = QLatin1String( qgetenv("http_proxy") );
0744 
0745     KGpgRefreshKeys *t = new KGpgRefreshKeys(this, KGpgSettings::keyServers().first(), keyIDS, true, proxy);
0746     connect(t, &KGpgRefreshKeys::done, this, &KeysManager::slotKeyRefreshDone);
0747     QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
0748     t->start();
0749 }
0750 
0751 void KeysManager::slotKeyRefreshDone(int result)
0752 {
0753     KGpgRefreshKeys *t = qobject_cast<KGpgRefreshKeys *>(sender());
0754     Q_ASSERT(t != nullptr);
0755 
0756     if (result == KGpgTransaction::TS_USER_ABORTED) {
0757         t->deleteLater();
0758         QApplication::restoreOverrideCursor();
0759         return;
0760     }
0761 
0762     const QStringList log(t->getLog());
0763     const QStringList keys = KGpgImport::getImportedIds(log, 0xffff);
0764     const QStringList message(KGpgImport::getImportMessage(log));
0765 
0766     t->deleteLater();
0767 
0768     if (!keys.empty())
0769         imodel->refreshKeys(keys);
0770 
0771     QApplication::restoreOverrideCursor();
0772     (void) new KgpgDetailedInfo(this, message.join(QLatin1String("\n")), log.join(QLatin1String("<br/>")),
0773             KGpgImport::getDetailedImportMessage(log, imodel).split(QLatin1Char('\n')));
0774 }
0775 
0776 void KeysManager::slotDelUid()
0777 {
0778     KGpgUidNode *nd = iview->selectedNode()->toUidNode();
0779 
0780     KGpgDelUid *deluid = new KGpgDelUid(this, nd);
0781 
0782     connect(deluid, &KGpgDelUid::done, this, &KeysManager::slotDelUidDone);
0783     deluid->start();
0784 }
0785 
0786 void KeysManager::slotDelUidDone(int result)
0787 {
0788     KGpgDelUid * const deluid = qobject_cast<KGpgDelUid *>(sender());
0789     Q_ASSERT(deluid != nullptr);
0790 
0791     sender()->deleteLater();
0792     if (result == KGpgTransaction::TS_OK)
0793         imodel->refreshKey(deluid->getKeyId());
0794     // FIXME: do something useful with result if it is a failure
0795 }
0796 
0797 void KeysManager::slotPrimUid()
0798 {
0799     KGpgPrimaryUid *puid = new KGpgPrimaryUid(this, iview->selectedNode()->toUidNode());
0800 
0801     connect(puid, &KGpgPrimaryUid::done, this, &KeysManager::slotPrimUidDone);
0802 
0803     puid->start();
0804 }
0805 
0806 void KeysManager::slotPrimUidDone(int result)
0807 {
0808     const QString kid(qobject_cast<KGpgPrimaryUid *>(sender())->getKeyId());
0809 
0810     sender()->deleteLater();
0811 
0812     if (result == KGpgTransaction::TS_OK)
0813         imodel->refreshKey(kid);
0814     // FIXME: some error reporting
0815 }
0816 
0817 void KeysManager::slotregenerate()
0818 {
0819     QString regID = iview->selectedNode()->getId();
0820     KProcess *p1, *p2, *p3;
0821 
0822     p1 = new KProcess(this);
0823     *p1 << KGpgSettings::gpgBinaryPath()
0824             << QLatin1String("--no-secmem-warning")
0825             << QLatin1String("--export-secret-key")
0826             << regID;
0827     p1->setOutputChannelMode(KProcess::OnlyStdoutChannel);
0828 
0829     p2 = new KProcess(this);
0830     *p2 << QLatin1String("gpgsplit")
0831             << QLatin1String("--no-split")
0832             << QLatin1String("--secret-to-public");
0833     p2->setOutputChannelMode(KProcess::OnlyStdoutChannel);
0834 
0835     p3 = new KProcess(this);
0836     *p3 << KGpgSettings::gpgBinaryPath()
0837             << QLatin1String("--import");
0838 
0839     p1->setStandardOutputProcess(p2);
0840     p2->setStandardOutputProcess(p3);
0841 
0842     p1->start();
0843     p2->start();
0844     p3->start();
0845 
0846     p1->waitForFinished();
0847     p2->waitForFinished();
0848     p3->waitForFinished();
0849 
0850     delete p1;
0851     delete p2;
0852     delete p3;
0853 
0854     imodel->refreshKey(regID);
0855 }
0856 
0857 void KeysManager::slotAddUid()
0858 {
0859     if (m_adduid) {
0860         KMessageBox::error(this, i18n("Another operation is still in progress.\nPlease wait a moment until this operation is complete."),
0861                 i18n("Add New User Id"));
0862         return;
0863     }
0864 
0865     addUidWidget = new QDialog(this);
0866     addUidWidget->setWindowTitle(i18n("Add New User Id"));
0867     QVBoxLayout *mainLayout = new QVBoxLayout(addUidWidget);
0868     addUidWidget->setLayout(mainLayout);
0869     keyUid = new AddUid(addUidWidget);
0870     mainLayout->addWidget(keyUid);
0871 
0872     keyUid->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
0873     keyUid->buttonBox->button(QDialogButtonBox::Ok)->setShortcut(Qt::CTRL | Qt::Key_Return);
0874     connect(keyUid->buttonBox, &QDialogButtonBox::accepted, addUidWidget, &QDialog::accept);
0875     connect(keyUid->buttonBox, &QDialogButtonBox::rejected, addUidWidget, &QDialog::reject);
0876 
0877     //keyUid->setMinimumSize(keyUid->sizeHint());
0878     keyUid->setMinimumWidth(300);
0879 
0880     connect(keyUid->qLineEdit1, &QLineEdit::textChanged, this, &KeysManager::slotAddUidEnable);
0881     if (addUidWidget->exec() != QDialog::Accepted)
0882         return;
0883 
0884     m_adduid = new KGpgAddUid(this, iview->selectedNode()->getId(), keyUid->qLineEdit1->text(),
0885             keyUid->qLineEdit2->text(), keyUid->qLineEdit3->text());
0886     connect(m_adduid, &KGpgAddUid::done, this, &KeysManager::slotAddUidFin);
0887     m_adduid->start();
0888 }
0889 
0890 void KeysManager::slotAddUidFin(int res)
0891 {
0892     // TODO error reporting
0893     if (res == 0)
0894         imodel->refreshKey(m_adduid->getKeyid());
0895     m_adduid->deleteLater();
0896     m_adduid = nullptr;
0897 }
0898 
0899 void KeysManager::slotAddUidEnable(const QString & name)
0900 {
0901     keyUid->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(name.length() > 4);
0902 }
0903 
0904 void KeysManager::slotAddPhoto()
0905 {
0906     QString mess = i18n("The image must be a JPEG file. Remember that the image is stored within your public key, so "
0907     "if you use a very large picture, your key will become very large as well. The size should not exceed 6 KiB. "
0908     "An image size of around 240x288 is a good size to use.");
0909 
0910     if (KMessageBox::warningContinueCancel(nullptr, mess) != KMessageBox::Continue)
0911         return;
0912 
0913     QString imagepath = QFileDialog::getOpenFileName(nullptr, QString(), QString(), QLatin1String( "image/jpeg" ));
0914     if (imagepath.isEmpty())
0915         return;
0916 
0917     KGpgAddPhoto *addphoto = new KGpgAddPhoto(this, iview->selectedNode()->getId(), imagepath);
0918     connect(addphoto, &KGpgAddPhoto::done, this, &KeysManager::slotAddPhotoFinished);
0919     addphoto->start();
0920 }
0921 
0922 void KeysManager::slotAddPhotoFinished(int res)
0923 {
0924     sender()->deleteLater();
0925 
0926     // TODO : add res == 3 (bad passphrase)
0927 
0928     if (res == 0)
0929         slotUpdatePhoto();
0930 }
0931 
0932 void KeysManager::slotDeletePhoto()
0933 {
0934     KGpgNode *nd = iview->selectedNode();
0935     KGpgUatNode *und = nd->toUatNode();
0936     KGpgKeyNode *parent = und->getParentKeyNode();
0937 
0938     QString mess = i18n("<qt>Are you sure you want to delete Photo id <b>%1</b><br/>from key <b>%2 &lt;%3&gt;</b>?</qt>",
0939                 und->getId(), parent->getName(), parent->getEmail());
0940 
0941     if (KMessageBox::warningContinueCancel(this, mess) != KMessageBox::Continue)
0942         return;
0943 
0944     KGpgDelUid *deluid = new KGpgDelUid(this, und);
0945     connect(deluid, &KGpgDelUid::done, this, &KeysManager::slotDelPhotoFinished);
0946 
0947     deluid->start();
0948 }
0949 
0950 void KeysManager::slotDelPhotoFinished(int res)
0951 {
0952     sender()->deleteLater();
0953 
0954     // TODO : add res == 3 (bad passphrase)
0955 
0956     if (res == 0) {
0957         KGpgNode *nd = iview->selectedNode();
0958         imodel->refreshKey(nd->getParentKeyNode()->toKeyNode());
0959     }
0960 }
0961 
0962 void KeysManager::slotUpdatePhoto()
0963 {
0964     KGpgNode *nd = iview->selectedNode();
0965     imodel->refreshKey(nd->toKeyNode());
0966 }
0967 
0968 void KeysManager::slotSetPhotoSize(int size)
0969 {
0970     switch(size) {
0971     case 1:
0972         iproxy->setPreviewSize(22);
0973         break;
0974     case 2:
0975         iproxy->setPreviewSize(42);
0976         break;
0977     case 3:
0978         iproxy->setPreviewSize(65);
0979         break;
0980     default:
0981         iproxy->setPreviewSize(0);
0982         break;
0983     }
0984 }
0985 
0986 void KeysManager::addToKAB()
0987 {
0988     KGpgNode *nd = iview->selectedNode();
0989     if (nd == nullptr)
0990         return;
0991 
0992     Akonadi::ContactSearchJob * const job = new Akonadi::ContactSearchJob();
0993     job->setLimit(1);
0994     job->setQuery(Akonadi::ContactSearchJob::Email, nd->getEmail());
0995     connect(job, &Akonadi::ContactSearchJob::result, this, &KeysManager::slotAddressbookSearchResult);
0996 
0997     m_addIds[job] = nd;
0998 }
0999 
1000 void KeysManager::slotAddressbookSearchResult(KJob *job)
1001 {
1002     KGpgNode * const nd = m_addIds.value(job, nullptr);
1003 
1004     if (!nd)
1005         return;
1006 
1007     Akonadi::ContactSearchJob *searchJob = qobject_cast<Akonadi::ContactSearchJob*>(job);
1008     Q_ASSERT(searchJob);
1009     const KContacts::Addressee::List addresseeList = searchJob->contacts();
1010 
1011     m_addIds.take(job);
1012 
1013     Akonadi::ContactEditorDialog *dlg;
1014 //  KContacts::Key key; TODO
1015     if (!addresseeList.isEmpty()) {
1016         dlg = new Akonadi::ContactEditorDialog(Akonadi::ContactEditorDialog::EditMode, this);
1017         dlg->setContact(searchJob->items().at(0));
1018     } else {
1019         KContacts::Addressee addressee;
1020         addressee.setNameFromString(nd->getName());
1021         addressee.setEmails(QStringList(nd->getEmail()));
1022         dlg = new Akonadi::ContactEditorDialog(Akonadi::ContactEditorDialog::CreateMode, this);
1023         dlg->editor()->setContactTemplate(addressee);
1024     }
1025 
1026     connect(dlg, &Akonadi::ContactEditorDialog::finished, dlg, &Akonadi::ContactEditorDialog::deleteLater);
1027     dlg->show();
1028 }
1029 
1030 void KeysManager::slotManpage()
1031 {
1032     const KService::Ptr helpCenter = KService::serviceByDesktopName(QStringLiteral("org.kde.khelpcenter"));
1033     auto job = new KIO::ApplicationLauncherJob(helpCenter);
1034     job->setUrls({QUrl(QStringLiteral("man:/gpg"))});
1035     job->start();
1036 }
1037 
1038 void KeysManager::showKeyServer()
1039 {
1040     QPointer<KeyServer> ks = new KeyServer(this, imodel);
1041     connect(ks.data(), &KeyServer::importFinished, imodel, QOverload<const QStringList &>::of(&KGpgItemModel::refreshKeys));
1042     ks->exec();
1043 
1044     delete ks;
1045     refreshkey();
1046 }
1047 
1048 void KeysManager::checkList()
1049 {
1050     const auto exportList = iview->selectedNodes();
1051 
1052     switch (exportList.size()) {
1053     case 0:
1054         stateChanged(QStringLiteral("empty_list"));
1055         return;
1056     case 1:
1057         if (exportList.at(0)->getType() == ITYPE_GROUP) {
1058             stateChanged(QLatin1String( "group_selected" ));
1059         } else {
1060             stateChanged(QLatin1String( "single_selected" ));
1061             m_revokeKey->setEnabled(exportList.at(0)->getType() == ITYPE_PAIR);
1062             if (terminalkey)
1063                 editKey->setEnabled(false);
1064             m_sendEmail->setEnabled(!exportList[0]->getEmail().isEmpty());
1065             setDefaultKey->setEnabled(!imodel->isDefaultKey(exportList[0]));
1066         }
1067         break;
1068     default:
1069         stateChanged(QLatin1String( "multi_selected" ));
1070     }
1071     if (!m_online)
1072         refreshKey->setEnabled(false);
1073 
1074     switch (exportList.at(0)->getType()) {
1075     case ITYPE_PUBLIC:
1076         changeMessage(i18n("Public Key"));
1077         break;
1078     case ITYPE_SUB:
1079         changeMessage(i18n("Sub Key"));
1080         break;
1081     case ITYPE_PAIR:
1082         changeMessage(i18n("Secret Key Pair"));
1083         break;
1084     case ITYPE_GROUP:
1085         changeMessage(i18n("Key Group"));
1086         break;
1087     case ITYPE_SIGN:
1088         changeMessage(i18n("Signature"));
1089         break;
1090     case ITYPE_UID:
1091         changeMessage(i18n("User ID"));
1092         break;
1093     case ITYPE_REVSIGN:
1094         changeMessage(i18n("Revocation Signature"));
1095         break;
1096     case ITYPE_UAT:
1097         changeMessage(i18n("Photo ID"));
1098         break;
1099     case ITYPE_SECRET:
1100         changeMessage(i18n("Orphaned Secret Key"));
1101         break;
1102     case ITYPE_GPUBLIC:
1103     case ITYPE_GSECRET:
1104     case ITYPE_GPAIR:
1105         changeMessage(i18n("Group member"));
1106         break;
1107     default:
1108         qCDebug(KGPG_LOG_GENERAL) << "Oops, unmatched type value" << exportList.at(0)->getType();
1109     }
1110 }
1111 
1112 void KeysManager::quitApp()
1113 {
1114     // close window
1115     saveToggleOpts();
1116     qApp->quit();
1117 }
1118 
1119 void KeysManager::saveToggleOpts(void)
1120 {
1121     KConfigGroup cg = KConfigGroup(KSharedConfig::openConfig().data(), u"KeyView"_s);
1122     iview->saveLayout(cg);
1123     KGpgSettings::setPhotoProperties(photoProps->currentItem());
1124     KGpgSettings::setShowTrust(sTrust->isChecked());
1125     KGpgSettings::setShowExpi(sExpi->isChecked());
1126     KGpgSettings::setShowCreat(sCreat->isChecked());
1127     KGpgSettings::setShowSize(sSize->isChecked());
1128     KGpgSettings::setTrustLevel(trustProps->currentItem());
1129     KGpgSettings::setShowSecret(hPublic->isChecked());
1130     KGpgSettings::setShowLongKeyId(longId->isChecked());
1131     KGpgSettings::self()->save();
1132 }
1133 
1134 void KeysManager::readOptions()
1135 {
1136     m_clipboardmode = QClipboard::Clipboard;
1137     if (KGpgSettings::useMouseSelection() && (qApp->clipboard()->supportsSelection()))
1138         m_clipboardmode = QClipboard::Selection;
1139 
1140     if (imodel != nullptr)
1141         updateStatusCounter();
1142 
1143     if (KGpgSettings::showSystray()) {
1144         setupTrayIcon();
1145     } else {
1146         delete m_trayicon;
1147         m_trayicon = nullptr;
1148     }
1149 }
1150 
1151 void KeysManager::showOptions()
1152 {
1153     if (KConfigDialog::showDialog(QLatin1String( "settings" )))
1154         return;
1155 
1156     QPointer<kgpgOptions> optionsDialog = new kgpgOptions(this, imodel);
1157     connect(optionsDialog.data(), &kgpgOptions::settingsUpdated, this, &KeysManager::readAllOptions);
1158     connect(optionsDialog.data(), &kgpgOptions::homeChanged, imodel, &KGpgItemModel::refreshAllKeys);
1159     connect(optionsDialog.data(), &kgpgOptions::homeChanged, imodel, &KGpgItemModel::refreshGroups);
1160     connect(optionsDialog.data(), &kgpgOptions::refreshTrust, imodel, &KGpgItemModel::refreshTrust);
1161     connect(optionsDialog.data(), &kgpgOptions::changeFont, this, &KeysManager::fontChanged);
1162     optionsDialog->exec();
1163     delete optionsDialog;
1164 
1165     s_kgpgEditor->m_recentfiles->setMaxItems(KGpgSettings::recentFiles());
1166 }
1167 
1168 void KeysManager::readAllOptions()
1169 {
1170     readOptions();
1171     Q_EMIT readAgainOptions();
1172 }
1173 
1174 void KeysManager::slotSetDefKey()
1175 {
1176     setDefaultKeyNode(iview->selectedNode()->toKeyNode());
1177 }
1178 
1179 void KeysManager::slotSetDefaultKey(const QString &newID)
1180 {
1181     KGpgKeyNode *ndef = imodel->getRootNode()->findKey(newID);
1182 
1183     if (ndef == nullptr) {
1184         KGpgSettings::setDefaultKey(newID);
1185         KGpgSettings::self()->save();
1186         return;
1187     }
1188 
1189     setDefaultKeyNode(ndef);
1190 }
1191 
1192 void KeysManager::setDefaultKeyNode(KGpgKeyNode *key)
1193 {
1194     const QString &newID(key->getId());
1195 
1196     if (newID == KGpgSettings::defaultKey())
1197         return;
1198 
1199     KGpgSettings::setDefaultKey(newID);
1200     KGpgSettings::self()->save();
1201 
1202     imodel->setDefaultKey(key);
1203 }
1204 
1205 void
1206 KeysManager::setActionDescriptions(int cnt)
1207 {
1208     signUid->setText(i18np("&Sign User ID ...", "&Sign User IDs ...", cnt));
1209     signMailUid->setText(i18np("Sign and &Mail User ID ...", "Sign and &Mail User IDs ...", cnt));
1210     exportPublicKey->setText(i18np("E&xport Public Key...", "E&xport Public Keys...", cnt));
1211     refreshKey->setText(i18np("&Refresh Key From Keyserver", "&Refresh Keys From Keyserver", cnt));
1212     createGroup->setText(i18np("&Create Group with Selected Key...", "&Create Group with Selected Keys...", cnt));
1213     signKey->setText(i18np("&Sign Key...", "&Sign Keys...", cnt));
1214     delUid->setText(i18np("&Delete User ID", "&Delete User IDs", cnt));
1215     delSignKey->setText(i18np("Delete Sign&ature", "Delete Sign&atures", cnt));
1216     importSignatureKey->setText(i18np("Import Key From Keyserver", "Import Keys From Keyserver", cnt));
1217     deleteKey->setText(i18np("&Delete Key", "&Delete Keys", cnt));
1218 }
1219 
1220 void
1221 KeysManager::slotMenu(const QPoint &pos)
1222 {
1223     QPoint globpos = iview->mapToGlobal(pos);
1224     bool sametype;
1225     KgpgItemType itype;
1226     const auto ndlist = iview->selectedNodes(&sametype, &itype);
1227     bool unksig = false;
1228     QSet<QString> l;
1229     const int cnt = ndlist.size();
1230 
1231     // find out if an item has unknown signatures. Only check if the item has been
1232     // expanded before as expansion is very expensive and can take several seconds
1233     // that will freeze the UI meanwhile.
1234     for (KGpgNode *nd : ndlist) {
1235         if (!nd->hasChildren())
1236             continue;
1237 
1238         KGpgExpandableNode *exnd = nd->toExpandableNode();
1239         if (!exnd->wasExpanded()) {
1240             unksig = true;
1241             break;
1242         }
1243         getMissingSigs(l, exnd);
1244         if (!l.isEmpty()) {
1245             unksig = true;
1246             break;
1247         }
1248     }
1249     importAllSignKeys->setEnabled(unksig && m_online);
1250 
1251     signUid->setEnabled(!(itype & ~(ITYPE_PAIR | ITYPE_UID | ITYPE_UAT)));
1252     signMailUid->setEnabled(signUid->isEnabled());
1253     setActionDescriptions(cnt);
1254 
1255     if (itype == ITYPE_SIGN) {
1256         bool allunksig = true;
1257         for (KGpgNode *nd : ndlist) {
1258             allunksig = nd->toSignNode()->isUnknown();
1259             if (!allunksig)
1260                 break;
1261         }
1262 
1263         importSignatureKey->setEnabled(allunksig && m_online);
1264         delSignKey->setEnabled( (cnt == 1) );
1265         m_popupsig->exec(globpos);
1266     } else if (itype == ITYPE_UID) {
1267         if (cnt == 1) {
1268             KGpgKeyNode *knd = ndlist.at(0)->toUidNode()->getParentKeyNode();
1269             setPrimUid->setEnabled(knd->getType() & ITYPE_SECRET);
1270         }
1271         m_popupuid->exec(globpos);
1272     } else if ((itype == ITYPE_UAT) && (cnt == 1)) {
1273         m_popupphoto->exec(globpos);
1274     } else if ((itype == ITYPE_PAIR) && (cnt == 1)) {
1275         m_popupsec->exec(globpos);
1276     } else if ((itype == ITYPE_SECRET) && (cnt == 1)) {
1277         m_popuporphan->exec(globpos);
1278     } else if (itype == ITYPE_GROUP) {
1279         delGroup->setEnabled( (cnt == 1) );
1280         editCurrentGroup->setEnabled( (cnt == 1) );
1281         m_groupRename->setEnabled( (cnt == 1) );
1282         m_popupgroup->exec(globpos);
1283     } else if (!(itype & ~(ITYPE_PAIR | ITYPE_GROUP))) {
1284         signKey->setEnabled(!(itype & ITYPE_GROUP));
1285         deleteKey->setEnabled(!(itype & ITYPE_GROUP));
1286         setDefaultKey->setEnabled( (cnt == 1) );
1287         m_popuppub->exec(globpos);
1288     } else if (!(itype & ~(ITYPE_UID | ITYPE_PAIR | ITYPE_UAT))) {
1289         setPrimUid->setEnabled(false);
1290         delUid->setEnabled(false);
1291         m_popupuid->exec(globpos);
1292     } else {
1293         m_popupout->exec(globpos);
1294     }
1295 }
1296 
1297 void KeysManager::revokeWidget()
1298 {
1299     KGpgNode *nd = iview->selectedNode();
1300     QDialog *keyRevokeDialog = new KGpgRevokeDialog(this, nd->toKeyNode());
1301 
1302     connect(keyRevokeDialog, &QDialog::finished, this, &KeysManager::slotRevokeDialogFinished);
1303 
1304     keyRevokeDialog->open();
1305 }
1306 
1307 void KeysManager::slotRevokeDialogFinished(int result)
1308 {
1309     sender()->deleteLater();
1310 
1311     if (result != QDialog::Accepted)
1312         return;
1313 
1314     KGpgRevokeDialog *keyRevokeDialog = qobject_cast<KGpgRevokeDialog *>(sender());
1315 
1316     KGpgGenerateRevoke *genRev = new KGpgGenerateRevoke(this, keyRevokeDialog->getId(), keyRevokeDialog->saveUrl(),
1317             keyRevokeDialog->getReason(), keyRevokeDialog->getDescription());
1318 
1319     connect(genRev, &KGpgGenerateRevoke::done, this, &KeysManager::slotRevokeGenerated);
1320 
1321     if (keyRevokeDialog->printChecked())
1322         connect(genRev, &KGpgGenerateRevoke::revokeCertificate, this, &KeysManager::doPrint);
1323     if (keyRevokeDialog->importChecked())
1324         connect(genRev, &KGpgGenerateRevoke::revokeCertificate, this, &KeysManager::slotImportRevokeTxt);
1325 
1326     genRev->start();
1327 }
1328 
1329 void KeysManager::slotRevokeGenerated(int result)
1330 {
1331     KGpgGenerateRevoke *genRev = qobject_cast<KGpgGenerateRevoke *>(sender());
1332 
1333     genRev->deleteLater();
1334 
1335     switch (result) {
1336     case KGpgTransaction::TS_OK:
1337     case KGpgTransaction::TS_USER_ABORTED:
1338         break;
1339     default:
1340         KMessageBox::detailedError(this, i18n("Creation of the revocation certificate failed..."), genRev->getOutput());
1341         break;
1342     }
1343 }
1344 
1345 void KeysManager::slotImportRevokeTxt(const QString &revokeText)
1346 {
1347     KGpgImport *import = new KGpgImport(this, revokeText);
1348     connect(import, &KGpgImport::done, this, &KeysManager::slotImportDone);
1349     import->start();
1350 }
1351 
1352 void KeysManager::slotexportsec()
1353 {
1354     // export secret key
1355     const QString warn(i18n("<qt>Secret keys <b>should not</b> be saved in an unsafe place.<br/>"
1356                 "If someone else can access this file, encryption with this key will be compromised.<br/>Continue key export?</qt>"));
1357     int result = KMessageBox::warningContinueCancel(this, warn);
1358     if (result != KMessageBox::Continue)
1359         return;
1360     KGpgNode *nd = iview->selectedNode();
1361 
1362     QString sname(nd->getEmail().section(QLatin1Char( '@' ), 0, 0).section(QLatin1Char( '.' ), 0, 0));
1363     if (sname.isEmpty())
1364         sname = nd->getName().section(QLatin1Char( ' ' ), 0, 0);
1365     sname.append(QLatin1String( ".asc" ));
1366     sname.prepend(QDir::homePath() + QLatin1Char( '/' ));
1367     QUrl url(QFileDialog::getSaveFileUrl(this, i18n("Export PRIVATE KEY As"), QUrl(sname), i18n( "*.asc|*.asc Files" )));
1368 
1369     if(!url.isEmpty()) {
1370         KGpgExport *exp = new KGpgExport(this, QStringList(nd->getId()), url.path(), QStringList(QLatin1String( "--armor" )), true);
1371 
1372         connect(exp, &KGpgExport::done, this, &KeysManager::slotExportSecFinished);
1373 
1374         exp->start();
1375     }
1376 }
1377 
1378 void KeysManager::slotExportSecFinished(int result)
1379 {
1380     KGpgExport *exp = qobject_cast<KGpgExport *>(sender());
1381     Q_ASSERT(exp != nullptr);
1382 
1383     if (result == KGpgTransaction::TS_OK) {
1384         KMessageBox::information(this,
1385                 i18n("<qt>Your <b>private</b> key \"%1\" was successfully exported to<br/>%2.<br/><b>Do not</b> leave it in an insecure place.</qt>",
1386                 exp->getKeyIds().first(), exp->getOutputFile()));
1387     } else {
1388         KMessageBox::error(this, i18n("Your secret key could not be exported.\nCheck the key."));
1389     }
1390 }
1391 
1392 void KeysManager::slotexport()
1393 {
1394     bool same;
1395     KgpgItemType tp;
1396 
1397     const auto ndlist = iview->selectedNodes(&same, &tp);
1398     if (ndlist.empty())
1399         return;
1400     if (!(tp & ITYPE_PUBLIC) || (tp & ~ITYPE_GPAIR))
1401         return;
1402 
1403     QString sname;
1404 
1405     if (ndlist.size() == 1) {
1406         sname = ndlist.at(0)->getEmail().section(QLatin1Char( '@' ), 0, 0).section(QLatin1Char( '.' ), 0, 0);
1407         if (sname.isEmpty())
1408             sname = ndlist.at(0)->getName().section(QLatin1Char(' '), 0, 0);
1409     } else
1410         sname = QLatin1String( "keyring" );
1411 
1412     QStringList klist;
1413     for (const auto k : ndlist)
1414         klist << k->getId();
1415 
1416     sname.append(QLatin1String( ".asc" ));
1417     sname.prepend(QDir::homePath() + QLatin1Char( '/' ));
1418 
1419     QStringList serverList(KGpgSettings::keyServers());
1420     serverList.replaceInStrings(QRegularExpression(QStringLiteral(" .*")), QString()); // Remove kde 3.5 (Default) tag.
1421     if (!serverList.isEmpty()) {
1422         QString defaultServer = serverList.takeFirst();
1423         std::sort(serverList.begin(), serverList.end());
1424         serverList.prepend(defaultServer);
1425     }
1426 
1427     QPointer<KeyExport> page = new KeyExport(this, serverList);
1428 
1429     page->newFilename->setUrl(QUrl(sname));
1430 
1431     if (!m_online)
1432         page->checkServer->setEnabled(false);
1433 
1434     if (page->exec() == QDialog::Accepted) {
1435         // export to file
1436         QString exportAttr;
1437 
1438         if (page->checkAttrAll->isChecked()) {
1439             // nothing
1440         } else if (page->checkAttrPhoto->isChecked()) {
1441             exportAttr = QLatin1String( "no-export-attributes" );
1442         } else {
1443             exportAttr = QLatin1String( "export-minimal" );
1444         }
1445         QStringList expopts;
1446 
1447         if (!exportAttr.isEmpty())
1448             expopts << QLatin1String( "--export-options" ) << exportAttr;
1449 
1450         if (page->checkServer->isChecked()) {
1451             KeyServer *expServer = new KeyServer(nullptr, imodel);
1452             expServer->slotSetExportAttribute(exportAttr);
1453             expServer->slotSetKeyserver(page->destServer->currentText());
1454 
1455             expServer->slotExport(klist);
1456         } else if (page->checkFile->isChecked()) {
1457             const QString expname(page->newFilename->url().path().simplified());
1458             if (!expname.isEmpty()) {
1459 
1460                 expopts.append(QLatin1String( "--armor" ));
1461 
1462                 KGpgExport *exp = new KGpgExport(this, klist, expname, expopts);
1463 
1464                 connect(exp, &KGpgExport::done, this, &KeysManager::slotExportFinished);
1465 
1466                 exp->start();
1467             }
1468         } else {
1469             KGpgExport *exp = new KGpgExport(this, klist, expopts);
1470 
1471             if (page->checkClipboard->isChecked())
1472                 connect(exp, &KGpgExport::done, this, &KeysManager::slotProcessExportClip);
1473             else
1474                 connect(exp, &KGpgExport::done, this, &KeysManager::slotProcessExportMail);
1475 
1476             exp->start();
1477         }
1478     }
1479 
1480     delete page;
1481 }
1482 
1483 void KeysManager::slotExportFinished(int result)
1484 {
1485     KGpgExport *exp = qobject_cast<KGpgExport *>(sender());
1486     Q_ASSERT(exp != nullptr);
1487 
1488     if (result == KGpgTransaction::TS_OK) {
1489         KMessageBox::information(this,
1490                 i18np("<qt>The public key was successfully exported to<br/>%2</qt>",
1491                         "<qt>The %1 public keys were successfully exported to<br/>%2</qt>",
1492                         exp->getKeyIds().count(), exp->getOutputFile()));
1493     } else {
1494         KMessageBox::error(this, i18n("Your public key could not be exported\nCheck the key."));
1495     }
1496 
1497     exp->deleteLater();
1498 }
1499 
1500 void KeysManager::slotProcessExportMail(int result)
1501 {
1502     KGpgExport *exp = qobject_cast<KGpgExport *>(sender());
1503     Q_ASSERT(exp != nullptr);
1504 
1505     // start default Mail application
1506     if (result == KGpgTransaction::TS_OK) {
1507         QDesktopServices::openUrl(QUrl(QLatin1String("mailto:?body=") + QLatin1String(exp->getOutputData())));
1508     } else {
1509         KMessageBox::error(this, i18n("Your public key could not be exported\nCheck the key."));
1510     }
1511 
1512     exp->deleteLater();
1513 }
1514 
1515 void KeysManager::slotProcessExportClip(int result)
1516 {
1517     KGpgExport *exp = qobject_cast<KGpgExport *>(sender());
1518     Q_ASSERT(exp != nullptr);
1519 
1520     if (result == KGpgTransaction::TS_OK) {
1521         qApp->clipboard()->setText(QLatin1String( exp->getOutputData() ), m_clipboardmode);
1522     } else {
1523         KMessageBox::error(this, i18n("Your public key could not be exported\nCheck the key."));
1524     }
1525 
1526     exp->deleteLater();
1527 }
1528 
1529 void KeysManager::showKeyInfo(const QString &keyID)
1530 {
1531     KGpgKeyNode *key = imodel->getRootNode()->findKey(keyID);
1532 
1533     if (key == nullptr)
1534         return;
1535 
1536     showProperties(key);
1537 }
1538 
1539 void KeysManager::slotShowPhoto()
1540 {
1541     KGpgNode *nd = iview->selectedNode();
1542     KGpgUatNode *und = nd->toUatNode();
1543     KGpgKeyNode *parent = und->getParentKeyNode();
1544 
1545     KProcess p;
1546     p << KGpgSettings::gpgBinaryPath()
1547             << QLatin1String("--no-tty")
1548             << QLatin1String("--edit-key")
1549             << parent->getId()
1550             << QLatin1String("uid")
1551             << und->getId()
1552             << QLatin1String("showphoto")
1553             << QLatin1String("quit");
1554     p.startDetached();
1555 }
1556 
1557 void KeysManager::defaultAction(const QModelIndex &index)
1558 {
1559     KGpgNode *nd = iproxy->nodeForIndex(index);
1560 
1561     defaultAction(nd);
1562 }
1563 
1564 void KeysManager::slotDefaultAction()
1565 {
1566     defaultAction(iview->selectedNode());
1567 }
1568 
1569 void KeysManager::defaultAction(KGpgNode *nd)
1570 {
1571     if (nd == nullptr)
1572         return;
1573 
1574     if (iview->isEditing())
1575         return;
1576 
1577     switch (nd->getType()) {
1578     case ITYPE_GROUP:
1579         editGroup();
1580         break;
1581     case ITYPE_UAT:
1582         slotShowPhoto();
1583         break;
1584     case ITYPE_SIGN:
1585     case ITYPE_GPUBLIC:
1586     case ITYPE_GSECRET:
1587     case ITYPE_GPAIR:
1588         iview->selectNode(nd->toRefNode()->getRefNode());
1589         break;
1590     case ITYPE_SECRET:
1591         slotregenerate();
1592         break;
1593     case ITYPE_PAIR:
1594     case ITYPE_PUBLIC:
1595         showProperties(nd);
1596         return;
1597         }
1598 }
1599 
1600 void
1601 KeysManager::showProperties(KGpgNode *n)
1602 {
1603     switch (n->getType()) {
1604     case ITYPE_UAT:
1605         return;
1606     case ITYPE_PUBLIC:
1607     case ITYPE_PAIR: {
1608         KGpgKeyNode *k = n->toKeyNode();
1609         QPointer<KgpgKeyInfo> opts = new KgpgKeyInfo(k, imodel, this);
1610         connect(opts.data(), &KgpgKeyInfo::keyNeedsRefresh, imodel, QOverload<KGpgKeyNode *>::of(&KGpgItemModel::refreshKey));
1611         connect(opts->keychange, &KGpgChangeKey::keyNeedsRefresh, imodel, QOverload<KGpgKeyNode *>::of(&KGpgItemModel::refreshKey));
1612         opts->exec();
1613         delete opts;
1614     }
1615     default:
1616         return;
1617     }
1618 }
1619 
1620 void KeysManager::keyproperties()
1621 {
1622     KGpgNode *cur = iview->selectedNode();
1623     if (cur == nullptr)
1624         return;
1625 
1626     KGpgKeyNode *kn;
1627 
1628     switch (cur->getType()) {
1629     case ITYPE_SECRET:
1630     case ITYPE_GSECRET:
1631         if (KMessageBox::questionTwoActions(this,
1632             i18n("<p>This key is an orphaned secret key (secret key without public key.) It is currently not usable.</p>"
1633                     "<p>Would you like to regenerate the public key?</p>"),
1634                     QString(), KGuiItem(i18n("Generate")), KGuiItem(i18n("Do Not Generate"))) == KMessageBox::PrimaryAction)
1635         slotregenerate();
1636         return;
1637     case ITYPE_PAIR:
1638     case ITYPE_PUBLIC: {
1639         kn = cur->toKeyNode();
1640         break;
1641     }
1642     case ITYPE_GPAIR:
1643     case ITYPE_GPUBLIC: {
1644         kn = cur->toGroupMemberNode()->getRefNode();
1645         break;
1646     }
1647     default:
1648         qCDebug(KGPG_LOG_GENERAL) << "Oops, called with invalid item type" << cur->getType();
1649         return;
1650     }
1651 
1652     QPointer<KgpgKeyInfo> opts = new KgpgKeyInfo(kn, imodel, this);
1653     connect(opts.data(), &KgpgKeyInfo::keyNeedsRefresh, imodel, QOverload<KGpgKeyNode *>::of(&KGpgItemModel::refreshKey));
1654     opts->exec();
1655     delete opts;
1656 }
1657 
1658 void KeysManager::deleteGroup()
1659 {
1660     KGpgNode *nd = iview->selectedNode();
1661     if (!nd || (nd->getType() != ITYPE_GROUP))
1662         return;
1663 
1664     int result = KMessageBox::warningContinueCancel(this, i18n("<qt>Are you sure you want to delete group <b>%1</b> ?</qt>",
1665             nd->getName()), QString(), KGuiItem(i18n("Delete"), QLatin1String("edit-delete")));
1666     if (result != KMessageBox::Continue)
1667         return;
1668 
1669     nd->toGroupNode()->remove();
1670     imodel->delNode(nd);
1671 
1672     updateStatusCounter();
1673 }
1674 
1675 void KeysManager::renameGroup()
1676 {
1677     if (iview->selectionModel()->selectedIndexes().isEmpty())
1678         return;
1679 
1680     QModelIndex selectedNodeIndex = iview->selectionModel()->selectedIndexes().at(0);
1681 
1682     iview->edit(selectedNodeIndex);
1683 }
1684 
1685 void KeysManager::createNewGroup()
1686 {
1687     QStringList badkeys;
1688     KGpgKeyNode::List keysList;
1689     KgpgItemType tp;
1690     const auto ndlist = iview->selectedNodes(nullptr, &tp);
1691 
1692     if (ndlist.empty())
1693         return;
1694     if (tp & ~ITYPE_PAIR) {
1695         KMessageBox::error(this, i18n("<qt>You cannot create a group containing signatures, subkeys or other groups.</qt>"));
1696         return;
1697     }
1698 
1699     KgpgKeyTrustFlag mintrust;
1700     if (KGpgSettings::allowUntrustedGroupMembers()) {
1701         mintrust = KgpgCore::TRUST_UNDEFINED;
1702     } else {
1703         mintrust = KgpgCore::TRUST_FULL;
1704     }
1705 
1706     for (KGpgNode *nd : ndlist) {
1707         if (nd->getTrust() >= mintrust) {
1708             keysList.append(nd->toKeyNode());
1709         } else {
1710             badkeys += i18nc("<Name> (<Email>) ID: <KeyId>", "%1 (%2) ID: %3",
1711                     nd->getName(), nd->getEmail(), nd->getId());
1712         }
1713     }
1714 
1715         QString groupName(QInputDialog::getText(this, i18n("Create New Group"),
1716             i18nc("Enter the name of the group you are creating now", "Enter new group name:")));
1717     if (groupName.isEmpty())
1718         return;
1719     if (!keysList.isEmpty()) {
1720         if (!badkeys.isEmpty())
1721             KMessageBox::informationList(this, i18n("Following keys are not valid or not trusted and will not be added to the group:"), badkeys);
1722 
1723         iview->selectNode(imodel->addGroup(groupName, keysList));
1724         updateStatusCounter();
1725         } else {
1726         KMessageBox::error(this,
1727                 i18n("<qt>No valid or trusted key was selected. The group <b>%1</b> will not be created.</qt>",
1728                 groupName));
1729     }
1730 }
1731 
1732 void KeysManager::editGroup()
1733 {
1734     KGpgNode *nd = iview->selectedNode();
1735     if (!nd || (nd->getType() != ITYPE_GROUP))
1736         return;
1737     KGpgGroupNode *gnd = nd->toGroupNode();
1738     QPointer<QDialog> dialogGroupEdit = new QDialog(this );
1739     dialogGroupEdit->setWindowTitle(i18n("Group Properties"));
1740     QVBoxLayout *mainLayout = new QVBoxLayout(dialogGroupEdit);
1741     QWidget *mainWidget = new QWidget(this);
1742     mainLayout->addWidget(mainWidget);
1743     dialogGroupEdit->setLayout(mainLayout);
1744 
1745 
1746     QList<KGpgNode *> members(gnd->getChildren());
1747 
1748     groupEdit *gEdit = new groupEdit(dialogGroupEdit, &members, imodel);
1749 
1750     mainLayout->addWidget(gEdit);
1751     gEdit->buttonBox->button(QDialogButtonBox::Ok)->setShortcut(Qt::CTRL | Qt::Key_Return);
1752     connect(gEdit->buttonBox, &QDialogButtonBox::accepted, dialogGroupEdit.data(), &QDialog::accept);
1753     connect(gEdit->buttonBox, &QDialogButtonBox::rejected, dialogGroupEdit.data(), &QDialog::reject);
1754 
1755     gEdit->show();
1756 
1757     if (dialogGroupEdit->exec() == QDialog::Accepted)
1758         imodel->changeGroup(gnd, members);
1759 
1760     delete dialogGroupEdit;
1761 }
1762 
1763 void KeysManager::signkey()
1764 {
1765     // another sign operation is still running
1766     if (!signList.isEmpty())
1767         return;
1768 
1769     KgpgItemType tp;
1770     const auto tmplist = iview->selectedNodes(nullptr, &tp);
1771     if (tmplist.empty())
1772         return;
1773 
1774     if (tp & ~ITYPE_PAIR) {
1775         KMessageBox::error(this, i18n("You can only sign primary keys. Please check your selection."));
1776         return;
1777     }
1778 
1779     if (tmplist.size() == 1) {
1780         KGpgKeyNode *nd = tmplist.at(0)->toKeyNode();
1781         QString opt;
1782 
1783         if (nd->getEmail().isEmpty())
1784             opt = i18n("<qt>You are about to sign key:<br /><br />%1<br />ID: %2<br />Fingerprint: <br /><b>%3</b>.<br /><br />"
1785                     "You should check the key fingerprint by phoning or meeting the key owner to be sure that someone "
1786                     "is not trying to intercept your communications.</qt>",
1787                     nd->getName(), nd->getId().right(8), nd->getBeautifiedFingerprint());
1788         else
1789             opt = i18n("<qt>You are about to sign key:<br /><br />%1 (%2)<br />ID: %3<br />Fingerprint: <br /><b>%4</b>.<br /><br />"
1790                     "You should check the key fingerprint by phoning or meeting the key owner to be sure that someone "
1791                     "is not trying to intercept your communications.</qt>",
1792                     nd->getName(), nd->getEmail(), nd->getId().right(8), nd->getBeautifiedFingerprint());
1793 
1794         if (KMessageBox::warningContinueCancel(this, opt) != KMessageBox::Continue) {
1795             return;
1796         }
1797         signList.append(nd);
1798     } else {
1799         QStringList signKeyList;
1800         for (KGpgNode *n : tmplist) {
1801             const KGpgKeyNode *nd = n->toKeyNode();
1802 
1803             if (nd->getEmail().isEmpty())
1804                 signKeyList += i18nc("Name: ID", "%1: %2", nd->getName(), nd->getBeautifiedFingerprint());
1805             else
1806                 signKeyList += i18nc("Name (Email): ID", "%1 (%2): %3", nd->getName(), nd->getEmail(), nd->getBeautifiedFingerprint());
1807 
1808             signList.append(n->toSignableNode());
1809         }
1810 
1811         if (KMessageBox::Continue != KMessageBox::warningContinueCancelList(this,
1812                 i18n("<qt>You are about to sign the following keys in one pass.<br/><b>If you have not carefully checked all fingerprints,"
1813                         " the security of your communications may be compromised.</b></qt>"),
1814                 signKeyList))
1815             return;
1816     }
1817 
1818     QPointer<KgpgSelectSecretKey> opts = new KgpgSelectSecretKey(this, imodel, signList.count());
1819     if (opts->exec() != QDialog::Accepted) {
1820         delete opts;
1821         signList.clear();
1822         return;
1823     }
1824 
1825     globalkeyID = QString(opts->getKeyID());
1826     const bool localsign = opts->isLocalSign();
1827     const int checklevel = opts->getSignTrust();
1828     bool isterminal = opts->isTerminalSign();
1829     delete opts;
1830 
1831     if (isterminal) {
1832         const QString keyid(signList.at(0)->getId());
1833         signList.clear();
1834         signKeyOpenConsole(globalkeyID, keyid, checklevel, localsign);
1835     } else {
1836         keyCount = 0;
1837         m_signuids = false;
1838         signLoop(localsign, checklevel);
1839     }
1840 }
1841 
1842 void KeysManager::signuid()
1843 {
1844     // another sign operation is still running
1845     if (!signList.isEmpty())
1846         return;
1847 
1848     KgpgItemType tp;
1849     const auto tmplist = iview->selectedNodes(nullptr, &tp);
1850     if (tmplist.empty())
1851         return;
1852 
1853     if (tp & ~(ITYPE_PAIR | ITYPE_UID | ITYPE_UAT)) {
1854         KMessageBox::error(this, i18n("You can only sign user ids and photo ids. Please check your selection."));
1855         return;
1856     }
1857 
1858     if (tmplist.size() == 1) {
1859         KGpgSignableNode *nd = tmplist.at(0)->toSignableNode();
1860         KGpgKeyNode *pnd;
1861         if (tp & ITYPE_PUBLIC)
1862             pnd = nd->toKeyNode();
1863         else
1864             pnd = nd->getParentKeyNode()->toKeyNode();
1865         QString opt;
1866 
1867         if (nd->getEmail().isEmpty())
1868             opt = i18n("<qt>You are about to sign user id:<br /><br />%1<br />ID: %2<br />Fingerprint: <br /><b>%3</b>.<br /><br />"
1869             "You should check the key fingerprint by phoning or meeting the key owner to be sure that someone "
1870             "is not trying to intercept your communications.</qt>", nd->getName(), nd->getId(), pnd->getBeautifiedFingerprint());
1871         else
1872             opt = i18n("<qt>You are about to sign user id:<br /><br />%1 (%2)<br />ID: %3<br />Fingerprint: <br /><b>%4</b>.<br /><br />"
1873             "You should check the key fingerprint by phoning or meeting the key owner to be sure that someone "
1874             "is not trying to intercept your communications.</qt>", nd->getName(), nd->getEmail(), nd->getId(), pnd->getBeautifiedFingerprint());
1875 
1876         if (KMessageBox::warningContinueCancel(this, opt) != KMessageBox::Continue) {
1877             return;
1878         }
1879         signList.append(nd);
1880     } else {
1881         QStringList signKeyList;
1882 
1883         for (KGpgNode *nd : tmplist) {
1884             const KGpgKeyNode *pnd = (nd->getType() & (ITYPE_UID | ITYPE_UAT)) ?
1885                     nd->getParentKeyNode()->toKeyNode() : nd->toKeyNode();
1886 
1887             if (nd->getEmail().isEmpty())
1888                 signKeyList += i18nc("Name: ID", "%1: %2",
1889                     nd->getName(), pnd->getBeautifiedFingerprint());
1890             else
1891                 signKeyList += i18nc("Name (Email): ID", "%1 (%2): %3",
1892                     nd->getName(), nd->getEmail(), pnd->getBeautifiedFingerprint());
1893 
1894             signList.append(nd->toSignableNode());
1895         }
1896 
1897         if (KMessageBox::warningContinueCancelList(this,
1898                 i18n("<qt>You are about to sign the following user ids in one pass.<br/><b>If you have not carefully checked all fingerprints,"
1899                         " the security of your communications may be compromised.</b></qt>"),
1900                 signKeyList) != KMessageBox::Continue)
1901             return;
1902     }
1903 
1904     QPointer<KgpgSelectSecretKey> opts = new KgpgSelectSecretKey(this, imodel, signList.count());
1905     if (opts->exec() != QDialog::Accepted) {
1906         delete opts;
1907         signList.clear();
1908         return;
1909     }
1910 
1911     globalkeyID = QString(opts->getKeyID());
1912     const bool localsign = opts->isLocalSign();
1913     const int checklevel = opts->getSignTrust();
1914     bool isterminal = opts->isTerminalSign();
1915     delete opts;
1916 
1917     if (isterminal) {
1918         const QString keyid(signList.at(0)->getId());
1919         signList.clear();
1920         signKeyOpenConsole(globalkeyID, keyid, checklevel, localsign);
1921     } else {
1922         keyCount = 0;
1923         m_signuids = true;
1924         signLoop(localsign, checklevel);
1925     }
1926 }
1927 
1928 void KeysManager::signLoop(const bool localsign, const int checklevel)
1929 {
1930     Q_ASSERT(keyCount < signList.count());
1931 
1932     KGpgSignableNode *nd = signList.at(keyCount);
1933     const KGpgSignTransactionHelper::carefulCheck cc = static_cast<KGpgSignTransactionHelper::carefulCheck>(checklevel);
1934     KGpgTransaction *sta;
1935 
1936     if (m_signuids) {
1937         sta = new KGpgSignUid(this, globalkeyID, nd, localsign, cc);
1938     } else {
1939         sta = new KGpgSignKey(this, globalkeyID, nd->toKeyNode(), localsign, cc);
1940     }
1941 
1942     connect(sta, &KGpgTransaction::done, this, &KeysManager::signatureResult);
1943     sta->start();
1944 }
1945 
1946 void KeysManager::signatureResult(int success)
1947 {
1948     KGpgSignTransactionHelper *ta;
1949     KGpgSignUid *suid = qobject_cast<KGpgSignUid *>(sender());
1950     if (suid != nullptr) {
1951         ta = static_cast<KGpgSignTransactionHelper *>(suid);
1952     } else {
1953         ta = static_cast<KGpgSignTransactionHelper *>(static_cast<KGpgSignKey *>(sender()));
1954     }
1955     KGpgKeyNode *nd = const_cast<KGpgKeyNode *>(ta->getKey());
1956     const bool localsign = ta->getLocal();
1957     const int checklevel = ta->getChecking();
1958     const QString signer(ta->getSigner());
1959     sender()->deleteLater();
1960 
1961     switch (success) {
1962     case KGpgTransaction::TS_OK:
1963         if (refreshList.indexOf(nd) == -1)
1964             refreshList.append(nd);
1965         break;
1966     case KGpgTransaction::TS_BAD_PASSPHRASE:
1967         KMessageBox::error(this, i18n("<qt>Bad passphrase, key <b>%1 (%2)</b> not signed.</qt>",
1968                 nd->getName(), nd->getEmail()));
1969         break;
1970     case KGpgSignTransactionHelper::TS_ALREADY_SIGNED:
1971         KMessageBox::error(this, i18n("<qt>The key <b>%1 (%2)</b> is already signed.</qt>",
1972                 nd->getName(), nd->getEmail()));
1973         break;
1974     default:
1975         if (KMessageBox::questionTwoActions(this,
1976                 i18n("<qt>Signing key <b>%1</b> with key <b>%2</b> failed.<br />"
1977                         "Do you want to try signing the key in console mode?</qt>",
1978                         nd->getId(), signer), QString{}, KGuiItem(i18nc("@action:button", "Sign")), KStandardGuiItem::cancel()) == KMessageBox::PrimaryAction)
1979             signKeyOpenConsole(signer, nd->getId(), checklevel, localsign);
1980     }
1981 
1982     if (++keyCount == signList.count()) {
1983         signList.clear();
1984         if (!refreshList.isEmpty())
1985             imodel->refreshKeys(refreshList);
1986         refreshList.clear();
1987     } else {
1988         signLoop(localsign, checklevel);
1989     }
1990 }
1991 
1992 void KeysManager::caff()
1993 {
1994     KgpgItemType tp;
1995     const auto tmplist = iview->selectedNodes(nullptr, &tp);
1996     KGpgSignableNode::List slist;
1997     if (tmplist.empty())
1998         return;
1999 
2000     if (tp & ~(ITYPE_PAIR | ITYPE_UID | ITYPE_UAT)) {
2001         KMessageBox::error(this, i18n("You can only sign user ids and photo ids. Please check your selection."));
2002         return;
2003     }
2004 
2005     for (KGpgNode *nd : tmplist) {
2006         switch (nd->getType()) {
2007         case KgpgCore::ITYPE_PAIR:
2008         case KgpgCore::ITYPE_PUBLIC: {
2009             KGpgKeyNode *knd = qobject_cast<KGpgKeyNode *>(nd);
2010             if (!knd->wasExpanded())
2011                 knd->getChildCount();
2012             }
2013         }
2014         slist.append(nd->toSignableNode());
2015     }
2016 
2017     QPointer<KgpgSelectSecretKey> opts = new KgpgSelectSecretKey(this, imodel, slist.count(), false, false);
2018     if (opts->exec() != QDialog::Accepted) {
2019         delete opts;
2020         return;
2021     }
2022 
2023     KGpgCaff *ca = new KGpgCaff(this, slist, QStringList(opts->getKeyID()), opts->getSignTrust(), KGpgCaff::IgnoreAlreadySigned);
2024     delete opts;
2025 
2026     connect(ca, &KGpgCaff::done, this, &KeysManager::slotCaffDone);
2027     connect(ca, &KGpgCaff::aborted, this, &KeysManager::slotCaffDone);
2028 
2029     ca->run();
2030 }
2031 
2032 void KeysManager::slotCaffDone()
2033 {
2034     Q_ASSERT(qobject_cast<KGpgCaff *>(sender()) != nullptr);
2035 
2036     sender()->deleteLater();
2037 }
2038 
2039 void KeysManager::signKeyOpenConsole(const QString &signer, const QString &keyid, const int checking, const bool local)
2040 {
2041     KConfigGroup config(KSharedConfig::openConfig(), u"General"_s);
2042 
2043     KProcess process;
2044     process << config.readPathEntry("TerminalApplication", QLatin1String("konsole"))
2045             << QLatin1String("-e")
2046             << KGpgSettings::gpgBinaryPath()
2047             << QLatin1String("--no-secmem-warning")
2048             << QLatin1String("-u") << signer
2049             << QLatin1String("--default-cert-level")
2050             << QString::number(checking);
2051 
2052     if (!local)
2053         process << QLatin1String( "--sign-key" ) << keyid;
2054     else
2055         process << QLatin1String( "--lsign-key" ) << keyid;
2056 
2057     process.execute();
2058 }
2059 
2060 void KeysManager::getMissingSigs(QSet<QString> &missingKeys, const KGpgExpandableNode *nd)
2061 {
2062     for (const KGpgNode *ch : nd->getChildren()) {
2063         if (ch->hasChildren()) {
2064             getMissingSigs(missingKeys, ch->toExpandableNode());
2065             continue;
2066         } else if (ch->getType() == ITYPE_SIGN) {
2067             if (ch->toSignNode()->isUnknown())
2068                 missingKeys << ch->getId();
2069         }
2070     }
2071 }
2072 
2073 void KeysManager::importallsignkey()
2074 {
2075     const auto sel = iview->selectedNodes();
2076     QSet<QString> missingKeys;
2077 
2078     if (sel.empty())
2079         return;
2080 
2081     for (const KGpgNode *nd : sel) {
2082         if (nd->hasChildren()) {
2083             getMissingSigs(missingKeys, nd->toExpandableNode());
2084         } else if (nd->getType() == ITYPE_SIGN) {
2085             const KGpgSignNode *sn = nd->toSignNode();
2086 
2087             if (sn->isUnknown())
2088                 missingKeys << sn->getId();
2089         }
2090     }
2091 
2092     if (missingKeys.isEmpty()) {
2093         KMessageBox::information(this,
2094             i18np("All signatures for this key are already in your keyring",
2095             "All signatures for this keys are already in your keyring", sel.size()));
2096         return;
2097     }
2098 
2099     importRemoteKeys(missingKeys.values());
2100 }
2101 
2102 void KeysManager::preimportsignkey()
2103 {
2104     const auto exportList = iview->selectedNodes();
2105     QStringList idlist;
2106 
2107     if (exportList.empty())
2108         return;
2109 
2110     for (const KGpgNode *nd : exportList)
2111         idlist << nd->getId();
2112 
2113     importRemoteKeys(idlist);
2114 }
2115 
2116 bool KeysManager::importRemoteKey(const QString &keyIDs)
2117 {
2118     return importRemoteKeys(keyIDs.simplified().split(QLatin1Char( ' ' )), false);
2119 }
2120 
2121 bool KeysManager::importRemoteKeys(const QStringList &keyIDs, const bool dialog)
2122 {
2123     const QStringList kservers = KeyServer::getServerList();
2124     if (kservers.isEmpty())
2125         return false;
2126 
2127     KGpgReceiveKeys *proc = new KGpgReceiveKeys(this, kservers.first(), keyIDs, dialog, QLatin1String( qgetenv("http_proxy") ));
2128     connect(proc, &KGpgReceiveKeys::done, this, &KeysManager::importRemoteFinished);
2129 
2130     proc->start();
2131 
2132     return true;
2133 }
2134 
2135 void KeysManager::importRemoteFinished(int result)
2136 {
2137     KGpgReceiveKeys *t = qobject_cast<KGpgReceiveKeys *>(sender());
2138     Q_ASSERT(t != nullptr);
2139 
2140     const QStringList keys = KGpgImport::getImportedIds(t->getLog());
2141 
2142     t->deleteLater();
2143 
2144     if ((result == KGpgTransaction::TS_OK) && !keys.isEmpty())
2145         imodel->refreshKeys(keys);
2146 }
2147 
2148 void KeysManager::delsignkey()
2149 {
2150     KGpgNode *nd = iview->selectedNode();
2151     if (nd == nullptr)
2152         return;
2153 
2154     QString parentKey;
2155 
2156     KGpgExpandableNode *parent = nd->getParentKeyNode();
2157     switch (parent->getType()) {
2158     case ITYPE_PAIR:
2159     case ITYPE_PUBLIC:
2160         parentKey = parent->getId();
2161         break;
2162     case ITYPE_UID:
2163     case ITYPE_UAT:
2164         parentKey = parent->getParentKeyNode()->getId();
2165         break;
2166     default:
2167         Q_ASSERT(0);
2168         return;
2169     }
2170 
2171     const QString signID(nd->getId());
2172     QString signMail(nd->getNameComment());
2173     QString parentMail(parent->getNameComment());
2174 
2175     if (!parent->getEmail().isEmpty())
2176         parentMail += QLatin1String( " &lt;" ) + parent->getEmail() + QLatin1String( "&gt;" );
2177     if (!nd->getEmail().isEmpty())
2178         signMail += QLatin1String( " &lt;" ) + nd->getEmail() + QLatin1String( "&gt;" );
2179 
2180     if (parentKey == signID) {
2181         KMessageBox::error(this, i18n("Edit key manually to delete a self-signature."));
2182         return;
2183     }
2184 
2185     QString ask = i18n("<qt>Are you sure you want to delete signature<br /><b>%1</b><br />from user id <b>%2</b><br />of key: <b>%3</b>?</qt>",
2186             signMail, parentMail, parentKey);
2187 
2188     if (KMessageBox::questionTwoActions(this, ask, QString(), KStandardGuiItem::del(), KStandardGuiItem::cancel()) != KMessageBox::PrimaryAction)
2189         return;
2190 
2191     KGpgDelSign *delsig = new KGpgDelSign(this, nd->toSignNode());
2192     connect(delsig, &KGpgDelSign::done, this, &KeysManager::delsignatureResult);
2193     delsig->start();
2194 }
2195 
2196 void KeysManager::delsignatureResult(int success)
2197 {
2198     sender()->deleteLater();
2199 
2200     if (success == KGpgTransaction::TS_OK) {
2201         KGpgNode *nd = iview->selectedNode()->getParentKeyNode();
2202 
2203         while (!(nd->getType() & ITYPE_PAIR))
2204             nd = nd->getParentKeyNode();
2205         imodel->refreshKey(nd->toKeyNode());
2206     } else {
2207         KMessageBox::error(this, i18n("Requested operation was unsuccessful, please edit the key manually."));
2208     }
2209 }
2210 
2211 void KeysManager::slotSendEmail()
2212 {
2213     QStringList maillist;
2214 
2215     const auto nodes = iview->selectedNodes();
2216     for (const KGpgNode *nd : nodes) {
2217         if (nd->getEmail().isEmpty())
2218             continue;
2219 
2220         maillist << QLatin1Char('"') + nd->getName() + QLatin1String("\" <") + nd->getEmail() + QLatin1Char('>');
2221     }
2222 
2223     if (maillist.isEmpty())
2224         return;
2225 
2226     QDesktopServices::openUrl(QUrl(QLatin1String("mailto:") + maillist.join(QLatin1String(", "))));
2227 }
2228 
2229 void KeysManager::slotedit()
2230 {
2231     KGpgNode *nd = iview->selectedNode();
2232     Q_ASSERT(nd != nullptr);
2233 
2234     if (!(nd->getType() & ITYPE_PAIR))
2235         return;
2236     if (terminalkey)
2237         return;
2238     if ((m_delkey != nullptr) && m_delkey->keys.contains(nd->toKeyNode()))
2239         return;
2240 
2241     KProcess *kp = new KProcess(this);
2242     KConfigGroup config(KSharedConfig::openConfig(), u"General"_s);
2243     *kp << config.readPathEntry("TerminalApplication", QLatin1String("konsole"))
2244             << QLatin1String("-e")
2245             << KGpgSettings::gpgBinaryPath()
2246             << QLatin1String("--no-secmem-warning")
2247             << QLatin1String("--edit-key")
2248             << nd->getId()
2249             << QLatin1String("help");
2250     terminalkey = nd->toKeyNode();
2251     editKey->setEnabled(false);
2252 
2253     connect(kp, &KProcess::finished, this, &KeysManager::slotEditDone);
2254     kp->start();
2255 }
2256 
2257 void KeysManager::slotEditDone(int exitcode)
2258 {
2259     if (exitcode == 0)
2260         imodel->refreshKey(terminalkey);
2261 
2262     terminalkey = nullptr;
2263     editKey->setEnabled(true);
2264 }
2265 
2266 void KeysManager::doPrint(const QString &txt)
2267 {
2268     QPrinter prt;
2269     //qCDebug(KGPG_LOG_GENERAL) << "Printing..." ;
2270     QPointer<QPrintDialog> printDialog = new QPrintDialog(&prt, this);
2271     if (printDialog->exec() == QDialog::Accepted) {
2272         QPainter painter(&prt);
2273         int width = painter.device()->width();
2274         int height = painter.device()->height();
2275         painter.drawText(0, 0, width, height, Qt::AlignLeft|Qt::AlignTop|Qt::TextDontClip, txt);
2276     }
2277     delete printDialog;
2278 }
2279 
2280 void KeysManager::removeFromGroups(KGpgKeyNode *node)
2281 {
2282     QStringList groupNames;
2283 
2284     const auto groups = node->getGroups();
2285     for (const KGpgGroupNode *gnd : groups)
2286         groupNames << gnd->getName();
2287 
2288     if (groupNames.isEmpty())
2289         return;
2290 
2291     const QString ask = i18np("<qt>The key you are deleting is a member of the following key group. Do you want to remove it from this group?</qt>",
2292             "<qt>The key you are deleting is a member of the following key groups. Do you want to remove it from these groups?</qt>",
2293             groupNames.count());
2294 
2295     if (KMessageBox::questionTwoActionsList(this, ask, groupNames, i18n("Delete key"), KStandardGuiItem::remove(), KStandardGuiItem::cancel()) != KMessageBox::PrimaryAction)
2296         return;
2297 
2298     bool groupDeleted = false;
2299 
2300     const auto grefs = node->getGroupRefs();
2301     for (KGpgGroupMemberNode *gref : grefs) {
2302         KGpgGroupNode *group = gref->getParentKeyNode();
2303 
2304         bool deleteWholeGroup = (group->getChildCount() == 1) &&
2305                 (group->getChild(0)->toGroupMemberNode() == gref);
2306         if (deleteWholeGroup)
2307             deleteWholeGroup = (KMessageBox::questionTwoActions(this,
2308                     i18n("You are removing the last key from key group %1.<br/>Do you want to delete the group, too?", group->getName()),
2309                     i18n("Delete key"), KStandardGuiItem::remove(), KStandardGuiItem::cancel()) == KMessageBox::PrimaryAction);
2310 
2311         if (!deleteWholeGroup) {
2312             imodel->deleteFromGroup(group, gref);
2313         } else {
2314             group->remove();
2315             imodel->delNode(group);
2316             groupDeleted = true;
2317         }
2318     }
2319 
2320     if (groupDeleted) {
2321         updateStatusCounter();
2322     }
2323 }
2324 
2325 void KeysManager::deleteseckey()
2326 {
2327     KGpgKeyNode *nd = iview->selectedNode()->toKeyNode();
2328     Q_ASSERT(nd != nullptr);
2329 
2330     // delete a key
2331     int result = KMessageBox::warningContinueCancel(this,
2332             i18n("<p>Delete <b>secret</b> key pair <b>%1</b>?</p>Deleting this key pair means you will never be able to decrypt files encrypted with this key again.",
2333                     nd->getNameComment()),
2334             QString(),
2335             KGuiItem(i18n("Delete"), QLatin1String( "edit-delete" )));
2336     if (result != KMessageBox::Continue)
2337         return;
2338 
2339     if (terminalkey == nd)
2340         return;
2341     if (m_delkey != nullptr) {
2342         KMessageBox::error(this,
2343                 i18n("Another key delete operation is still in progress.\nPlease wait a moment until this operation is complete."),
2344                 i18n("Delete key"));
2345         return;
2346     }
2347 
2348     removeFromGroups(nd);
2349 
2350     m_delkey = new KGpgDelKey(this, nd);
2351     connect(m_delkey, &KGpgDelKey::done, this, &KeysManager::secretKeyDeleted);
2352     m_delkey->start();
2353 }
2354 
2355 void KeysManager::secretKeyDeleted(int retcode)
2356 {
2357     KGpgKeyNode *delkey = m_delkey->keys.at(0);
2358     if (retcode == 0) {
2359         KMessageBox::information(this, i18n("Key <b>%1</b> deleted.", delkey->getBeautifiedFingerprint()), i18n("Delete key"));
2360         imodel->delNode(delkey);
2361     } else {
2362         KMessageBox::error(this, i18n("Deleting key <b>%1</b> failed.", delkey->getBeautifiedFingerprint()), i18n("Delete key"));
2363     }
2364     m_delkey->deleteLater();
2365     m_delkey = nullptr;
2366 }
2367 
2368 void KeysManager::confirmdeletekey()
2369 {
2370     if (m_delkey) {
2371         KMessageBox::error(this,
2372                 i18n("Another key delete operation is still in progress.\nPlease wait a moment until this operation is complete."),
2373                 i18n("Delete key"));
2374         return;
2375     }
2376 
2377     KgpgCore::KgpgItemType pt;
2378     bool same;
2379     const auto ndlist = iview->selectedNodes(&same, &pt);
2380     if (ndlist.empty())
2381         return;
2382 
2383     // do not delete a key currently edited in terminal
2384     if ((!(pt & ~ITYPE_PAIR)) && (ndlist.at(0) == terminalkey) && (ndlist.size() == 1)) {
2385         KMessageBox::error(this,
2386                 i18n("Can not delete key <b>%1</b> while it is edited in terminal.",
2387                 terminalkey->getBeautifiedFingerprint()), i18n("Delete key"));
2388         return;
2389     } else if (pt == ITYPE_GROUP) {
2390         deleteGroup();
2391         return;
2392     } else if (!(pt & ITYPE_GROUP) && (pt & ITYPE_SECRET) && (ndlist.size() == 1)) {
2393         deleteseckey();
2394         return;
2395     } else if ((pt == ITYPE_UID) && (ndlist.size() == 1)) {
2396         slotDelUid();
2397         return;
2398     } else if ((pt & ITYPE_GROUP) && !(pt & ~ITYPE_GPAIR)) {
2399         bool invalidDelete = std::any_of(ndlist.cbegin(), ndlist.cend(),
2400                 [](const KGpgNode *nd) { return nd->getType() == ITYPE_GROUP; });
2401 
2402         // only allow removing group members if they belong to the same group
2403         if (!invalidDelete) {
2404             const KGpgNode * const group = ndlist.front()->getParentKeyNode();
2405             invalidDelete = std::any_of(ndlist.cbegin(), ndlist.cend(),
2406                     [group] (const KGpgNode *nd) { return nd->getParentKeyNode() != group; });
2407         }
2408 
2409         if (!invalidDelete) {
2410             KGpgGroupNode *gnd = ndlist.front()->getParentKeyNode()->toGroupNode();
2411 
2412             QList<KGpgNode *> members = gnd->getChildren();
2413 
2414             for (KGpgNode *nd : ndlist) {
2415                 int r = members.removeAll(nd);
2416                 Q_ASSERT(r == 1);
2417                 Q_UNUSED(r);
2418             }
2419 
2420             imodel->changeGroup(gnd, members);
2421             return;
2422         }
2423     }
2424 
2425     if (pt & ~ITYPE_PAIR) {
2426         KMessageBox::error(this,
2427                 i18n("You have selected items that are not keys. They can not be deleted with this menu entry."),
2428                 i18n("Delete key"));
2429         return;
2430     }
2431 
2432     QStringList keysToDelete;
2433     QStringList deleteIds;
2434     QStringList secList;
2435     KGpgKeyNode::List delkeys;
2436 
2437     bool secretKeyInside = (pt & ITYPE_SECRET);
2438     for (KGpgNode *nd : ndlist) {
2439         KGpgKeyNode *ki = nd->toKeyNode();
2440 
2441         if (ki->getType() & ITYPE_SECRET) {
2442             secList += ki->getNameComment();
2443         } else if (ki != terminalkey) {
2444             keysToDelete += ki->getNameComment();
2445             deleteIds << ki->getId();
2446             delkeys << ki;
2447         }
2448     }
2449 
2450     if (secretKeyInside) {
2451         int result = KMessageBox::warningContinueCancel(this,
2452                 i18n("<qt>The following are secret key pairs:<br/><b>%1</b><br/>They will not be deleted.</qt>",
2453                         secList.join( QLatin1String( "<br />" ))));
2454         if (result != KMessageBox::Continue)
2455             return;
2456     }
2457 
2458     if (keysToDelete.isEmpty())
2459         return;
2460 
2461     int result = KMessageBox::warningContinueCancelList(this,
2462             i18np("<qt><b>Delete the following public key?</b></qt>",
2463                     "<qt><b>Delete the following %1 public keys?</b></qt>",
2464                     keysToDelete.count()), keysToDelete, QString(),
2465                     KStandardGuiItem::del());
2466     if (result != KMessageBox::Continue)
2467         return;
2468 
2469     for (KGpgNode *nd : ndlist)
2470         removeFromGroups(nd->toKeyNode());
2471 
2472     m_delkey = new KGpgDelKey(this, delkeys);
2473     connect(m_delkey, &KGpgDelKey::done, this, &KeysManager::slotDelKeyDone);
2474     m_delkey->start();
2475 }
2476 
2477 void KeysManager::slotDelKeyDone(int res)
2478 {
2479     if (res == 0) {
2480         for (KGpgKeyNode *kn : m_delkey->keys)
2481             imodel->delNode(kn);
2482     }
2483 
2484     m_delkey->deleteLater();
2485     m_delkey = nullptr;
2486 
2487     updateStatusCounter();
2488 }
2489 
2490 void KeysManager::slotPreImportKey()
2491 {
2492     QPointer<QDialog> dial = new QDialog(this);
2493     dial->setWindowTitle(i18n("Key Import"));
2494     QVBoxLayout *mainLayout = new QVBoxLayout(dial);
2495     QWidget *mainWidget = new QWidget(this);
2496     mainLayout->addWidget(mainWidget);
2497     dial->setLayout(mainLayout);
2498 
2499     SrcSelect *page = new SrcSelect();
2500     mainLayout->addWidget(page);
2501     page->newFilename->setWindowTitle(i18n("Open File"));
2502     page->newFilename->setMode(KFile::File);
2503 
2504     page->buttonBox->button(QDialogButtonBox::Ok)->setShortcut(Qt::CTRL | Qt::Key_Return);
2505     connect(page->buttonBox, &QDialogButtonBox::accepted, dial.data(), &QDialog::accept);
2506     connect(page->buttonBox, &QDialogButtonBox::rejected, dial.data(), &QDialog::reject);
2507 
2508     if (dial->exec() == QDialog::Accepted) {
2509         if (page->checkFile->isChecked()) {
2510             QUrl impname = page->newFilename->url();
2511             if (!impname.isEmpty())
2512                 slotImport(QList<QUrl>({impname}));
2513         } else if (page->checkServer->isChecked()) {
2514             const QString ids(page->keyIds->text().simplified());
2515             if (!ids.isEmpty())
2516                 importRemoteKeys(ids.split(QLatin1Char( ' ' )));
2517         } else {
2518             slotImport(qApp->clipboard()->text(m_clipboardmode));
2519         }
2520     }
2521 
2522     delete dial;
2523 }
2524 
2525 void KeysManager::slotImport(const QString &text)
2526 {
2527     if (text.isEmpty())
2528         return;
2529 
2530     KGpgImport *imp;
2531 
2532     if (!KGpgImport::isKey(text) && KGpgDecrypt::isEncryptedText(text)) {
2533         if (KMessageBox::questionTwoActions(this,
2534                 i18n("<qt>The text in the clipboard does not look like a key, but like encrypted text.<br />Do you want to decrypt it first"
2535                         " and then try importing it?</qt>"),
2536                         i18n("Import from Clipboard"), KStandardGuiItem::ok(), KStandardGuiItem::cancel()) != KMessageBox::PrimaryAction)
2537             return;
2538 
2539         imp = new KGpgImport(this);
2540         KGpgDecrypt *decr = new KGpgDecrypt(this, text);
2541         imp->setInputTransaction(decr);
2542     } else {
2543         imp = new KGpgImport(this, text);
2544     }
2545 
2546     startImport(imp);
2547 }
2548 
2549 void KeysManager::slotImport(const QList<QUrl> &files)
2550 {
2551     startImport(new KGpgImport(this, files));
2552 }
2553 
2554 void KeysManager::startImport(KGpgImport *import)
2555 {
2556     changeMessage(i18n("Importing..."), true);
2557     connect(import, &KGpgImport::done, this, &KeysManager::slotImportDone);
2558     import->start();
2559 }
2560 
2561 void KeysManager::slotImportDone(int result)
2562 {
2563     KGpgImport *import = qobject_cast<KGpgImport *>(sender());
2564     Q_ASSERT(import != nullptr);
2565     const QStringList rawmsgs(import->getMessages());
2566 
2567     if (result != 0) {
2568         KMessageBox::detailedError(this, i18n("Key importing failed. Please see the detailed log for more information."),
2569                 rawmsgs.join(QLatin1String("\n")) , i18n("Key Import"));
2570     }
2571 
2572     QStringList keys(import->getImportedIds(0x1f));
2573     const bool needsRefresh = !keys.isEmpty();
2574     keys << import->getImportedIds(0);
2575 
2576     if (!keys.isEmpty()) {
2577         const QString msg(import->getImportMessage());
2578         const QStringList keynames(import->getImportedKeys());
2579 
2580         new KgpgDetailedInfo(this, msg, rawmsgs.join(QLatin1String("\n")), keynames, i18n("Key Import"));
2581         if (needsRefresh)
2582             imodel->refreshKeys(keys);
2583         else
2584             changeMessage(i18nc("Application ready for user input", "Ready"));
2585     } else{
2586         changeMessage(i18nc("Application ready for user input", "Ready"));
2587     }
2588 
2589     import->deleteLater();
2590 }
2591 
2592 void KeysManager::refreshkey()
2593 {
2594     imodel->refreshAllKeys();
2595     updateStatusCounter();
2596 }
2597 
2598 KGpgItemModel *KeysManager::getModel()
2599 {
2600     return imodel;
2601 }
2602 
2603 void
2604 KeysManager::toggleNetworkActions(bool online)
2605 {
2606     m_online = online;
2607     kserver->setEnabled(online);
2608     importSignatureKey->setEnabled(online);
2609     importAllSignKeys->setEnabled(online);
2610     refreshKey->setEnabled(online);
2611 }
2612 
2613 void
2614 KeysManager::setupTrayIcon()
2615 {
2616     bool newtray = (m_trayicon == nullptr);
2617 
2618     if (newtray) {
2619         m_trayicon = new KStatusNotifierItem(this);
2620         m_trayicon->setIconByName(QLatin1String( "kgpg" ));
2621         m_trayicon->setToolTip(QLatin1String( "kgpg" ), i18n("KGpg - encryption tool"), QString());
2622     }
2623 
2624     switch (KGpgSettings::leftClick()) {
2625     case KGpgSettings::EnumLeftClick::Editor:
2626         s_kgpgEditor->winId(); // create windowHandle
2627         m_trayicon->setAssociatedWindow(s_kgpgEditor->windowHandle());
2628         break;
2629     case KGpgSettings::EnumLeftClick::KeyManager:
2630         winId(); // create windowHandle
2631         m_trayicon->setAssociatedWindow(windowHandle());
2632         break;
2633     }
2634 
2635     m_trayicon->setCategory(KStatusNotifierItem::ApplicationStatus);
2636 
2637     if (!newtray)
2638         return;
2639 
2640     QMenu *conf_menu = m_trayicon->contextMenu();
2641 
2642     QAction *KgpgOpenManager = actionCollection()->addAction(QLatin1String("kgpg_manager"), this, &KeysManager::show);
2643     KgpgOpenManager->setIcon(QIcon::fromTheme( QLatin1String( "kgpg" )));
2644     KgpgOpenManager->setText(i18n("Ke&y Manager"));
2645 
2646     QAction *KgpgEncryptClipboard = actionCollection()->addAction(QLatin1String("clip_encrypt"), this, &KeysManager::clipEncrypt);
2647     KgpgEncryptClipboard->setText(i18n("&Encrypt Clipboard"));
2648 
2649     QAction *KgpgDecryptClipboard = actionCollection()->addAction(QLatin1String("clip_decrypt"), this, &KeysManager::clipDecrypt);
2650     KgpgDecryptClipboard->setText(i18n("&Decrypt Clipboard"));
2651 
2652     QAction *KgpgSignClipboard = actionCollection()->addAction(QLatin1String("clip_sign"), this, &KeysManager::clipSign);
2653     KgpgSignClipboard->setText(i18n("&Sign/Verify Clipboard"));
2654     KgpgSignClipboard->setIcon(QIcon::fromTheme( QLatin1String( "document-sign-key" )));
2655 
2656     QAction *KgpgPreferences = KStandardAction::preferences(this, &KeysManager::showOptions, actionCollection());
2657 
2658     conf_menu->addAction( KgpgEncryptClipboard );
2659     conf_menu->addAction( KgpgDecryptClipboard );
2660     conf_menu->addAction( KgpgSignClipboard );
2661     conf_menu->addAction( KgpgOpenManager );
2662     conf_menu->addAction( openEditor );
2663     conf_menu->addAction( kserver );
2664     conf_menu->addSeparator();
2665     conf_menu->addAction( KgpgPreferences );
2666 }
2667 
2668 void
2669 KeysManager::showTrayMessage(const QString &message)
2670 {
2671     if (m_trayicon == nullptr)
2672         return;
2673 
2674     m_trayicon->showMessage(QString(), message, QLatin1String( "kgpg" ));
2675 }
2676 
2677 QKeySequence
2678 KeysManager::goDefaultShortcut() const
2679 {
2680     return QKeySequence(goToDefaultKey->shortcut());
2681 }
2682 
2683 void
2684 KeysManager::clipEncrypt()
2685 {
2686     const QString cliptext(qApp->clipboard()->text(m_clipboardmode));
2687 
2688     if (cliptext.isEmpty()) {
2689         Q_ASSERT(m_trayicon != nullptr);
2690         m_trayicon->showMessage(QString(), i18n("Clipboard is empty."), QLatin1String( "kgpg" ));
2691         return;
2692     }
2693 
2694     QPointer<KgpgSelectPublicKeyDlg> dialog = new KgpgSelectPublicKeyDlg(this, imodel, QKeySequence(goToDefaultKey->shortcut()), true);
2695     if (dialog->exec() == QDialog::Accepted) {
2696         KGpgEncrypt::EncryptOptions encOptions = KGpgEncrypt::AsciiArmored;
2697         QStringList options;
2698 
2699         if (!dialog->getCustomOptions().isEmpty() && KGpgSettings::allowCustomEncryptionOptions())
2700             options = dialog->getCustomOptions().split(QLatin1Char(' '), Qt::SkipEmptyParts);
2701 
2702         if (dialog->getUntrusted())
2703             encOptions |= KGpgEncrypt::AllowUntrustedEncryption;
2704         if (dialog->getHideId())
2705             encOptions |= KGpgEncrypt::HideKeyId;
2706 
2707         if (KGpgSettings::pgpCompatibility())
2708             options.append(QLatin1String( "--pgp6" ));
2709 
2710         KGpgEncrypt *enc = new KGpgEncrypt(this, dialog->selectedKeys(), cliptext, encOptions, options);
2711         connect(enc, &KGpgEncrypt::done, this, &KeysManager::slotSetClip);
2712 
2713         m_trayicon->setStatus(KStatusNotifierItem::Active);
2714         enc->start();
2715     }
2716 
2717     delete dialog;
2718 }
2719 
2720 void
2721 KeysManager::slotSetClip(int result)
2722 {
2723     KGpgEncrypt *enc = qobject_cast<KGpgEncrypt *>(sender());
2724     Q_ASSERT(enc != nullptr);
2725     sender()->deleteLater();
2726 
2727     m_trayicon->setStatus(KStatusNotifierItem::Passive);
2728 
2729     if (result != KGpgTransaction::TS_OK)
2730         return;
2731 
2732     qApp->clipboard()->setText(enc->encryptedText().join(QLatin1String("\n")), m_clipboardmode);
2733 
2734     Q_ASSERT(m_trayicon != nullptr);
2735     m_trayicon->showMessage(QString(), i18n("Text successfully encrypted."), QLatin1String( "kgpg" ));
2736 }
2737 
2738 void
2739 KeysManager::slotOpenKeyUrl()
2740 {
2741     KGpgNode *cur = iview->selectedNode();
2742     if (cur == nullptr)
2743         return;
2744 
2745     QString id;
2746 
2747     switch (cur->getType()) {
2748     case ITYPE_PAIR:
2749     case ITYPE_PUBLIC: {
2750         id = cur->toKeyNode()->getFingerprint();
2751         break;
2752         }
2753     case ITYPE_GPAIR:
2754     case ITYPE_GPUBLIC: {
2755         id = cur->getId();
2756         break;
2757         }
2758     default:
2759         return;
2760     }
2761 
2762     const QStringList servers = KGpgSettings::infoServers();
2763     if (servers.isEmpty())
2764         return;
2765 
2766     QString url = servers.first();
2767     const QString idUC = id.toUpper();
2768     const QString idLC = id.toLower();
2769 
2770     url.replace(QLatin1String("$$ID8$$"), idUC.right(8));
2771     url.replace(QLatin1String("$$ID16$$"), idUC.right(16));
2772     url.replace(QLatin1String("$$FPR$$"), idUC);
2773     url.replace(QLatin1String("$$id8$$"), idLC.right(8));
2774     url.replace(QLatin1String("$$id16$$"), idLC.right(16));
2775     url.replace(QLatin1String("$$fpr$$"), idLC);
2776 
2777     auto *job = new KIO::OpenUrlJob(QUrl(url));
2778     job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
2779     job->start();
2780 }
2781 
2782 void
2783 KeysManager::clipDecrypt()
2784 {
2785     const QString cliptext(qApp->clipboard()->text(m_clipboardmode).trimmed());
2786 
2787     if (cliptext.isEmpty()) {
2788         Q_ASSERT(m_trayicon != nullptr);
2789         m_trayicon->showMessage(QString(), i18n("Clipboard is empty."), QLatin1String( "kgpg" ));
2790         return;
2791     }
2792 
2793     KgpgEditor *kgpgtxtedit = new KgpgEditor(this, imodel,  {});
2794     kgpgtxtedit->setAttribute(Qt::WA_DeleteOnClose);
2795     connect(this, &KeysManager::fontChanged, kgpgtxtedit, &KgpgEditor::slotSetFont);
2796     kgpgtxtedit->m_editor->setPlainText(cliptext);
2797     kgpgtxtedit->m_editor->slotDecode();
2798     kgpgtxtedit->show();
2799 }
2800 
2801 void
2802 KeysManager::clipSign()
2803 {
2804     QString cliptext = qApp->clipboard()->text(m_clipboardmode);
2805     if (cliptext.isEmpty()) {
2806         Q_ASSERT(m_trayicon != nullptr);
2807         m_trayicon->showMessage(QString(), i18n("Clipboard is empty."), QLatin1String( "kgpg" ));
2808         return;
2809     }
2810 
2811         KgpgEditor *kgpgtxtedit = new KgpgEditor(this, imodel, {});
2812     kgpgtxtedit->setAttribute(Qt::WA_DeleteOnClose);
2813     connect(kgpgtxtedit->m_editor, &KgpgTextEdit::verifyFinished, kgpgtxtedit, &KgpgEditor::closeWindow);
2814 
2815     kgpgtxtedit->m_editor->signVerifyText(cliptext);
2816     kgpgtxtedit->show();
2817 }