File indexing completed on 2023-10-01 08:39:29

0001 /**************************************************************************
0002  *   Copyright (C) 2009-2011 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 "keydownloader.h"
0021 #include "settings.h"
0022 #include "signature_p.h"
0023 
0024 #include "kget_debug.h"
0025 #include <QDebug>
0026 
0027 #include <KLocalizedString>
0028 #include <KMessageBox>
0029 #include <kwidgetsaddons_version.h>
0030 
0031 #include <QDomElement>
0032 #include <QGlobalStatic>
0033 
0034 #ifdef HAVE_QGPGME
0035 #include <gpgme++/context.h>
0036 #include <gpgme++/data.h>
0037 #include <qgpgme/dataprovider.h>
0038 
0039 #include <QFile>
0040 #endif
0041 
0042 #ifdef HAVE_QGPGME
0043 Q_GLOBAL_STATIC(KeyDownloader, signatureDownloader)
0044 #endif // HAVE_QGPGME
0045 
0046 SignaturePrivate::SignaturePrivate(Signature *signature)
0047     : q(signature)
0048     , type(Signature::NoType)
0049     , status(Signature::NoResult)
0050     , verifyTried(false)
0051     , sigSummary(0)
0052     , error(0)
0053 {
0054 }
0055 
0056 SignaturePrivate::~SignaturePrivate()
0057 {
0058 }
0059 
0060 void SignaturePrivate::signatureDownloaded()
0061 {
0062     if (verifyTried) {
0063         qCDebug(KGET_DEBUG) << "Rerun verification.";
0064         q->verify();
0065     }
0066 }
0067 
0068 #ifdef HAVE_QGPGME
0069 GpgME::VerificationResult SignaturePrivate::verify(const QUrl &dest, const QByteArray &sig)
0070 {
0071     GpgME::VerificationResult result;
0072     if (!QFile::exists(dest.toDisplayString(QUrl::PreferLocalFile)) || sig.isEmpty()) {
0073         return result;
0074     }
0075 
0076     GpgME::initializeLibrary();
0077     GpgME::Error error = GpgME::checkEngine(GpgME::OpenPGP);
0078     if (error) {
0079         qCDebug(KGET_DEBUG) << "OpenPGP not supported!";
0080         return result;
0081     }
0082 
0083     QScopedPointer<GpgME::Context> context(GpgME::Context::createForProtocol(GpgME::OpenPGP));
0084     if (!context.data()) {
0085         qCDebug(KGET_DEBUG) << "Could not create context.";
0086         return result;
0087     }
0088 
0089     std::shared_ptr<QFile> qFile(new QFile(dest.toDisplayString(QUrl::PreferLocalFile)));
0090     qFile->open(QIODevice::ReadOnly);
0091     auto *file = new QGpgME::QIODeviceDataProvider(qFile);
0092     GpgME::Data dFile(file);
0093 
0094     QGpgME::QByteArrayDataProvider signatureBA(sig);
0095     GpgME::Data signature(&signatureBA);
0096 
0097     return context->verifyDetachedSignature(signature, dFile);
0098 }
0099 #endif // HAVE_QGPGME
0100 
0101 Signature::Signature(const QUrl &dest, QObject *object)
0102     : QObject(object)
0103     , d(new SignaturePrivate(this))
0104 {
0105     d->dest = dest;
0106 #ifdef HAVE_QGPGME
0107     qRegisterMetaType<GpgME::VerificationResult>("GpgME::VerificationResult");
0108     connect(&d->thread, &SignatureThread::verified, this, &Signature::slotVerified);
0109 #endif // HAVE_QGPGME
0110 }
0111 
0112 Signature::~Signature()
0113 {
0114     delete d;
0115 }
0116 
0117 QUrl Signature::destination() const
0118 {
0119     return d->dest;
0120 }
0121 
0122 void Signature::setDestination(const QUrl &destination)
0123 {
0124     d->dest = destination;
0125 }
0126 
0127 Signature::VerificationStatus Signature::status() const
0128 {
0129     return d->status;
0130 }
0131 
0132 #ifdef HAVE_QGPGME
0133 GpgME::VerificationResult Signature::verificationResult()
0134 {
0135     return d->verificationResult;
0136 }
0137 #endif // HAVE_QGPGME
0138 
0139 QByteArray Signature::signature()
0140 {
0141     return d->signature;
0142 }
0143 
0144 void Signature::setAsciiDetachedSignature(const QString &signature)
0145 {
0146     setSignature(signature.toLatin1(), AsciiDetached);
0147 }
0148 
0149 void Signature::setSignature(const QByteArray &signature, SignatureType type)
0150 {
0151     if ((signature == d->signature) && (type == d->type)) {
0152         return;
0153     }
0154 
0155     d->type = type;
0156     d->signature = signature;
0157 
0158     d->fingerprint.clear();
0159     d->error = 0;
0160     d->sigSummary = 0;
0161     d->status = Signature::NoResult;
0162 
0163 #ifdef HAVE_QGPGME
0164     d->verificationResult = GpgME::VerificationResult();
0165 #endif // HAVE_QGPGME
0166 
0167     Q_EMIT verified(d->status); // FIXME
0168 }
0169 
0170 Signature::SignatureType Signature::type() const
0171 {
0172     return d->type;
0173 }
0174 
0175 QString Signature::fingerprint()
0176 {
0177     return d->fingerprint;
0178 }
0179 
0180 void Signature::downloadKey(QString fingerprint) // krazy:exclude=passbyvalue
0181 {
0182 #ifdef HAVE_QGPGME
0183     qCDebug(KGET_DEBUG) << "Downloading key:" << fingerprint;
0184     signatureDownloader->downloadKey(fingerprint, this);
0185 #else
0186     Q_UNUSED(fingerprint)
0187 #endif // HAVE_QGPGME
0188 }
0189 
0190 bool Signature::isVerifyable()
0191 {
0192 #ifdef HAVE_QGPGME
0193     return QFile::exists(d->dest.toDisplayString(QUrl::PreferLocalFile)) && !d->signature.isEmpty();
0194 #else
0195     return false;
0196 #endif // HAVE_QGPGME
0197 }
0198 
0199 void Signature::verify()
0200 {
0201 #ifdef HAVE_QGPGME
0202     d->thread.verify(d->dest, d->signature);
0203 #endif // HAVE_QGPGME
0204 }
0205 
0206 #ifdef HAVE_QGPGME
0207 void Signature::slotVerified(const GpgME::VerificationResult &result)
0208 {
0209     d->verificationResult = result;
0210     d->status = Signature::NotWorked;
0211 
0212     if (!d->verificationResult.numSignatures()) {
0213         qCDebug(KGET_DEBUG) << "No signatures\n";
0214         Q_EMIT verified(d->status);
0215         return;
0216     }
0217 
0218     GpgME::Signature signature = d->verificationResult.signature(0);
0219     d->sigSummary = signature.summary();
0220     d->error = signature.status().code();
0221     d->fingerprint = signature.fingerprint();
0222 
0223     qCDebug(KGET_DEBUG) << "Fingerprint:" << d->fingerprint;
0224     qCDebug(KGET_DEBUG) << "Signature summary:" << d->sigSummary;
0225     qCDebug(KGET_DEBUG) << "Error code:" << d->error;
0226 
0227     if (d->sigSummary & GpgME::Signature::KeyMissing) {
0228         qCDebug(KGET_DEBUG) << "Public key missing.";
0229         if (Settings::signatureAutomaticDownloading() ||
0230 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0231             (KMessageBox::warningTwoActions(nullptr,
0232 #else
0233             (KMessageBox::warningYesNo(nullptr,
0234 #endif
0235                                             i18n("The key to verify the signature is missing, do you want to download it?"),
0236                                             QString(),
0237                                             KGuiItem(i18nc("@action:button", "Download"), QStringLiteral("document-save")),
0238                                             KGuiItem(i18nc("@action:button", "Continue Without"), QStringLiteral("dialog-cancel")))
0239 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0240              == KMessageBox::PrimaryAction)) {
0241 #else
0242              == KMessageBox::Yes)) {
0243 #endif
0244             d->verifyTried = true;
0245             downloadKey(d->fingerprint);
0246             Q_EMIT verified(d->status);
0247             return;
0248         }
0249     }
0250 
0251     if (!signature.status()) {
0252         if (d->sigSummary & GpgME::Signature::Valid) {
0253             d->status = Signature::Verified;
0254         } else if ((d->sigSummary & GpgME::Signature::Green) || (d->sigSummary == 0)) {
0255             d->status = Signature::VerifiedInformation;
0256         }
0257     } else if (signature.status()) {
0258         if ((d->sigSummary & GpgME::Signature::KeyExpired) || (d->sigSummary & GpgME::Signature::KeyRevoked)) {
0259             d->status = Signature::VerifiedWarning;
0260         }
0261         if (d->sigSummary & GpgME::Signature::Red) { // TODO handle more cases!
0262             d->status = Signature::NotVerified;
0263             // TODO handle that dialog better in 4.5
0264             KMessageBox::error(nullptr,
0265                                i18n("The signature could not be verified for %1. See transfer settings for more information.", d->dest.fileName()),
0266                                i18n("Signature not verified"));
0267         }
0268     }
0269 
0270     Q_EMIT verified(d->status);
0271 }
0272 #endif // HAVE_QGPGME
0273 
0274 void Signature::save(const QDomElement &element)
0275 {
0276     QDomElement e = element;
0277 
0278     QDomElement verification = e.ownerDocument().createElement("signature");
0279     verification.setAttribute("status", d->status);
0280     verification.setAttribute("sigStatus", d->sigSummary);
0281     verification.setAttribute("error", d->error);
0282     verification.setAttribute("fingerprint", d->fingerprint);
0283     verification.setAttribute("type", d->type);
0284     QDomText value;
0285     switch (d->type) {
0286     case NoType:
0287     case AsciiDetached:
0288         value = e.ownerDocument().createTextNode(d->signature);
0289         break;
0290     case BinaryDetached:
0291         value = e.ownerDocument().createTextNode(d->signature.toBase64());
0292         break;
0293     }
0294     verification.appendChild(value);
0295 
0296     e.appendChild(verification);
0297 }
0298 
0299 void Signature::load(const QDomElement &e)
0300 {
0301     QDomElement verification = e.firstChildElement("signature");
0302     d->status = static_cast<VerificationStatus>(verification.attribute("status").toInt());
0303     d->sigSummary = verification.attribute("sigStatus").toInt();
0304     d->error = verification.attribute("error").toInt();
0305     d->fingerprint = verification.attribute("fingerprint");
0306     d->type = static_cast<SignatureType>(verification.attribute("type").toInt());
0307     switch (d->type) {
0308     case NoType:
0309     case AsciiDetached:
0310         d->signature = verification.text().toLatin1();
0311         break;
0312     case BinaryDetached:
0313         d->signature = QByteArray::fromBase64(verification.text().toLatin1());
0314     }
0315 }
0316 
0317 #include "moc_signature.cpp"