File indexing completed on 2022-08-04 16:24:08

0001 /*
0002     SPDX-FileCopyrightText: 2002 Jean-Baptiste Mardelle <bj@altern.org>
0003     SPDX-FileCopyrightText: 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2014, 2016, 2017 Rolf Eike Beer <kde@opensource.sf-tec.de>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "keyservers.h"
0008 
0009 #include "core/convert.h"
0010 #include "detailedconsole.h"
0011 #include "gpgproc.h"
0012 #include "kgpgsettings.h"
0013 #include "model/keylistproxymodel.h"
0014 #include "model/kgpgitemmodel.h"
0015 #include "transactions/kgpgimport.h"
0016 #include "transactions/kgpgkeyservergettransaction.h"
0017 #include "transactions/kgpgkeyserversearchtransaction.h"
0018 #include "transactions/kgpgsendkeys.h"
0019 
0020 #include <algorithm>
0021 
0022 #include <KLocalizedString>
0023 #include <KMessageBox>
0024 #include <QCursor>
0025 #include <QRegularExpression>
0026 
0027 KeyServer::KeyServer(QWidget *parent, KGpgItemModel *model, const bool autoclose)
0028     : QDialog(parent),
0029     m_dialogserver(nullptr),
0030     m_searchproc(nullptr),
0031     page(new keyServerWidget()),
0032     m_listpop(nullptr),
0033     m_itemmodel(new KeyListProxyModel(this, KeyListProxyModel::SingleColumnIdFirst))
0034 {
0035     setWindowTitle(i18n("Key Server"));
0036 
0037     m_autoclose = autoclose;
0038     m_resultmodel.setSortCaseSensitivity(Qt::CaseInsensitive);
0039     m_resultmodel.setDynamicSortFilter(true);
0040     m_resultmodel.setFilterKeyColumn(0);
0041 
0042     QVBoxLayout *mainLayout = new QVBoxLayout(this);
0043     setLayout(mainLayout);
0044     mainLayout->addWidget(page);
0045 
0046     const QStringList serverlist(getServerList());
0047     page->kCBexportks->addItems(serverlist);
0048     page->kCBimportks->addItems(serverlist);
0049     page->qLEimportid->setFocus();
0050     page->Buttonsearch->setDefault(true);
0051 
0052     connect(page->buttonBox, &QDialogButtonBox::rejected, this, &KeyServer::accept);
0053     connect(page->Buttonimport, &QPushButton::clicked, this, &KeyServer::slotImport);
0054     connect(page->Buttonsearch, &QPushButton::clicked, this, &KeyServer::slotSearch);
0055     connect(page->Buttonexport, &QPushButton::clicked, this, &KeyServer::slotPreExport);
0056     connect(page->buttonBox, &QDialogButtonBox::accepted, this, &KeyServer::slotOk);
0057     connect(page->cBproxyI, &QCheckBox::toggled, this, &KeyServer::slotEnableProxyI);
0058     connect(page->cBproxyE, &QCheckBox::toggled, this, &KeyServer::slotEnableProxyE);
0059     connect(page->qLEimportid, &QLineEdit::textChanged, this, &KeyServer::slotTextChanged);
0060 
0061     page->cBproxyI->setChecked(KGpgSettings::useProxy());
0062     page->cBproxyE->setChecked(KGpgSettings::useProxy());
0063 
0064     const QString httpproxy(QLatin1String( qgetenv("http_proxy") ));
0065     if (!httpproxy.isEmpty()) {
0066         page->cBproxyI->setEnabled(true);
0067         page->cBproxyE->setEnabled(true);
0068         page->kLEproxyI->setText(httpproxy);
0069         page->kLEproxyE->setText(httpproxy);
0070     }
0071 
0072     page->Buttonimport->setEnabled(!page->qLEimportid->text().isEmpty());
0073     page->Buttonsearch->setEnabled(!page->qLEimportid->text().isEmpty());
0074     setMinimumSize(sizeHint());
0075 
0076     m_itemmodel->setKeyModel(model);
0077     m_itemmodel->setTrustFilter(KgpgCore::TRUST_UNDEFINED);
0078     page->kCBexportkey->setModel(m_itemmodel);
0079 }
0080 
0081 KeyServer::~KeyServer()
0082 {
0083     delete page;
0084 }
0085 
0086 void KeyServer::slotImport()
0087 {
0088     if (page->kCBimportks->currentText().isEmpty())
0089         return;
0090 
0091     if (page->qLEimportid->text().isEmpty()) {
0092         KMessageBox::error(this, i18n("You must enter a search string."));
0093         return;
0094     }
0095 
0096     startImport(page->qLEimportid->text().simplified().split(QLatin1Char( ' ' )), page->kCBimportks->currentText(), page->kLEproxyI->text());
0097 }
0098 
0099 void KeyServer::startImport(const QStringList &keys, QString server, const QString &proxy)
0100 {
0101     if (server.isEmpty()) {
0102         const QStringList kservers = KeyServer::getServerList();
0103         if (kservers.isEmpty()) {
0104             KMessageBox::error(this, i18n("You need to configure keyservers before trying to download keys."),
0105                     i18n("No keyservers defined"));
0106             return;
0107         }
0108 
0109         server = kservers.first();
0110     }
0111 
0112     QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
0113 
0114     KGpgReceiveKeys *proc = new KGpgReceiveKeys(this, server, keys, true, proxy);
0115     connect(proc, &KGpgReceiveKeys::done, this, &KeyServer::slotDownloadKeysFinished);
0116 
0117     proc->start();
0118 }
0119 
0120 void KeyServer::slotDownloadKeysFinished(int resultcode)
0121 {
0122     QApplication::restoreOverrideCursor();
0123 
0124     KGpgKeyserverGetTransaction *t = qobject_cast<KGpgKeyserverGetTransaction *>(sender());
0125     const QStringList log(t->getLog());
0126 
0127     t->deleteLater();
0128 
0129     if (resultcode == KGpgTransaction::TS_USER_ABORTED) {
0130         Q_EMIT importFailed();
0131         return;
0132     }
0133 
0134     const QStringList keys(KGpgImport::getImportedIds(log));
0135     const QString resultmessage(KGpgImport::getImportMessage(log));
0136 
0137     if (!keys.empty())
0138         Q_EMIT importFinished(keys);
0139 
0140     (void) new KgpgDetailedInfo(this, resultmessage, log.join(QLatin1String("\n")),
0141             KGpgImport::getDetailedImportMessage(log).split(QLatin1Char( '\n' )),
0142             i18nc("Caption of message box", "Key Import Finished"));
0143 }
0144 
0145 void KeyServer::slotExport(const QStringList &keyIds)
0146 {
0147     if (page->kCBexportks->currentText().isEmpty())
0148         return;
0149 
0150     QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
0151 
0152     KGpgSendKeys *nk = new KGpgSendKeys(this, page->kCBexportks->currentText(), keyIds, expattr, true, page->kLEproxyI->text());
0153     connect(nk, &KGpgSendKeys::done, this, &KeyServer::slotUploadKeysFinished);
0154 
0155     nk->start();
0156 }
0157 
0158 void KeyServer::slotUploadKeysFinished(int resultcode)
0159 {
0160     KGpgSendKeys *nk = qobject_cast<KGpgSendKeys *>(sender());
0161     Q_ASSERT(nk != nullptr);
0162 
0163     const QStringList message(nk->getLog());
0164     nk->deleteLater();
0165 
0166     QApplication::restoreOverrideCursor();
0167 
0168     QString title;
0169     if (resultcode == KGpgTransaction::TS_OK)
0170         title = i18n("Upload to keyserver finished without errors");
0171     else
0172         title = i18n("Upload to keyserver failed");
0173     KMessageBox::informationList(this, title, message);
0174 }
0175 
0176 void KeyServer::slotSearch()
0177 {
0178     if (page->kCBimportks->currentText().isEmpty())
0179         return;
0180 
0181     if (page->qLEimportid->text().isEmpty()) {
0182         KMessageBox::error(this, i18n("You must enter a search string."));
0183         return;
0184     }
0185 
0186     page->Buttonsearch->setEnabled(false);
0187     if (m_searchproc)
0188         return;
0189 
0190     m_resultmodel.resetSourceModel();
0191     m_resultmodel.setFilterRegularExpression(QRegularExpression());
0192     m_resultmodel.setFilterByValidity(true);
0193 
0194     m_dialogserver = new QDialog(this);
0195     m_dialogserver->setWindowTitle(i18nc("Window title", "Import Key From Keyserver"));
0196 
0197     QVBoxLayout *mainLayout = new QVBoxLayout(m_dialogserver);
0198     m_dialogserver->setLayout(mainLayout);
0199 
0200     m_listpop = new searchRes(m_dialogserver);
0201     m_listpop->buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("&Import"));
0202     m_listpop->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
0203     m_listpop->kLVsearch->setModel(&m_resultmodel);
0204     m_listpop->kLVsearch->setColumnWidth(0, 180);
0205     m_listpop->validFilterCheckbox->setChecked(m_resultmodel.filterByValidity());
0206     m_listpop->statusText->setText(i18n("Connecting to the server..."));
0207 
0208     connect(m_listpop->filterEdit, &QLineEdit::textChanged, this, &KeyServer::slotSetFilterString);
0209     connect(m_listpop->kLVsearch->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KeyServer::transferKeyID);
0210     connect(m_listpop->validFilterCheckbox, &QCheckBox::toggled, &m_resultmodel, &KGpgSearchResultModel::setFilterByValidity);
0211     connect(m_listpop->validFilterCheckbox, &QCheckBox::toggled, this, &KeyServer::slotUpdateLabelOnFilterChange);
0212     connect(m_listpop->buttonBox, &QDialogButtonBox::accepted, this, &KeyServer::slotPreImport);
0213     connect(m_listpop->kLVsearch, &QTreeView::activated, m_dialogserver, &QDialog::accepted);
0214     connect(m_listpop->buttonBox, &QDialogButtonBox::rejected, this, &KeyServer::handleQuit);
0215     connect(m_listpop->qLEID, &QLineEdit::textChanged, this, [&] (const QString & text) {
0216         if (text.isEmpty())
0217             m_listpop->kLVsearch->selectionModel()->clearSelection();
0218         });
0219 
0220     m_listpop->kLVsearch->setSelectionMode(QAbstractItemView::ExtendedSelection);
0221 
0222     m_readmessage.clear();
0223 
0224     const QString keyserv(page->kCBimportks->currentText());
0225 
0226     bool useproxy = page->cBproxyI->isChecked();
0227     QString proxy;
0228     if (useproxy)
0229         proxy = page->kLEproxyI->text();
0230 
0231     m_searchproc = new KGpgKeyserverSearchTransaction(this, keyserv, page->qLEimportid->text().simplified(),
0232             true, proxy);
0233     connect(m_searchproc, &KGpgKeyserverSearchTransaction::done, this, &KeyServer::slotSearchResult);
0234     connect(m_searchproc, &KGpgKeyserverSearchTransaction::newKey, &m_resultmodel, &KGpgSearchResultModel::slotAddKey);
0235     m_searchproc->start();
0236 
0237     QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
0238     mainLayout->addWidget(m_listpop);
0239     m_listpop->setMinimumSize(m_listpop->sizeHint());
0240     m_dialogserver->exec();
0241 }
0242 
0243 void KeyServer::slotSearchResult(int result)
0244 {
0245     Q_ASSERT(sender() == m_searchproc);
0246     m_searchproc->deleteLater();
0247     m_searchproc = nullptr;
0248     page->Buttonsearch->setEnabled(true);
0249     QApplication::restoreOverrideCursor();
0250 
0251     if (result == KGpgTransaction::TS_USER_ABORTED) {
0252         delete m_dialogserver;
0253         m_dialogserver = nullptr;
0254         return;
0255     }
0256 
0257     m_listpop->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
0258 
0259     const int keys = m_resultmodel.sourceRowCount(QModelIndex());
0260     if (keys > 0) {
0261         m_listpop->kLVsearch->selectionModel()->setCurrentIndex(m_resultmodel.index(0, 0),
0262                 QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
0263     }
0264     slotUpdateLabelOnFilterChange();
0265 }
0266 
0267 void KeyServer::slotSetText(const QString &text)
0268 {
0269     page->qLEimportid->setText(text);
0270 }
0271 
0272 void KeyServer::slotTextChanged(const QString &text)
0273 {
0274     page->Buttonimport->setEnabled(!text.isEmpty());
0275     page->Buttonsearch->setEnabled(!text.isEmpty() && (m_searchproc == nullptr));
0276 }
0277 
0278 void KeyServer::slotSetExportAttribute(const QString &state)
0279 {
0280     if (!state.isEmpty())
0281         expattr = state;
0282     else
0283         expattr.clear();
0284 }
0285 
0286 void KeyServer::slotEnableProxyI(const bool on)
0287 {
0288     page->kLEproxyI->setEnabled(on);
0289 }
0290 
0291 void KeyServer::slotEnableProxyE(const bool on)
0292 {
0293     page->kLEproxyE->setEnabled(on);
0294 }
0295 
0296 void KeyServer::transferKeyID()
0297 {
0298     QSet<QString> ids;
0299 
0300     const QModelIndexList indexes = m_listpop->kLVsearch->selectionModel()->selectedIndexes();
0301     for (const QModelIndex &index : indexes)
0302         ids << m_resultmodel.idForIndex(index);
0303 
0304     const QStringList idlist(ids.values());
0305     m_listpop->qLEID->setText(idlist.join( QLatin1String( " " )));
0306 }
0307 
0308 void KeyServer::slotPreImport()
0309 {
0310     transferKeyID();
0311     if (m_listpop->qLEID->text().isEmpty()) {
0312         KMessageBox::error(this, i18n("You must choose a key."));
0313         return;
0314     }
0315     const QStringList keys = m_listpop->qLEID->text().simplified().split(QLatin1Char(' '));
0316     m_dialogserver->close();
0317     startImport(keys, page->kCBimportks->currentText(), page->kLEproxyI->text());
0318 }
0319 
0320 void KeyServer::slotPreExport()
0321 {
0322     slotExport(QStringList(page->kCBexportkey->currentText().section(QLatin1Char( ':' ), 0, 0)));
0323 }
0324 
0325 void KeyServer::slotOk()
0326 {
0327     accept();
0328 }
0329 
0330 QStringList KeyServer::getServerList()
0331 {
0332     QStringList serverList(KGpgSettings::keyServers()); // From kgpg config
0333     if (!serverList.isEmpty()) {
0334         const QString defaultServer(serverList.takeFirst());
0335         std::sort(serverList.begin(), serverList.end());
0336         serverList.prepend(defaultServer);
0337     }
0338 
0339     return serverList;
0340 }
0341 
0342 void KeyServer::handleQuit()
0343 {
0344     if (m_searchproc != nullptr) {
0345         QApplication::restoreOverrideCursor();
0346         disconnect(m_searchproc, nullptr, nullptr, nullptr);
0347         m_searchproc->deleteLater();
0348         m_searchproc = nullptr;
0349     }
0350     m_dialogserver->close();
0351     page->Buttonsearch->setEnabled(true);
0352 }
0353 
0354 void KeyServer::slotSetKeyserver(const QString &server)
0355 {
0356     page->kCBimportks->setCurrentIndex(page->kCBimportks->findText(server));
0357 }
0358 
0359 void KeyServer::slotSetFilterString(const QString &expression)
0360 {
0361     m_resultmodel.setFilterRegularExpression(QRegularExpression(expression, QRegularExpression::CaseInsensitiveOption));
0362     slotUpdateLabelOnFilterChange();
0363 }
0364 
0365 void KeyServer::slotUpdateLabelOnFilterChange()
0366 {
0367     const int keys = m_resultmodel.sourceRowCount(QModelIndex());
0368     const int keysShown = m_resultmodel.rowCount(QModelIndex());
0369     Q_ASSERT(keysShown <= keys);
0370 
0371     if (keys == 0) {
0372         m_listpop->statusText->setText(i18n("No matching keys found"));
0373     } else {
0374         if (keysShown == keys) {
0375             m_listpop->statusText->setText(i18np("Found 1 matching key", "Found %1 matching keys", keys));
0376         } else {
0377             if (keys == 1 && keysShown == 0) {
0378                 m_listpop->statusText->setText(i18n("Found 1 matching key (not shown)"));
0379             } else {
0380                 m_listpop->statusText->setText(i18n("Found %1 matching keys (%2 shown)", keys, keysShown));
0381             }
0382         }
0383     }
0384 }