/**
* This file is part of GPGFrontend.
*
* GPGFrontend 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 3 of the License, or
* (at your option) any later version.
*
* Foobar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see .
*
* The initial version of the source code is inherited from gpg4usb-team.
* Their source code version also complies with GNU General Public License.
*
* The source code version of this software was modified and released
* by Saturneric starting on May 12, 2021.
*
*/
#include "ui/KeyServerImportDialog.h"
#include
KeyServerImportDialog::KeyServerImportDialog(GpgME::GpgContext *ctx, KeyList *keyList, QWidget *parent)
: QDialog(parent), appPath(qApp->applicationDirPath()),
settings(appPath + "/conf/gpgfrontend.ini", QSettings::IniFormat), mCtx(ctx), mKeyList(keyList) {
// Buttons
closeButton = createButton(tr("&Close"), SLOT(close()));
importButton = createButton(tr("&Import"), SLOT(slotImport()));
searchButton = createButton(tr("&Search"), SLOT(slotSearch()));
// Line edit for search string
searchLabel = new QLabel(tr("Search string:"));
searchLineEdit = new QLineEdit();
// combobox for keyserverlist
keyServerLabel = new QLabel(tr("Keyserver:"));
keyServerComboBox = createComboBox();
// table containing the keys found
createKeysTable();
message = new QLabel;
icon = new QLabel;
// Layout for messagebox
auto *messageLayout = new QHBoxLayout;
messageLayout->addWidget(icon);
messageLayout->addWidget(message);
messageLayout->addStretch();
// Layout for import and close button
auto *buttonsLayout = new QHBoxLayout;
buttonsLayout->addStretch();
buttonsLayout->addWidget(importButton);
buttonsLayout->addWidget(closeButton);
auto *mainLayout = new QGridLayout;
mainLayout->addWidget(searchLabel, 1, 0);
mainLayout->addWidget(searchLineEdit, 1, 1);
mainLayout->addWidget(searchButton, 1, 2);
mainLayout->addWidget(keyServerLabel, 2, 0);
mainLayout->addWidget(keyServerComboBox, 2, 1);
mainLayout->addWidget(keysTable, 3, 0, 1, 3);
mainLayout->addLayout(messageLayout, 4, 0, 1, 3);
mainLayout->addLayout(buttonsLayout, 5, 0, 1, 3);
this->setLayout(mainLayout);
this->setWindowTitle(tr("Import Keys from Keyserver"));
this->resize(700, 300);
this->setModal(true);
}
QPushButton *KeyServerImportDialog::createButton(const QString &text, const char *member) {
auto *button = new QPushButton(text);
connect(button, SIGNAL(clicked()), this, member);
return button;
}
QComboBox *KeyServerImportDialog::createComboBox() {
auto *comboBox = new QComboBox;
comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
// Read keylist from ini-file and fill it into combobox
comboBox->addItems(settings.value("keyserver/keyServerList").toStringList());
// set default keyserver in combobox
QString keyserver = settings.value("keyserver/defaultKeyServer").toString();
comboBox->setCurrentIndex(comboBox->findText(keyserver));
return comboBox;
}
void KeyServerImportDialog::createKeysTable() {
keysTable = new QTableWidget();
keysTable->setColumnCount(4);
// always a whole row is marked
keysTable->setSelectionBehavior(QAbstractItemView::SelectRows);
keysTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
// Make just one row selectable
keysTable->setSelectionMode(QAbstractItemView::SingleSelection);
QStringList labels;
labels << tr("UID") << tr("Creation date") << tr("KeyID") << tr("Tag");
keysTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
keysTable->setHorizontalHeaderLabels(labels);
keysTable->verticalHeader()->hide();
connect(keysTable, SIGNAL(cellActivated(int, int)),
this, SLOT(slotImport()));
}
void KeyServerImportDialog::setMessage(const QString &text, bool error) {
message->setText(text);
if (error) {
QIcon undoicon = QIcon::fromTheme("dialog-error");
QPixmap pixmap = undoicon.pixmap(QSize(32, 32), QIcon::Normal, QIcon::On);
icon->setPixmap(pixmap);
} else {
QIcon undoicon = QIcon::fromTheme("dialog-information");
QPixmap pixmap = undoicon.pixmap(QSize(32, 32), QIcon::Normal, QIcon::On);
icon->setPixmap(pixmap);
}
}
void KeyServerImportDialog::slotSearch() {
QUrl urlFromRemote = keyServerComboBox->currentText() + "/pks/lookup?search=" + searchLineEdit->text() +
"&op=index&options=mr";
qnam = new QNetworkAccessManager(this);
QNetworkReply *reply = qnam->get(QNetworkRequest(urlFromRemote));
connect(reply, SIGNAL(finished()),
this, SLOT(slotSearchFinished()));
}
void KeyServerImportDialog::slotSearchFinished() {
auto *reply = qobject_cast(sender());
keysTable->clearContents();
keysTable->setRowCount(0);
QString firstLine = QString(reply->readLine(1024));
QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (reply->error()) {
setMessage(tr("Couldn't contact keyserver!"), true);
//setMessage(reply->error());
qDebug() << reply->error();
}
if (firstLine.contains("Error")) {
QString text = QString(reply->readLine(1024));
if (text.contains("Too many responses")) {
setMessage(tr("Too many responses from keyserver!"), true);
} else if (text.contains("No keys found")) {
// if string looks like hex string, search again with 0x prepended
QRegExp rx("[0-9A-Fa-f]*");
QString query = searchLineEdit->text();
if (rx.exactMatch(query)) {
setMessage(tr("No keys found, input may be kexId, retrying search with 0x."), true);
searchLineEdit->setText(query.prepend("0x"));
this->slotSearch();
} else {
setMessage(tr("No keys found containing the search string!"), true);
}
} else if (text.contains("Insufficiently specific words")) {
setMessage(tr("Insufficiently specific search string!"), true);
} else {
setMessage(text, true);
}
} else {
int row = 0;
bool strikeout = false;
while (reply->canReadLine()) {
auto line_buff = reply->readLine().trimmed();
QString decoded = QString::fromUtf8(line_buff.constData(), line_buff.size());
QStringList line = decoded.split(":");
//TODO: have a look at two following pub lines
if (line[0] == "pub") {
strikeout = false;
QString flags = line[line.size() - 1];
keysTable->setRowCount(row + 1);
// flags can be "d" for disabled, "r" for revoked
// or "e" for expired
if (flags.contains("r") or flags.contains("d") or flags.contains("e")) {
strikeout = true;
if (flags.contains("e")) {
keysTable->setItem(row, 3, new QTableWidgetItem(QString("expired")));
}
if (flags.contains("r")) {
keysTable->setItem(row, 3, new QTableWidgetItem(QString(tr("revoked"))));
}
if (flags.contains("d")) {
keysTable->setItem(row, 3, new QTableWidgetItem(QString(tr("disabled"))));
}
}
QStringList line2 = QString(reply->readLine()).split(":");
auto *uid = new QTableWidgetItem();
if (line2.size() > 1) {
uid->setText(line2[1]);
keysTable->setItem(row, 0, uid);
}
auto *creation_date = new QTableWidgetItem(
QDateTime::fromTime_t(line[4].toInt()).toString("dd. MMM. yyyy"));
keysTable->setItem(row, 1, creation_date);
auto *keyid = new QTableWidgetItem(line[1]);
keysTable->setItem(row, 2, keyid);
if (strikeout) {
QFont strike = uid->font();
strike.setStrikeOut(true);
uid->setFont(strike);
creation_date->setFont(strike);
keyid->setFont(strike);
}
row++;
} else {
if (line[0] == "uid") {
QStringList l;
int height = keysTable->rowHeight(row - 1);
keysTable->setRowHeight(row - 1, height + 16);
QString tmp = keysTable->item(row - 1, 0)->text();
tmp.append(QString("\n") + line[1]);
auto *tmp1 = new QTableWidgetItem(tmp);
keysTable->setItem(row - 1, 0, tmp1);
if (strikeout) {
QFont strike = tmp1->font();
strike.setStrikeOut(true);
tmp1->setFont(strike);
}
}
}
setMessage(tr("%1 keys found. Doubleclick a key to import it.").arg(row), false);
}
keysTable->resizeColumnsToContents();
}
reply->deleteLater();
}
void KeyServerImportDialog::slotImport() {
if (keysTable->currentRow() > -1) {
QString keyid = keysTable->item(keysTable->currentRow(), 2)->text();
slotImport(QStringList(keyid), keyServerComboBox->currentText());
}
}
void KeyServerImportDialog::slotImport(QStringList keyIds) {
QString keyserver = settings.value("keyserver/defaultKeyServer").toString();;
slotImport(std::move(keyIds), QUrl(keyserver));
}
void KeyServerImportDialog::slotImport(QStringList keyIds, const QUrl &keyServerUrl) {
foreach(QString keyId, keyIds) {
QUrl reqUrl(
keyServerUrl.scheme() + "://" + keyServerUrl.host() + ":11371/pks/lookup?op=get&search=0x" + keyId +
"&options=mr");
//qDebug() << "req to " << reqUrl;
qnam = new QNetworkAccessManager(this);
QNetworkReply *reply = qnam->get(QNetworkRequest(reqUrl));
connect(reply, SIGNAL(finished()),
this, SLOT(slotImportFinished()));
}
}
void KeyServerImportDialog::slotImportFinished() {
auto *reply = qobject_cast(sender());
QByteArray key = reply->readAll();
QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (reply->error()) {
setMessage(tr("Error while contacting keyserver!"), true);
return;
}
this->importKeys(key.constData());
setMessage(tr("Key imported"), false);
// Add keyserver to list in config-file, if it isn't contained
QStringList keyServerList = settings.value("keyserver/keyServerList").toStringList();
if (!keyServerList.contains(keyServerComboBox->currentText())) {
keyServerList.append(keyServerComboBox->currentText());
settings.setValue("keyserver/keyServerList", keyServerList);
}
reply->deleteLater();
reply = nullptr;
}
void KeyServerImportDialog::importKeys(QByteArray inBuffer) {
GpgImportInformation result = mCtx->importKey(std::move(inBuffer));
new KeyImportDetailDialog(mCtx, result, this);
}