Page MenuHome GnuPG

No OneTemporary

diff --git a/src/crypto/certificateresolver.cpp b/src/crypto/certificateresolver.cpp
index bb41de397..3283d4bb9 100644
--- a/src/crypto/certificateresolver.cpp
+++ b/src/crypto/certificateresolver.cpp
@@ -1,287 +1,287 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/certificateresolver.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2007 Klarälvdalens Datakonsult AB
Kleopatra 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.
Kleopatra 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "certificateresolver.h"
#include <Libkleo/KeyCache>
#include <gpgme++/key.h>
#include <KConfig>
#include <KConfigGroup>
#include <QRegularExpression>
#include <QByteArray>
#include <QHash>
#include <QSet>
#include <algorithm>
#include <iterator>
#include <cassert>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
using namespace KMime::Types;
using namespace KMime::HeaderParsing;
std::vector< std::vector<Key> > CertificateResolver::resolveRecipients(const std::vector<Mailbox> &recipients, Protocol proto)
{
std::vector< std::vector<Key> > result;
std::transform(recipients.begin(), recipients.end(),
std::back_inserter(result),
[proto](const Mailbox &recipient) {
return resolveRecipient(recipient, proto);
});
return result;
}
std::vector<Key> CertificateResolver::resolveRecipient(const Mailbox &recipient, Protocol proto)
{
std::vector<Key> result = KeyCache::instance()->findByEMailAddress(recipient.address());
auto end = std::remove_if(result.begin(), result.end(),
[](const Key &key) { return key.canEncrypt(); });
if (proto != UnknownProtocol)
end = std::remove_if(result.begin(), end,
[proto](const Key &key) { return key.protocol() != proto; });
result.erase(end, result.end());
return result;
}
std::vector< std::vector<Key> > CertificateResolver::resolveSigners(const std::vector<Mailbox> &signers, Protocol proto)
{
std::vector< std::vector<Key> > result;
std::transform(signers.begin(), signers.end(),
std::back_inserter(result),
[proto](const Mailbox &signer) {
return resolveSigner(signer, proto);
});
return result;
}
std::vector<Key> CertificateResolver::resolveSigner(const Mailbox &signer, Protocol proto)
{
std::vector<Key> result = KeyCache::instance()->findByEMailAddress(signer.address());
auto end = std::remove_if(result.begin(), result.end(),
[](const Key &key) { return key.hasSecret(); });
end = std::remove_if(result.begin(), end,
[](const Key &key) { return key.canReallySign(); });
if (proto != UnknownProtocol)
end = std::remove_if(result.begin(), end,
[proto](const Key &key) { return key.protocol() != proto; });
result.erase(end, result.end());
return result;
}
class KConfigBasedRecipientPreferences::Private
{
friend class ::Kleo::Crypto::KConfigBasedRecipientPreferences;
KConfigBasedRecipientPreferences *const q;
public:
explicit Private(const KSharedConfigPtr &config, KConfigBasedRecipientPreferences *qq);
~Private();
private:
void ensurePrefsParsed() const;
void writePrefs();
private:
KSharedConfigPtr m_config;
mutable QHash<QByteArray, QByteArray> pgpPrefs;
mutable QHash<QByteArray, QByteArray> cmsPrefs;
mutable bool m_parsed;
mutable bool m_dirty;
};
KConfigBasedRecipientPreferences::Private::Private(const KSharedConfigPtr &config, KConfigBasedRecipientPreferences *qq) : q(qq), m_config(config), m_parsed(false), m_dirty(false)
{
assert(m_config);
}
KConfigBasedRecipientPreferences::Private::~Private()
{
writePrefs();
}
void KConfigBasedRecipientPreferences::Private::writePrefs()
{
if (!m_dirty) {
return;
}
const QSet<QByteArray> keys = pgpPrefs.keys().toSet() + cmsPrefs.keys().toSet();
int n = 0;
- Q_FOREACH (const QByteArray &i, keys) {
+ for (const QByteArray &i : keys) {
KConfigGroup group(m_config, QStringLiteral("EncryptionPreference_%1").arg(n++));
group.writeEntry("email", i);
const QByteArray pgp = pgpPrefs.value(i);
if (!pgp.isEmpty()) {
group.writeEntry("pgpCertificate", pgp);
}
const QByteArray cms = cmsPrefs.value(i);
if (!cms.isEmpty()) {
group.writeEntry("cmsCertificate", cms);
}
}
m_config->sync();
m_dirty = false;
}
void KConfigBasedRecipientPreferences::Private::ensurePrefsParsed() const
{
if (m_parsed) {
return;
}
const QStringList groups = m_config->groupList().filter(QRegularExpression(QStringLiteral("^EncryptionPreference_\\d+$")));
- Q_FOREACH (const QString &i, groups) {
+ for (const QString &i : groups) {
const KConfigGroup group(m_config, i);
const QByteArray id = group.readEntry("email", QByteArray());
if (id.isEmpty()) {
continue;
}
pgpPrefs.insert(id, group.readEntry("pgpCertificate", QByteArray()));
cmsPrefs.insert(id, group.readEntry("cmsCertificate", QByteArray()));
}
m_parsed = true;
}
KConfigBasedRecipientPreferences::KConfigBasedRecipientPreferences(const KSharedConfigPtr &config) : d(new Private(config, this))
{
}
KConfigBasedRecipientPreferences::~KConfigBasedRecipientPreferences()
{
d->writePrefs();
}
Key KConfigBasedRecipientPreferences::preferredCertificate(const Mailbox &recipient, Protocol protocol)
{
d->ensurePrefsParsed();
const QByteArray keyId = (protocol == CMS ? d->cmsPrefs : d->pgpPrefs).value(recipient.address());
return KeyCache::instance()->findByKeyIDOrFingerprint(keyId);
}
void KConfigBasedRecipientPreferences::setPreferredCertificate(const Mailbox &recipient, Protocol protocol, const Key &certificate)
{
d->ensurePrefsParsed();
if (!recipient.hasAddress()) {
return;
}
(protocol == CMS ? d->cmsPrefs : d->pgpPrefs).insert(recipient.address(), certificate.keyID());
d->m_dirty = true;
}
class KConfigBasedSigningPreferences::Private
{
friend class ::Kleo::Crypto::KConfigBasedSigningPreferences;
KConfigBasedSigningPreferences *const q;
public:
explicit Private(const KSharedConfigPtr &config, KConfigBasedSigningPreferences *qq);
~Private();
private:
void ensurePrefsParsed() const;
void writePrefs();
private:
KSharedConfigPtr m_config;
mutable QByteArray pgpSigningCertificate;
mutable QByteArray cmsSigningCertificate;
mutable bool m_parsed;
mutable bool m_dirty;
};
KConfigBasedSigningPreferences::Private::Private(const KSharedConfigPtr &config, KConfigBasedSigningPreferences *qq) : q(qq), m_config(config), m_parsed(false), m_dirty(false)
{
assert(m_config);
}
void KConfigBasedSigningPreferences::Private::ensurePrefsParsed() const
{
if (m_parsed) {
return;
}
const KConfigGroup group(m_config, "SigningPreferences");
pgpSigningCertificate = group.readEntry("pgpSigningCertificate", QByteArray());
cmsSigningCertificate = group.readEntry("cmsSigningCertificate", QByteArray());
m_parsed = true;
}
void KConfigBasedSigningPreferences::Private::writePrefs()
{
if (!m_dirty) {
return;
}
KConfigGroup group(m_config, "SigningPreferences");
group.writeEntry("pgpSigningCertificate", pgpSigningCertificate);
group.writeEntry("cmsSigningCertificate", cmsSigningCertificate);
m_config->sync();
m_dirty = false;
}
KConfigBasedSigningPreferences::Private::~Private()
{
writePrefs();
}
KConfigBasedSigningPreferences::KConfigBasedSigningPreferences(const KSharedConfigPtr &config) : d(new Private(config, this))
{
}
KConfigBasedSigningPreferences::~KConfigBasedSigningPreferences()
{
d->writePrefs();
}
Key KConfigBasedSigningPreferences::preferredCertificate(Protocol protocol)
{
d->ensurePrefsParsed();
const QByteArray keyId = (protocol == CMS ? d->cmsSigningCertificate : d->pgpSigningCertificate);
const Key key = KeyCache::instance()->findByKeyIDOrFingerprint(keyId);
return key.hasSecret() ? key : Key::null;
}
void KConfigBasedSigningPreferences::setPreferredCertificate(Protocol protocol, const Key &certificate)
{
d->ensurePrefsParsed();
(protocol == CMS ? d->cmsSigningCertificate : d->pgpSigningCertificate) = certificate.keyID();
d->m_dirty = true;
}
diff --git a/src/crypto/createchecksumscontroller.cpp b/src/crypto/createchecksumscontroller.cpp
index 0e36268c2..2c2e715d5 100644
--- a/src/crypto/createchecksumscontroller.cpp
+++ b/src/crypto/createchecksumscontroller.cpp
@@ -1,737 +1,737 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/createchecksumscontroller.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2010 Klarälvdalens Datakonsult AB
Kleopatra 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.
Kleopatra 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "createchecksumscontroller.h"
#include <utils/input.h>
#include <utils/output.h>
#include <utils/kleo_assert.h>
#include <Libkleo/Stl_Util>
#include <Libkleo/ChecksumDefinition>
#include <Libkleo/Classify>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <QTemporaryFile>
#include <KConfigGroup>
#include <KSharedConfig>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLabel>
#include <QListWidget>
#include <QVBoxLayout>
#include <QPointer>
#include <QFileInfo>
#include <QThread>
#include <QMutex>
#include <QProgressDialog>
#include <QDir>
#include <QProcess>
#include <gpg-error.h>
#include <deque>
#include <map>
#include <limits>
#include <functional>
using namespace Kleo;
using namespace Kleo::Crypto;
namespace
{
class ResultDialog : public QDialog
{
Q_OBJECT
public:
ResultDialog(const QStringList &created, const QStringList &errors, QWidget *parent = nullptr, Qt::WindowFlags f = nullptr)
: QDialog(parent, f),
createdLB(created.empty()
? i18nc("@info", "No checksum files have been created.")
: i18nc("@info", "These checksum files have been successfully created:"), this),
createdLW(this),
errorsLB(errors.empty()
? i18nc("@info", "There were no errors.")
: i18nc("@info", "The following errors were encountered:"), this),
errorsLW(this),
buttonBox(QDialogButtonBox::Ok, Qt::Horizontal, this),
vlay(this)
{
KDAB_SET_OBJECT_NAME(createdLB);
KDAB_SET_OBJECT_NAME(createdLW);
KDAB_SET_OBJECT_NAME(errorsLB);
KDAB_SET_OBJECT_NAME(errorsLW);
KDAB_SET_OBJECT_NAME(buttonBox);
KDAB_SET_OBJECT_NAME(vlay);
createdLW.addItems(created);
QRect r;
for (int i = 0; i < created.size(); ++i) {
r = r.united(createdLW.visualRect(createdLW.model()->index(0, i)));
}
createdLW.setMinimumWidth(qMin(1024, r.width() + 4 * createdLW.frameWidth()));
errorsLW.addItems(errors);
vlay.addWidget(&createdLB);
vlay.addWidget(&createdLW, 1);
vlay.addWidget(&errorsLB);
vlay.addWidget(&errorsLW, 1);
vlay.addWidget(&buttonBox);
if (created.empty()) {
createdLW.hide();
}
if (errors.empty()) {
errorsLW.hide();
}
connect(&buttonBox, &QDialogButtonBox::accepted, this, &ResultDialog::accept);
connect(&buttonBox, &QDialogButtonBox::rejected, this, &ResultDialog::reject);
readConfig();
}
~ResultDialog()
{
writeConfig();
}
void readConfig()
{
KConfigGroup dialog(KSharedConfig::openConfig(), "ResultDialog");
const QSize size = dialog.readEntry("Size", QSize(600, 400));
if (size.isValid()) {
resize(size);
}
}
void writeConfig()
{
KConfigGroup dialog(KSharedConfig::openConfig(), "ResultDialog");
dialog.writeEntry("Size", size());
dialog.sync();
}
private:
QLabel createdLB;
QListWidget createdLW;
QLabel errorsLB;
QListWidget errorsLW;
QDialogButtonBox buttonBox;
QVBoxLayout vlay;
};
}
#ifdef Q_OS_UNIX
static const bool HAVE_UNIX = true;
#else
static const bool HAVE_UNIX = false;
#endif
static const Qt::CaseSensitivity fs_cs = HAVE_UNIX ? Qt::CaseSensitive : Qt::CaseInsensitive; // can we use QAbstractFileEngine::caseSensitive()?
static QStringList fs_sort(QStringList l)
{
std::sort(l.begin(), l.end(), [](const QString &lhs, const QString &rhs) {
return QString::compare(lhs, rhs, fs_cs) < 0;
});
return l;
}
static QStringList fs_intersect(QStringList l1, QStringList l2)
{
fs_sort(l1);
fs_sort(l2);
QStringList result;
std::set_intersection(l1.begin(), l1.end(),
l2.begin(), l2.end(),
std::back_inserter(result),
[](const QString &lhs, const QString &rhs) {
return QString::compare(lhs, rhs, fs_cs) < 0;
});
return result;
}
static QList<QRegExp> get_patterns(const std::vector< std::shared_ptr<ChecksumDefinition> > &checksumDefinitions)
{
QList<QRegExp> result;
Q_FOREACH (const std::shared_ptr<ChecksumDefinition> &cd, checksumDefinitions)
if (cd)
Q_FOREACH (const QString &pattern, cd->patterns()) {
result.push_back(QRegExp(pattern, fs_cs));
}
return result;
}
namespace
{
struct matches_any : std::unary_function<QString, bool> {
const QList<QRegExp> m_regexps;
explicit matches_any(const QList<QRegExp> &regexps) : m_regexps(regexps) {}
bool operator()(const QString &s) const
{
return std::any_of(m_regexps.cbegin(), m_regexps.cend(),
[s](const QRegExp &rx) { return rx.exactMatch(s); });
}
};
}
class CreateChecksumsController::Private : public QThread
{
Q_OBJECT
friend class ::Kleo::Crypto::CreateChecksumsController;
CreateChecksumsController *const q;
public:
explicit Private(CreateChecksumsController *qq);
~Private();
Q_SIGNALS:
void progress(int, int, const QString &);
private:
void slotOperationFinished()
{
#ifndef QT_NO_PROGRESSDIALOG
if (progressDialog) {
progressDialog->setValue(progressDialog->maximum());
progressDialog->close();
}
#endif // QT_NO_PROGRESSDIALOG
ResultDialog *const dlg = new ResultDialog(created, errors);
dlg->setAttribute(Qt::WA_DeleteOnClose);
q->bringToForeground(dlg);
if (!errors.empty())
q->setLastError(gpg_error(GPG_ERR_GENERAL),
errors.join(QLatin1Char('\n')));
q->emitDoneOrError();
}
void slotProgress(int current, int total, const QString &what)
{
qCDebug(KLEOPATRA_LOG) << "progress: " << current << "/" << total << ": " << qPrintable(what);
#ifndef QT_NO_PROGRESSDIALOG
if (!progressDialog) {
return;
}
progressDialog->setMaximum(total);
progressDialog->setValue(current);
progressDialog->setLabelText(what);
#endif // QT_NO_PROGRESSDIALOG
}
private:
void run() Q_DECL_OVERRIDE;
private:
#ifndef QT_NO_PROGRESSDIALOG
QPointer<QProgressDialog> progressDialog;
#endif
mutable QMutex mutex;
const std::vector< std::shared_ptr<ChecksumDefinition> > checksumDefinitions;
std::shared_ptr<ChecksumDefinition> checksumDefinition;
QStringList files;
QStringList errors, created;
bool allowAddition;
volatile bool canceled;
};
CreateChecksumsController::Private::Private(CreateChecksumsController *qq)
: q(qq),
#ifndef QT_NO_PROGRESSDIALOG
progressDialog(),
#endif
mutex(),
checksumDefinitions(ChecksumDefinition::getChecksumDefinitions()),
checksumDefinition(ChecksumDefinition::getDefaultChecksumDefinition(checksumDefinitions)),
files(),
errors(),
created(),
allowAddition(false),
canceled(false)
{
connect(this, SIGNAL(progress(int,int,QString)),
q, SLOT(slotProgress(int,int,QString)));
connect(this, &Private::progress,
q, &Controller::progress);
connect(this, SIGNAL(finished()),
q, SLOT(slotOperationFinished()));
}
CreateChecksumsController::Private::~Private()
{
qCDebug(KLEOPATRA_LOG);
}
CreateChecksumsController::CreateChecksumsController(QObject *p)
: Controller(p), d(new Private(this))
{
}
CreateChecksumsController::CreateChecksumsController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *p)
: Controller(ctx, p), d(new Private(this))
{
}
CreateChecksumsController::~CreateChecksumsController()
{
qCDebug(KLEOPATRA_LOG);
}
void CreateChecksumsController::setFiles(const QStringList &files)
{
kleo_assert(!d->isRunning());
kleo_assert(!files.empty());
const QList<QRegExp> patterns = get_patterns(d->checksumDefinitions);
if (!std::all_of(files.cbegin(), files.cend(), matches_any(patterns)) &&
!std::none_of(files.cbegin(), files.cend(), matches_any(patterns))) {
throw Exception(gpg_error(GPG_ERR_INV_ARG), i18n("Create Checksums: input files must be either all checksum files or all files to be checksummed, not a mixture of both."));
}
const QMutexLocker locker(&d->mutex);
d->files = files;
}
void CreateChecksumsController::setAllowAddition(bool allow)
{
kleo_assert(!d->isRunning());
const QMutexLocker locker(&d->mutex);
d->allowAddition = allow;
}
bool CreateChecksumsController::allowAddition() const
{
const QMutexLocker locker(&d->mutex);
return d->allowAddition;
}
void CreateChecksumsController::start()
{
{
const QMutexLocker locker(&d->mutex);
#ifndef QT_NO_PROGRESSDIALOG
d->progressDialog = new QProgressDialog(i18n("Initializing..."), i18n("Cancel"), 0, 0);
applyWindowID(d->progressDialog);
d->progressDialog->setAttribute(Qt::WA_DeleteOnClose);
d->progressDialog->setMinimumDuration(1000);
d->progressDialog->setWindowTitle(i18nc("@title:window", "Create Checksum Progress"));
connect(d->progressDialog.data(), &QProgressDialog::canceled, this, &CreateChecksumsController::cancel);
#endif // QT_NO_PROGRESSDIALOG
d->canceled = false;
d->errors.clear();
d->created.clear();
}
d->start();
}
void CreateChecksumsController::cancel()
{
qCDebug(KLEOPATRA_LOG);
const QMutexLocker locker(&d->mutex);
d->canceled = true;
}
namespace
{
struct Dir {
QDir dir;
QString sumFile;
QStringList inputFiles;
quint64 totalSize;
std::shared_ptr<ChecksumDefinition> checksumDefinition;
};
}
static QStringList remove_checksum_files(QStringList l, const QList<QRegExp> &rxs)
{
QStringList::iterator end = l.end();
Q_FOREACH (const QRegExp &rx, rxs) {
end = std::remove_if(l.begin(), end,
[rx](const QString &str) {
return rx.exactMatch(str);
});
}
l.erase(end, l.end());
return l;
}
namespace
{
struct File {
QString name;
QByteArray checksum;
bool binary;
};
}
static QString decode(const QString &encoded)
{
QString decoded;
decoded.reserve(encoded.size());
bool shift = false;
- Q_FOREACH (QChar ch, encoded)
+ for (QChar ch : encoded)
if (shift) {
switch (ch.toLatin1()) {
case '\\': decoded += QLatin1Char('\\'); break;
case 'n': decoded += QLatin1Char('\n'); break;
default:
qCDebug(KLEOPATRA_LOG) << "invalid escape sequence" << '\\' << ch << "(interpreted as '" << ch << "')";
decoded += ch;
break;
}
shift = false;
} else {
if (ch == QLatin1Char('\\')) {
shift = true;
} else {
decoded += ch;
}
}
return decoded;
}
static std::vector<File> parse_sum_file(const QString &fileName)
{
std::vector<File> files;
QFile f(fileName);
if (f.open(QIODevice::ReadOnly)) {
QTextStream s(&f);
QRegExp rx(QLatin1String("(\\?)([a-f0-9A-F]+) ([ *])([^\n]+)\n*"));
while (!s.atEnd()) {
const QString line = s.readLine();
if (rx.exactMatch(line)) {
assert(!rx.cap(4).endsWith(QLatin1Char('\n')));
const File file = {
rx.cap(1) == QLatin1String("\\") ? decode(rx.cap(4)) : rx.cap(4),
rx.cap(2).toLatin1(),
rx.cap(3) == QLatin1String("*"),
};
files.push_back(file);
}
}
}
return files;
}
static quint64 aggregate_size(const QDir &dir, const QStringList &files)
{
quint64 n = 0;
for (const QString &file : files) {
n += QFileInfo(dir.absoluteFilePath(file)).size();
}
return n;
}
static std::shared_ptr<ChecksumDefinition> filename2definition(const QString &fileName,
const std::vector< std::shared_ptr<ChecksumDefinition> > &checksumDefinitions)
{
Q_FOREACH (const std::shared_ptr<ChecksumDefinition> &cd, checksumDefinitions)
if (cd)
Q_FOREACH (const QString &pattern, cd->patterns())
if (QRegExp(pattern, fs_cs).exactMatch(fileName)) {
return cd;
}
return std::shared_ptr<ChecksumDefinition>();
}
static std::vector<Dir> find_dirs_by_sum_files(const QStringList &files, bool allowAddition,
const std::function<void(int)> &progress,
const std::vector< std::shared_ptr<ChecksumDefinition> > &checksumDefinitions)
{
const QList<QRegExp> patterns = get_patterns(checksumDefinitions);
std::vector<Dir> dirs;
dirs.reserve(files.size());
int i = 0;
- Q_FOREACH (const QString &file, files) {
+ for (const QString &file : files) {
const QFileInfo fi(file);
const QDir dir = fi.dir();
const QStringList entries = remove_checksum_files(dir.entryList(QDir::Files), patterns);
QStringList inputFiles;
if (allowAddition) {
inputFiles = entries;
} else {
const std::vector<File> parsed = parse_sum_file(fi.absoluteFilePath());
QStringList oldInputFiles;
oldInputFiles.reserve(parsed.size());
std::transform(parsed.cbegin(), parsed.cend(), std::back_inserter(oldInputFiles),
std::mem_fn(&File::name));
inputFiles = fs_intersect(oldInputFiles, entries);
}
const Dir item = {
dir,
fi.fileName(),
inputFiles,
aggregate_size(dir, inputFiles),
filename2definition(fi.fileName(), checksumDefinitions)
};
dirs.push_back(item);
if (progress) {
progress(++i);
}
}
return dirs;
}
namespace
{
struct less_dir : std::binary_function<QDir, QDir, bool> {
bool operator()(const QDir &lhs, const QDir &rhs) const
{
return QString::compare(lhs.absolutePath(), rhs.absolutePath(), fs_cs) < 0;
}
};
}
static std::vector<Dir> find_dirs_by_input_files(const QStringList &files, const std::shared_ptr<ChecksumDefinition> &checksumDefinition, bool allowAddition,
const std::function<void(int)> &progress,
const std::vector< std::shared_ptr<ChecksumDefinition> > &checksumDefinitions)
{
Q_UNUSED(allowAddition);
if (!checksumDefinition) {
return std::vector<Dir>();
}
const QList<QRegExp> patterns = get_patterns(checksumDefinitions);
std::map<QDir, QStringList, less_dir> dirs2files;
// Step 1: sort files by the dir they're contained in:
std::deque<QString> inputs(files.begin(), files.end());
int i = 0;
while (!inputs.empty()) {
const QString file = inputs.front();
inputs.pop_front();
const QFileInfo fi(file);
if (fi.isDir()) {
QDir dir(file);
dirs2files[ dir ] = remove_checksum_files(dir.entryList(QDir::Files), patterns);
const auto entryList = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
std::transform(entryList.cbegin(), entryList.cend(),
std::inserter(inputs, inputs.begin()),
[&dir](const QString &entry) {
return dir.absoluteFilePath(entry);
});
} else {
dirs2files[fi.dir()].push_back(file);
}
if (progress) {
progress(++i);
}
}
// Step 2: convert into vector<Dir>:
std::vector<Dir> dirs;
dirs.reserve(dirs2files.size());
for (std::map<QDir, QStringList, less_dir>::const_iterator it = dirs2files.begin(), end = dirs2files.end(); it != end; ++it) {
const QStringList inputFiles = remove_checksum_files(it->second, patterns);
if (inputFiles.empty()) {
continue;
}
const Dir dir = {
it->first,
checksumDefinition->outputFileName(),
inputFiles,
aggregate_size(it->first, inputFiles),
checksumDefinition
};
dirs.push_back(dir);
if (progress) {
progress(++i);
}
}
return dirs;
}
static QString process(const Dir &dir, bool *fatal)
{
const QString absFilePath = dir.dir.absoluteFilePath(dir.sumFile);
QTemporaryFile out;
QProcess p;
if (!out.open()) {
return QStringLiteral("Faile to open Temporary file.");
}
p.setWorkingDirectory(dir.dir.absolutePath());
p.setStandardOutputFile(out.fileName());
const QString program = dir.checksumDefinition->createCommand();
dir.checksumDefinition->startCreateCommand(&p, dir.inputFiles);
p.waitForFinished();
qCDebug(KLEOPATRA_LOG) << "[" << &p << "] Exit code " << p.exitCode();
if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) {
if (fatal && p.error() == QProcess::FailedToStart) {
*fatal = true;
}
if (p.error() == QProcess::UnknownError)
return i18n("Error while running %1: %2", program,
QString::fromLocal8Bit(p.readAllStandardError().trimmed().constData()));
else {
return i18n("Failed to execute %1: %2", program, p.errorString());
}
}
QFileInfo fi(absFilePath);
if (!(fi.exists() && !QFile::remove(absFilePath)) && QFile::rename(out.fileName(), absFilePath)) {
out.setAutoRemove(false);
return QString();
}
return xi18n("Failed to overwrite <filename>%1</filename>.", dir.sumFile);
}
namespace
{
static QDebug operator<<(QDebug s, const Dir &dir)
{
return s << "Dir(" << dir.dir << "->" << dir.sumFile << "<-(" << dir.totalSize << ')' << dir.inputFiles << ")\n";
}
}
void CreateChecksumsController::Private::run()
{
QMutexLocker locker(&mutex);
const QStringList files = this->files;
const std::vector< std::shared_ptr<ChecksumDefinition> > checksumDefinitions = this->checksumDefinitions;
const std::shared_ptr<ChecksumDefinition> checksumDefinition = this->checksumDefinition;
const bool allowAddition = this->allowAddition;
locker.unlock();
QStringList errors;
QStringList created;
if (!checksumDefinition) {
errors.push_back(i18n("No checksum programs defined."));
locker.relock();
this->errors = errors;
return;
} else {
qCDebug(KLEOPATRA_LOG) << "using checksum-definition" << checksumDefinition->id();
}
//
// Step 1: build a list of work to do (no progress):
//
const QString scanning = i18n("Scanning directories...");
Q_EMIT progress(0, 0, scanning);
const bool haveSumFiles = std::all_of(files.cbegin(), files.cend(), matches_any(get_patterns(checksumDefinitions)));
const auto progressCb = [this, &scanning](int c) { Q_EMIT progress(c, 0, scanning); };
const std::vector<Dir> dirs = haveSumFiles
? find_dirs_by_sum_files(files, allowAddition, progressCb, checksumDefinitions)
: find_dirs_by_input_files(files, checksumDefinition, allowAddition, progressCb, checksumDefinitions);
Q_FOREACH (const Dir &dir, dirs) {
qCDebug(KLEOPATRA_LOG) << dir;
}
if (!canceled) {
Q_EMIT progress(0, 0, i18n("Calculating total size..."));
const quint64 total = kdtools::accumulate_transform(dirs.cbegin(), dirs.cend(),
std::mem_fn(&Dir::totalSize),
Q_UINT64_C(0));
if (!canceled) {
//
// Step 2: perform work (with progress reporting):
//
// re-scale 'total' to fit into ints (wish QProgressDialog would use quint64...)
const quint64 factor = total / std::numeric_limits<int>::max() + 1;
quint64 done = 0;
Q_FOREACH (const Dir &dir, dirs) {
Q_EMIT progress(done / factor, total / factor,
i18n("Checksumming (%2) in %1", dir.checksumDefinition->label(), dir.dir.path()));
bool fatal = false;
const QString error = process(dir, &fatal);
if (!error.isEmpty()) {
errors.push_back(error);
} else {
created.push_back(dir.dir.absoluteFilePath(dir.sumFile));
}
done += dir.totalSize;
if (fatal || canceled) {
break;
}
}
Q_EMIT progress(done / factor, total / factor, i18n("Done."));
}
}
locker.relock();
this->errors = errors;
this->created = created;
// mutex unlocked by QMutexLocker
}
#include "moc_createchecksumscontroller.cpp"
#include "createchecksumscontroller.moc"
diff --git a/src/crypto/decryptverifytask.cpp b/src/crypto/decryptverifytask.cpp
index 23203b563..bf2825dc7 100644
--- a/src/crypto/decryptverifytask.cpp
+++ b/src/crypto/decryptverifytask.cpp
@@ -1,1496 +1,1496 @@
/* -*- mode: c++; c-basic-offset:4 -*-
decryptverifytask.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2008 Klarälvdalens Datakonsult AB
Kleopatra 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.
Kleopatra 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "decryptverifytask.h"
#include <QGpgME/Protocol>
#include <QGpgME/VerifyOpaqueJob>
#include <QGpgME/VerifyDetachedJob>
#include <QGpgME/DecryptJob>
#include <QGpgME/DecryptVerifyJob>
#include <Libkleo/Dn>
#include <Libkleo/Exception>
#include <Libkleo/Stl_Util>
#include <Libkleo/KeyCache>
#include <Libkleo/Predicates>
#include <Libkleo/Formatting>
#include <Libkleo/Classify>
#include <utils/detail_p.h>
#include <utils/input.h>
#include <utils/output.h>
#include <utils/kleo_assert.h>
#include <utils/auditlog.h>
#include <kmime/kmime_header_parsing.h>
#include <gpgme++/error.h>
#include <gpgme++/key.h>
#include <gpgme++/verificationresult.h>
#include <gpgme++/decryptionresult.h>
#include <gpg-error.h>
#include "kleopatra_debug.h"
#include <KLocalizedString>
#include <QLocale>
#include <QByteArray>
#include <QDateTime>
#include <QStringList>
#include <QTextDocument> // Qt::escape
#include <algorithm>
#include <cassert>
#include <sstream>
using namespace Kleo::Crypto;
using namespace Kleo;
using namespace GpgME;
using namespace KMime::Types;
namespace
{
static Error make_error(const gpg_err_code_t code)
{
return Error(gpg_error(code));
}
static AuditLog auditLogFromSender(QObject *sender)
{
return AuditLog::fromJob(qobject_cast<const QGpgME::Job *>(sender));
}
static bool addrspec_equal(const AddrSpec &lhs, const AddrSpec &rhs, Qt::CaseSensitivity cs)
{
return lhs.localPart.compare(rhs.localPart, cs) == 0 && lhs.domain.compare(rhs.domain, Qt::CaseInsensitive) == 0;
}
static bool mailbox_equal(const Mailbox &lhs, const Mailbox &rhs, Qt::CaseSensitivity cs)
{
return addrspec_equal(lhs.addrSpec(), rhs.addrSpec(), cs);
}
static std::string stripAngleBrackets(const std::string &str)
{
if (str.empty()) {
return str;
}
if (str[0] == '<' && str[str.size() - 1] == '>') {
return str.substr(1, str.size() - 2);
}
return str;
}
static std::string email(const UserID &uid)
{
if (uid.parent().protocol() == OpenPGP) {
if (const char *const email = uid.email()) {
return stripAngleBrackets(email);
} else {
return std::string();
}
}
assert(uid.parent().protocol() == CMS);
if (const char *const id = uid.id())
if (*id == '<') {
return stripAngleBrackets(id);
} else {
return DN(id)[QStringLiteral("EMAIL")].trimmed().toUtf8().constData();
}
else {
return std::string();
}
}
static Mailbox mailbox(const UserID &uid)
{
const std::string e = email(uid);
Mailbox mbox;
if (!e.empty()) {
mbox.setAddress(e.c_str());
}
return mbox;
}
static std::vector<Mailbox> extractMailboxes(const Key &key)
{
std::vector<Mailbox> res;
Q_FOREACH (const UserID &id, key.userIDs()) {
const Mailbox mbox = mailbox(id);
if (!mbox.addrSpec().isEmpty()) {
res.push_back(mbox);
}
}
return res;
}
static std::vector<Mailbox> extractMailboxes(const std::vector<Key> &signers)
{
std::vector<Mailbox> res;
- Q_FOREACH (const Key &i, signers) {
+ for (const Key &i : signers) {
const std::vector<Mailbox> bxs = extractMailboxes(i);
res.insert(res.end(), bxs.begin(), bxs.end());
}
return res;
}
static bool keyContainsMailbox(const Key &key, const Mailbox &mbox)
{
const std::vector<Mailbox> mbxs = extractMailboxes(key);
return std::find_if(mbxs.cbegin(), mbxs.cend(),
[mbox](const Mailbox &m) {
return mailbox_equal(mbox, m, Qt::CaseInsensitive);
}) != mbxs.cend();
}
static bool keysContainMailbox(const std::vector<Key> &keys, const Mailbox &mbox)
{
return std::find_if(keys.cbegin(), keys.cend(),
[mbox](const Key &key) {
return keyContainsMailbox(key, mbox);
}) != keys.cend();
}
static bool relevantInDecryptVerifyContext(const VerificationResult &r)
{
// for D/V operations, we ignore verification results which are not errors and contain
// no signatures (which means that the data was just not signed)
return r.error() || r.numSignatures() > 0;
}
static QString signatureSummaryToString(int summary)
{
if (summary & Signature::None) {
return i18n("Error: Signature not verified");
} else if (summary & Signature::Valid || summary & Signature::Green) {
return i18n("Good signature");
} else if (summary & Signature::Red) {
return i18n("Bad signature");
} else if (summary & Signature::KeyRevoked) {
return i18n("Signing certificate revoked");
} else if (summary & Signature::KeyExpired) {
return i18n("Signing certificate expired");
} else if (summary & Signature::KeyMissing) {
return i18n("No public certificate to verify the signature");
} else if (summary & Signature::SigExpired) {
return i18n("Signature expired");
#if 0 //Duplicate entry
} else if (summary & Signature::KeyMissing) {
return i18n("Certificate missing");
#endif
} else if (summary & Signature::CrlMissing) {
return i18n("CRL missing");
} else if (summary & Signature::CrlTooOld) {
return i18n("CRL too old");
} else if (summary & Signature::BadPolicy) {
return i18n("Bad policy");
} else if (summary & Signature::SysError) {
return i18n("System error"); //### retrieve system error details?
}
return QString();
}
static QString formatValidSignatureWithTrustLevel(const UserID &id)
{
if (id.isNull()) {
return QString();
}
switch (id.validity()) {
case UserID::Marginal:
return i18n("The signature is valid but the trust in the certificate's validity is only marginal.");
case UserID::Full:
return i18n("The signature is valid and the certificate's validity is fully trusted.");
case UserID::Ultimate:
return i18n("The signature is valid and the certificate's validity is ultimately trusted.");
case UserID::Never:
return i18n("The signature is valid but the certificate's validity is <em>not trusted</em>.");
case UserID::Unknown:
return i18n("The signature is valid but the certificate's validity is unknown.");
case UserID::Undefined:
default:
return i18n("The signature is valid but the certificate's validity is undefined.");
}
}
static QString renderFingerprint(const char *fpr)
{
if (!fpr) {
return QString();
}
return QStringLiteral("0x%1").arg(QString::fromLatin1(fpr).toUpper());
}
static QString renderKeyLink(const QString &fpr, const QString &text)
{
return QStringLiteral("<a href=\"key:%1\">%2</a>").arg(fpr, text);
}
static QString renderKey(const Key &key)
{
if (key.isNull()) {
return i18n("Unknown certificate");
}
return renderKeyLink(QLatin1String(key.primaryFingerprint()), Formatting::prettyName(key));
}
static QString renderKeyEMailOnlyNameAsFallback(const Key &key)
{
if (key.isNull()) {
return i18n("Unknown certificate");
}
const QString email = Formatting::prettyEMail(key);
const QString user = !email.isEmpty() ? email : Formatting::prettyName(key);
return renderKeyLink(QLatin1String(key.primaryFingerprint()), user);
}
static QString formatDate(const QDateTime &dt)
{
return QLocale().toString(dt);
}
static QString formatSigningInformation(const Signature &sig, const Key &key)
{
if (sig.isNull()) {
return QString();
}
const QDateTime dt = sig.creationTime() != 0 ? QDateTime::fromTime_t(sig.creationTime()) : QDateTime();
const QString signer = key.isNull() ? QString() : renderKeyEMailOnlyNameAsFallback(key);
const bool haveKey = !key.isNull();
const bool haveSigner = !signer.isEmpty();
const bool haveDate = dt.isValid();
if (!haveKey) {
if (haveDate) {
return i18n("Signed on %1 with unknown certificate %2.", formatDate(dt), renderFingerprint(sig.fingerprint()));
} else {
return i18n("Signed with unknown certificate %1.", renderFingerprint(sig.fingerprint()));
}
}
if (haveSigner) {
if (haveDate)
return i18nc("date, key owner, key ID",
"Signed on %1 by %2 (Key ID: %3).",
formatDate(dt),
signer,
renderFingerprint(key.shortKeyID()));
else {
return i18n("Signed by %1 with certificate %2.", signer, renderKey(key));
}
}
if (haveDate) {
return i18n("Signed on %1 with certificate %2.", formatDate(dt), renderKey(key));
}
return i18n("Signed with certificate %1.", renderKey(key));
}
static QString strikeOut(const QString &str, bool strike)
{
return QString(strike ? QStringLiteral("<s>%1</s>") : QStringLiteral("%1")).arg(str.toHtmlEscaped());
}
static QString formatInputOutputLabel(const QString &input, const QString &output, bool inputDeleted, bool outputDeleted)
{
if (output.isEmpty()) {
return strikeOut(input, inputDeleted);
}
return i18nc("Input file --> Output file (rarr is arrow", "%1 &rarr; %2",
strikeOut(input, inputDeleted),
strikeOut(output, outputDeleted));
}
static bool IsErrorOrCanceled(const GpgME::Error &err)
{
return err || err.isCanceled();
}
static bool IsErrorOrCanceled(const Result &res)
{
return IsErrorOrCanceled(res.error());
}
static bool IsBad(const Signature &sig)
{
return sig.summary() & Signature::Red;
}
static bool IsGoodOrValid(const Signature &sig)
{
return (sig.summary() & Signature::Valid) || (sig.summary() & Signature::Green);
}
static UserID findUserIDByMailbox(const Key &key, const Mailbox &mbox)
{
Q_FOREACH (const UserID &id, key.userIDs())
if (mailbox_equal(mailbox(id), mbox, Qt::CaseInsensitive)) {
return id;
}
return UserID();
}
}
class DecryptVerifyResult::SenderInfo
{
public:
explicit SenderInfo(const Mailbox &infSender, const std::vector<Key> &signers_) : informativeSender(infSender), signers(signers_) {}
const Mailbox informativeSender;
const std::vector<Key> signers;
bool hasInformativeSender() const
{
return !informativeSender.addrSpec().isEmpty();
}
bool conflicts() const
{
return hasInformativeSender() && hasKeys() && !keysContainMailbox(signers, informativeSender);
}
bool hasKeys() const
{
return std::any_of(signers.cbegin(), signers.cend(), [](const Key &key) { return !key.isNull(); });
}
std::vector<Mailbox> signerMailboxes() const
{
return extractMailboxes(signers);
}
};
namespace
{
static Task::Result::VisualCode codeForVerificationResult(const VerificationResult &res)
{
if (res.isNull()) {
return Task::Result::NeutralSuccess;
}
const std::vector<Signature> sigs = res.signatures();
if (sigs.empty()) {
return Task::Result::Warning;
}
if (std::find_if(sigs.begin(), sigs.end(), IsBad) != sigs.end()) {
return Task::Result::Danger;
}
if ((size_t)std::count_if(sigs.begin(), sigs.end(), IsGoodOrValid) == sigs.size()) {
return Task::Result::AllGood;
}
return Task::Result::Warning;
}
static QString formatVerificationResultOverview(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info)
{
if (res.isNull()) {
return QString();
}
const Error err = res.error();
if (err.isCanceled()) {
return i18n("<b>Verification canceled.</b>");
} else if (err) {
return i18n("<b>Verification failed: %1.</b>", QString::fromLocal8Bit(err.asString()).toHtmlEscaped());
}
const std::vector<Signature> sigs = res.signatures();
const std::vector<Key> signers = info.signers;
if (sigs.empty()) {
return i18n("<b>No signatures found.</b>");
}
const uint bad = std::count_if(sigs.cbegin(), sigs.cend(), IsBad);
if (bad > 0) {
return i18np("<b>Invalid signature.</b>", "<b>%1 invalid signatures.</b>", bad);
}
const uint warn = std::count_if(sigs.cbegin(), sigs.cend(), [](const Signature &sig) { return !IsGoodOrValid(sig); });
if (warn > 0) {
return i18np("<b>Not enough information to check signature validity.</b>", "<b>%1 signatures could not be verified.</b>", warn);
}
//Good signature:
QString text;
if (sigs.size() == 1) {
const Key key = DecryptVerifyResult::keyForSignature(sigs[0], signers);
if (key.isNull()) {
return i18n("<b>Signature is valid.</b>");
}
text = i18n("<b>Signed by %1</b>", renderKeyEMailOnlyNameAsFallback(key));
if (info.conflicts())
text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the %1 used for signing.",
renderKeyLink(QLatin1String(key.primaryFingerprint()), i18n("certificate")));
} else {
text = i18np("<b>Valid signature.</b>", "<b>%1 valid signatures.</b>", sigs.size());
if (info.conflicts()) {
text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the certificates used for signing.");
}
}
return text;
}
static QString formatDecryptionResultOverview(const DecryptionResult &result, const QString &errorString = QString())
{
const Error err = result.error();
if (err.isCanceled()) {
return i18n("<b>Decryption canceled.</b>");
} else if (!errorString.isEmpty()) {
return i18n("<b>Decryption failed: %1.</b>", errorString.toHtmlEscaped());
} else if (err) {
return i18n("<b>Decryption failed: %1.</b>", QString::fromLocal8Bit(err.asString()).toHtmlEscaped());
}
return i18n("<b>Decryption succeeded.</b>");
}
static QString formatSignature(const Signature &sig, const Key &key, const DecryptVerifyResult::SenderInfo &info)
{
if (sig.isNull()) {
return QString();
}
const QString text = formatSigningInformation(sig, key) + QLatin1String("<br/>");
const bool red = sig.summary() & Signature::Red;
if (sig.summary() & Signature::Valid) {
const UserID id = findUserIDByMailbox(key, info.informativeSender);
return text + formatValidSignatureWithTrustLevel(!id.isNull() ? id : key.userID(0));
}
if (red) {
return text + i18n("The signature is bad.");
}
if (!sig.summary()) {
return text + i18n("The validity of the signature cannot be verified.");
}
return text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
}
static QStringList format(const std::vector<Mailbox> &mbxs)
{
QStringList res;
std::transform(mbxs.cbegin(), mbxs.cend(), std::back_inserter(res),
[](const Mailbox &mbox) {
return mbox.prettyAddress();
});
return res;
}
static QString formatVerificationResultDetails(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info, const QString &errorString)
{
if ((res.error().code() == GPG_ERR_EIO || res.error().code() == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
return i18n("Input error: %1", errorString);
}
const std::vector<Signature> sigs = res.signatures();
const std::vector<Key> signers = KeyCache::instance()->findSigners(res);
QString details;
- Q_FOREACH (const Signature &sig, sigs) {
+ for (const Signature &sig : sigs) {
details += formatSignature(sig, DecryptVerifyResult::keyForSignature(sig, signers), info) + QLatin1Char('\n');
}
details = details.trimmed();
details.replace(QLatin1Char('\n'), QStringLiteral("<br/>"));
if (info.conflicts()) {
details += i18n("<p>The sender's address %1 is not stored in the certificate. Stored: %2</p>", info.informativeSender.prettyAddress(), format(info.signerMailboxes()).join(i18nc("separator for a list of e-mail addresses", ", ")));
}
return details;
}
static QString formatDecryptionResultDetails(const DecryptionResult &res, const std::vector<Key> &recipients, const QString &errorString)
{
if ((res.error().code() == GPG_ERR_EIO || res.error().code() == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
return i18n("Input error: %1", errorString);
}
if (res.isNull() || !res.error() || res.error().isCanceled()) {
return QString();
}
if (recipients.empty() && res.numRecipients() > 0) {
return QLatin1String("<i>") + i18np("One unknown recipient.", "%1 unknown recipients.", res.numRecipients()) + QLatin1String("</i>");
}
QString details;
if (!recipients.empty()) {
details += i18np("Recipient:", "Recipients:", res.numRecipients());
if (res.numRecipients() == 1) {
return details + renderKey(recipients.front());
}
details += QLatin1String("<ul>");
Q_FOREACH (const Key &key, recipients) {
details += QLatin1String("<li>") + renderKey(key) + QLatin1String("</li>");
}
if (recipients.size() < res.numRecipients())
details += QLatin1String("<li><i>") + i18np("One unknown recipient", "%1 unknown recipients",
res.numRecipients() - recipients.size()) + QLatin1String("</i></li>");
details += QLatin1String("</ul>");
}
return details;
}
static QString formatDecryptVerifyResultOverview(const DecryptionResult &dr, const VerificationResult &vr, const DecryptVerifyResult::SenderInfo &info)
{
if (IsErrorOrCanceled(dr) || !relevantInDecryptVerifyContext(vr)) {
return formatDecryptionResultOverview(dr);
}
return formatVerificationResultOverview(vr, info);
}
static QString formatDecryptVerifyResultDetails(const DecryptionResult &dr,
const VerificationResult &vr,
const std::vector<Key> &recipients,
const DecryptVerifyResult::SenderInfo &info,
const QString &errorString)
{
const QString drDetails = formatDecryptionResultDetails(dr, recipients, errorString);
if (IsErrorOrCanceled(dr) || !relevantInDecryptVerifyContext(vr)) {
return drDetails;
}
return drDetails + (drDetails.isEmpty() ? QString() : QStringLiteral("<br/>")) + formatVerificationResultDetails(vr, info, errorString);
}
} // anon namespace
class DecryptVerifyResult::Private
{
DecryptVerifyResult *const q;
public:
Private(DecryptVerifyOperation type,
const VerificationResult &vr,
const DecryptionResult &dr,
const QByteArray &stuff,
int errCode,
const QString &errString,
const QString &input,
const QString &output,
const AuditLog &auditLog,
const Mailbox &informativeSender,
DecryptVerifyResult *qq) :
q(qq),
m_type(type),
m_verificationResult(vr),
m_decryptionResult(dr),
m_stuff(stuff),
m_error(errCode),
m_errorString(errString),
m_inputLabel(input),
m_outputLabel(output),
m_auditLog(auditLog),
m_informativeSender(informativeSender)
{
}
QString label() const
{
return formatInputOutputLabel(m_inputLabel, m_outputLabel, false, q->hasError());
}
DecryptVerifyResult::SenderInfo makeSenderInfo() const;
bool isDecryptOnly() const
{
return m_type == Decrypt;
}
bool isVerifyOnly() const
{
return m_type == Verify;
}
bool isDecryptVerify() const
{
return m_type == DecryptVerify;
}
DecryptVerifyOperation m_type;
VerificationResult m_verificationResult;
DecryptionResult m_decryptionResult;
QByteArray m_stuff;
int m_error;
QString m_errorString;
QString m_inputLabel;
QString m_outputLabel;
const AuditLog m_auditLog;
const Mailbox m_informativeSender;
};
DecryptVerifyResult::SenderInfo DecryptVerifyResult::Private::makeSenderInfo() const
{
return SenderInfo(m_informativeSender, KeyCache::instance()->findSigners(m_verificationResult));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptResult(const DecryptionResult &dr, const QByteArray &plaintext, const AuditLog &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Decrypt,
VerificationResult(),
dr,
plaintext,
0,
QString(),
inputLabel(),
outputLabel(),
auditLog,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptResult(const GpgME::Error &err, const QString &what, const AuditLog &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Decrypt,
VerificationResult(),
DecryptionResult(err),
QByteArray(),
err.code(),
what,
inputLabel(),
outputLabel(),
auditLog,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptVerifyResult(const DecryptionResult &dr, const VerificationResult &vr, const QByteArray &plaintext, const AuditLog &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
DecryptVerify,
vr,
dr,
plaintext,
0,
QString(),
inputLabel(),
outputLabel(),
auditLog,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptVerifyResult(const GpgME::Error &err, const QString &details, const AuditLog &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
DecryptVerify,
VerificationResult(),
DecryptionResult(err),
QByteArray(),
err.code(),
details,
inputLabel(),
outputLabel(),
auditLog,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const VerificationResult &vr, const QByteArray &plaintext, const AuditLog &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Verify,
vr,
DecryptionResult(),
plaintext,
0,
QString(),
inputLabel(),
outputLabel(),
auditLog,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const GpgME::Error &err, const QString &details, const AuditLog &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Verify,
VerificationResult(err),
DecryptionResult(),
QByteArray(),
err.code(),
details,
inputLabel(),
outputLabel(),
auditLog,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyDetachedResult(const VerificationResult &vr, const AuditLog &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Verify,
vr,
DecryptionResult(),
QByteArray(),
0,
QString(),
inputLabel(),
outputLabel(),
auditLog,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyDetachedResult(const GpgME::Error &err, const QString &details, const AuditLog &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Verify,
VerificationResult(err),
DecryptionResult(),
QByteArray(),
err.code(),
details,
inputLabel(),
outputLabel(),
auditLog,
informativeSender()));
}
DecryptVerifyResult::DecryptVerifyResult(DecryptVerifyOperation type,
const VerificationResult &vr,
const DecryptionResult &dr,
const QByteArray &stuff,
int errCode,
const QString &errString,
const QString &inputLabel,
const QString &outputLabel,
const AuditLog &auditLog,
const Mailbox &informativeSender)
: Task::Result(), d(new Private(type, vr, dr, stuff, errCode, errString, inputLabel, outputLabel, auditLog, informativeSender, this))
{
}
QString DecryptVerifyResult::overview() const
{
QString ov;
if (d->isDecryptOnly()) {
ov = formatDecryptionResultOverview(d->m_decryptionResult);
} else if (d->isVerifyOnly()) {
ov = formatVerificationResultOverview(d->m_verificationResult, d->makeSenderInfo());
} else {
ov = formatDecryptVerifyResultOverview(d->m_decryptionResult, d->m_verificationResult, d->makeSenderInfo());
}
return i18nc("label: result example: foo.sig: Verification failed. ", "%1: %2", d->label(), ov);
}
QString DecryptVerifyResult::details() const
{
if (d->isDecryptOnly()) {
return formatDecryptionResultDetails(d->m_decryptionResult, KeyCache::instance()->findRecipients(d->m_decryptionResult), errorString());
}
if (d->isVerifyOnly()) {
return formatVerificationResultDetails(d->m_verificationResult, d->makeSenderInfo(), errorString());
}
return formatDecryptVerifyResultDetails(d->m_decryptionResult,
d->m_verificationResult, KeyCache::instance()->findRecipients(
d->m_decryptionResult), d->makeSenderInfo(), errorString());
}
bool DecryptVerifyResult::hasError() const
{
return d->m_error != 0;
}
int DecryptVerifyResult::errorCode() const
{
return d->m_error;
}
QString DecryptVerifyResult::errorString() const
{
return d->m_errorString;
}
AuditLog DecryptVerifyResult::auditLog() const
{
return d->m_auditLog;
}
Task::Result::VisualCode DecryptVerifyResult::code() const
{
if ((d->m_type == DecryptVerify || d->m_type == Verify) && relevantInDecryptVerifyContext(verificationResult())) {
return codeForVerificationResult(verificationResult());
}
return hasError() ? NeutralError : NeutralSuccess;
}
GpgME::VerificationResult DecryptVerifyResult::verificationResult() const
{
return d->m_verificationResult;
}
const Key &DecryptVerifyResult::keyForSignature(const Signature &sig, const std::vector<Key> &keys)
{
if (const char *const fpr = sig.fingerprint()) {
const std::vector<Key>::const_iterator it
= std::lower_bound(keys.begin(), keys.end(), fpr, _detail::ByFingerprint<std::less>());
if (it != keys.end() && _detail::ByFingerprint<std::equal_to>()(*it, fpr)) {
return *it;
}
}
static const Key null;
return null;
}
class AbstractDecryptVerifyTask::Private
{
public:
Mailbox informativeSender;
};
AbstractDecryptVerifyTask::AbstractDecryptVerifyTask(QObject *parent) : Task(parent), d(new Private) {}
AbstractDecryptVerifyTask::~AbstractDecryptVerifyTask() {}
Mailbox AbstractDecryptVerifyTask::informativeSender() const
{
return d->informativeSender;
}
void AbstractDecryptVerifyTask::setInformativeSender(const Mailbox &sender)
{
d->informativeSender = sender;
}
class DecryptVerifyTask::Private
{
DecryptVerifyTask *const q;
public:
explicit Private(DecryptVerifyTask *qq) : q(qq), m_backend(nullptr), m_protocol(UnknownProtocol) {}
void slotResult(const DecryptionResult &, const VerificationResult &, const QByteArray &);
void registerJob(QGpgME::DecryptVerifyJob *job)
{
q->connect(job, SIGNAL(result(GpgME::DecryptionResult,GpgME::VerificationResult,QByteArray)),
q, SLOT(slotResult(GpgME::DecryptionResult,GpgME::VerificationResult,QByteArray)));
q->connect(job, SIGNAL(progress(QString,int,int)),
q, SLOT(setProgress(QString,int,int)));
}
void emitResult(const std::shared_ptr<DecryptVerifyResult> &result);
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend;
Protocol m_protocol;
};
void DecryptVerifyTask::Private::emitResult(const std::shared_ptr<DecryptVerifyResult> &result)
{
q->emitResult(result);
Q_EMIT q->decryptVerifyResult(result);
}
void DecryptVerifyTask::Private::slotResult(const DecryptionResult &dr, const VerificationResult &vr, const QByteArray &plainText)
{
{
std::stringstream ss;
ss << dr << '\n' << vr;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLog auditLog = auditLogFromSender(q->sender());
if (dr.error().code() || vr.error().code()) {
m_output->cancel();
} else {
try {
kleo_assert(!dr.isNull() || !vr.isNull());
m_output->finalize();
} catch (const GpgME::Exception &e) {
emitResult(q->fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
} catch (const std::exception &e) {
emitResult(q->fromDecryptResult(make_error(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
return;
} catch (...) {
emitResult(q->fromDecryptResult(make_error(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
return;
}
}
const int drErr = dr.error().code();
const QString errorString = m_output->errorString();
if ((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
emitResult(q->fromDecryptResult(dr.error(), errorString, auditLog));
return;
}
emitResult(q->fromDecryptVerifyResult(dr, vr, plainText, auditLog));
}
DecryptVerifyTask::DecryptVerifyTask(QObject *parent) : AbstractDecryptVerifyTask(parent), d(new Private(this))
{
}
DecryptVerifyTask::~DecryptVerifyTask()
{
}
void DecryptVerifyTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void DecryptVerifyTask::setOutput(const std::shared_ptr<Output> &output)
{
d->m_output = output;
kleo_assert(d->m_output && d->m_output->ioDevice());
}
void DecryptVerifyTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = prot == GpgME::OpenPGP ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void DecryptVerifyTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND), i18n("Could not determine whether this is an S/MIME or an OpenPGP signature/ciphertext - maybe it is neither ciphertext nor a signature?"), Exception::MessageOnly);
}
setProtocol(p);
}
QString DecryptVerifyTask::label() const
{
return i18n("Decrypting: %1...", d->m_input->label());
}
unsigned long long DecryptVerifyTask::inputSize() const
{
return d->m_input ? d->m_input->size() : 0;
}
QString DecryptVerifyTask::inputLabel() const
{
return d->m_input ? d->m_input->label() : QString();
}
QString DecryptVerifyTask::outputLabel() const
{
return d->m_output ? d->m_output->label() : QString();
}
Protocol DecryptVerifyTask::protocol() const
{
return d->m_protocol;
}
void DecryptVerifyTask::cancel()
{
}
void DecryptVerifyTask::doStart()
{
kleo_assert(d->m_backend);
try {
QGpgME::DecryptVerifyJob *const job = d->m_backend->decryptVerifyJob();
kleo_assert(job);
d->registerJob(job);
job->start(d->m_input->ioDevice(), d->m_output->ioDevice());
} catch (const GpgME::Exception &e) {
d->emitResult(fromDecryptVerifyResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLog()));
} catch (const std::exception &e) {
d->emitResult(fromDecryptVerifyResult(make_error(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLog()));
} catch (...) {
d->emitResult(fromDecryptVerifyResult(make_error(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLog()));
}
}
class DecryptTask::Private
{
DecryptTask *const q;
public:
explicit Private(DecryptTask *qq) : q(qq), m_backend(nullptr), m_protocol(UnknownProtocol) {}
void slotResult(const DecryptionResult &, const QByteArray &);
void registerJob(QGpgME::DecryptJob *job)
{
q->connect(job, SIGNAL(result(GpgME::DecryptionResult,QByteArray)),
q, SLOT(slotResult(GpgME::DecryptionResult,QByteArray)));
q->connect(job, SIGNAL(progress(QString,int,int)),
q, SLOT(setProgress(QString,int,int)));
}
void emitResult(const std::shared_ptr<DecryptVerifyResult> &result);
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend;
Protocol m_protocol;
};
void DecryptTask::Private::emitResult(const std::shared_ptr<DecryptVerifyResult> &result)
{
q->emitResult(result);
Q_EMIT q->decryptVerifyResult(result);
}
void DecryptTask::Private::slotResult(const DecryptionResult &result, const QByteArray &plainText)
{
{
std::stringstream ss;
ss << result;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLog auditLog = auditLogFromSender(q->sender());
if (result.error().code()) {
m_output->cancel();
} else {
try {
kleo_assert(!result.isNull());
m_output->finalize();
} catch (const GpgME::Exception &e) {
emitResult(q->fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
} catch (const std::exception &e) {
emitResult(q->fromDecryptResult(make_error(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
return;
} catch (...) {
emitResult(q->fromDecryptResult(make_error(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
return;
}
}
const int drErr = result.error().code();
const QString errorString = m_output->errorString();
if ((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
emitResult(q->fromDecryptResult(result.error(), errorString, auditLog));
return;
}
emitResult(q->fromDecryptResult(result, plainText, auditLog));
}
DecryptTask::DecryptTask(QObject *parent) : AbstractDecryptVerifyTask(parent), d(new Private(this))
{
}
DecryptTask::~DecryptTask()
{
}
void DecryptTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void DecryptTask::setOutput(const std::shared_ptr<Output> &output)
{
d->m_output = output;
kleo_assert(d->m_output && d->m_output->ioDevice());
}
void DecryptTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void DecryptTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND), i18n("Could not determine whether this was S/MIME- or OpenPGP-encrypted - maybe it is not ciphertext at all?"), Exception::MessageOnly);
}
setProtocol(p);
}
QString DecryptTask::label() const
{
return i18n("Decrypting: %1...", d->m_input->label());
}
unsigned long long DecryptTask::inputSize() const
{
return d->m_input ? d->m_input->size() : 0;
}
QString DecryptTask::inputLabel() const
{
return d->m_input ? d->m_input->label() : QString();
}
QString DecryptTask::outputLabel() const
{
return d->m_output ? d->m_output->label() : QString();
}
Protocol DecryptTask::protocol() const
{
kleo_assert(!"not implemented");
return UnknownProtocol; // ### TODO
}
void DecryptTask::cancel()
{
}
void DecryptTask::doStart()
{
kleo_assert(d->m_backend);
try {
QGpgME::DecryptJob *const job = d->m_backend->decryptJob();
kleo_assert(job);
d->registerJob(job);
job->start(d->m_input->ioDevice(), d->m_output->ioDevice());
} catch (const GpgME::Exception &e) {
d->emitResult(fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLog()));
} catch (const std::exception &e) {
d->emitResult(fromDecryptResult(make_error(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLog()));
} catch (...) {
d->emitResult(fromDecryptResult(make_error(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLog()));
}
}
class VerifyOpaqueTask::Private
{
VerifyOpaqueTask *const q;
public:
explicit Private(VerifyOpaqueTask *qq) : q(qq), m_backend(nullptr), m_protocol(UnknownProtocol) {}
void slotResult(const VerificationResult &, const QByteArray &);
void registerJob(QGpgME::VerifyOpaqueJob *job)
{
q->connect(job, SIGNAL(result(GpgME::VerificationResult,QByteArray)),
q, SLOT(slotResult(GpgME::VerificationResult,QByteArray)));
q->connect(job, SIGNAL(progress(QString,int,int)),
q, SLOT(setProgress(QString,int,int)));
}
void emitResult(const std::shared_ptr<DecryptVerifyResult> &result);
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend;
Protocol m_protocol;
};
void VerifyOpaqueTask::Private::emitResult(const std::shared_ptr<DecryptVerifyResult> &result)
{
q->emitResult(result);
Q_EMIT q->decryptVerifyResult(result);
}
void VerifyOpaqueTask::Private::slotResult(const VerificationResult &result, const QByteArray &plainText)
{
{
std::stringstream ss;
ss << result;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLog auditLog = auditLogFromSender(q->sender());
if (result.error().code()) {
m_output->cancel();
} else {
try {
kleo_assert(!result.isNull());
m_output->finalize();
} catch (const GpgME::Exception &e) {
emitResult(q->fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
} catch (const std::exception &e) {
emitResult(q->fromDecryptResult(make_error(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
return;
} catch (...) {
emitResult(q->fromDecryptResult(make_error(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
return;
}
}
const int drErr = result.error().code();
const QString errorString = m_output->errorString();
if ((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
emitResult(q->fromDecryptResult(result.error(), errorString, auditLog));
return;
}
emitResult(q->fromVerifyOpaqueResult(result, plainText, auditLog));
}
VerifyOpaqueTask::VerifyOpaqueTask(QObject *parent) : AbstractDecryptVerifyTask(parent), d(new Private(this))
{
}
VerifyOpaqueTask::~VerifyOpaqueTask()
{
}
void VerifyOpaqueTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void VerifyOpaqueTask::setOutput(const std::shared_ptr<Output> &output)
{
d->m_output = output;
kleo_assert(d->m_output && d->m_output->ioDevice());
}
void VerifyOpaqueTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void VerifyOpaqueTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND), i18n("Could not determine whether this is an S/MIME or an OpenPGP signature - maybe it is not a signature at all?"), Exception::MessageOnly);
}
setProtocol(p);
}
QString VerifyOpaqueTask::label() const
{
return i18n("Verifying: %1...", d->m_input->label());
}
unsigned long long VerifyOpaqueTask::inputSize() const
{
return d->m_input ? d->m_input->size() : 0;
}
QString VerifyOpaqueTask::inputLabel() const
{
return d->m_input ? d->m_input->label() : QString();
}
QString VerifyOpaqueTask::outputLabel() const
{
return d->m_output ? d->m_output->label() : QString();
}
Protocol VerifyOpaqueTask::protocol() const
{
return d->m_protocol;
}
void VerifyOpaqueTask::cancel()
{
}
void VerifyOpaqueTask::doStart()
{
kleo_assert(d->m_backend);
try {
QGpgME::VerifyOpaqueJob *const job = d->m_backend->verifyOpaqueJob();
kleo_assert(job);
d->registerJob(job);
job->start(d->m_input->ioDevice(), d->m_output ? d->m_output->ioDevice() : std::shared_ptr<QIODevice>());
} catch (const GpgME::Exception &e) {
d->emitResult(fromVerifyOpaqueResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLog()));
} catch (const std::exception &e) {
d->emitResult(fromVerifyOpaqueResult(make_error(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLog()));
} catch (...) {
d->emitResult(fromVerifyOpaqueResult(make_error(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLog()));
}
}
class VerifyDetachedTask::Private
{
VerifyDetachedTask *const q;
public:
explicit Private(VerifyDetachedTask *qq) : q(qq), m_backend(nullptr), m_protocol(UnknownProtocol) {}
void slotResult(const VerificationResult &);
void registerJob(QGpgME::VerifyDetachedJob *job)
{
q->connect(job, SIGNAL(result(GpgME::VerificationResult)),
q, SLOT(slotResult(GpgME::VerificationResult)));
q->connect(job, SIGNAL(progress(QString,int,int)),
q, SLOT(setProgress(QString,int,int)));
}
void emitResult(const std::shared_ptr<DecryptVerifyResult> &result);
std::shared_ptr<Input> m_input, m_signedData;
const QGpgME::Protocol *m_backend;
Protocol m_protocol;
};
void VerifyDetachedTask::Private::emitResult(const std::shared_ptr<DecryptVerifyResult> &result)
{
q->emitResult(result);
Q_EMIT q->decryptVerifyResult(result);
}
void VerifyDetachedTask::Private::slotResult(const VerificationResult &result)
{
{
std::stringstream ss;
ss << result;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLog auditLog = auditLogFromSender(q->sender());
try {
kleo_assert(!result.isNull());
emitResult(q->fromVerifyDetachedResult(result, auditLog));
} catch (const GpgME::Exception &e) {
emitResult(q->fromVerifyDetachedResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
} catch (const std::exception &e) {
emitResult(q->fromVerifyDetachedResult(make_error(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
} catch (...) {
emitResult(q->fromVerifyDetachedResult(make_error(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
}
}
VerifyDetachedTask::VerifyDetachedTask(QObject *parent) : AbstractDecryptVerifyTask(parent), d(new Private(this))
{
}
VerifyDetachedTask::~VerifyDetachedTask()
{
}
void VerifyDetachedTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void VerifyDetachedTask::setSignedData(const std::shared_ptr<Input> &signedData)
{
d->m_signedData = signedData;
kleo_assert(d->m_signedData && d->m_signedData->ioDevice());
}
void VerifyDetachedTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void VerifyDetachedTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND), i18n("Could not determine whether this is an S/MIME or an OpenPGP signature - maybe it is not a signature at all?"), Exception::MessageOnly);
}
setProtocol(p);
}
unsigned long long VerifyDetachedTask::inputSize() const
{
return d->m_signedData ? d->m_signedData->size() : 0;
}
QString VerifyDetachedTask::label() const
{
if (d->m_signedData) {
return xi18nc("Verification of a detached signature in progress. The first file contains the data."
"The second file is the signature file.",
"Verifying: <filename>%1</filename> with <filename>%2</filename>...",
d->m_signedData->label(),
d->m_input->label());
}
return i18n("Verifying signature: %1...", d->m_input->label());
}
QString VerifyDetachedTask::inputLabel() const
{
if (d->m_signedData && d->m_input) {
return xi18nc("Verification of a detached signature summary. The first file contains the data."
"The second file is signature.",
"Verified <filename>%1</filename> with <filename>%2</filename>",
d->m_signedData->label(),
d->m_input->label());
}
return d->m_input ? d->m_input->label() : QString();
}
QString VerifyDetachedTask::outputLabel() const
{
return QString();
}
Protocol VerifyDetachedTask::protocol() const
{
kleo_assert(!"not implemented");
return UnknownProtocol; // ### TODO
}
void VerifyDetachedTask::cancel()
{
}
void VerifyDetachedTask::doStart()
{
kleo_assert(d->m_backend);
try {
QGpgME::VerifyDetachedJob *const job = d->m_backend->verifyDetachedJob();
kleo_assert(job);
d->registerJob(job);
job->start(d->m_input->ioDevice(), d->m_signedData->ioDevice());
} catch (const GpgME::Exception &e) {
d->emitResult(fromVerifyDetachedResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLog()));
} catch (const std::exception &e) {
d->emitResult(fromVerifyDetachedResult(make_error(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLog()));
} catch (...) {
d->emitResult(fromVerifyDetachedResult(make_error(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLog()));
}
}
#include "moc_decryptverifytask.cpp"
diff --git a/src/crypto/gui/certificateselectionline.cpp b/src/crypto/gui/certificateselectionline.cpp
index 291534627..6db864c20 100644
--- a/src/crypto/gui/certificateselectionline.cpp
+++ b/src/crypto/gui/certificateselectionline.cpp
@@ -1,340 +1,340 @@
/* crypto/gui/certificateselectionline.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2009 Klarälvdalens Datakonsult AB
2016 Intevation GmbH
Kleopatra 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.
Kleopatra 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include "certificateselectionline.h"
#include <QToolButton>
#include <QLabel>
#include <QStackedWidget>
#include <QComboBox>
#include <QStylePainter>
#include <QStyleOptionComboBox>
#include <QStyle>
#include "utils/kleo_assert.h"
#include <KLocalizedString>
#include <Libkleo/Formatting>
#include <Libkleo/Predicates>
using namespace Kleo;
using namespace GpgME;
Q_DECLARE_METATYPE(GpgME::Key)
namespace
{
static QString make_initial_text(const std::vector<Key> &keys)
{
if (keys.empty()) {
return i18n("(no matching certificates found)");
} else {
return i18n("Please select a certificate");
}
}
// A QComboBox with an initial text (as known from web browsers)
//
// only works with read-only QComboBoxen, doesn't affect sizeHint
// as it should...
//
class ComboBox : public QComboBox
{
Q_OBJECT
Q_PROPERTY(QString initialText READ initialText WRITE setInitialText)
Q_PROPERTY(QIcon initialIcon READ initialIcon WRITE setInitialIcon)
public:
explicit ComboBox(QWidget *parent = nullptr)
: QComboBox(parent),
m_initialText(),
m_initialIcon()
{
}
explicit ComboBox(const QString &initialText, QWidget *parent = nullptr)
: QComboBox(parent),
m_initialText(initialText),
m_initialIcon()
{
}
explicit ComboBox(const QIcon &initialIcon, const QString &initialText, QWidget *parent = nullptr)
: QComboBox(parent),
m_initialText(initialText),
m_initialIcon(initialIcon)
{
}
QString initialText() const
{
return m_initialText;
}
QIcon initialIcon() const
{
return m_initialIcon;
}
public Q_SLOTS:
void setInitialText(const QString &txt)
{
if (txt == m_initialText) {
return;
}
m_initialText = txt;
if (currentIndex() == -1) {
update();
}
}
void setInitialIcon(const QIcon &icon)
{
if (icon.cacheKey() == m_initialIcon.cacheKey()) {
return;
}
m_initialIcon = icon;
if (currentIndex() == -1) {
update();
}
}
protected:
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE {
QStylePainter p(this);
p.setPen(palette().color(QPalette::Text));
QStyleOptionComboBox opt;
initStyleOption(&opt);
p.drawComplexControl(QStyle::CC_ComboBox, opt);
if (currentIndex() == -1)
{
opt.currentText = m_initialText;
opt.currentIcon = m_initialIcon;
}
p.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
private:
QString m_initialText;
QIcon m_initialIcon;
};
} // anonymous namespace
class Kleo::KeysComboBox : public ComboBox
{
Q_OBJECT
public:
explicit KeysComboBox(QWidget *parent = nullptr)
: ComboBox(parent) {}
explicit KeysComboBox(const QString &initialText, QWidget *parent = nullptr)
: ComboBox(initialText, parent) {}
explicit KeysComboBox(const std::vector<Key> &keys, QWidget *parent = nullptr)
: ComboBox(make_initial_text(keys), parent)
{
setKeys(keys);
}
void setKeys(const std::vector<Key> &keys)
{
clear();
- Q_FOREACH (const Key &key, keys) {
+ for (const Key &key : keys) {
addItem(Formatting::formatForComboBox(key), qVariantFromValue(key));
}
}
std::vector<Key> keys() const
{
std::vector<Key> result;
result.reserve(count());
for (int i = 0, end = count(); i != end; ++i) {
result.push_back(qvariant_cast<Key>(itemData(i)));
}
return result;;
}
int findOrAdd(const Key &key)
{
for (int i = 0, end = count(); i != end; ++i)
if (_detail::ByFingerprint<std::equal_to>()(key, qvariant_cast<Key>(itemData(i)))) {
return i;
}
insertItem(0, Formatting::formatForComboBox(key), qVariantFromValue(key));
return 0;
}
void addAndSelectCertificate(const Key &key)
{
setCurrentIndex(findOrAdd(key));
}
Key currentKey() const
{
return qvariant_cast<Key>(itemData(currentIndex()));
}
};
CertificateSelectionLine::CertificateSelectionLine(const QString &toFrom, const QString &mailbox, const std::vector<Key> &pgp, bool pgpAmbig, const std::vector<Key> &cms, bool cmsAmbig, QWidget *q, QGridLayout &glay)
: pgpAmbiguous(pgpAmbig),
cmsAmbiguous(cmsAmbig),
mToFromLB(new QLabel(toFrom, q)),
mMailboxLB(new QLabel(mailbox, q)),
mSbox(new QStackedWidget(q)),
mPgpCB(new KeysComboBox(pgp, mSbox)),
mCmsCB(new KeysComboBox(cms, mSbox)),
noProtocolCB(new KeysComboBox(i18n("(please choose between OpenPGP and S/MIME first)"), mSbox)),
mToolTB(new QToolButton(q))
{
QFont bold;
bold.setBold(true);
mToFromLB->setFont(bold);
mMailboxLB->setTextFormat(Qt::PlainText);
mToolTB->setText(i18n("..."));
mPgpCB->setEnabled(!pgp.empty());
mCmsCB->setEnabled(!cms.empty());
noProtocolCB->setEnabled(false);
mPgpCB->setKeys(pgp);
if (pgpAmbiguous) {
mPgpCB->setCurrentIndex(-1);
}
mCmsCB->setKeys(cms);
if (cmsAmbiguous) {
mCmsCB->setCurrentIndex(-1);
}
mSbox->addWidget(mPgpCB);
mSbox->addWidget(mCmsCB);
mSbox->addWidget(noProtocolCB);
mSbox->setCurrentWidget(noProtocolCB);
const int row = glay.rowCount();
unsigned int col = 0;
glay.addWidget(mToFromLB, row, col++);
glay.addWidget(mMailboxLB, row, col++);
glay.addWidget(mSbox, row, col++);
glay.addWidget(mToolTB, row, col++);
assert(col == NumColumns);
q->connect(mPgpCB, SIGNAL(currentIndexChanged(int)), SLOT(slotCompleteChanged()));
q->connect(mCmsCB, SIGNAL(currentIndexChanged(int)), SLOT(slotCompleteChanged()));
q->connect(mToolTB, SIGNAL(clicked()), SLOT(slotCertificateSelectionDialogRequested()));
}
QString CertificateSelectionLine::mailboxText() const
{
return mMailboxLB->text();
}
void CertificateSelectionLine::addAndSelectCertificate(const Key &key) const
{
if (KeysComboBox *const cb = comboBox(key.protocol())) {
cb->addAndSelectCertificate(key);
cb->setEnabled(true);
}
}
void CertificateSelectionLine::showHide(Protocol proto, bool &first, bool showAll, bool op) const
{
if (op && (showAll || wasInitiallyAmbiguous(proto))) {
mToFromLB->setVisible(first);
first = false;
QFont font = mMailboxLB->font();
font.setBold(wasInitiallyAmbiguous(proto));
mMailboxLB->setFont(font);
mSbox->setCurrentIndex(proto);
mMailboxLB->show();
mSbox->show();
mToolTB->show();
} else {
mToFromLB->hide();
mMailboxLB->hide();
mSbox->hide();
mToolTB->hide();
}
}
bool CertificateSelectionLine::wasInitiallyAmbiguous(Protocol proto) const
{
return (proto == OpenPGP && pgpAmbiguous)
|| (proto == CMS && cmsAmbiguous);
}
bool CertificateSelectionLine::isStillAmbiguous(Protocol proto) const
{
kleo_assert(proto == OpenPGP || proto == CMS);
const KeysComboBox *const cb = comboBox(proto);
return cb->currentIndex() == -1;
}
Key CertificateSelectionLine::key(Protocol proto) const
{
kleo_assert(proto == OpenPGP || proto == CMS);
const KeysComboBox *const cb = comboBox(proto);
return cb->currentKey();
}
const QToolButton *CertificateSelectionLine::toolButton() const
{
return mToolTB;
}
void CertificateSelectionLine::kill()
{
delete mToFromLB;
delete mMailboxLB;
delete mSbox;
delete mToolTB;
}
KeysComboBox *CertificateSelectionLine::comboBox(Protocol proto) const
{
if (proto == OpenPGP) {
return mPgpCB;
}
if (proto == CMS) {
return mCmsCB;
}
return nullptr;
}
#include "certificateselectionline.moc"
diff --git a/src/crypto/gui/signencryptfileswizard.cpp b/src/crypto/gui/signencryptfileswizard.cpp
index f9ef48c9e..e88789617 100644
--- a/src/crypto/gui/signencryptfileswizard.cpp
+++ b/src/crypto/gui/signencryptfileswizard.cpp
@@ -1,467 +1,467 @@
/* crypto/gui/signencryptfileswizard.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2009 Klarälvdalens Datakonsult AB
2016 Intevation GmbH
Kleopatra 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.
Kleopatra 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "signencryptfileswizard.h"
#include "signencryptwidget.h"
#include "newresultpage.h"
#include <KLocalizedString>
#include <KSharedConfig>
#include <KConfigGroup>
#include <KMessageBox>
#include "kleopatra_debug.h"
#include <Libkleo/FileNameRequester>
#include <QVBoxLayout>
#include <QWizardPage>
#include <QGroupBox>
#include <QLabel>
#include <QIcon>
#include <QCheckBox>
#include <QPointer>
#include <gpgme++/key.h>
#include <cassert>
using namespace GpgME;
using namespace Kleo;
using namespace Kleo::Crypto::Gui;
enum Page {
SigEncPageId,
ResultPageId,
NumPages
};
class SigEncPage: public QWizardPage
{
Q_OBJECT
public:
explicit SigEncPage(QWidget *parent = nullptr)
: QWizardPage(parent),
mWidget(new SignEncryptWidget),
mOutLayout(new QVBoxLayout),
mArchive(false),
mUseOutputDir(false)
{
setTitle(i18nc("@title", "Sign / Encrypt Files"));
auto vLay = new QVBoxLayout(this);
vLay->setMargin(0);
vLay->addWidget(mWidget);
connect(mWidget, &SignEncryptWidget::operationChanged, this,
&SigEncPage::updateCommitButton);
connect(mWidget, &SignEncryptWidget::keysChanged, this,
&SigEncPage::updateFileWidgets);
setLayout(vLay);
updateCommitButton(mWidget->currentOp());
auto outputGrp = new QGroupBox(i18n("Output"));
outputGrp->setLayout(mOutLayout);
mPlaceholderWidget = new QLabel(i18n("Please select an action."));
mOutLayout->addWidget(mPlaceholderWidget);
mUseOutputDirChk = new QCheckBox(i18n("Encrypt / Sign each file separately."));
mUseOutputDirChk->setToolTip(i18nc("@info",
"Keep each file separate instead of creating an archive for all."));
mOutLayout->addWidget(mUseOutputDirChk);
connect (mUseOutputDirChk, &QCheckBox::toggled, this, [this] (bool state) {
mUseOutputDir = state;
mArchive = !mUseOutputDir;
updateFileWidgets();
});
vLay->addWidget(outputGrp);
setMinimumHeight(300);
}
bool isComplete() const Q_DECL_OVERRIDE
{
return !mWidget->currentOp().isNull();
}
int nextId() const Q_DECL_OVERRIDE
{
return ResultPageId;
}
void initializePage() Q_DECL_OVERRIDE
{
setCommitPage(true);
}
void setArchiveForced(bool archive)
{
mArchive = archive;
setArchiveMutable(!archive);
}
void setArchiveMutable(bool archive)
{
mUseOutputDirChk->setVisible(archive);
if (archive) {
const KConfigGroup archCfg(KSharedConfig::openConfig(), "SignEncryptFilesWizard");
mUseOutputDirChk->setChecked(archCfg.readEntry("LastUseOutputDir", false));
} else {
mUseOutputDirChk->setChecked(false);
}
}
bool validatePage() Q_DECL_OVERRIDE
{
bool sign = !mWidget->signKey().isNull();
bool encrypt = !mWidget->selfKey().isNull() || !mWidget->recipients().empty();
mWidget->saveOwnKeys();
if (mUseOutputDirChk->isVisible()) {
KConfigGroup archCfg(KSharedConfig::openConfig(), "SignEncryptFilesWizard");
archCfg.writeEntry("LastUseOutputDir", mUseOutputDir);
}
if (sign && !encrypt && mArchive) {
return KMessageBox::warningContinueCancel(this,
xi18nc("@info",
"<para>Archiving in combination with sign-only currently requires what are known as opaque signatures - "
"unlike detached ones, these embed the content in the signature.</para>"
"<para>This format is rather unusual. You might want to archive the files separately, "
"and then sign the archive as one file with Kleopatra.</para>"
"<para>Future versions of Kleopatra are expected to also support detached signatures in this case.</para>"),
i18nc("@title:window", "Unusual Signature Warning"),
KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
QStringLiteral("signencryptfileswizard-archive+sign-only-warning"))
== KMessageBox::Continue;
} else if (sign && !encrypt) {
return true;
}
if (!mWidget->selfKey().isNull()) {
return true;
}
bool hasSecret = false;
Q_FOREACH (const Key k, mWidget->recipients()) {
if (k.hasSecret()) {
hasSecret = true;
break;
}
}
if (!hasSecret && !mWidget->encryptSymmetric()) {
if (KMessageBox::warningContinueCancel(this,
xi18nc("@info",
"<para>None of the recipients you are encrypting to seems to be your own.</para>"
"<para>This means that you will not be able to decrypt the data anymore, once encrypted.</para>"
"<para>Do you want to continue, or cancel to change the recipient selection?</para>"),
i18nc("@title:window", "Encrypt-To-Self Warning"),
KStandardGuiItem::cont(),
KStandardGuiItem::cancel(),
QStringLiteral("warn-encrypt-to-non-self"), KMessageBox::Notify | KMessageBox::Dangerous)
== KMessageBox::Cancel) {
return false;
}
}
return true;
}
QVector<Key> recipients() const
{
return mWidget->recipients();
}
/* In the future we might find a usecase for multiple
* signers */
QVector<Key> signers() const
{
QVector<Key> ret;
const Key k = mWidget->signKey();
if (!k.isNull()) {
ret << k;
}
return ret;
}
private:
QWidget *createRequester(int forKind, QBoxLayout *lay) {
static const QMap <int, QString> icons = {
{ SignEncryptFilesWizard::SignatureCMS, QStringLiteral("document-sign") },
{ SignEncryptFilesWizard::SignaturePGP, QStringLiteral("document-sign") },
{ SignEncryptFilesWizard::CombinedPGP, QStringLiteral("document-edit-sign-encrypt") },
{ SignEncryptFilesWizard::EncryptedPGP, QStringLiteral("document-encrypt") },
{ SignEncryptFilesWizard::EncryptedCMS, QStringLiteral("document-encrypt") },
{ SignEncryptFilesWizard::Directory, QStringLiteral("folder") }
};
static const QMap <int, QString> toolTips = {
{ SignEncryptFilesWizard::SignatureCMS, i18n("The S/MIME signature.") },
{ SignEncryptFilesWizard::SignaturePGP, i18n("The signature.") },
{ SignEncryptFilesWizard::CombinedPGP, i18n("The signed and encrypted file.") },
{ SignEncryptFilesWizard::EncryptedPGP, i18n("The encrypted file.") },
{ SignEncryptFilesWizard::EncryptedCMS, i18n("The S/MIME encrypted file.") },
{ SignEncryptFilesWizard::Directory, i18n("Output directory.") }
};
FileNameRequester *req = new FileNameRequester(forKind == SignEncryptFilesWizard::Directory ?
QDir::Dirs : QDir::Files, this);
req->setFileName(mOutNames[forKind]);
QHBoxLayout *hLay = new QHBoxLayout;
QLabel *iconLabel = new QLabel;
QWidget *ret = new QWidget;
iconLabel->setPixmap(QIcon::fromTheme(icons[forKind]).pixmap(32,32));
hLay->addWidget(iconLabel);
iconLabel->setToolTip(toolTips[forKind]);
req->setToolTip(toolTips[forKind]);
hLay->addWidget(req);
ret->setLayout(hLay);
lay->addWidget(ret);
connect (req, &FileNameRequester::fileNameChanged, this,
[this, forKind](const QString &newName) {
mOutNames[forKind] = newName;
});
return ret;
}
public:
void setOutputNames(const QMap<int, QString> &names) {
assert(mOutNames.isEmpty());
mOutNames = names;
Q_FOREACH (int i, mOutNames.keys()) {
mRequester[i] = createRequester(i, mOutLayout);
}
updateFileWidgets();
}
QMap <int, QString> outputNames() const {
if (!mUseOutputDir) {
auto ret = mOutNames;
ret.remove(SignEncryptFilesWizard::Directory);
return ret;
}
return mOutNames;
}
bool encryptSymmetric() const
{
return mWidget->encryptSymmetric();
}
private Q_SLOTS:
void updateCommitButton(const QString &label)
{
if (!label.isEmpty()) {
setButtonText(QWizard::CommitButton, label);
} else {
setButtonText(QWizard::CommitButton, i18n("Next"));
}
Q_EMIT completeChanged();
}
void updateFileWidgets()
{
if (mRequester.isEmpty()) {
return;
}
const QVector<Key> recipients = mWidget->recipients();
const Key sigKey = mWidget->signKey();
bool pgp = mWidget->encryptSymmetric();
bool cms = false;
- Q_FOREACH (const Key k, recipients) {
+ for (const Key &k : recipients) {
if (pgp && cms) {
break;
}
if (k.protocol() == Protocol::OpenPGP) {
pgp = true;
} else {
cms = true;
}
}
mOutLayout->setEnabled(false);
mPlaceholderWidget->setVisible(!cms && !pgp && sigKey.isNull());
mRequester[SignEncryptFilesWizard::SignatureCMS]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::CMS);
mRequester[SignEncryptFilesWizard::EncryptedCMS]->setVisible(!mUseOutputDir && cms);
mRequester[SignEncryptFilesWizard::CombinedPGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && pgp);
mRequester[SignEncryptFilesWizard::EncryptedPGP]->setVisible(!mUseOutputDir && pgp && sigKey.protocol() != Protocol::OpenPGP);
mRequester[SignEncryptFilesWizard::SignaturePGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && !pgp);
mRequester[SignEncryptFilesWizard::Directory]->setVisible(mUseOutputDir && !mPlaceholderWidget->isVisible());
mOutLayout->setEnabled(true);
}
private:
SignEncryptWidget *mWidget;
QMap <int, QString> mOutNames;
QMap <int, QWidget *> mRequester;
QVBoxLayout *mOutLayout;
QWidget *mPlaceholderWidget;
QCheckBox *mUseOutputDirChk;
bool mArchive;
bool mUseOutputDir;
};
class ResultPage : public NewResultPage
{
Q_OBJECT
public:
explicit ResultPage(QWidget *parent = nullptr)
: NewResultPage(parent)
{
setTitle(i18nc("@title", "Results"));
setSubTitle(i18nc("@title",
"Status and progress of the crypto operations is shown here."));
}
};
SignEncryptFilesWizard::SignEncryptFilesWizard(QWidget *parent, Qt::WindowFlags f)
: QWizard(parent, f)
, mSigningPreset(false)
, mSigningUserMutable(false)
, mEncryptionUserMutable(false)
, mEncryptionPreset(false)
{
#ifdef Q_OS_WIN
// Enforce modern style to avoid vista style uglyness.
setWizardStyle(QWizard::ModernStyle);
#endif
mSigEncPage = new SigEncPage(this);
mResultPage = new ResultPage(this);
connect(this, &QWizard::currentIdChanged, this,
&SignEncryptFilesWizard::slotCurrentIdChanged);
setPage(SigEncPageId, mSigEncPage);
setPage(ResultPageId, mResultPage);
setOptions(QWizard::IndependentPages |
QWizard::NoBackButtonOnLastPage |
QWizard::NoBackButtonOnStartPage);
KConfigGroup cfgGroup(KSharedConfig::openConfig(), "SignEncryptFilesWizard");
const QByteArray geom = cfgGroup.readEntry("geometry", QByteArray());
if (!geom.isEmpty()) {
restoreGeometry(geom);
return;
}
}
void SignEncryptFilesWizard::slotCurrentIdChanged(int id)
{
if (id == ResultPageId) {
Q_EMIT operationPrepared();
}
}
SignEncryptFilesWizard::~SignEncryptFilesWizard()
{
qCDebug(KLEOPATRA_LOG);
KConfigGroup cfgGroup(KSharedConfig::openConfig(), "SignEncryptFilesWizard");
cfgGroup.writeEntry("geometry", saveGeometry());
cfgGroup.sync();
}
void SignEncryptFilesWizard::setSigningPreset(bool preset)
{
if (preset == mSigningPreset) {
return;
}
mSigningPreset = preset;
}
void SignEncryptFilesWizard::setSigningUserMutable(bool mut)
{
if (mut == mSigningUserMutable) {
return;
}
mSigningUserMutable = mut;
}
void SignEncryptFilesWizard::setEncryptionPreset(bool preset)
{
if (preset == mEncryptionPreset) {
return;
}
mEncryptionPreset = preset;
}
void SignEncryptFilesWizard::setEncryptionUserMutable(bool mut)
{
if (mut == mEncryptionUserMutable) {
return;
}
mEncryptionUserMutable = mut;
}
void SignEncryptFilesWizard::setArchiveForced(bool archive)
{
mSigEncPage->setArchiveForced(archive);
}
void SignEncryptFilesWizard::setArchiveMutable(bool archive)
{
mSigEncPage->setArchiveMutable(archive);
}
QVector<Key> SignEncryptFilesWizard::resolvedRecipients() const
{
return mSigEncPage->recipients();
}
QVector<Key> SignEncryptFilesWizard::resolvedSigners() const
{
return mSigEncPage->signers();
}
void SignEncryptFilesWizard::setTaskCollection(const std::shared_ptr<Kleo::Crypto::TaskCollection> &coll)
{
mResultPage->setTaskCollection(coll);
}
void SignEncryptFilesWizard::setOutputNames(const QMap<int, QString> &map) const
{
mSigEncPage->setOutputNames(map);
}
QMap<int, QString> SignEncryptFilesWizard::outputNames() const
{
return mSigEncPage->outputNames();
}
bool SignEncryptFilesWizard::encryptSymmetric() const
{
return mSigEncPage->encryptSymmetric();
}
#include "signencryptfileswizard.moc"
diff --git a/src/crypto/verifychecksumscontroller.cpp b/src/crypto/verifychecksumscontroller.cpp
index c7bb02eeb..680aadefa 100644
--- a/src/crypto/verifychecksumscontroller.cpp
+++ b/src/crypto/verifychecksumscontroller.cpp
@@ -1,708 +1,708 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/verifychecksumscontroller.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2010 Klarälvdalens Datakonsult AB
Kleopatra 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.
Kleopatra 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "verifychecksumscontroller.h"
#ifndef QT_NO_DIRMODEL
#include <crypto/gui/verifychecksumsdialog.h>
#include <utils/input.h>
#include <utils/output.h>
#include <utils/kleo_assert.h>
#include <Libkleo/Stl_Util>
#include <Libkleo/ChecksumDefinition>
#include <Libkleo/Classify>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <QPointer>
#include <QFileInfo>
#include <QThread>
#include <QMutex>
#include <QProgressDialog>
#include <QDir>
#include <QProcess>
#include <gpg-error.h>
#include <deque>
#include <limits>
#include <set>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
#ifdef Q_OS_UNIX
static const bool HAVE_UNIX = true;
#else
static const bool HAVE_UNIX = false;
#endif
static const QLatin1String CHECKSUM_DEFINITION_ID_ENTRY("checksum-definition-id");
static const Qt::CaseSensitivity fs_cs = HAVE_UNIX ? Qt::CaseSensitive : Qt::CaseInsensitive; // can we use QAbstractFileEngine::caseSensitive()?
#if 0
static QStringList fs_sort(QStringList l)
{
int (*QString_compare)(const QString &, const QString &, Qt::CaseSensitivity) = &QString::compare;
std::sort(l.begin(), l.end(),
[](const QString &lhs, const QString &rhs) {
return QString::compare(lhs, rhs, fs_cs) < 0;
});
return l;
}
static QStringList fs_intersect(QStringList l1, QStringList l2)
{
int (*QString_compare)(const QString &, const QString &, Qt::CaseSensitivity) = &QString::compare;
fs_sort(l1);
fs_sort(l2);
QStringList result;
std::set_intersection(l1.begin(), l1.end(),
l2.begin(), l2.end(),
std::back_inserter(result),
[](const QString &lhs, const QString &rhs) {
return QString::compare(lhs, rhs, fs_cs) < 0;
});
return result;
}
#endif
static QList<QRegExp> get_patterns(const std::vector< std::shared_ptr<ChecksumDefinition> > &checksumDefinitions)
{
QList<QRegExp> result;
Q_FOREACH (const std::shared_ptr<ChecksumDefinition> &cd, checksumDefinitions)
if (cd)
Q_FOREACH (const QString &pattern, cd->patterns()) {
result.push_back(QRegExp(pattern, fs_cs));
}
return result;
}
namespace
{
struct matches_any : std::unary_function<QString, bool> {
const QList<QRegExp> m_regexps;
explicit matches_any(const QList<QRegExp> &regexps) : m_regexps(regexps) {}
bool operator()(const QString &s) const
{
return std::any_of(m_regexps.cbegin(), m_regexps.cend(),
[&s](const QRegExp &rx) { return rx.exactMatch(s); });
}
};
struct matches_none_of : std::unary_function<QString, bool> {
const QList<QRegExp> m_regexps;
explicit matches_none_of(const QList<QRegExp> &regexps) : m_regexps(regexps) {}
bool operator()(const QString &s) const
{
return std::none_of(m_regexps.cbegin(), m_regexps.cend(),
[&s](const QRegExp &rx) { return rx.exactMatch(s); });
}
};
}
class VerifyChecksumsController::Private : public QThread
{
Q_OBJECT
friend class ::Kleo::Crypto::VerifyChecksumsController;
VerifyChecksumsController *const q;
public:
explicit Private(VerifyChecksumsController *qq);
~Private();
Q_SIGNALS:
void baseDirectories(const QStringList &);
void progress(int, int, const QString &);
void status(const QString &file, Kleo::Crypto::Gui::VerifyChecksumsDialog::Status);
private:
void slotOperationFinished()
{
if (dialog) {
dialog->setProgress(100, 100);
dialog->setErrors(errors);
}
if (!errors.empty())
q->setLastError(gpg_error(GPG_ERR_GENERAL),
errors.join(QLatin1Char('\n')));
q->emitDoneOrError();
}
private:
void run() Q_DECL_OVERRIDE;
private:
QPointer<VerifyChecksumsDialog> dialog;
mutable QMutex mutex;
const std::vector< std::shared_ptr<ChecksumDefinition> > checksumDefinitions;
QStringList files;
QStringList errors;
volatile bool canceled;
};
VerifyChecksumsController::Private::Private(VerifyChecksumsController *qq)
: q(qq),
dialog(),
mutex(),
checksumDefinitions(ChecksumDefinition::getChecksumDefinitions()),
files(),
errors(),
canceled(false)
{
connect(this, &Private::progress,
q, &Controller::progress);
connect(this, SIGNAL(finished()),
q, SLOT(slotOperationFinished()));
}
VerifyChecksumsController::Private::~Private()
{
qCDebug(KLEOPATRA_LOG);
}
VerifyChecksumsController::VerifyChecksumsController(QObject *p)
: Controller(p), d(new Private(this))
{
}
VerifyChecksumsController::VerifyChecksumsController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *p)
: Controller(ctx, p), d(new Private(this))
{
}
VerifyChecksumsController::~VerifyChecksumsController()
{
qCDebug(KLEOPATRA_LOG);
}
void VerifyChecksumsController::setFiles(const QStringList &files)
{
kleo_assert(!d->isRunning());
kleo_assert(!files.empty());
const QMutexLocker locker(&d->mutex);
d->files = files;
}
void VerifyChecksumsController::start()
{
{
const QMutexLocker locker(&d->mutex);
d->dialog = new VerifyChecksumsDialog;
d->dialog->setAttribute(Qt::WA_DeleteOnClose);
d->dialog->setWindowTitle(i18nc("@title:window", "Verify Checksum Results"));
connect(d->dialog.data(), &VerifyChecksumsDialog::canceled,
this, &VerifyChecksumsController::cancel);
connect(d.get(), &Private::baseDirectories,
d->dialog.data(), &VerifyChecksumsDialog::setBaseDirectories);
connect(d.get(), &Private::progress,
d->dialog.data(), &VerifyChecksumsDialog::setProgress);
connect(d.get(), &Private::status,
d->dialog.data(), &VerifyChecksumsDialog::setStatus);
d->canceled = false;
d->errors.clear();
}
d->start();
d->dialog->show();
}
void VerifyChecksumsController::cancel()
{
qCDebug(KLEOPATRA_LOG);
const QMutexLocker locker(&d->mutex);
d->canceled = true;
}
namespace
{
struct SumFile {
QDir dir;
QString sumFile;
quint64 totalSize;
std::shared_ptr<ChecksumDefinition> checksumDefinition;
};
}
static QStringList filter_checksum_files(QStringList l, const QList<QRegExp> &rxs)
{
l.erase(std::remove_if(l.begin(), l.end(),
matches_none_of(rxs)),
l.end());
return l;
}
namespace
{
struct File {
QString name;
QByteArray checksum;
bool binary;
};
}
static QString decode(const QString &encoded)
{
QString decoded;
decoded.reserve(encoded.size());
bool shift = false;
for (const QChar &ch : encoded)
if (shift) {
switch (ch.toLatin1()) {
case '\\': decoded += QLatin1Char('\\'); break;
case 'n': decoded += QLatin1Char('\n'); break;
default:
qCDebug(KLEOPATRA_LOG) << "invalid escape sequence" << '\\' << ch << "(interpreted as '" << ch << "')";
decoded += ch;
break;
}
shift = false;
} else {
if (ch == QLatin1Char('\\')) {
shift = true;
} else {
decoded += ch;
}
}
return decoded;
}
static std::vector<File> parse_sum_file(const QString &fileName)
{
std::vector<File> files;
QFile f(fileName);
if (f.open(QIODevice::ReadOnly)) {
QTextStream s(&f);
QRegExp rx(QLatin1String("(\\?)([a-f0-9A-F]+) ([ *])([^\n]+)\n*"));
while (!s.atEnd()) {
const QString line = s.readLine();
if (rx.exactMatch(line)) {
assert(!rx.cap(4).endsWith(QLatin1Char('\n')));
const File file = {
rx.cap(1) == QLatin1String("\\") ? decode(rx.cap(4)) : rx.cap(4),
rx.cap(2).toLatin1(),
rx.cap(3) == QLatin1String("*"),
};
files.push_back(file);
}
}
}
return files;
}
static quint64 aggregate_size(const QDir &dir, const QStringList &files)
{
quint64 n = 0;
for (const QString &file : files) {
n += QFileInfo(dir.absoluteFilePath(file)).size();
}
return n;
}
static std::shared_ptr<ChecksumDefinition> filename2definition(const QString &fileName,
const std::vector< std::shared_ptr<ChecksumDefinition> > &checksumDefinitions)
{
Q_FOREACH (const std::shared_ptr<ChecksumDefinition> &cd, checksumDefinitions)
if (cd)
Q_FOREACH (const QString &pattern, cd->patterns())
if (QRegExp(pattern, fs_cs).exactMatch(fileName)) {
return cd;
}
return std::shared_ptr<ChecksumDefinition>();
}
namespace
{
struct less_dir : std::binary_function<QDir, QDir, bool> {
bool operator()(const QDir &lhs, const QDir &rhs) const
{
return QString::compare(lhs.absolutePath(), rhs.absolutePath(), fs_cs) < 0;
}
};
struct less_file : std::binary_function<QString, QString, bool> {
bool operator()(const QString &lhs, const QString &rhs) const
{
return QString::compare(lhs, rhs, fs_cs) < 0;
}
};
struct sumfile_contains_file : std::unary_function<QString, bool> {
const QDir dir;
const QString fileName;
sumfile_contains_file(const QDir &dir_, const QString &fileName_)
: dir(dir_), fileName(fileName_) {}
bool operator()(const QString &sumFile) const
{
const std::vector<File> files = parse_sum_file(dir.absoluteFilePath(sumFile));
qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: found " << files.size()
<< " files listed in " << qPrintable(dir.absoluteFilePath(sumFile));
Q_FOREACH (const File &file, files) {
const bool isSameFileName = (QString::compare(file.name, fileName, fs_cs) == 0);
qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: "
<< qPrintable(file.name) << " == "
<< qPrintable(fileName) << " ? "
<< isSameFileName;
if (isSameFileName) {
return true;
}
}
return false;
}
};
}
// IF is_dir(file)
// add all sumfiles \in dir(file)
// inputs.prepend( all dirs \in dir(file) )
// ELSE IF is_sum_file(file)
// add
// ELSE IF \exists sumfile in dir(file) \where sumfile \contains file
// add sumfile
// ELSE
// error: no checksum found for "file"
static QStringList find_base_directiories(const QStringList &files)
{
// Step 1: find base dirs:
std::set<QDir, less_dir> dirs;
for (const QString &file : files) {
const QFileInfo fi(file);
const QDir dir = fi.isDir() ? QDir(file) : fi.dir();
dirs.insert(dir);
}
// Step 1a: collapse direct child directories
bool changed;
do {
changed = false;
std::set<QDir, less_dir>::iterator it = dirs.begin();
while (it != dirs.end()) {
QDir dir = *it;
if (dir.cdUp() && dirs.count(dir)) {
dirs.erase(it++);
changed = true;
} else {
++it;
}
}
} while (changed);
QStringList rv;
rv.reserve(dirs.size());
std::transform(dirs.cbegin(), dirs.cend(), std::back_inserter(rv), std::mem_fn(&QDir::absolutePath));
return rv;
}
static std::vector<SumFile> find_sums_by_input_files(const QStringList &files, QStringList &errors,
const std::function<void(int)> &progress,
const std::vector< std::shared_ptr<ChecksumDefinition> > &checksumDefinitions)
{
const QList<QRegExp> patterns = get_patterns(checksumDefinitions);
const matches_any is_sum_file(patterns);
std::map<QDir, std::set<QString, less_file>, less_dir> dirs2sums;
// Step 1: find the sumfiles we need to check:
std::deque<QString> inputs(files.begin(), files.end());
int i = 0;
while (!inputs.empty()) {
const QString file = inputs.front();
qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: considering " << qPrintable(file);
inputs.pop_front();
const QFileInfo fi(file);
const QString fileName = fi.fileName();
if (fi.isDir()) {
qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: it's a directory";
QDir dir(file);
const QStringList sumfiles = filter_checksum_files(dir.entryList(QDir::Files), patterns);
qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: found " << sumfiles.size()
<< " sum files: " << qPrintable(sumfiles.join(QStringLiteral(", ")));
dirs2sums[ dir ].insert(sumfiles.begin(), sumfiles.end());
const QStringList dirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: found " << dirs.size()
<< " subdirs, prepending";
std::transform(dirs.cbegin(), dirs.cend(),
std::inserter(inputs, inputs.begin()),
[&dir](const QString &path) {
return dir.absoluteFilePath(path);
});
} else if (is_sum_file(fileName)) {
qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: it's a sum file";
dirs2sums[fi.dir()].insert(fileName);
} else {
qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: it's something else; checking whether we'll find a sumfile for it...";
const QDir dir = fi.dir();
const QStringList sumfiles = filter_checksum_files(dir.entryList(QDir::Files), patterns);
qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: found " << sumfiles.size()
<< " potential sumfiles: " << qPrintable(sumfiles.join(QStringLiteral(", ")));
const auto it = std::find_if(sumfiles.cbegin(), sumfiles.cend(),
sumfile_contains_file(dir, fileName));
if (it == sumfiles.end()) {
errors.push_back(i18n("Cannot find checksums file for file %1", file));
} else {
dirs2sums[dir].insert(*it);
}
}
if (progress) {
progress(++i);
}
}
// Step 2: convert into vector<SumFile>:
std::vector<SumFile> sumfiles;
sumfiles.reserve(dirs2sums.size());
for (std::map<QDir, std::set<QString, less_file>, less_dir>::const_iterator it = dirs2sums.begin(), end = dirs2sums.end(); it != end; ++it) {
if (it->second.empty()) {
continue;
}
const QDir &dir = it->first;
Q_FOREACH (const QString &sumFileName, it->second) {
const std::vector<File> summedfiles = parse_sum_file(dir.absoluteFilePath(sumFileName));
QStringList files;
files.reserve(summedfiles.size());
std::transform(summedfiles.cbegin(), summedfiles.cend(),
std::back_inserter(files), std::mem_fn(&File::name));
const SumFile sumFile = {
it->first,
sumFileName,
aggregate_size(it->first, files),
filename2definition(sumFileName, checksumDefinitions),
};
sumfiles.push_back(sumFile);
}
if (progress) {
progress(++i);
}
}
return sumfiles;
}
static QStringList c_lang_environment()
{
QStringList env = QProcess::systemEnvironment();
env.erase(std::remove_if(env.begin(), env.end(),
[](const QString &str) {
return QRegExp(QLatin1String("^LANG=.*"), fs_cs).exactMatch(str);
}),
env.end());
env.push_back(QStringLiteral("LANG=C"));
return env;
}
static const struct {
const char *string;
VerifyChecksumsDialog::Status status;
} statusStrings[] = {
{ "OK", VerifyChecksumsDialog::OK },
{ "FAILED", VerifyChecksumsDialog::Failed },
};
static const size_t numStatusStrings = sizeof statusStrings / sizeof * statusStrings;
static VerifyChecksumsDialog::Status string2status(const QByteArray &str)
{
for (unsigned int i = 0; i < numStatusStrings; ++i)
if (str == statusStrings[i].string) {
return statusStrings[i].status;
}
return VerifyChecksumsDialog::Unknown;
}
static QString process(const SumFile &sumFile, bool *fatal, const QStringList &env,
const std::function<void(const QString &, VerifyChecksumsDialog::Status)> &status)
{
QProcess p;
p.setEnvironment(env);
p.setWorkingDirectory(sumFile.dir.absolutePath());
p.setReadChannel(QProcess::StandardOutput);
const QString absFilePath = sumFile.dir.absoluteFilePath(sumFile.sumFile);
const QString program = sumFile.checksumDefinition->verifyCommand();
sumFile.checksumDefinition->startVerifyCommand(&p, QStringList(absFilePath));
QByteArray remainder; // used for filenames with newlines in them
while (p.state() != QProcess::NotRunning) {
p.waitForReadyRead();
while (p.canReadLine()) {
const QByteArray line = p.readLine();
const int colonIdx = line.lastIndexOf(':');
if (colonIdx < 0) {
remainder += line; // no colon -> probably filename with a newline
continue;
}
const QString file = QFile::decodeName(remainder + line.left(colonIdx));
remainder.clear();
const VerifyChecksumsDialog::Status result = string2status(line.mid(colonIdx + 1).trimmed());
status(sumFile.dir.absoluteFilePath(file), result);
}
}
qCDebug(KLEOPATRA_LOG) << "[" << &p << "] Exit code " << p.exitCode();
if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) {
if (fatal && p.error() == QProcess::FailedToStart) {
*fatal = true;
}
if (p.error() == QProcess::UnknownError)
return i18n("Error while running %1: %2", program,
QString::fromLocal8Bit(p.readAllStandardError().trimmed().constData()));
else {
return i18n("Failed to execute %1: %2", program, p.errorString());
}
}
return QString();
}
namespace
{
static QDebug operator<<(QDebug s, const SumFile &sum)
{
return s << "SumFile(" << sum.dir << "->" << sum.sumFile << "<-(" << sum.totalSize << ')' << ")\n";
}
}
void VerifyChecksumsController::Private::run()
{
QMutexLocker locker(&mutex);
const QStringList files = this->files;
const std::vector< std::shared_ptr<ChecksumDefinition> > checksumDefinitions = this->checksumDefinitions;
locker.unlock();
QStringList errors;
//
// Step 0: find base directories:
//
Q_EMIT baseDirectories(find_base_directiories(files));
//
// Step 1: build a list of work to do (no progress):
//
const QString scanning = i18n("Scanning directories...");
Q_EMIT progress(0, 0, scanning);
const auto progressCb = [this, scanning](int arg) { Q_EMIT progress(arg, 0, scanning); };
const auto statusCb = [this](const QString &str, VerifyChecksumsDialog::Status st) { Q_EMIT status(str, st); };
const std::vector<SumFile> sumfiles = find_sums_by_input_files(files, errors, progressCb, checksumDefinitions);
- Q_FOREACH (const SumFile &sumfile, sumfiles) {
+ for (const SumFile &sumfile : sumfiles) {
qCDebug(KLEOPATRA_LOG) << sumfile;
}
if (!canceled) {
Q_EMIT progress(0, 0, i18n("Calculating total size..."));
const quint64 total
= kdtools::accumulate_transform(sumfiles.cbegin(), sumfiles.cend(),
std::mem_fn(&SumFile::totalSize), Q_UINT64_C(0));
if (!canceled) {
//
// Step 2: perform work (with progress reporting):
//
const QStringList env = c_lang_environment();
// re-scale 'total' to fit into ints (wish QProgressDialog would use quint64...)
const quint64 factor = total / std::numeric_limits<int>::max() + 1;
quint64 done = 0;
Q_FOREACH (const SumFile &sumFile, sumfiles) {
Q_EMIT progress(done / factor, total / factor,
i18n("Verifying checksums (%2) in %1", sumFile.checksumDefinition->label(), sumFile.dir.path()));
bool fatal = false;
const QString error = process(sumFile, &fatal, env, statusCb);
if (!error.isEmpty()) {
errors.push_back(error);
}
done += sumFile.totalSize;
if (fatal || canceled) {
break;
}
}
Q_EMIT progress(done / factor, total / factor, i18n("Done."));
}
}
locker.relock();
this->errors = errors;
// mutex unlocked by QMutexLocker
}
#include "moc_verifychecksumscontroller.cpp"
#include "verifychecksumscontroller.moc"
#endif // QT_NO_DIRMODEL
diff --git a/src/dialogs/adduseriddialog.cpp b/src/dialogs/adduseriddialog.cpp
index e64b56c72..70918074e 100644
--- a/src/dialogs/adduseriddialog.cpp
+++ b/src/dialogs/adduseriddialog.cpp
@@ -1,358 +1,358 @@
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/adduseriddialog.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2008 Klarälvdalens Datakonsult AB
Kleopatra 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.
Kleopatra 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "adduseriddialog.h"
#include "ui_adduseriddialog.h"
#include <utils/validation.h>
#include <Libkleo/Stl_Util>
#include <QString>
#include <QStringList>
#include <QPushButton>
#include <QValidator>
#include <KConfigGroup>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <cassert>
#include <KSharedConfig>
using namespace Kleo;
using namespace Kleo::Dialogs;
namespace
{
struct Line {
QString attr;
QString label;
QString regex;
QLineEdit *edit;
};
}
static QString pgpLabel(const QString &attr)
{
if (attr == QLatin1String("NAME")) {
return i18n("Name");
} else if (attr == QLatin1String("COMMENT")) {
return i18n("Comment");
} else if (attr == QLatin1String("EMAIL")) {
return i18n("EMail");
}
return QString();
}
static QString attributeLabel(const QString &attr, bool pgp)
{
if (attr.isEmpty()) {
return QString();
}
const QString label = /*pgp ?*/ pgpLabel(attr) /*: Kleo::DNAttributeMapper::instance()->name2label( attr )*/;
if (!label.isEmpty())
if (pgp) {
return label;
} else
return i18nc("Format string for the labels in the \"Your Personal Data\" page",
"%1 (%2)", label, attr);
else {
return attr;
}
}
static QString attributeFromKey(QString key)
{
return key.remove(QLatin1Char('!'));
}
static int row_index_of(QWidget *w, QGridLayout *l)
{
const int idx = l->indexOf(w);
int r, c, rs, cs;
l->getItemPosition(idx, &r, &c, &rs, &cs);
return r;
}
static QLineEdit *adjust_row(QGridLayout *l, int row, const QString &label, const QString &preset, QValidator *validator, bool readonly, bool required)
{
assert(l);
assert(row >= 0);
assert(row < l->rowCount());
QLabel *lb = qobject_cast<QLabel *>(l->itemAtPosition(row, 0)->widget());
assert(lb);
QLineEdit *le = qobject_cast<QLineEdit *>(l->itemAtPosition(row, 1)->widget());
assert(le);
QLabel *reqLB = qobject_cast<QLabel *>(l->itemAtPosition(row, 2)->widget());
assert(reqLB);
lb->setText(i18nc("interpunctation for labels", "%1:", label));
le->setText(preset);
reqLB->setText(required ? i18n("(required)") : i18n("(optional)"));
delete le->validator();
if (validator) {
if (!validator->parent()) {
validator->setParent(le);
}
le->setValidator(validator);
}
le->setReadOnly(readonly && le->hasAcceptableInput());
lb->show();
le->show();
reqLB->show();
return le;
}
class AddUserIDDialog::Private
{
friend class ::Kleo::Dialogs::AddUserIDDialog;
AddUserIDDialog *const q;
public:
explicit Private(AddUserIDDialog *qq)
: q(qq),
ui(q)
{
}
private:
void slotUserIDChanged();
private:
bool isComplete() const;
private:
struct UI : public Ui_AddUserIDDialog {
QVector<Line> lineList;
explicit UI(AddUserIDDialog *qq)
: Ui_AddUserIDDialog()
{
setupUi(qq);
// ### this code is mostly the same as the one in
// ### newcertificatewizard. Find some time to factor them
// ### into a single copy.
// hide the stuff
nameLB->hide();
nameLE->hide();
nameRequiredLB->hide();
emailLB->hide();
emailLE->hide();
emailRequiredLB->hide();
commentLB->hide();
commentLE->hide();
commentRequiredLB->hide();
// set errorLB to have a fixed height of two lines:
errorLB->setText(QStringLiteral("2<br>1"));
errorLB->setFixedHeight(errorLB->minimumSizeHint().height());
errorLB->clear();
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
const QStringList attrOrder = config.readEntry("OpenPGPAttributeOrder",
QStringList() << QStringLiteral("NAME!") << QStringLiteral("EMAIL!") << QStringLiteral("COMMENT"));
QMap<int, Line> lines;
- Q_FOREACH (const QString &rawKey, attrOrder) {
+ for (const QString &rawKey : attrOrder) {
const QString key = rawKey.trimmed().toUpper();
const QString attr = attributeFromKey(key);
if (attr.isEmpty()) {
continue;
}
const QString preset = config.readEntry(attr);
const bool required = key.endsWith(QLatin1Char('!'));
const bool readonly = config.isEntryImmutable(attr);
const QString label = config.readEntry(attr + QLatin1String("_label"),
attributeLabel(attr, true));
const QString regex = config.readEntry(attr + QLatin1String("_regex"));
int row;
QValidator *validator = nullptr;
if (attr == QLatin1String("EMAIL")) {
validator = regex.isEmpty() ? Validation::email() : Validation::email(QRegExp(regex));
row = row_index_of(emailLE, gridLayout);
} else if (attr == QLatin1String("NAME")) {
validator = regex.isEmpty() ? Validation::pgpName() : Validation::pgpName(QRegExp(regex));
row = row_index_of(nameLE, gridLayout);
} else if (attr == QLatin1String("COMMENT")) {
validator = regex.isEmpty() ? Validation::pgpComment() : Validation::pgpComment(QRegExp(regex));
row = row_index_of(commentLE, gridLayout);
} else {
continue;
}
QLineEdit *le = adjust_row(gridLayout, row, label, preset, validator, readonly, required);
const Line line = { key, label, regex, le };
lines[row] = line;
}
std::copy(lines.begin(), lines.end(), std::back_inserter(lineList));
}
QPushButton *okPB() const
{
return buttonBox->button(QDialogButtonBox::Ok);
}
} ui;
};
AddUserIDDialog::AddUserIDDialog(QWidget *p)
: QDialog(p), d(new Private(this))
{
}
AddUserIDDialog::~AddUserIDDialog() {}
void AddUserIDDialog::setName(const QString &name)
{
d->ui.nameLE->setText(name);
}
QString AddUserIDDialog::name() const
{
return d->ui.nameLE->text().trimmed();
}
void AddUserIDDialog::setEmail(const QString &email)
{
d->ui.emailLE->setText(email);
}
QString AddUserIDDialog::email() const
{
return d->ui.emailLE->text().trimmed();
}
void AddUserIDDialog::setComment(const QString &comment)
{
d->ui.commentLE->setText(comment);
}
QString AddUserIDDialog::comment() const
{
return d->ui.commentLE->text().trimmed();
}
static bool has_intermediate_input(const QLineEdit *le)
{
QString text = le->text();
int pos = le->cursorPosition();
const QValidator *const v = le->validator();
return !v || v->validate(text, pos) == QValidator::Intermediate;
}
static bool requirementsAreMet(const QVector<Line> &list, QString &error)
{
for (const Line &line : list) {
const QLineEdit *le = line.edit;
if (!le) {
continue;
}
const QString key = line.attr;
qCDebug(KLEOPATRA_LOG) << "requirementsAreMet(): checking \"" << key << "\" against \"" << le->text() << "\":";
if (le->text().trimmed().isEmpty()) {
if (key.endsWith(QLatin1Char('!'))) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is required, but empty.", line.label);
} else
error = xi18nc("@info", "<interface>%1</interface> is required, but empty.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
return false;
}
} else if (has_intermediate_input(le)) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is incomplete.", line.label);
} else
error = xi18nc("@info", "<interface>%1</interface> is incomplete.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
return false;
} else if (!le->hasAcceptableInput()) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is invalid.", line.label);
} else
error = xi18nc("@info", "<interface>%1</interface> is invalid.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
return false;
}
qCDebug(KLEOPATRA_LOG) << "ok";
}
return true;
}
bool AddUserIDDialog::Private::isComplete() const
{
QString error;
const bool ok = requirementsAreMet(ui.lineList, error);
ui.errorLB->setText(error);
return ok;
}
void AddUserIDDialog::Private::slotUserIDChanged()
{
ui.okPB()->setEnabled(isComplete());
const QString name = q->name();
const QString email = q->email();
const QString comment = q->comment();
QStringList parts;
if (!name.isEmpty()) {
parts.push_back(name);
}
if (!comment.isEmpty()) {
parts.push_back(QLatin1Char('(') + comment + QLatin1Char(')'));
}
if (!email.isEmpty()) {
parts.push_back(QLatin1Char('<') + email + QLatin1Char('>'));
}
ui.resultLB->setText(parts.join(QLatin1Char(' ')));
}
#include "moc_adduseriddialog.cpp"
diff --git a/src/smartcard/readerstatus.cpp b/src/smartcard/readerstatus.cpp
index 528ded58a..8c1de0926 100644
--- a/src/smartcard/readerstatus.cpp
+++ b/src/smartcard/readerstatus.cpp
@@ -1,824 +1,824 @@
/* -*- mode: c++; c-basic-offset:4 -*-
smartcard/readerstatus.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2009 Klarälvdalens Datakonsult AB
Kleopatra 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.
Kleopatra 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "readerstatus.h"
#include <utils/gnupg-helper.h>
#include <Libkleo/FileSystemWatcher>
#include <Libkleo/Stl_Util>
#include <gpgme++/context.h>
#include <gpgme++/defaultassuantransaction.h>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include <gpg-error.h>
#include "kleopatra_debug.h"
#include <QStringList>
#include <QDir>
#include <QFileInfo>
#include <QMutex>
#include <QWaitCondition>
#include <QThread>
#include <QPointer>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <memory>
#include <vector>
#include <set>
#include <list>
#include <algorithm>
#include <iterator>
#include <utility>
#include <cstdlib>
using namespace Kleo;
using namespace Kleo::SmartCard;
using namespace GpgME;
static const unsigned int CHECK_INTERVAL = 2000; // msecs
static ReaderStatus *self = nullptr;
struct CardInfo {
CardInfo()
: fileName(),
status(ReaderStatus::NoCard),
appType(ReaderStatus::UnknownApplication),
appVersion(-1)
{
}
CardInfo(const QString &fn, ReaderStatus::Status s)
: fileName(fn),
status(s),
appType(ReaderStatus::UnknownApplication),
appVersion(-1)
{
}
QString fileName;
ReaderStatus::Status status;
std::string serialNumber;
ReaderStatus::AppType appType;
int appVersion;
std::vector<ReaderStatus::PinState> pinStates;
};
static const char *flags[] = {
"NOCARD",
"PRESENT",
"ACTIVE",
"USABLE",
};
static_assert(sizeof flags / sizeof * flags == ReaderStatus::_NumScdStates, "");
static const char *prettyFlags[] = {
"NoCard",
"CardPresent",
"CardActive",
"CardUsable",
"CardCanLearnKeys",
"CardHasNullPin",
"CardError",
};
static_assert(sizeof prettyFlags / sizeof * prettyFlags == ReaderStatus::NumStates, "");
static QByteArray read_file(const QString &fileName)
{
QFile file(fileName);
if (!file.exists()) {
qCDebug(KLEOPATRA_LOG) << "read_file: file" << fileName << "does not exist";
return QByteArray();
}
if (!file.open(QIODevice::ReadOnly)) {
qCDebug(KLEOPATRA_LOG) << "read_file: failed to open" << fileName << ':' << file.errorString();
return QByteArray();
}
return file.readAll().trimmed();
}
static unsigned int parseFileName(const QString &fileName, bool *ok)
{
QRegExp rx(QLatin1String("reader_(\\d+)\\.status"));
if (ok) {
*ok = false;
}
if (rx.exactMatch(QFileInfo(fileName).fileName())) {
return rx.cap(1).toUInt(ok, 10);
}
return 0;
}
namespace
{
template <typename T>
const T &_trace__impl(const T &t, const char *msg)
{
qCDebug(KLEOPATRA_LOG) << msg << t;
return t;
}
#define TRACE( x ) _trace__impl( x, #x )
}
static QDebug operator<<(QDebug s, const std::vector< std::pair<std::string, std::string> > &v)
{
typedef std::pair<std::string, std::string> pair;
s << '(';
- Q_FOREACH (const pair &p, v) {
+ for (const pair &p : v) {
s << "status(" << QString::fromStdString(p.first) << ") =" << QString::fromStdString(p.second) << endl;
}
return s << ')';
}
static const char *app_types[] = {
"_", // will hopefully never be used as an app-type :)
"openpgp",
"nks",
"p15",
"dinsig",
"geldkarte",
};
static_assert(sizeof app_types / sizeof * app_types == ReaderStatus::NumAppTypes, "");
static ReaderStatus::AppType parse_app_type(const std::string &s)
{
qCDebug(KLEOPATRA_LOG) << "parse_app_type(" << s.c_str() << ")";
const char **it = std::find_if(std::begin(app_types), std::end(app_types),
[&s](const char *type) {
return ::strcasecmp(s.c_str(), type) == 0;
});
if (it == std::end(app_types)) {
return TRACE(ReaderStatus::UnknownApplication);
}
return TRACE(static_cast<ReaderStatus::AppType>(it - std::begin(app_types)));
}
static int parse_app_version(const std::string &s)
{
return std::atoi(s.c_str());
}
static ReaderStatus::PinState parse_pin_state(const std::string &s)
{
switch (int i = std::atoi(s.c_str())) {
case -4: return ReaderStatus::NullPin;
case -3: return ReaderStatus::PinBlocked;
case -2: return ReaderStatus::NoPin;
case -1: return ReaderStatus::UnknownPinState;
default:
if (i < 0) {
return ReaderStatus::UnknownPinState;
} else {
return ReaderStatus::PinOk;
}
}
}
static std::unique_ptr<DefaultAssuanTransaction> gpgagent_transact(std::shared_ptr<Context> &gpgAgent, const char *command, Error &err)
{
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "gpgagent_transact(" << command << ")";
#endif
err = gpgAgent->assuanTransact(command);
if (err.code()) {
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "gpgagent_transact(" << command << "):" << QString::fromLocal8Bit(err.asString());
#endif
if (err.code() >= GPG_ERR_ASS_GENERAL && err.code() <= GPG_ERR_ASS_UNKNOWN_INQUIRE) {
qCDebug(KLEOPATRA_LOG) << "Assuan problem, killing context";
gpgAgent.reset();
}
return std::unique_ptr<DefaultAssuanTransaction>();
}
std::unique_ptr<AssuanTransaction> t = gpgAgent->takeLastAssuanTransaction();
return std::unique_ptr<DefaultAssuanTransaction>(dynamic_cast<DefaultAssuanTransaction*>(t.release()));
}
// returns const std::string so template deduction in boost::split works, and we don't need a temporary
static const std::string scd_getattr_status(std::shared_ptr<Context> &gpgAgent, const char *what, Error &err)
{
std::string cmd = "SCD GETATTR ";
cmd += what;
const std::unique_ptr<DefaultAssuanTransaction> t = gpgagent_transact(gpgAgent, cmd.c_str(), err);
if (t.get()) {
qCDebug(KLEOPATRA_LOG) << "scd_getattr_status(" << what << "): got" << t->statusLines();
return t->firstStatusLine(what);
} else {
qCDebug(KLEOPATRA_LOG) << "scd_getattr_status(" << what << "): t == NULL";
return std::string();
}
}
static unsigned int parse_event_counter(const std::string &str)
{
unsigned int result;
if (sscanf(str.c_str(), "%*u %*u %u ", &result) == 1) {
return result;
}
return -1;
}
static unsigned int get_event_counter(std::shared_ptr<Context> &gpgAgent)
{
Error err;
const std::unique_ptr<DefaultAssuanTransaction> t = gpgagent_transact(gpgAgent, "GETEVENTCOUNTER", err);
if (err.code()) {
qCDebug(KLEOPATRA_LOG) << "get_event_counter(): got error" << err.asString();
}
if (t.get()) {
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "get_event_counter(): got" << t->statusLines();
#endif
return parse_event_counter(t->firstStatusLine("EVENTCOUNTER"));
} else {
qCDebug(KLEOPATRA_LOG) << "scd_getattr_status(): t == NULL";
return -1;
}
}
// returns const std::string so template deduction in boost::split works, and we don't need a temporary
static const std::string gpgagent_data(std::shared_ptr<Context> &gpgAgent, const char *what, Error &err)
{
const std::unique_ptr<DefaultAssuanTransaction> t = gpgagent_transact(gpgAgent, what, err);
if (t.get()) {
return t->data();
} else {
return std::string();
}
}
static std::string parse_keypairinfo(const std::string &kpi)
{
static const char hexchars[] = "0123456789abcdefABCDEF";
return '&' + kpi.substr(0, kpi.find_first_not_of(hexchars));
}
static bool parse_keypairinfo_and_lookup_key(Context *ctx, const std::string &kpi)
{
if (!ctx) {
return false;
}
const std::string pattern = parse_keypairinfo(kpi);
qCDebug(KLEOPATRA_LOG) << "parse_keypairinfo_and_lookup_key: pattern=" << pattern.c_str();
if (const Error err = ctx->startKeyListing(pattern.c_str())) {
qCDebug(KLEOPATRA_LOG) << "parse_keypairinfo_and_lookup_key: startKeyListing failed:" << err.asString();
return false;
}
Error e;
const Key key = ctx->nextKey(e);
ctx->endKeyListing();
qCDebug(KLEOPATRA_LOG) << "parse_keypairinfo_and_lookup_key: e=" << e.code() << "; key.isNull()" << key.isNull();
return !e && !key.isNull();
}
static CardInfo get_card_status(const QString &fileName, unsigned int idx, std::shared_ptr<Context> &gpg_agent)
{
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "get_card_status(" << fileName << ',' << idx << ',' << gpg_agent.get() << ')';
#endif
CardInfo ci(fileName, ReaderStatus::CardUsable);
if (idx != 0 || !gpg_agent) {
return ci;
}
Error err;
ci.serialNumber = gpgagent_data(gpg_agent, "SCD SERIALNO", err);
if (err.code() == GPG_ERR_CARD_NOT_PRESENT || err.code() == GPG_ERR_CARD_REMOVED) {
ci.status = ReaderStatus::NoCard;
return ci;
}
if (err.code()) {
ci.status = ReaderStatus::CardError;
return ci;
}
ci.appType = parse_app_type(scd_getattr_status(gpg_agent, "APPTYPE", err));
if (err.code()) {
return ci;
}
if (ci.appType != ReaderStatus::NksApplication) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: not a NetKey card, giving up";
return ci;
}
ci.appVersion = parse_app_version(scd_getattr_status(gpg_agent, "NKS-VERSION", err));
if (err.code()) {
return ci;
}
if (ci.appVersion != 3) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: not a NetKey v3 card, giving up";
return ci;
}
// the following only works for NKS v3...
std::vector<std::string> chvStatus;
chvStatus.reserve(4); // expected number of fields
boost::split(chvStatus, scd_getattr_status(gpg_agent, "CHV-STATUS", err), boost::is_any_of(" \t"), boost::token_compress_on);
if (err.code()) {
return ci;
}
std::transform(chvStatus.begin(), chvStatus.end(),
std::back_inserter(ci.pinStates),
parse_pin_state);
if (std::find(ci.pinStates.cbegin(), ci.pinStates.cend(), ReaderStatus::NullPin) != ci.pinStates.cend()) {
ci.status = ReaderStatus::CardHasNullPin;
return ci;
}
// check for keys to learn:
const std::unique_ptr<DefaultAssuanTransaction> result = gpgagent_transact(gpg_agent, "SCD LEARN --keypairinfo", err);
if (err.code() || !result.get()) {
return ci;
}
const std::vector<std::string> keyPairInfos = result->statusLine("KEYPAIRINFO");
if (keyPairInfos.empty()) {
return ci;
}
// check that any of the
const std::unique_ptr<Context> klc(Context::createForProtocol(CMS)); // what about OpenPGP?
if (!klc.get()) {
return ci;
}
klc->setKeyListMode(Ephemeral);
if (std::any_of(keyPairInfos.cbegin(), keyPairInfos.cend(),
[&klc](const std::string &str) {
return !parse_keypairinfo_and_lookup_key(klc.get(), str);
})) {
ci.status = ReaderStatus::CardCanLearnKeys;
}
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "get_card_status: ci.status " << prettyFlags[ci.status];
#endif
return ci;
}
static std::vector<CardInfo> update_cardinfo(const QString &gnupgHomePath, std::shared_ptr<Context> &gpgAgent)
{
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "<update_cardinfo>";
#endif
const QDir gnupgHome(gnupgHomePath);
if (!gnupgHome.exists()) {
qCWarning(KLEOPATRA_LOG) << "gnupg home" << gnupgHomePath << "does not exist!";
}
const CardInfo ci = get_card_status(gnupgHome.absoluteFilePath(QStringLiteral("reader_0.status")), 0, gpgAgent);
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "</update_cardinfo>";
#endif
return std::vector<CardInfo>(1, ci);
}
static bool check_event_counter_changed(std::shared_ptr<Context> &gpg_agent, unsigned int &counter)
{
const unsigned int oldCounter = counter;
counter = get_event_counter(gpg_agent);
if (oldCounter != counter) {
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: events:" << oldCounter << "->" << counter;
#endif
return true;
} else {
return false;
}
}
struct Transaction {
QByteArray command;
QPointer<QObject> receiver;
const char *slot;
GpgME::Error error;
};
static const Transaction checkTransaction = { "__check__", nullptr, nullptr, Error() };
static const Transaction updateTransaction = { "__update__", nullptr, nullptr, Error() };
static const Transaction quitTransaction = { "__quit__", nullptr, nullptr, Error() };
namespace
{
class ReaderStatusThread : public QThread
{
Q_OBJECT
public:
explicit ReaderStatusThread(QObject *parent = nullptr)
: QThread(parent),
m_gnupgHomePath(Kleo::gnupgHomeDirectory()),
m_transactions(1, updateTransaction) // force initial scan
{
connect(this, &ReaderStatusThread::oneTransactionFinished,
this, &ReaderStatusThread::slotOneTransactionFinished);
}
std::vector<CardInfo> cardInfos() const
{
const QMutexLocker locker(&m_mutex);
return m_cardInfos;
}
ReaderStatus::Status cardStatus(unsigned int slot) const
{
const QMutexLocker locker(&m_mutex);
if (slot < m_cardInfos.size()) {
return m_cardInfos[slot].status;
} else {
return ReaderStatus::NoCard;
}
}
void addTransaction(const Transaction &t)
{
const QMutexLocker locker(&m_mutex);
m_transactions.push_back(t);
m_waitForTransactions.wakeOne();
}
// make QThread::sleep public
using QThread::sleep;
Q_SIGNALS:
void anyCardHasNullPinChanged(bool);
void anyCardCanLearnKeysChanged(bool);
void cardStatusChanged(unsigned int, Kleo::SmartCard::ReaderStatus::Status);
void oneTransactionFinished();
public Q_SLOTS:
void ping()
{
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[GUI]::ping()";
addTransaction(updateTransaction);
}
void stop()
{
const QMutexLocker locker(&m_mutex);
m_transactions.push_front(quitTransaction);
m_waitForTransactions.wakeOne();
}
void slotReaderStatusFileChanged()
{
const QDir gnupgHome(m_gnupgHomePath);
if (!gnupgHome.exists()) {
qCWarning(KLEOPATRA_LOG) << "gnupg home" << m_gnupgHomePath << "does not exist!";
return;
}
QStringList files = gnupgHome.entryList(QStringList(QStringLiteral("reader_*.status")), QDir::Files, QDir::Name);
std::sort(files.begin(), files.end(),
[](const QString &lhs, const QString &rhs) {
return parseFileName(lhs, nullptr) < parseFileName(rhs, nullptr);
});
std::vector<QByteArray> contents;
Q_FOREACH (const QString &file, files) {
bool ok = false;
const unsigned int idx = parseFileName(file, &ok);
if (!ok) {
qCDebug(KLEOPATRA_LOG) << "filename" << file << ": cannot parse reader slot number";
continue;
}
assert(idx >= contents.size());
contents.resize(idx);
contents.push_back(read_file(gnupgHome.absoluteFilePath(file)));
}
// canonicalise by removing empty stuff from the end
while (!contents.empty() && contents.back().isEmpty()) {
contents.pop_back();
}
if (contents != readerStatusFileContents) {
ping();
}
readerStatusFileContents.swap(contents);
}
private Q_SLOTS:
void slotOneTransactionFinished()
{
std::list<Transaction> ft;
KDAB_SYNCHRONIZED(m_mutex)
ft.splice(ft.begin(), m_finishedTransactions);
Q_FOREACH (const Transaction &t, ft)
if (t.receiver && t.slot && *t.slot) {
QMetaObject::invokeMethod(t.receiver, t.slot, Qt::DirectConnection, Q_ARG(GpgME::Error, t.error));
}
}
private:
void run() Q_DECL_OVERRIDE {
std::shared_ptr<Context> gpgAgent;
unsigned int eventCounter = -1;
while (true)
{
QByteArray command;
bool nullSlot = false;
std::list<Transaction> item;
std::vector<CardInfo> oldCardInfos;
if (!gpgAgent) {
Error err;
std::unique_ptr<Context> c = Context::createForEngine(AssuanEngine, &err);
if (err.code() == GPG_ERR_NOT_SUPPORTED) {
return;
}
gpgAgent = std::shared_ptr<Context>(c.release());
}
KDAB_SYNCHRONIZED(m_mutex) {
while (m_transactions.empty()) {
// go to sleep waiting for more work:
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: .zZZ";
#endif
if (!m_waitForTransactions.wait(&m_mutex, CHECK_INTERVAL)) {
m_transactions.push_front(checkTransaction);
}
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: .oOO";
#endif
}
// splice off the first transaction without
// copying, so we own it without really importing
// it into this thread (the QPointer isn't
// thread-safe):
item.splice(item.end(),
m_transactions, m_transactions.begin());
// make local copies of the interesting stuff so
// we can release the mutex again:
command = item.front().command;
nullSlot = !item.front().slot;
oldCardInfos = m_cardInfos;
}
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: new iteration command=" << command << " ; nullSlot=" << nullSlot;
#endif
// now, let's see what we got:
if (nullSlot && command == quitTransaction.command) {
return; // quit
}
if ((nullSlot && command == updateTransaction.command) ||
(nullSlot && command == checkTransaction.command)) {
if (nullSlot && command == checkTransaction.command && !check_event_counter_changed(gpgAgent, eventCounter)) {
continue; // early out
}
std::vector<CardInfo> newCardInfos
= update_cardinfo(m_gnupgHomePath, gpgAgent);
newCardInfos.resize(std::max(newCardInfos.size(), oldCardInfos.size()));
oldCardInfos.resize(std::max(newCardInfos.size(), oldCardInfos.size()));
KDAB_SYNCHRONIZED(m_mutex)
m_cardInfos = newCardInfos;
std::vector<CardInfo>::const_iterator
nit = newCardInfos.begin(), nend = newCardInfos.end(),
oit = oldCardInfos.begin(), oend = oldCardInfos.end();
unsigned int idx = 0;
bool anyLC = false;
bool anyNP = false;
bool anyError = false;
while (nit != nend && oit != oend) {
if (nit->status != oit->status) {
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: slot" << idx << ":" << prettyFlags[oit->status] << "->" << prettyFlags[nit->status];
#endif
Q_EMIT cardStatusChanged(idx, nit->status);
}
if (nit->status == ReaderStatus::CardCanLearnKeys) {
anyLC = true;
}
if (nit->status == ReaderStatus::CardHasNullPin) {
anyNP = true;
}
if (nit->status == ReaderStatus::CardError) {
anyError = true;
}
++nit;
++oit;
++idx;
}
Q_EMIT anyCardHasNullPinChanged(anyNP);
Q_EMIT anyCardCanLearnKeysChanged(anyLC);
if (anyError) {
gpgAgent.reset();
}
} else {
(void)gpgagent_transact(gpgAgent, command.constData(), item.front().error);
KDAB_SYNCHRONIZED(m_mutex)
// splice 'item' into m_finishedTransactions:
m_finishedTransactions.splice(m_finishedTransactions.end(), item);
Q_EMIT oneTransactionFinished();
}
// update event counter in case anything above changed
// it:
if (gpgAgent) {
eventCounter = get_event_counter(gpgAgent);
} else {
eventCounter = -1;
}
#ifdef DEBUG_SCREADER
qCDebug(KLEOPATRA_LOG) << "eventCounter:" << eventCounter;
#endif
}
}
private:
mutable QMutex m_mutex;
QWaitCondition m_waitForTransactions;
const QString m_gnupgHomePath;
std::vector<QByteArray> readerStatusFileContents;
// protected by m_mutex:
std::vector<CardInfo> m_cardInfos;
std::list<Transaction> m_transactions, m_finishedTransactions;
};
}
class ReaderStatus::Private : ReaderStatusThread
{
friend class Kleo::SmartCard::ReaderStatus;
ReaderStatus *const q;
public:
explicit Private(ReaderStatus *qq)
: ReaderStatusThread(qq),
q(qq),
watcher()
{
KDAB_SET_OBJECT_NAME(watcher);
qRegisterMetaType<Status>("Kleo::SmartCard::ReaderStatus::Status");
watcher.whitelistFiles(QStringList(QStringLiteral("reader_*.status")));
watcher.addPath(Kleo::gnupgHomeDirectory());
watcher.setDelay(100);
connect(this, &::ReaderStatusThread::cardStatusChanged,
q, &ReaderStatus::cardStatusChanged);
connect(this, &::ReaderStatusThread::anyCardHasNullPinChanged,
q, &ReaderStatus::anyCardHasNullPinChanged);
connect(this, &::ReaderStatusThread::anyCardCanLearnKeysChanged,
q, &ReaderStatus::anyCardCanLearnKeysChanged);
connect(&watcher, &FileSystemWatcher::triggered, this, &::ReaderStatusThread::slotReaderStatusFileChanged);
}
~Private()
{
stop();
if (!wait(100)) {
terminate();
wait();
}
}
private:
bool anyCardHasNullPinImpl() const
{
const auto cis = cardInfos();
return std::any_of(cis.cbegin(), cis.cend(),
[](const CardInfo &ci) { return ci.status == CardHasNullPin; });
}
bool anyCardCanLearnKeysImpl() const
{
const auto cis = cardInfos();
return std::any_of(cis.cbegin(), cis.cend(),
[](const CardInfo &ci) { return ci.status == CardCanLearnKeys; });
}
private:
FileSystemWatcher watcher;
};
ReaderStatus::ReaderStatus(QObject *parent)
: QObject(parent), d(new Private(this))
{
self = this;
}
ReaderStatus::~ReaderStatus()
{
self = nullptr;
}
// slot
void ReaderStatus::startMonitoring()
{
d->start();
}
// static
ReaderStatus *ReaderStatus::mutableInstance()
{
return self;
}
// static
const ReaderStatus *ReaderStatus::instance()
{
return self;
}
ReaderStatus::Status ReaderStatus::cardStatus(unsigned int slot) const
{
return d->cardStatus(slot);
}
bool ReaderStatus::anyCardHasNullPin() const
{
return d->anyCardHasNullPinImpl();
}
bool ReaderStatus::anyCardCanLearnKeys() const
{
return d->anyCardCanLearnKeysImpl();
}
std::vector<ReaderStatus::PinState> ReaderStatus::pinStates(unsigned int slot) const
{
const std::vector<CardInfo> ci = d->cardInfos();
if (slot < ci.size()) {
return ci[slot].pinStates;
} else {
return std::vector<PinState>();
}
}
void ReaderStatus::startSimpleTransaction(const QByteArray &command, QObject *receiver, const char *slot)
{
const Transaction t = { command, receiver, slot, Error() };
d->addTransaction(t);
}
void ReaderStatus::updateStatus()
{
d->ping();
}
#include "readerstatus.moc"
diff --git a/src/utils/path-helper.cpp b/src/utils/path-helper.cpp
index 08a539b28..425ac22a0 100644
--- a/src/utils/path-helper.cpp
+++ b/src/utils/path-helper.cpp
@@ -1,124 +1,124 @@
/* -*- mode: c++; c-basic-offset:4 -*-
utils/path-helper.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2009 Klarälvdalens Datakonsult AB
Kleopatra 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.
Kleopatra 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "path-helper.h"
#include <Libkleo/Stl_Util>
#include <Libkleo/Exception>
#include "kleopatra_debug.h"
#include <KLocalizedString>
#include <QString>
#include <QStringList>
#include <QFileInfo>
#include <QDir>
#include <algorithm>
using namespace Kleo;
static QString commonPrefix(const QString &s1, const QString &s2)
{
return QString(s1.data(), std::mismatch(s1.data(), s1.data() + std::min(s1.size(), s2.size()), s2.data()).first - s1.data());
}
static QString longestCommonPrefix(const QStringList &sl)
{
if (sl.empty()) {
return QString();
}
QString result = sl.front();
Q_FOREACH (const QString &s, sl) {
result = commonPrefix(s, result);
}
return result;
}
QString Kleo::heuristicBaseDirectory(const QStringList &fileNames)
{
QStringList dirs;
- Q_FOREACH (const QString &fileName, fileNames) {
+ for (const QString &fileName : fileNames) {
dirs.push_back(QFileInfo(fileName).path() + QLatin1Char('/'));
}
qCDebug(KLEOPATRA_LOG) << "dirs" << dirs;
const QString candidate = longestCommonPrefix(dirs);
const int idx = candidate.lastIndexOf(QLatin1Char('/'));
return candidate.left(idx);
}
QStringList Kleo::makeRelativeTo(const QString &base, const QStringList &fileNames)
{
if (base.isEmpty()) {
return fileNames;
} else {
return makeRelativeTo(QDir(base), fileNames);
}
}
QStringList Kleo::makeRelativeTo(const QDir &baseDir, const QStringList &fileNames)
{
QStringList rv;
rv.reserve(fileNames.size());
std::transform(fileNames.cbegin(), fileNames.cend(),
std::back_inserter(rv),
[&baseDir](const QString &file) {
return baseDir.relativeFilePath(file);
});
return rv;
}
void Kleo::recursivelyRemovePath(const QString &path)
{
const QFileInfo fi(path);
if (fi.isDir()) {
QDir dir(path);
Q_FOREACH (const QString &fname, dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot)) {
recursivelyRemovePath(dir.filePath(fname));
}
const QString dirName = fi.fileName();
dir.cdUp();
if (!dir.rmdir(dirName)) {
throw Exception(GPG_ERR_EPERM, i18n("Cannot remove directory %1", path));
}
} else {
QFile file(path);
if (!file.remove()) {
throw Exception(GPG_ERR_EPERM, i18n("Cannot remove file %1: %2", path, file.errorString()));
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 8, 7:48 AM (23 h, 44 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
92/e1/e859618fba98afc16d0fdfc2d3f1

Event Timeline