File indexing completed on 2024-12-01 07:38:57

0001 /***************************************************************************
0002  *   Copyright (C) 2009 Matthias Fuchs <mat69@gmx.net>                     *
0003  *                                                                         *
0004  *   This program is free software; you can redistribute it and/or modify  *
0005  *   it under the terms of the GNU General Public License as published by  *
0006  *   the Free Software Foundation; either version 2 of the License, or     *
0007  *   (at your option) any later version.                                   *
0008  *                                                                         *
0009  *   This program is distributed in the hope that it will be useful,       *
0010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0012  *   GNU General Public License for more details.                          *
0013  *                                                                         *
0014  *   You should have received a copy of the GNU General Public License     *
0015  *   along with this program; if not, write to the                         *
0016  *   Free Software Foundation, Inc.,                                       *
0017  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
0018  ***************************************************************************/
0019 
0020 #include "signaturedlg.h"
0021 
0022 #include "core/filemodel.h"
0023 #include "core/kget.h"
0024 #include "core/signature.h"
0025 #include "core/transferhandler.h"
0026 
0027 #include "kget_debug.h"
0028 #include <QDebug>
0029 
0030 #ifdef HAVE_QGPGME
0031 #include <gpgme++/context.h>
0032 #include <gpgme++/key.h>
0033 #endif
0034 
0035 #include <QLayoutItem>
0036 #include <QStyle>
0037 
0038 #include <KLocalizedString>
0039 #include <QFileDialog>
0040 
0041 SignatureDlg::SignatureDlg(TransferHandler *transfer, const QUrl &dest, QWidget *parent, Qt::WindowFlags flags)
0042     : KGetSaveSizeDialog("SignatureDlg", parent, flags)
0043     , m_signature(transfer->signature(dest))
0044     , m_fileModel(transfer->fileModel())
0045 {
0046     setWindowTitle(i18nc("Signature here is meant in cryptographic terms, so the signature of a file.", "Signature of %1.", dest.fileName()));
0047 
0048     ui.setupUi(this);
0049     ui.loadSignature->setIcon(QIcon::fromTheme("document-open"));
0050     ui.verify->setIcon(QIcon::fromTheme("document-encrypt"));
0051 
0052     ui.information->setCloseButtonVisible(false);
0053     ui.information->setWordWrap(true);
0054     if (m_signature) {
0055         connect(ui.loadSignature, &QPushButton::clicked, this, &SignatureDlg::loadSignatureClicked);
0056         connect(ui.verify, &QPushButton::clicked, this, &SignatureDlg::verifyClicked);
0057         connect(ui.signature, &QTextEdit::textChanged, this, &SignatureDlg::textChanged);
0058         connect(m_signature, &Signature::verified, this, &SignatureDlg::updateData);
0059 
0060         if (m_fileModel) {
0061             m_file = m_fileModel->index(dest, FileItem::File);
0062             connect(m_fileModel, &FileModel::fileFinished, this, &SignatureDlg::fileFinished);
0063         }
0064 
0065         updateData();
0066         updateButtons();
0067     } else {
0068         ui.information->setMessageType(KMessageWidget::Warning);
0069         ui.information->setText(i18n("This option is not supported for the current transfer."));
0070         ui.sigGroup->hide();
0071         ui.keyGroup->hide();
0072     }
0073     connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0074     connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0075 }
0076 
0077 void SignatureDlg::fileFinished(const QUrl &file)
0078 {
0079     if (m_fileModel && (m_fileModel->getUrl(m_file) == file)) {
0080         updateButtons();
0081     }
0082 }
0083 
0084 void SignatureDlg::textChanged()
0085 {
0086     if (m_signature) {
0087         m_signature->setAsciiDetachedSignature(ui.signature->toPlainText());
0088 
0089         clearData();
0090         updateButtons();
0091     }
0092 }
0093 
0094 void SignatureDlg::loadSignatureClicked()
0095 {
0096     const QUrl url = QFileDialog::getOpenFileUrl(this,
0097                                                  i18n("Load Signature File"),
0098                                                  QUrl::fromLocalFile(KGet::generalDestDir()),
0099                                                  "*.asc|" + i18n("Detached OpenPGP ASCII signature (*.asc)") + '\n' + "*.sig|"
0100                                                      + i18n("Detached OpenPGP binary signature (*.sig)"));
0101     if (url.isEmpty()) {
0102         return;
0103     }
0104 
0105     const bool isAsciiSig = url.fileName().endsWith(QLatin1String(".asc"));
0106     clearData();
0107     handleWidgets(isAsciiSig);
0108     ui.signature->clear();
0109 
0110     QFile file(url.path());
0111     if (!file.open(QIODevice::ReadOnly)) {
0112         qCWarning(KGET_DEBUG) << "Could not open file" << url;
0113         return;
0114     }
0115     if (file.size() > 1 * 1024) {
0116         qCWarning(KGET_DEBUG) << "File is larger than 1 KiB, which is not supported.";
0117         return;
0118     }
0119 
0120     const QByteArray data = file.readAll();
0121     if (isAsciiSig) {
0122         ui.signature->setText(data);
0123     } else if (m_signature) {
0124         m_signature->setSignature(data, Signature::BinaryDetached);
0125         clearData();
0126         updateButtons();
0127     }
0128 }
0129 
0130 void SignatureDlg::updateButtons()
0131 {
0132     bool enableVerify = m_signature && m_signature->isVerifyable();
0133     if (!m_fileModel || !m_fileModel->downloadFinished(m_fileModel->getUrl(m_file))) {
0134         enableVerify = false;
0135     }
0136     ui.verify->setEnabled(enableVerify);
0137 }
0138 
0139 void SignatureDlg::updateData()
0140 {
0141     if (!m_signature) {
0142         return;
0143     }
0144 
0145     const QString fingerprintString = m_signature->fingerprint();
0146     const QByteArray signature = m_signature->signature();
0147 
0148     QStringList information;
0149 
0150     bool problem = false;
0151     bool error = false;
0152     if (signature.isEmpty()) {
0153         information << i18n("You need to define a signature.");
0154         problem = true;
0155     }
0156     if (fingerprintString.isEmpty()) {
0157         information << i18n("No fingerprint could be found, check if the signature is correct or verify the download."); // TODO get fingerprint from signature!
0158         problem = true;
0159     }
0160 
0161     ui.fingerprint->setText(fingerprintString);
0162 
0163     const bool isAsciiSig = (m_signature->type() != Signature::BinaryDetached);
0164     handleWidgets(isAsciiSig);
0165     if (isAsciiSig) {
0166         ui.signature->blockSignals(true);
0167         ui.signature->setText(signature);
0168         ui.signature->blockSignals(false);
0169     }
0170 
0171     ui.keyGroup->setVisible(!signature.isEmpty());
0172 
0173     const int iconSize = style()->pixelMetric(QStyle::PixelMetric::PM_SmallIconSize);
0174 
0175 #ifdef HAVE_QGPGME
0176     if (!fingerprintString.isEmpty()) {
0177         GpgME::initializeLibrary();
0178         GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP);
0179         QScopedPointer<GpgME::Context> context(GpgME::Context::createForProtocol(GpgME::OpenPGP));
0180         if (err) {
0181             qCDebug(KGET_DEBUG) << "OpenPGP not supported!";
0182         } else if (!context.data()) {
0183             qCDebug(KGET_DEBUG) << "Could not create context.";
0184         } else {
0185             QByteArray fingerprint = fingerprintString.toLatin1();
0186             const GpgME::Key key = context->key(fingerprint.constData(), err);
0187             if (err || key.isNull() || !key.numUserIDs() || !key.numSubkeys()) {
0188                 qCDebug(KGET_DEBUG) << "There was an error while loading the key:" << err;
0189             } else {
0190                 static const QStringList OWNERTRUST = QStringList()
0191                     << i18nc("trust level", "Unknown") << i18nc("trust level", "Undefined") << i18nc("trust level", "Never") << i18nc("trust level", "Marginal")
0192                     << i18nc("trust level", "Full") << i18nc("trust level", "Ultimate");
0193 
0194                 if (key.isRevoked()) {
0195                     information << i18n("The key has been revoked.");
0196                     problem = true;
0197                 }
0198                 if (key.isDisabled()) {
0199                     information << i18n("The key is disabled.");
0200                     problem = true;
0201                 }
0202                 if (key.isInvalid()) {
0203                     information << i18n("The key is invalid.");
0204                     error = true;
0205                 }
0206                 ui.expirationIcon->clear();
0207                 if (key.isExpired()) {
0208                     information << i18n("The key is expired.");
0209                     ui.expirationIcon->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(iconSize));
0210                     ui.expirationIcon->show();
0211                     problem = true;
0212                 } else {
0213                     ui.expirationIcon->hide();
0214                 }
0215 
0216                 // handle the trust of the key
0217                 const GpgME::Key::OwnerTrust ownerTrust = key.ownerTrust();
0218                 ui.trust->setText(OWNERTRUST.value(ownerTrust));
0219 
0220                 switch (ownerTrust) {
0221                 case GpgME::Key::Never:
0222                     information.prepend(i18n("The key is not to be trusted."));
0223                     ui.trustIcon->setPixmap(QIcon::fromTheme("dialog-error").pixmap(iconSize));
0224                     error = true;
0225                     break;
0226                 case GpgME::Key::Marginal:
0227                     information.prepend(i18n("The key is to be trusted marginally."));
0228                     ui.trustIcon->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(iconSize));
0229                     problem = true;
0230                     break;
0231                 case GpgME::Key::Full:
0232                     ui.trustIcon->setPixmap(QIcon::fromTheme("dialog-ok").pixmap(iconSize));
0233                     break;
0234                 case GpgME::Key::Ultimate:
0235                     ui.trustIcon->setPixmap(QIcon::fromTheme("dialog-ok").pixmap(iconSize));
0236                     break;
0237                 case GpgME::Key::Unknown:
0238                 case GpgME::Key::Undefined:
0239                 default:
0240                     information.prepend(i18n("Trust level of the key is unclear."));
0241                     ui.trustIcon->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(iconSize));
0242                     problem = true;
0243                     break;
0244                 }
0245 
0246                 // issuer, issuer mail and comment
0247                 if (key.numUserIDs()) {
0248                     const GpgME::UserID userID = key.userID(0);
0249                     ui.issuer->setText(userID.name());
0250                     ui.comment->setText(userID.comment());
0251                     ui.email->setText(userID.email());
0252                 }
0253 
0254                 // key creation/expiration-time
0255                 if (key.numSubkeys()) {
0256                     const GpgME::Subkey subKey = key.subkey(0);
0257 
0258                     time_t creation = subKey.creationTime();
0259                     QDateTime creationTime;
0260                     creationTime.setSecsSinceEpoch(creation);
0261                     ui.creation->setText(creationTime.toString());
0262 
0263                     time_t expiration = subKey.expirationTime();
0264                     if (expiration) {
0265                         QDateTime expirationTime;
0266                         expirationTime.setSecsSinceEpoch(expiration);
0267                         ui.expiration->setText(expirationTime.toString());
0268                     } else {
0269                         ui.expiration->setText(i18n("Unlimited"));
0270                     }
0271                 }
0272             }
0273         }
0274     }
0275 #endif // HAVE_QGPGME
0276 
0277     const Signature::VerificationStatus verificationStatus = m_signature->status();
0278 
0279     // display the verification status
0280     ui.verificationIcon->hide();
0281     switch (verificationStatus) {
0282     case Signature::Verified:
0283     case Signature::VerifiedInformation:
0284     case Signature::VerifiedWarning:
0285         ui.verificationIcon->setPixmap(QIcon::fromTheme("dialog-ok").pixmap(iconSize));
0286         ui.verificationIcon->show();
0287         ui.verified->setText(i18nc("pgp signature is verified", "Verified"));
0288         break;
0289     case Signature::NotVerified:
0290         ui.verified->setText(i18nc("pgp signature is not verified", "Failed"));
0291         ui.verificationIcon->setPixmap(QIcon::fromTheme("dialog-error").pixmap(iconSize));
0292         ui.verificationIcon->show();
0293         information.prepend(i18n("Caution: Verification failed. Either you entered the wrong signature, or the data has been modified."));
0294         error = true;
0295         break;
0296     case Signature::NotWorked: // TODO downloading state? --> currently downloading
0297         ui.verified->clear(); // TODO
0298         information.prepend(
0299             i18n("Verification not possible. Check the entered data, whether gpg-agent is running, or whether you have an Internet connection (for retrieving "
0300                  "keys.)"));
0301         problem = true;
0302         break;
0303     case Signature::NoResult:
0304         ui.verified->clear(); // TODO
0305         break;
0306     }
0307 
0308     if (verificationStatus == Signature::VerifiedWarning) {
0309         problem = true;
0310     }
0311 
0312 #ifndef HAVE_QGPGME
0313     ui.sigGroup->hide();
0314     ui.keyGroup->hide();
0315     ui.verify->hide();
0316     information.clear();
0317     information << i18n("Feature is not supported, as KGet is not compiled with QPGME support.");
0318     resize(350, 200);
0319 #endif // HAVE_QPGME
0320 
0321     // TODO more messages, e.g. from result etc.
0322     // TODO enter more error cases
0323 
0324     // change the icon of the titlewidget
0325     if (error) {
0326         ui.information->setMessageType(KMessageWidget::Error);
0327     } else if (problem) {
0328         ui.information->setMessageType(KMessageWidget::Warning);
0329     } else {
0330         if (verificationStatus != Signature::Verified) {
0331             ui.information->setMessageType(KMessageWidget::Information);
0332         }
0333     }
0334 
0335     const QString text = information.join(" ");
0336     ui.information->setVisible(!text.isEmpty());
0337     ui.information->setText(text);
0338 }
0339 
0340 void SignatureDlg::verifyClicked()
0341 {
0342     clearData();
0343 
0344     m_signature->verify();
0345 }
0346 
0347 void SignatureDlg::clearData()
0348 {
0349     ui.verified->clear();
0350     ui.verificationIcon->clear();
0351     ui.issuer->clear();
0352     ui.email->clear();
0353     ui.comment->clear();
0354     ui.creation->clear();
0355     ui.expiration->clear();
0356     ui.expirationIcon->hide();
0357     ui.trust->clear();
0358     ui.trustIcon->clear();
0359     ui.fingerprint->clear();
0360 }
0361 
0362 void SignatureDlg::handleWidgets(bool isAsciiSig)
0363 {
0364     ui.asciiLabel->setVisible(isAsciiSig);
0365     ui.signature->setVisible(isAsciiSig);
0366     ui.binaryLabel->setVisible(!isAsciiSig);
0367     QLayoutItem *item = ui.verticalLayout_2->itemAt(ui.verticalLayout_2->count() - 1);
0368     QSpacerItem *spacer = item->spacerItem();
0369     if (isAsciiSig) {
0370         if (spacer) {
0371             ui.verticalLayout_2->removeItem(item);
0372             delete item;
0373         }
0374     } else if (!spacer) {
0375         ui.verticalLayout_2->addStretch(1);
0376     }
0377 }
0378 
0379 #include "moc_signaturedlg.cpp"