diff options
Diffstat (limited to 'kgpg')
-rw-r--r-- | kgpg/transactions/kgpgverify.cpp | 204 | ||||
-rw-r--r-- | kgpg/transactions/kgpgverify.h | 90 |
2 files changed, 294 insertions, 0 deletions
diff --git a/kgpg/transactions/kgpgverify.cpp b/kgpg/transactions/kgpgverify.cpp new file mode 100644 index 0000000..e9dbb36 --- /dev/null +++ b/kgpg/transactions/kgpgverify.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2012 Rolf Eike Beer <[email protected]> + */ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "kgpgverify.h" + +#include "../gpgproc.h" +#include "../core/KGpgKeyNode.h" +//#include "../model/kgpgitemmodel.h" + +//#include <KGlobal> +//#include <KLocale> + +KGpgVerify::KGpgVerify(QObject *parent, const QString &text) + : KGpgTextOrFileTransaction(parent, text), + m_fileIndex(-1) +{ +} + +KGpgVerify::KGpgVerify(QObject *parent, const QList<QUrl> &files) + : KGpgTextOrFileTransaction(parent, files), + m_fileIndex(0) +{ +} + +KGpgVerify::~KGpgVerify() +{ +} + +QStringList +KGpgVerify::command() const +{ + QStringList ret(QLatin1String("--verify")); + + return ret; +} + +bool +KGpgVerify::nextLine(const QString &line) +{ + if (line.startsWith(QLatin1String("[GNUPG:] NO_PUBKEY "))) { + setSuccess(TS_MISSING_KEY); + m_missingId = line.mid(19).simplified(); + return false; + } + + if (line.startsWith(QLatin1String("[GNUPG:] ")) && + line.contains(QLatin1String("SIG"))) { + if (line.startsWith(QLatin1String("[GNUPG:] BADSIG"))) + setSuccess(KGpgVerify::TS_BAD_SIGNATURE); + else + setSuccess(KGpgTransaction::TS_OK); + } + + return KGpgTextOrFileTransaction::nextLine(line); +} + +void +KGpgVerify::finish() +{ + // GnuPG will return error code 2 if it wasn't able to verify the file. + // If it complained about a missing signature before that is fine. + if (((getProcess()->exitCode() == 2) && (getSuccess() == TS_MISSING_KEY)) || + ((getProcess()->exitCode() == 1) && (getSuccess() == TS_BAD_SIGNATURE))) + return; + + KGpgTextOrFileTransaction::finish(); +} + +static QString +sigTimeMessage(const QString &sigtime) +{ + QDateTime stamp; + if (sigtime.contains(QLatin1Char('T'))) { + stamp = QDateTime::fromString(sigtime, Qt::ISODate); + } else { + bool ok; + qint64 secs = sigtime.toLongLong(&ok); + if (ok) + stamp = QDateTime::fromMSecsSinceEpoch(secs * 1000); + } + + if (!stamp.isValid()) + return QString(); + +/* return tr("first argument is formatted date, second argument is formatted time", + "The signature was created at %1 %2", + KGlobal::locale()->formatDate(stamp.date(), KLocale::LongDate), + KGlobal::locale()->formatTime(stamp.time(), KLocale::LongDate)) + + QLatin1String("<br/>");*/ + return "todo"; +} + +/* +QString +KGpgVerify::getReport(const QStringList &log, const KGpgItemModel *model) +{ + QString result; + // newer versions of GnuPG emit both VALIDSIG and GOODSIG + // for a good signature. Since VALIDSIG has more information + // we use that. + const QRegExp validsig(QLatin1String("^\\[GNUPG:\\] VALIDSIG([ ]+[^ ]+){10,}.*$")); + const bool useGoodSig = (model != NULL) && (log.indexOf(validsig) == -1); + QString sigtime; // timestamp of signature creation + + foreach (const QString &line, log) { + if (!line.startsWith(QLatin1String("[GNUPG:] "))) + continue; + + const QString msg = line.mid(9); + + if (msg.startsWith(QLatin1String("VALIDSIG ")) && !useGoodSig) { + // from GnuPG source, doc/DETAILS: + // VALIDSIG <fingerprint in hex> <sig_creation_date> <sig-timestamp> + // <expire-timestamp> <sig-version> <reserved> <pubkey-algo> + // <hash-algo> <sig-class> <primary-key-fpr> + const QStringList vsig = msg.mid(9).split(QLatin1Char(' '), QString::SkipEmptyParts); + Q_ASSERT(vsig.count() >= 10); + + const KGpgKeyNode *node = model->findKeyNode(vsig[9]); + + if (node != NULL) { + // ignore for now if this is signed with the primary id (vsig[0] == vsig[9]) or not + if (node->getEmail().isEmpty()) + result += tr("<qt>Good signature from:<br /><b>%1</b><br />Key ID: %2<br /></qt>") + .arg(node->getName()).arg(vsig[9]); + else + result += tr("Good signature from: NAME <EMAIL>, Key ID: HEXID", + "<qt>Good signature from:<br /><b>%1 <%2></b><br />Key ID: %3<br /></qt>") + .arg(node->getName()).arg(node->getEmail()).arg(vsig[9]); + + result += sigTimeMessage(vsig[2]); + } else { + // this should normally never happen, but one could delete + // the key just after the verification. Brute force solution: + // do the whole report generation again, but this time make + // sure GOODSIG is used. + return getReport(log, NULL); + } + } else if (msg.startsWith(QLatin1String("UNEXPECTED")) || + msg.startsWith(QLatin1String("NODATA"))) { + result += tr("No signature found.") + QLatin1Char('\n'); + } else if (useGoodSig && msg.startsWith(QLatin1String("GOODSIG "))) { + int sigpos = msg.indexOf( ' ' , 8); + const QString keyid = msg.mid(8, sigpos - 8); + + // split the name/email pair to give translators more power to handle this + QString email; + QString name = msg.mid(sigpos + 1); + + int oPos = name.indexOf(QLatin1Char('<')); + int cPos = name.indexOf(QLatin1Char('>')); + if ((oPos >= 0) && (cPos >= 0)) { + email = name.mid(oPos + 1, cPos - oPos - 1); + name = name.left(oPos).simplified(); + } + + if (email.isEmpty()) + result += tr("<qt>Good signature from:<br /><b>%1</b><br />Key ID: %2<br /></qt>") + .arg(name).arg(keyid); + else + result += tr("Good signature from: NAME <EMAIL>, Key ID: HEXID", + "<qt>Good signature from:<br /><b>%1 <%2></b><br />Key ID: %3<br /></qt>") + .arg(name).arg(email).arg(keyid); + if (!sigtime.isEmpty()) { + result += sigTimeMessage(sigtime); + sigtime.clear(); + } + } else if (msg.startsWith(QLatin1String("SIG_ID "))) { + const QStringList parts = msg.simplified().split(QLatin1Char(' ')); + if (parts.count() > 2) + sigtime = parts[2]; + } else if (msg.startsWith(QLatin1String("BADSIG"))) { + int sigpos = msg.indexOf( ' ', 7); + result += tr("<qt><b>BAD signature</b> from:<br /> %1<br />Key id: %2<br /><br /><b>The file is corrupted</b><br /></qt>") + .arg(msg.mid(sigpos + 1).replace(QLatin1Char('<'), QLatin1String("<"))) + .arg(msg.mid(7, sigpos - 7)); + } else if (msg.startsWith(QLatin1String("TRUST_UNDEFINED"))) { + result += tr("<qt>The signature is valid, but the key is untrusted<br /></qt>"); + } else if (msg.startsWith(QLatin1String("TRUST_ULTIMATE"))) { + result += tr("<qt>The signature is valid, and the key is ultimately trusted<br /></qt>"); + } + } + + return result; +} +*/ + +QString +KGpgVerify::missingId() const +{ + return m_missingId; +} + +//#include "kgpgverify.moc" diff --git a/kgpg/transactions/kgpgverify.h b/kgpg/transactions/kgpgverify.h new file mode 100644 index 0000000..beba12f --- /dev/null +++ b/kgpg/transactions/kgpgverify.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012 Rolf Eike Beer <[email protected]> + */ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KGPGVERIFY_H +#define KGPGVERIFY_H + +#include "kgpgtextorfiletransaction.h" + +#include <QObject> +#include <QString> +#include <QStringList> + +#include <QUrl> + +//class KGpgItemModel; +class QProcess; + +/** + * @brief verify the signature of the given text or files + */ +class KGpgVerify: public KGpgTextOrFileTransaction { + Q_OBJECT + + Q_DISABLE_COPY(KGpgVerify) + KGpgVerify(); // = delete C++0x +public: + enum ts_verify { + TS_MISSING_KEY = KGpgTransaction::TS_COMMON_END + 1, ///< signing key not in keyring + TS_BAD_SIGNATURE = TS_MISSING_KEY + 1 ///< the file is signed, but the signature is invalid + }; + + /** + * @brief verify signature of given text + * @param parent parent object + * @param text text to verify + */ + explicit KGpgVerify(QObject *parent, const QString &text = QString()); + + /** + * @brief verify signatures of file(s) + * @param parent parent object + * @param files list of file locations to verify + */ + KGpgVerify(QObject *parent, const QList<QUrl> &files); + + /** + * @brief destructor + */ + virtual ~KGpgVerify(); + + /** + * @brief get verification report + * @param log the log lines to scan + * @param model key model to use for key lookups + * @return verification report of GnuPG + */ + //static QString getReport(const QStringList &log, const KGpgItemModel *model = NULL); + + /** + * @brief get the missing key id + * @return key id that signed the message + * + * This is only valid if the transaction returned with result + * TS_MISSING_KEY. + */ + QString missingId() const; + +protected: + virtual QStringList command() const; + virtual bool nextLine(const QString &line); + virtual void finish(); + +private: + int m_fileIndex; + QString m_currentFile; + QStringList m_report; + QString m_missingId; +}; + +#endif // KGPGVERIFY_H |