keyservers.cpp 12.4 KB
Newer Older
1 2
/*
 * Copyright (C) 2002 Jean-Baptiste Mardelle <bj@altern.org>
3 4
 * Copyright (C) 2006,2007,2008,2009,2010,2012,2013,2014,2016,2017
 *               Rolf Eike Beer <kde@opensource.sf-tec.de>
5
 */
6 7 8 9 10 11 12 13 14

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
15

Urs Wolfer's avatar
Urs Wolfer committed
16 17
#include "keyservers.h"

Rolf Eike Beer's avatar
Rolf Eike Beer committed
18 19 20 21 22 23 24 25 26 27 28
#include "core/convert.h"
#include "detailedconsole.h"
#include "gpgproc.h"
#include "kgpgsettings.h"
#include "model/keylistproxymodel.h"
#include "model/kgpgitemmodel.h"
#include "model/kgpgsearchresultmodel.h"
#include "transactions/kgpgimport.h"
#include "transactions/kgpgkeyservergettransaction.h"
#include "transactions/kgpgkeyserversearchtransaction.h"
#include "transactions/kgpgsendkeys.h"
29

30 31
#include <algorithm>

Andrius Štikonas's avatar
Andrius Štikonas committed
32
#include <KLocalizedString>
Rolf Eike Beer's avatar
Rolf Eike Beer committed
33 34
#include <KMessageBox>
#include <QCursor>
35
#include <QRegularExpression>
Urs Wolfer's avatar
Urs Wolfer committed
36

37
KeyServer::KeyServer(QWidget *parent, KGpgItemModel *model, const bool autoclose)
38
	: QDialog(parent),
Rolf Eike Beer's avatar
Rolf Eike Beer committed
39 40
	m_dialogserver(nullptr),
	m_searchproc(nullptr),
41
	page(new keyServerWidget()),
Rolf Eike Beer's avatar
Rolf Eike Beer committed
42
	m_listpop(nullptr),
43
	m_itemmodel(new KeyListProxyModel(this, KeyListProxyModel::SingleColumnIdFirst))
44
{
45
	setWindowTitle(i18n("Key Server"));
46 47

	m_autoclose = autoclose;
48 49 50
	m_resultmodel.setSortCaseSensitivity(Qt::CaseInsensitive);
	m_resultmodel.setDynamicSortFilter(true);
	m_resultmodel.setFilterKeyColumn(0);
51

52 53 54
	QVBoxLayout *mainLayout = new QVBoxLayout(this);
	setLayout(mainLayout);
	mainLayout->addWidget(page);
55 56 57 58

	const QStringList serverlist(getServerList());
	page->kCBexportks->addItems(serverlist);
	page->kCBimportks->addItems(serverlist);
59
	page->qLEimportid->setFocus();
60
	page->Buttonsearch->setDefault(true);
61 62 63 64 65 66 67 68 69

	connect(page->buttonBox, &QDialogButtonBox::rejected, this, &KeyServer::accept);
	connect(page->Buttonimport, &QPushButton::clicked, this, &KeyServer::slotImport);
	connect(page->Buttonsearch, &QPushButton::clicked, this, &KeyServer::slotSearch);
	connect(page->Buttonexport, &QPushButton::clicked, this, &KeyServer::slotPreExport);
	connect(page->buttonBox, &QDialogButtonBox::accepted, this, &KeyServer::slotOk);
	connect(page->cBproxyI, &QCheckBox::toggled, this, &KeyServer::slotEnableProxyI);
	connect(page->cBproxyE, &QCheckBox::toggled, this, &KeyServer::slotEnableProxyE);
	connect(page->qLEimportid, &QLineEdit::textChanged, this, &KeyServer::slotTextChanged);
70 71 72 73

	page->cBproxyI->setChecked(KGpgSettings::useProxy());
	page->cBproxyE->setChecked(KGpgSettings::useProxy());

74
	const QString httpproxy(QLatin1String( qgetenv("http_proxy") ));
75 76 77 78 79 80 81
	if (!httpproxy.isEmpty()) {
		page->cBproxyI->setEnabled(true);
		page->cBproxyE->setEnabled(true);
		page->kLEproxyI->setText(httpproxy);
		page->kLEproxyE->setText(httpproxy);
	}

82 83
	page->Buttonimport->setEnabled(!page->qLEimportid->text().isEmpty());
	page->Buttonsearch->setEnabled(!page->qLEimportid->text().isEmpty());
84
	setMinimumSize(sizeHint());
85

86 87 88
	m_itemmodel->setKeyModel(model);
	m_itemmodel->setTrustFilter(KgpgCore::TRUST_UNDEFINED);
	page->kCBexportkey->setModel(m_itemmodel);
89 90
}

91 92 93 94 95
KeyServer::~KeyServer()
{
	delete page;
}

Jimmy Gilles's avatar
Jimmy Gilles committed
96
void KeyServer::slotImport()
97
{
98 99
	if (page->kCBimportks->currentText().isEmpty())
		return;
100

101
	if (page->qLEimportid->text().isEmpty()) {
102 103 104
		KMessageBox::sorry(this, i18n("You must enter a search string."));
		return;
	}
105

106
	startImport(page->qLEimportid->text().simplified().split(QLatin1Char( ' ' )), page->kCBimportks->currentText(), page->kLEproxyI->text());
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
}

void KeyServer::startImport(const QStringList &keys, QString server, const QString &proxy)
{
	if (server.isEmpty()) {
		const QStringList kservers = KeyServer::getServerList();
		if (kservers.isEmpty()) {
			KMessageBox::sorry(this, i18n("You need to configure keyservers before trying to download keys."),
					i18n("No keyservers defined"));
			return;
		}

		server = kservers.first();
	}

122
	QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
123

124
	KGpgReceiveKeys *proc = new KGpgReceiveKeys(this, server, keys, true, proxy);
125
	connect(proc, &KGpgReceiveKeys::done, this, &KeyServer::slotDownloadKeysFinished);
126

127
	proc->start();
128
}
129

130
void KeyServer::slotDownloadKeysFinished(int resultcode)
131
{
132 133
	QApplication::restoreOverrideCursor();

134 135 136 137 138
	KGpgKeyserverGetTransaction *t = qobject_cast<KGpgKeyserverGetTransaction *>(sender());
	const QStringList log(t->getLog());

	t->deleteLater();

139 140
	if (resultcode == KGpgTransaction::TS_USER_ABORTED) {
		emit importFailed();
141
		return;
142
	}
143

144
	const QStringList keys(KGpgImport::getImportedIds(log));
Rolf Eike Beer's avatar
Rolf Eike Beer committed
145
	const QString resultmessage(KGpgImport::getImportMessage(log));
146 147 148 149

	if (!keys.empty())
		emit importFinished(keys);

150 151
	(void) new KgpgDetailedInfo(this, resultmessage, log.join(QLatin1String("\n")),
			KGpgImport::getDetailedImportMessage(log).split(QLatin1Char( '\n' )),
152
			i18nc("Caption of message box", "Key Import Finished"));
153
}
154

Rolf Eike Beer's avatar
Rolf Eike Beer committed
155
void KeyServer::slotExport(const QStringList &keyIds)
156
{
157 158
	if (page->kCBexportks->currentText().isEmpty())
		return;
159

160
	QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
161

162
	KGpgSendKeys *nk = new KGpgSendKeys(this, page->kCBexportks->currentText(), keyIds, expattr, true, page->kLEproxyI->text());
163
	connect(nk, &KGpgSendKeys::done, this, &KeyServer::slotUploadKeysFinished);
164

165
	nk->start();
166 167
}

168
void KeyServer::slotUploadKeysFinished(int resultcode)
169
{
170
	KGpgSendKeys *nk = qobject_cast<KGpgSendKeys *>(sender());
Rolf Eike Beer's avatar
Rolf Eike Beer committed
171
	Q_ASSERT(nk != nullptr);
172 173 174

	const QStringList message(nk->getLog());
	nk->deleteLater();
175

176
	QApplication::restoreOverrideCursor();
177

178 179 180 181 182 183
	QString title;
	if (resultcode == KGpgTransaction::TS_OK)
		title = i18n("Upload to keyserver finished without errors");
	else
		title = i18n("Upload to keyserver failed");
	KMessageBox::informationList(this, title, message);
184 185
}

Jimmy Gilles's avatar
Jimmy Gilles committed
186
void KeyServer::slotSearch()
187
{
188 189
	if (page->kCBimportks->currentText().isEmpty())
		return;
190

191
	if (page->qLEimportid->text().isEmpty()) {
192 193 194
		KMessageBox::sorry(this, i18n("You must enter a search string."));
		return;
	}
195

196 197 198 199
	page->Buttonsearch->setEnabled(false);
	if (m_searchproc)
		return;

200
	m_resultmodel.resetSourceModel();
201
	m_resultmodel.setFilterRegularExpression(QRegularExpression());
202
	m_resultmodel.setFilterByValidity(true);
203

Rolf Eike Beer's avatar
Rolf Eike Beer committed
204
	m_dialogserver = new QDialog(this);
205
	m_dialogserver->setWindowTitle(i18n("Import Key From Keyserver"));
206

207
	QVBoxLayout *mainLayout = new QVBoxLayout(m_dialogserver);
208 209
	m_dialogserver->setLayout(mainLayout);

210
	m_listpop = new searchRes(m_dialogserver);
211 212
	m_listpop->buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("&Import"));
	m_listpop->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
213
	m_listpop->kLVsearch->setModel(&m_resultmodel);
214
	m_listpop->kLVsearch->setColumnWidth(0, 180);
215
	m_listpop->validFilterCheckbox->setChecked(m_resultmodel.filterByValidity());
216
	m_listpop->statusText->setText(i18n("Connecting to the server..."));
217

218 219
	connect(m_listpop->filterEdit, &QLineEdit::textChanged, this, &KeyServer::slotSetFilterString);
	connect(m_listpop->kLVsearch->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KeyServer::transferKeyID);
220 221
	connect(m_listpop->validFilterCheckbox, &QCheckBox::toggled, &m_resultmodel, &KGpgSearchResultModel::setFilterByValidity);
	connect(m_listpop->validFilterCheckbox, &QCheckBox::toggled, this, &KeyServer::slotUpdateLabelOnFilterChange);
222 223 224
	connect(m_listpop->buttonBox, &QDialogButtonBox::accepted, this, &KeyServer::slotPreImport);
	connect(m_listpop->kLVsearch, &QTreeView::activated, m_dialogserver, &QDialog::accepted);
	connect(m_listpop->buttonBox, &QDialogButtonBox::rejected, this, &KeyServer::handleQuit);
Andrius Štikonas's avatar
Andrius Štikonas committed
225 226 227 228
	connect(m_listpop->qLEID, &QLineEdit::textChanged, this, [&] (const QString & text) {
		if (text.isEmpty())
			m_listpop->kLVsearch->selectionModel()->clearSelection();
		});
229

230
	m_listpop->kLVsearch->setSelectionMode(QAbstractItemView::ExtendedSelection);
231

232
	m_readmessage.clear();
Rolf Eike Beer's avatar
Rolf Eike Beer committed
233

234
	const QString keyserv(page->kCBimportks->currentText());
235

236
	bool useproxy = page->cBproxyI->isChecked();
237 238 239 240
	QString proxy;
	if (useproxy)
		proxy = page->kLEproxyI->text();

241
	m_searchproc = new KGpgKeyserverSearchTransaction(this, keyserv, page->qLEimportid->text().simplified(),
242
			true, proxy);
243
	connect(m_searchproc, &KGpgKeyserverSearchTransaction::done, this, &KeyServer::slotSearchResult);
244
	connect(m_searchproc, &KGpgKeyserverSearchTransaction::newKey, &m_resultmodel, &KGpgSearchResultModel::slotAddKey);
245
	m_searchproc->start();
246

247
	QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
248
	mainLayout->addWidget(m_listpop);
249 250
	m_listpop->setMinimumSize(m_listpop->sizeHint());
	m_dialogserver->exec();
251 252
}

253
void KeyServer::slotSearchResult(int result)
254
{
255 256
	Q_ASSERT(sender() == m_searchproc);
	m_searchproc->deleteLater();
Rolf Eike Beer's avatar
Rolf Eike Beer committed
257
	m_searchproc = nullptr;
258
	page->Buttonsearch->setEnabled(true);
259
	QApplication::restoreOverrideCursor();
260

261
	if (result == KGpgTransaction::TS_USER_ABORTED) {
262
		delete m_dialogserver;
Rolf Eike Beer's avatar
Rolf Eike Beer committed
263
		m_dialogserver = nullptr;
264
		return;
265
	}
266

267
	m_listpop->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
268

269
	const int keys = m_resultmodel.sourceRowCount(QModelIndex());
270
	if (keys > 0) {
271
		m_listpop->kLVsearch->selectionModel()->setCurrentIndex(m_resultmodel.index(0, 0),
272 273
				QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
	}
274
	slotUpdateLabelOnFilterChange();
275 276
}

Jimmy Gilles's avatar
Jimmy Gilles committed
277
void KeyServer::slotSetText(const QString &text)
278
{
279
	page->qLEimportid->setText(text);
280
}
281

Jimmy Gilles's avatar
Jimmy Gilles committed
282
void KeyServer::slotTextChanged(const QString &text)
283
{
284
	page->Buttonimport->setEnabled(!text.isEmpty());
Rolf Eike Beer's avatar
Rolf Eike Beer committed
285
	page->Buttonsearch->setEnabled(!text.isEmpty() && (m_searchproc == nullptr));
286 287
}

288
void KeyServer::slotSetExportAttribute(const QString &state)
289
{
290
	if (!state.isEmpty())
291 292 293
		expattr = state;
	else
		expattr.clear();
294 295
}

296
void KeyServer::slotEnableProxyI(const bool on)
297
{
298
	page->kLEproxyI->setEnabled(on);
299 300
}

301
void KeyServer::slotEnableProxyE(const bool on)
302
{
303
	page->kLEproxyE->setEnabled(on);
304
}
305

Jimmy Gilles's avatar
Jimmy Gilles committed
306
void KeyServer::transferKeyID()
307
{
308
	QSet<QString> ids;
309

310 311
	const QModelIndexList indexes = m_listpop->kLVsearch->selectionModel()->selectedIndexes();
	for (const QModelIndex &index : indexes)
312
		ids << m_resultmodel.idForIndex(index);
313

Rolf Eike Beer's avatar
Rolf Eike Beer committed
314
	const QStringList idlist(ids.values());
315
	m_listpop->qLEID->setText(idlist.join( QLatin1String( " " )));
316 317
}

Jimmy Gilles's avatar
Jimmy Gilles committed
318
void KeyServer::slotPreImport()
319
{
320
	transferKeyID();
321
	if (m_listpop->qLEID->text().isEmpty()) {
322 323 324
		KMessageBox::sorry(this, i18n("You must choose a key."));
		return;
	}
325
	const QStringList keys = m_listpop->qLEID->text().simplified().split(QLatin1Char(' '));
326
	m_dialogserver->close();
327
	startImport(keys, page->kCBimportks->currentText(), page->kLEproxyI->text());
328 329
}

Jimmy Gilles's avatar
Jimmy Gilles committed
330
void KeyServer::slotPreExport()
331
{
332
	slotExport(QStringList(page->kCBexportkey->currentText().section(QLatin1Char( ':' ), 0, 0)));
333 334
}

Jimmy Gilles's avatar
Jimmy Gilles committed
335
void KeyServer::slotOk()
336
{
337
	accept();
338 339
}

340
QStringList KeyServer::getServerList()
341
{
342
	QStringList serverList(KGpgSettings::keyServers()); // From kgpg config
343
	if (!serverList.isEmpty()) {
344
		const QString defaultServer(serverList.takeFirst());
345
		std::sort(serverList.begin(), serverList.end());
346
		serverList.prepend(defaultServer);
347 348 349
	}

	return serverList;
350 351
}

Jimmy Gilles's avatar
Jimmy Gilles committed
352
void KeyServer::handleQuit()
353
{
Rolf Eike Beer's avatar
Rolf Eike Beer committed
354
	if (m_searchproc != nullptr) {
355
		QApplication::restoreOverrideCursor();
Laurent Montel's avatar
Laurent Montel committed
356
        disconnect(m_searchproc, nullptr, nullptr, nullptr);
357
		m_searchproc->deleteLater();
Rolf Eike Beer's avatar
Rolf Eike Beer committed
358
		m_searchproc = nullptr;
359
	}
360 361
	m_dialogserver->close();
	page->Buttonsearch->setEnabled(true);
362
}
363

Rolf Eike Beer's avatar
Rolf Eike Beer committed
364 365
void KeyServer::slotSetKeyserver(const QString &server)
{
366
	page->kCBimportks->setCurrentIndex(page->kCBimportks->findText(server));
Rolf Eike Beer's avatar
Rolf Eike Beer committed
367 368
}

369
void KeyServer::slotSetFilterString(const QString &expression)
370
{
371
	m_resultmodel.setFilterRegularExpression(QRegularExpression(expression, QRegularExpression::CaseInsensitiveOption));
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
	slotUpdateLabelOnFilterChange();
}

void KeyServer::slotUpdateLabelOnFilterChange()
{
	const int keys = m_resultmodel.sourceRowCount(QModelIndex());
	const int keysShown = m_resultmodel.rowCount(QModelIndex());
	Q_ASSERT(keysShown <= keys);

	if (keys == 0) {
		m_listpop->statusText->setText(i18n("No matching keys found"));
	} else {
		if (keysShown == keys) {
			m_listpop->statusText->setText(i18np("Found 1 matching key", "Found %1 matching keys", keys));
		} else {
			if (keys == 1 && keysShown == 0) {
				m_listpop->statusText->setText(i18n("Found 1 matching key (not shown)"));
			} else {
				m_listpop->statusText->setText(i18n("Found %1 matching keys (%2 shown)", keys, keysShown));
			}
		}
	}
394
}