File indexing completed on 2024-04-21 04:57:04

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