Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F31845953
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
18 KB
Subscribers
None
View Options
diff --git a/src/libkleopatraclient/core/command.cpp b/src/libkleopatraclient/core/command.cpp
index 8476a4907..08fdfb821 100644
--- a/src/libkleopatraclient/core/command.cpp
+++ b/src/libkleopatraclient/core/command.cpp
@@ -1,678 +1,675 @@
/* -*- mode: c++; c-basic-offset:4 -*-
command.cpp
This file is part of KleopatraClient, the Kleopatra interface library
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "command.h"
#include "command_p.h"
#include <QtGlobal> // Q_OS_WIN
#ifdef Q_OS_WIN // HACK: AllowSetForegroundWindow needs _WIN32_WINDOWS >= 0x0490 set
# ifndef _WIN32_WINDOWS
# define _WIN32_WINDOWS 0x0500
# endif
# ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0500 // good enough for Vista too
# endif
# include <utils/gnupg-registry.h>
# include <windows.h>
#endif
#include <QMutexLocker>
#include <QFile>
#include "libkleopatraclientcore_debug.h"
#include <QDir>
#include <QProcess>
#include <KLocalizedString>
#include <assuan.h>
#include <gpg-error.h>
#include <gpgme++/global.h>
#include <algorithm>
#include <string>
#include <sstream>
#include <memory>
#include <type_traits>
using namespace KleopatraClientCopy;
// copied from kleopatra/utils/hex.cpp
static std::string hexencode(const std::string &in)
{
std::string result;
result.reserve(3 * in.size());
static const char hex[] = "0123456789ABCDEF";
for (std::string::const_iterator it = in.begin(), end = in.end(); it != end; ++it)
switch (const unsigned char ch = *it) {
default:
if ((ch >= '!' && ch <= '~') || ch > 0xA0) {
result += ch;
break;
}
Q_FALLTHROUGH();
// else fall through
case ' ':
result += '+';
break;
case '"':
case '#':
case '$':
case '%':
case '\'':
case '+':
case '=':
result += '%';
result += hex[(ch & 0xF0) >> 4 ];
result += hex[(ch & 0x0F) ];
break;
}
return result;
}
#ifdef UNUSED
static std::string hexencode(const char *in)
{
if (!in) {
return std::string();
}
return hexencode(std::string(in));
}
#endif
// changed from returning QByteArray to returning std::string
static std::string hexencode(const QByteArray &in)
{
if (in.isNull()) {
return std::string();
}
return hexencode(std::string(in.data(), in.size()));
}
// end copied from kleopatra/utils/hex.cpp
Command::Command(QObject *p)
: QObject(p), d(new Private(this))
{
d->init();
}
Command::Command(Private *pp, QObject *p)
: QObject(p), d(pp)
{
d->init();
}
Command::~Command()
{
delete d; d = nullptr;
}
void Command::Private::init()
{
connect(this, &QThread::started, q, &Command::started);
connect(this, &QThread::finished, q, &Command::finished);
}
void Command::setParentWId(WId wid)
{
const QMutexLocker locker(&d->mutex);
d->inputs.parentWId = wid;
}
WId Command::parentWId() const
{
const QMutexLocker locker(&d->mutex);
return d->inputs.parentWId;
}
void Command::setServerLocation(const QString &location)
{
const QMutexLocker locker(&d->mutex);
d->outputs.serverLocation = location;
}
QString Command::serverLocation() const
{
const QMutexLocker locker(&d->mutex);
return d->outputs.serverLocation;
}
bool Command::waitForFinished()
{
return d->wait();
}
bool Command::waitForFinished(unsigned long ms)
{
return d->wait(ms);
}
bool Command::error() const
{
const QMutexLocker locker(&d->mutex);
return !d->outputs.errorString.isEmpty();
}
bool Command::wasCanceled() const
{
const QMutexLocker locker(&d->mutex);
return d->outputs.canceled;
}
QString Command::errorString() const
{
const QMutexLocker locker(&d->mutex);
return d->outputs.errorString;
}
qint64 Command::serverPid() const
{
const QMutexLocker locker(&d->mutex);
return d->outputs.serverPid;
}
void Command::start()
{
d->start();
}
void Command::cancel()
{
qCDebug(LIBKLEOPATRACLIENTCORE_LOG) << "Sorry, not implemented: KleopatraClient::Command::Cancel";
}
void Command::setOptionValue(const char *name, const QVariant &value, bool critical)
{
if (!name || !*name) {
return;
}
const Private::Option opt = {
value,
true,
critical
};
const QMutexLocker locker(&d->mutex);
d->inputs.options[name] = opt;
}
QVariant Command::optionValue(const char *name) const
{
if (!name || !*name) {
return QVariant();
}
const QMutexLocker locker(&d->mutex);
const auto it = d->inputs.options.find(name);
if (it == d->inputs.options.end()) {
return QVariant();
} else {
return it->second.value;
}
}
void Command::setOption(const char *name, bool critical)
{
if (!name || !*name) {
return;
}
const QMutexLocker locker(&d->mutex);
if (isOptionSet(name)) {
unsetOption(name);
}
const Private::Option opt = {
QVariant(),
false,
critical
};
d->inputs.options[name] = opt;
}
void Command::unsetOption(const char *name)
{
if (!name || !*name) {
return;
}
const QMutexLocker locker(&d->mutex);
d->inputs.options.erase(name);
}
bool Command::isOptionSet(const char *name) const
{
if (!name || !*name) {
return false;
}
const QMutexLocker locker(&d->mutex);
return d->inputs.options.count(name);
}
bool Command::isOptionCritical(const char *name) const
{
if (!name || !*name) {
return false;
}
const QMutexLocker locker(&d->mutex);
const auto it = d->inputs.options.find(name);
return it != d->inputs.options.end() && it->second.isCritical;
}
void Command::setFilePaths(const QStringList &filePaths)
{
const QMutexLocker locker(&d->mutex);
d->inputs.filePaths = filePaths;
}
QStringList Command::filePaths() const
{
const QMutexLocker locker(&d->mutex);
return d->inputs.filePaths;
}
void Command::setRecipients(const QStringList &recipients, bool informative)
{
const QMutexLocker locker(&d->mutex);
d->inputs.recipients = recipients;
d->inputs.areRecipientsInformative = informative;
}
QStringList Command::recipients() const
{
const QMutexLocker locker(&d->mutex);
return d->inputs.recipients;
}
bool Command::areRecipientsInformative() const
{
const QMutexLocker locker(&d->mutex);
return d->inputs.areRecipientsInformative;
}
void Command::setSenders(const QStringList &senders, bool informative)
{
const QMutexLocker locker(&d->mutex);
d->inputs.senders = senders;
d->inputs.areSendersInformative = informative;
}
QStringList Command::senders() const
{
const QMutexLocker locker(&d->mutex);
return d->inputs.senders;
}
bool Command::areSendersInformative() const
{
const QMutexLocker locker(&d->mutex);
return d->inputs.areSendersInformative;
}
void Command::setInquireData(const char *what, const QByteArray &data)
{
const QMutexLocker locker(&d->mutex);
d->inputs.inquireData[what] = data;
}
void Command::unsetInquireData(const char *what)
{
const QMutexLocker locker(&d->mutex);
d->inputs.inquireData.erase(what);
}
QByteArray Command::inquireData(const char *what) const
{
const QMutexLocker locker(&d->mutex);
const auto it = d->inputs.inquireData.find(what);
if (it == d->inputs.inquireData.end()) {
return QByteArray();
} else {
return it->second;
}
}
bool Command::isInquireDataSet(const char *what) const
{
const QMutexLocker locker(&d->mutex);
const auto it = d->inputs.inquireData.find(what);
return it != d->inputs.inquireData.end();
}
QByteArray Command::receivedData() const
{
const QMutexLocker locker(&d->mutex);
return d->outputs.data;
}
void Command::setCommand(const char *command)
{
const QMutexLocker locker(&d->mutex);
d->inputs.command = command;
}
QByteArray Command::command() const
{
const QMutexLocker locker(&d->mutex);
return d->inputs.command;
}
//
// here comes the ugly part
//
static void my_assuan_release(assuan_context_t ctx)
{
if (ctx) {
assuan_release(ctx);
}
}
using AssuanContextBase = std::shared_ptr<std::remove_pointer<assuan_context_t>::type>;
namespace
{
struct AssuanClientContext : AssuanContextBase {
AssuanClientContext() : AssuanContextBase() {}
explicit AssuanClientContext(assuan_context_t ctx) : AssuanContextBase(ctx, &my_assuan_release) {}
void reset(assuan_context_t ctx = nullptr)
{
AssuanContextBase::reset(ctx, &my_assuan_release);
}
};
}
-// compatibility typedef - remove when we require assuan v2...
-using assuan_error_t = gpg_error_t;
-
-static assuan_error_t
+static gpg_error_t
my_assuan_transact(const AssuanClientContext &ctx,
const char *command,
- assuan_error_t (*data_cb)(void *, const void *, size_t) = nullptr,
+ gpg_error_t (*data_cb)(void *, const void *, size_t) = nullptr,
void *data_cb_arg = nullptr,
- assuan_error_t (*inquire_cb)(void *, const char *) = nullptr,
+ gpg_error_t (*inquire_cb)(void *, const char *) = nullptr,
void *inquire_cb_arg = nullptr,
- assuan_error_t (*status_cb)(void *, const char *) = nullptr,
+ gpg_error_t (*status_cb)(void *, const char *) = nullptr,
void *status_cb_arg = nullptr)
{
return assuan_transact(ctx.get(), command, data_cb, data_cb_arg, inquire_cb, inquire_cb_arg, status_cb, status_cb_arg);
}
static QString to_error_string(int err)
{
char buffer[1024];
gpg_strerror_r(static_cast<gpg_error_t>(err),
buffer, sizeof buffer);
buffer[sizeof buffer - 1] = '\0';
return QString::fromLocal8Bit(buffer);
}
static QString gnupg_home_directory()
{
static const char *hDir = GpgME::dirInfo("homedir");
return QFile::decodeName(hDir);
}
static QString get_default_socket_name()
{
const QString socketPath{QString::fromUtf8(GpgME::dirInfo("uiserver-socket"))};
if (!socketPath.isEmpty()) {
// Note: The socket directory exists after GpgME::dirInfo() has been called.
return socketPath;
}
const QString homeDir = gnupg_home_directory();
if (homeDir.isEmpty()) {
return QString();
}
return QDir(homeDir).absoluteFilePath(QStringLiteral("S.uiserver"));
}
static QString default_socket_name()
{
static QString name = get_default_socket_name();
return name;
}
static QString uiserver_executable()
{
return QStringLiteral("kleopatra");
}
static QString start_uiserver()
{
// Warning: Don't assume that the program needs to be in PATH. On Windows, it will also be found next to the calling process.
if (!QProcess::startDetached(uiserver_executable(), QStringList() << QStringLiteral("--daemon"))) {
return i18n("Failed to start uiserver %1", uiserver_executable());
} else {
return QString();
}
}
-static assuan_error_t getinfo_pid_cb(void *opaque, const void *buffer, size_t length)
+static gpg_error_t getinfo_pid_cb(void *opaque, const void *buffer, size_t length)
{
qint64 &pid = *static_cast<qint64 *>(opaque);
pid = QByteArray(static_cast<const char *>(buffer), length).toLongLong();
return 0;
}
-static assuan_error_t command_data_cb(void *opaque, const void *buffer, size_t length)
+static gpg_error_t command_data_cb(void *opaque, const void *buffer, size_t length)
{
QByteArray &ba = *static_cast<QByteArray *>(opaque);
ba.append(QByteArray(static_cast<const char *>(buffer), length));
return 0;
}
namespace
{
struct inquire_data {
const std::map<std::string, QByteArray> *map;
const AssuanClientContext *ctx;
};
}
-static assuan_error_t command_inquire_cb(void *opaque, const char *what)
+static gpg_error_t command_inquire_cb(void *opaque, const char *what)
{
if (!opaque) {
return 0;
}
const inquire_data &id = *static_cast<const inquire_data *>(opaque);
const auto it = id.map->find(what);
if (it != id.map->end()) {
const QByteArray &v = it->second;
assuan_send_data(id.ctx->get(), v.data(), v.size());
}
return 0;
}
static inline std::ostream &operator<<(std::ostream &s, const QByteArray &ba)
{
return s << std::string(ba.data(), ba.size());
}
-static assuan_error_t send_option(const AssuanClientContext &ctx, const char *name, const QVariant &value)
+static gpg_error_t send_option(const AssuanClientContext &ctx, const char *name, const QVariant &value)
{
std::stringstream ss;
ss << "OPTION " << name;
if (value.isValid()) {
ss << '=' << value.toString().toUtf8();
}
return my_assuan_transact(ctx, ss.str().c_str());
}
-static assuan_error_t send_file(const AssuanClientContext &ctx, const QString &file)
+static gpg_error_t send_file(const AssuanClientContext &ctx, const QString &file)
{
std::stringstream ss;
ss << "FILE " << hexencode(QFile::encodeName(file));
return my_assuan_transact(ctx, ss.str().c_str());
}
-static assuan_error_t send_recipient(const AssuanClientContext &ctx, const QString &recipient, bool info)
+static gpg_error_t send_recipient(const AssuanClientContext &ctx, const QString &recipient, bool info)
{
std::stringstream ss;
ss << "RECIPIENT ";
if (info) {
ss << "--info ";
}
ss << "--" << hexencode(recipient.toUtf8());
return my_assuan_transact(ctx, ss.str().c_str());
}
-static assuan_error_t send_sender(const AssuanClientContext &ctx, const QString &sender, bool info)
+static gpg_error_t send_sender(const AssuanClientContext &ctx, const QString &sender, bool info)
{
std::stringstream ss;
ss << "SENDER ";
if (info) {
ss << "--info ";
}
ss << "--" << hexencode(sender.toUtf8());
return my_assuan_transact(ctx, ss.str().c_str());
}
void Command::Private::run()
{
// Take a snapshot of the input data, and clear the output data:
Inputs in;
Outputs out;
{
const QMutexLocker locker(&mutex);
in = inputs;
outputs = out;
}
out.canceled = false;
if (out.serverLocation.isEmpty()) {
out.serverLocation = default_socket_name();
}
AssuanClientContext ctx;
- assuan_error_t err = 0;
+ gpg_error_t err = 0;
inquire_data id = { &in.inquireData, &ctx };
const QString socketName = out.serverLocation;
if (socketName.isEmpty()) {
out.errorString = i18n("Invalid socket name!");
goto leave;
}
{
assuan_context_t naked_ctx = nullptr;
err = assuan_new(&naked_ctx);
if (err) {
out.errorString = i18n("Could not allocate resources to connect to Kleopatra UI server at %1: %2"
, socketName, to_error_string(err));
goto leave;
}
ctx.reset(naked_ctx);
}
err = assuan_socket_connect(ctx.get(), socketName.toUtf8().constData(), -1, 0);
if (err) {
qDebug("UI server not running, starting it");
const QString errorString = start_uiserver();
if (!errorString.isEmpty()) {
out.errorString = errorString;
goto leave;
}
// give it a bit of time to start up and try a couple of times
for (int i = 0; err && i < 20; ++i) {
msleep(500);
err = assuan_socket_connect(ctx.get(), socketName.toUtf8().constData(), -1, 0);
}
}
if (err) {
out.errorString = i18n("Could not connect to Kleopatra UI server at %1: %2",
socketName, to_error_string(err));
goto leave;
}
out.serverPid = -1;
err = my_assuan_transact(ctx, "GETINFO pid", &getinfo_pid_cb, &out.serverPid);
if (err || out.serverPid <= 0) {
out.errorString = i18n("Could not get the process-id of the Kleopatra UI server at %1: %2", socketName, to_error_string(err));
goto leave;
}
qCDebug(LIBKLEOPATRACLIENTCORE_LOG) << "Server PID =" << out.serverPid;
#if defined(Q_OS_WIN)
if (!AllowSetForegroundWindow((pid_t)out.serverPid)) {
qCDebug(LIBKLEOPATRACLIENTCORE_LOG) << "AllowSetForegroundWindow(" << out.serverPid << ") failed: " << GetLastError();
}
#endif
if (in.command.isEmpty()) {
goto leave;
}
if (in.parentWId) {
err = send_option(ctx, "window-id", QString::number(in.parentWId, 16));
if (err) {
qDebug("sending option window-id failed - ignoring");
}
}
for (auto it = in.options.begin(), end = in.options.end(); it != end; ++it)
if ((err = send_option(ctx, it->first.c_str(), it->second.hasValue ? it->second.value.toString() : QVariant()))) {
if (it->second.isCritical) {
out.errorString = i18n("Failed to send critical option %1: %2", QString::fromLatin1(it->first.c_str()), to_error_string(err));
goto leave;
} else {
qCDebug(LIBKLEOPATRACLIENTCORE_LOG) << "Failed to send non-critical option" << it->first.c_str() << ":" << to_error_string(err);
}
}
for (const QString &filePath : std::as_const(in.filePaths)) {
if ((err = send_file(ctx, filePath))) {
out.errorString = i18n("Failed to send file path %1: %2", filePath, to_error_string(err));
goto leave;
}
}
for (const QString &sender : std::as_const(in.senders)) {
if ((err = send_sender(ctx, sender, in.areSendersInformative))) {
out.errorString = i18n("Failed to send sender %1: %2", sender, to_error_string(err));
goto leave;
}
}
for (const QString &recipient : std::as_const(in.recipients)) {
if ((err = send_recipient(ctx, recipient, in.areRecipientsInformative))) {
out.errorString = i18n("Failed to send recipient %1: %2", recipient, to_error_string(err));
goto leave;
}
}
#if 0
setup I / O;
#endif
err = my_assuan_transact(ctx, in.command.constData(), &command_data_cb, &out.data, &command_inquire_cb, &id);
if (err) {
if (gpg_err_code(err) == GPG_ERR_CANCELED) {
out.canceled = true;
} else {
out.errorString = i18n("Command (%1) failed: %2", QString::fromLatin1(in.command.constData()), to_error_string(err));
}
goto leave;
}
leave:
const QMutexLocker locker(&mutex);
// copy outputs to where Command can see them:
outputs = out;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 8, 4:44 AM (1 d, 23 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
1c/43/76f537395b3ffcdfd35500695f4e
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment