diff --git a/src/libkleopatraclient/core/CMakeLists.txt b/src/libkleopatraclient/core/CMakeLists.txt index 7ba2bd7a8..b76966139 100644 --- a/src/libkleopatraclient/core/CMakeLists.txt +++ b/src/libkleopatraclient/core/CMakeLists.txt @@ -1,65 +1,65 @@ add_definitions( -D_ASSUAN_ONLY_GPG_ERRORS ) if(WIN32) set(_kleopatraclientcore_extra_SRCS ../../utils/gnupg-registry.c) else() set(_kleopatraclientcore_extra_SRCS) endif() ecm_qt_declare_logging_category(_kleopatraclientcore_common_SRCS HEADER libkleopatraclientcore_debug.h IDENTIFIER LIBKLEOPATRACLIENTCORE_LOG CATEGORY_NAME org.kde.pim.libkleopatraclientcore DESCRIPTION "kleopatra (kleopatra)" OLD_CATEGORY_NAMES log_libkleopatraclientcore EXPORT KLEOPATRA ) add_library(kleopatraclientcore ${_kleopatraclientcore_extra_SRCS} initialization.cpp command.cpp selectcertificatecommand.cpp signencryptfilescommand.cpp decryptverifyfilescommand.cpp libkleopatraclientcore_debug.cpp ${_kleopatraclientcore_common_SRCS} ) generate_export_header(kleopatraclientcore BASE_NAME kleopatraclientcore) set_target_properties(kleopatraclientcore PROPERTIES VERSION ${libkleopatraclient_version} SOVERSION ${libkleopatraclient_soversion} ) if(WIN32) if(ASSUAN2_FOUND) target_link_libraries(kleopatraclientcore ${ASSUAN2_LIBRARIES} ws2_32 ) else() target_link_libraries(kleopatraclientcore ${ASSUAN_VANILLA_LIBRARIES} ws2_32 ) endif() else() if(ASSUAN2_FOUND) target_link_libraries(kleopatraclientcore ${ASSUAN2_LIBRARIES} ) else() target_link_libraries(kleopatraclientcore ${ASSUAN_PTHREAD_LIBRARIES} ) endif() endif() -target_link_libraries(kleopatraclientcore Qt5::Widgets KF5::I18n) +target_link_libraries(kleopatraclientcore Qt5::Widgets KF5::I18n Gpgmepp) install(TARGETS kleopatraclientcore ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/libkleopatraclient/core/command.cpp b/src/libkleopatraclient/core/command.cpp index b90b09137..03d14cdf8 100644 --- a/src/libkleopatraclient/core/command.cpp +++ b/src/libkleopatraclient/core/command.cpp @@ -1,706 +1,695 @@ /* -*- 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 #include "command.h" #include "command_p.h" #include // Q_OS_WIN #ifdef Q_OS_WIN // HACK: AllowSetForegroundWindow needs _WIN32_WINDOWS >= 0x0490 set # ifndef _WIN32_WINDOWS # define _WIN32_WINDOWS 0x0500 # define _WIN32_WINNT 0x0500 // good enough for Vista too # endif # include # include #endif #include #include #include "libkleopatraclientcore_debug.h" #include #include #include #include #include +#include #include #include #include #include #include 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 std::map::const_iterator 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 std::map::const_iterator 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 std::map::const_iterator 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 std::map::const_iterator 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 // #ifdef HAVE_ASSUAN2 static void my_assuan_release(assuan_context_t ctx) { if (ctx) { assuan_release(ctx); } } #endif typedef std::shared_ptr::type > AssuanContextBase; namespace { struct AssuanClientContext : AssuanContextBase { AssuanClientContext() : AssuanContextBase() {} #ifndef HAVE_ASSUAN2 explicit AssuanClientContext(assuan_context_t ctx) : AssuanContextBase(ctx, &assuan_disconnect) {} void reset(assuan_context_t ctx = nullptr) { AssuanContextBase::reset(ctx, &assuan_disconnect); } #else explicit AssuanClientContext(assuan_context_t ctx) : AssuanContextBase(ctx, &my_assuan_release) {} void reset(assuan_context_t ctx = nullptr) { AssuanContextBase::reset(ctx, &my_assuan_release); } #endif }; } #ifdef HAVE_ASSUAN2 // compatibility typedef - remove when we require assuan v2... typedef gpg_error_t assuan_error_t; #endif static assuan_error_t my_assuan_transact(const AssuanClientContext &ctx, const char *command, assuan_error_t (*data_cb)(void *, const void *, size_t) = nullptr, void *data_cb_arg = nullptr, assuan_error_t (*inquire_cb)(void *, const char *) = nullptr, void *inquire_cb_arg = nullptr, assuan_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(err), buffer, sizeof buffer); buffer[sizeof buffer - 1] = '\0'; return QString::fromLocal8Bit(buffer); } static QString gnupg_home_directory() { -#ifdef Q_OS_WIN - return QFile::decodeName(default_homedir()); -#else - const QByteArray gnupgHome = qgetenv("GNUPGHOME"); - if (!gnupgHome.isEmpty()) { - return QFile::decodeName(gnupgHome); - } else { - return QDir::homePath() + QLatin1String("/.gnupg"); - } -#endif + static const char *hDir = GpgME::dirInfo("homedir"); + return QFile::decodeName(hDir); } static QString get_default_socket_name() { 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() { 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) { qint64 &pid = *static_cast(opaque); pid = QByteArray(static_cast(buffer), length).toLongLong(); return 0; } static assuan_error_t command_data_cb(void *opaque, const void *buffer, size_t length) { QByteArray &ba = *static_cast(opaque); ba.append(QByteArray(static_cast(buffer), length)); return 0; } namespace { struct inquire_data { const std::map *map; const AssuanClientContext *ctx; }; } static assuan_error_t command_inquire_cb(void *opaque, const char *what) { if (!opaque) { return 0; } const inquire_data &id = *static_cast(opaque); const std::map::const_iterator 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) { 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) { 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) { 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) { 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(); } #ifndef HAVE_ASSUAN2 assuan_context_t naked_ctx = 0; #endif AssuanClientContext ctx; assuan_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; } #ifndef HAVE_ASSUAN2 err = assuan_socket_connect(&naked_ctx, QFile::encodeName(socketName).constData(), -1); #else { 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(), QFile::encodeName(socketName).constData(), -1, 0); #endif 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); -#ifndef HAVE_ASSUAN2 - err = assuan_socket_connect(&naked_ctx, QFile::encodeName(socketName).constData(), -1); -#else - err = assuan_socket_connect(ctx.get(), QFile::encodeName(socketName).constData(), -1, 0); -#endif + 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; } #ifndef HAVE_ASSUAN2 ctx.reset(naked_ctx); naked_ctx = 0; #endif 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) { #if defined(Q_OS_WIN32) err = send_option(ctx, "window-id", QString::asprintf("%lx", reinterpret_cast(in.parentWId))); #else err = send_option(ctx, "window-id", QString::asprintf("%lx", static_cast(in.parentWId))); #endif if (err) { qDebug("sending option window-id failed - ignoring"); } } for (std::map::const_iterator 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); } } Q_FOREACH (const QString &filePath, 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; } Q_FOREACH (const QString &sender, 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; } Q_FOREACH (const QString &recipient, 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; } diff --git a/src/uiserver/uiserver.cpp b/src/uiserver/uiserver.cpp index 080651327..549c2a62a 100644 --- a/src/uiserver/uiserver.cpp +++ b/src/uiserver/uiserver.cpp @@ -1,282 +1,282 @@ /* -*- mode: c++; c-basic-offset:4 -*- uiserver/uiserver.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "uiserver.h" #include "uiserver_p.h" #include "sessiondata.h" #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include using namespace Kleo; // static void UiServer::setLogStream(FILE *stream) { assuan_set_assuan_log_stream(stream); } UiServer::Private::Private(UiServer *qq) : QTcpServer(), q(qq), file(), factories(), connections(), suggestedSocketName(), actualSocketName(), cryptoCommandsEnabled(false) { #ifndef HAVE_ASSUAN2 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT); #else assuan_set_gpg_err_source(GPG_ERR_SOURCE_DEFAULT); assuan_sock_init(); #endif } bool UiServer::Private::isStaleAssuanSocket(const QString &fileName) { assuan_context_t ctx = nullptr; #ifndef HAVE_ASSUAN2 const bool error = assuan_socket_connect_ext(&ctx, QFile::encodeName(fileName).constData(), -1, 0); #else const bool error = assuan_new(&ctx) || assuan_socket_connect(ctx, QFile::encodeName(fileName).constData(), ASSUAN_INVALID_PID, 0); #endif if (!error) #ifndef HAVE_ASSUAN2 assuan_disconnect(ctx); #else assuan_release(ctx); #endif return error; } UiServer::UiServer(const QString &socket, QObject *p) : QObject(p), d(new Private(this)) { d->suggestedSocketName = d->makeFileName(socket); } UiServer::~UiServer() { if (QFile::exists(d->actualSocketName)) { QFile::remove(d->actualSocketName); } } namespace { using Iterator = std::vector>::iterator; static bool empty(std::pair iters) { return iters.first == iters.second; } } bool UiServer::registerCommandFactory(const std::shared_ptr &cf) { if (cf && empty(std::equal_range(d->factories.begin(), d->factories.end(), cf, _detail::ByName()))) { d->factories.push_back(cf); std::inplace_merge(d->factories.begin(), d->factories.end() - 1, d->factories.end(), _detail::ByName()); return true; } else { if (!cf) { qCWarning(KLEOPATRA_LOG) << "NULL factory"; } else { qCWarning(KLEOPATRA_LOG) << (void *)cf.get() << " factory already registered"; } return false; } } void UiServer::start() { d->makeListeningSocket(); } void UiServer::stop() { d->close(); if (d->file.exists()) { d->file.remove(); } if (isStopped()) { SessionDataHandler::instance()->clear(); Q_EMIT stopped(); } } void UiServer::enableCryptoCommands(bool on) { if (on == d->cryptoCommandsEnabled) { return; } d->cryptoCommandsEnabled = on; std::for_each(d->connections.cbegin(), d->connections.cend(), [on](std::shared_ptr conn) { conn->enableCryptoCommands(on); }); } QString UiServer::socketName() const { return d->actualSocketName; } bool UiServer::waitForStopped(unsigned int ms) { if (isStopped()) { return true; } QEventLoop loop; QTimer timer; timer.setInterval(ms); timer.setSingleShot(true); connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); connect(this, &UiServer::stopped, &loop, &QEventLoop::quit); loop.exec(); return !timer.isActive(); } bool UiServer::isStopped() const { return d->connections.empty() && !d->isListening(); } bool UiServer::isStopping() const { return !d->connections.empty() && !d->isListening(); } void UiServer::Private::slotConnectionClosed(Kleo::AssuanServerConnection *conn) { qCDebug(KLEOPATRA_LOG) << "UiServer: connection " << (void *)conn << " closed"; connections.erase(std::remove_if(connections.begin(), connections.end(), [conn](const std::shared_ptr &other) { return conn == other.get(); }), connections.end()); if (q->isStopped()) { SessionDataHandler::instance()->clear(); Q_EMIT q->stopped(); } } void UiServer::Private::incomingConnection(qintptr fd) { try { qCDebug(KLEOPATRA_LOG) << "UiServer: client connect on fd " << fd; #if defined(HAVE_ASSUAN_SOCK_GET_NONCE) || defined(HAVE_ASSUAN2) if (assuan_sock_check_nonce((assuan_fd_t)fd, &nonce)) { qCDebug(KLEOPATRA_LOG) << "UiServer: nonce check failed"; assuan_sock_close((assuan_fd_t)fd); return; } #endif const std::shared_ptr c(new AssuanServerConnection((assuan_fd_t)fd, factories)); connect(c.get(), &AssuanServerConnection::closed, this, &Private::slotConnectionClosed); connect(c.get(), &AssuanServerConnection::startKeyManagerRequested, q, &UiServer::startKeyManagerRequested, Qt::QueuedConnection); connect(c.get(), &AssuanServerConnection::startConfigDialogRequested, q, &UiServer::startConfigDialogRequested, Qt::QueuedConnection); c->enableCryptoCommands(cryptoCommandsEnabled); connections.push_back(c); qCDebug(KLEOPATRA_LOG) << "UiServer: client connection " << (void *)c.get() << " established successfully"; } catch (const Exception &e) { qCDebug(KLEOPATRA_LOG) << "UiServer: client connection failed: " << e.what(); QTcpSocket s; s.setSocketDescriptor(fd); QTextStream(&s) << "ERR " << e.error_code() << " " << e.what() << "\r\n"; s.waitForBytesWritten(); s.close(); } catch (...) { qCDebug(KLEOPATRA_LOG) << "UiServer: client connection failed: unknown exception caught"; // this should never happen... QTcpSocket s; s.setSocketDescriptor(fd); QTextStream(&s) << "ERR 63 unknown exception caught\r\n"; s.waitForBytesWritten(); s.close(); } } QString UiServer::Private::makeFileName(const QString &socket) const { if (!socket.isEmpty()) { return socket; } const QString gnupgHome = gnupgHomeDirectory(); if (gnupgHome.isEmpty()) { throw_(i18n("Could not determine the GnuPG home directory. Consider setting the GNUPGHOME environment variable.")); } ensureDirectoryExists(gnupgHome); const QDir dir(gnupgHome); Q_ASSERT(dir.exists()); return dir.absoluteFilePath(QStringLiteral("S.uiserver")); } void UiServer::Private::ensureDirectoryExists(const QString &path) const { const QFileInfo info(path); if (info.exists() && !info.isDir()) { throw_(i18n("Cannot determine the GnuPG home directory: %1 exists but is not a directory.", path)); } if (info.exists()) { return; } const QDir dummy; //there is no static QDir::mkpath()... errno = 0; if (!dummy.mkpath(path)) { throw_(i18n("Could not create GnuPG home directory %1: %2", path, systemErrorString())); } } void UiServer::Private::makeListeningSocket() { // First, create a file (we do this only for the name, gmpfh) const QString fileName = suggestedSocketName; if (QFile::exists(fileName)) { if (isStaleAssuanSocket(fileName)) { QFile::remove(fileName); } else { throw_(i18n("Detected another running gnupg UI server listening at %1.", fileName)); } } - doMakeListeningSocket(QFile::encodeName(fileName)); + doMakeListeningSocket(fileName.toUtf8()); actualSocketName = suggestedSocketName; } #include "moc_uiserver_p.cpp" diff --git a/src/utils/gnupg-registry.c b/src/utils/gnupg-registry.c index 389964474..f3dfc0b10 100644 --- a/src/utils/gnupg-registry.c +++ b/src/utils/gnupg-registry.c @@ -1,267 +1,185 @@ /* registry.c - Registry routines SPDX-FileCopyrightText: 2005, 2007 g 10 Code GmbH This file is part of GpgEX. SPDX-License-Identifier: LGPL-2.1-or-later */ /* keep this in sync with svn://cvs.gnupg.org/gpgex/trunk/src/registry.c (last checked against rev. 32) */ #include #if 0 /* We don't have a config.h in ONLY_KLEO, fix if needed */ #if HAVE_CONFIG_H #include #endif #endif #include #include #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a #endif #ifndef CSIDL_LOCAL_APPDATA #define CSIDL_LOCAL_APPDATA 0x001c #endif #ifndef CSIDL_FLAG_CREATE #define CSIDL_FLAG_CREATE 0x8000 #endif #include "gnupg-registry.h" /* This is a helper function to load a Windows function from either of one DLLs. */ HRESULT w32_shgetfolderpath(HWND a, int b, HANDLE c, DWORD d, LPSTR e) { static int initialized; static HRESULT(WINAPI * func)(HWND, int, HANDLE, DWORD, LPSTR); if (!initialized) { static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL }; void *handle; int i; initialized = 1; for (i = 0, handle = NULL; !handle && dllnames[i]; i++) { handle = LoadLibraryA(dllnames[i]); if (handle) { func = (HRESULT(WINAPI *)(HWND, int, HANDLE, DWORD, LPSTR)) GetProcAddress(handle, "SHGetFolderPathA"); if (!func) { FreeLibrary(handle); handle = NULL; } } } } if (func) { return func(a, b, c, d, e); } else { return -1; } } /* Helper for read_w32_registry_string(). */ static HKEY get_root_key(const char *root) { HKEY root_key; if (!root) { root_key = HKEY_CURRENT_USER; } else if (!strcmp(root, "HKEY_CLASSES_ROOT")) { root_key = HKEY_CLASSES_ROOT; } else if (!strcmp(root, "HKEY_CURRENT_USER")) { root_key = HKEY_CURRENT_USER; } else if (!strcmp(root, "HKEY_LOCAL_MACHINE")) { root_key = HKEY_LOCAL_MACHINE; } else if (!strcmp(root, "HKEY_USERS")) { root_key = HKEY_USERS; } else if (!strcmp(root, "HKEY_PERFORMANCE_DATA")) { root_key = HKEY_PERFORMANCE_DATA; } else if (!strcmp(root, "HKEY_CURRENT_CONFIG")) { root_key = HKEY_CURRENT_CONFIG; } else { return NULL; } return root_key; } /* Return a string from the Win32 Registry or NULL in case of error. Caller must release the return value. A NULL for root is an alias for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */ char * read_w32_registry_string(const char *root, const char *dir, const char *name) { HKEY root_key, key_handle; DWORD n1, nbytes, type; char *result = NULL; if (!(root_key = get_root_key(root))) { return NULL; } if (RegOpenKeyExA(root_key, dir, 0, KEY_READ, &key_handle)) { if (root) { return NULL; /* no need for a RegClose, so return direct */ } /* It seems to be common practice to fall back to HKLM. */ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) { return NULL; /* still no need for a RegClose, so return direct */ } } nbytes = 1; if (RegQueryValueExA(key_handle, name, 0, NULL, NULL, &nbytes)) { if (root) { goto leave; } /* Try to fallback to HKLM also vor a missing value. */ RegCloseKey(key_handle); if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) { return NULL; /* Nope. */ } if (RegQueryValueExA(key_handle, name, 0, NULL, NULL, &nbytes)) { goto leave; } } result = malloc((n1 = nbytes + 1)); if (!result) { goto leave; } if (RegQueryValueExA(key_handle, name, 0, &type, result, &n1)) { free(result); result = NULL; goto leave; } result[nbytes] = 0; /* make sure it is really a string */ if (type == REG_EXPAND_SZ && strchr(result, '%')) { char *tmp; n1 += 1000; tmp = malloc(n1 + 1); if (!tmp) { goto leave; } nbytes = ExpandEnvironmentStringsA(result, tmp, n1); if (nbytes && nbytes > n1) { free(tmp); n1 = nbytes; tmp = malloc(n1 + 1); if (!tmp) { goto leave; } nbytes = ExpandEnvironmentStringsA(result, tmp, n1); if (nbytes && nbytes > n1) { free(tmp); /* oops - truncated, better don't expand at all */ goto leave; } tmp[nbytes] = 0; free(result); result = tmp; } else if (nbytes) { /* okay, reduce the length */ tmp[nbytes] = 0; free(result); result = malloc(strlen(tmp) + 1); if (!result) { result = tmp; } else { strcpy(result, tmp); free(tmp); } } else { /* error - don't expand */ free(tmp); } } leave: RegCloseKey(key_handle); return result; } - -/* Get the standard home directory. In general this function should - not be used as it does not consider a registry value (under W32) or - the GNUPGHOME encironment variable. It is better to use - default_homedir(). */ -static char * -standard_homedir(void) -{ - static char *dir; - - if (!dir) { - char path[MAX_PATH]; - - /* It might be better to use LOCAL_APPDATA because this is - defined as "non roaming" and thus more likely to be kept - locally. For private keys this is desired. However, given - that many users copy private keys anyway forth and back, - using a system roaming services might be better than to let - them do it manually. A security conscious user will anyway - use the registry entry to have better control. */ - if (w32_shgetfolderpath(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, - NULL, 0, path) >= 0) { - char *tmp = malloc(strlen(path) + 6 + 1); - - if (!tmp) { - dir = strdup ("C:\\gnupg"); - return dir; - } - - strcpy(tmp, path); - strcat(tmp, "\\gnupg"); - - dir = tmp; - - /* Try to create the directory if it does not yet exists. */ - if (access(dir, F_OK)) { - CreateDirectoryA(dir, NULL); - } - } else { - dir = strdup("C:\\gnupg"); - } - } - return dir; -} - -/* Retrieve the default home directory. */ -char * -default_homedir(void) -{ - char *dir; - - dir = getenv("GNUPGHOME"); - if (!dir || !*dir) { - static char *saved_dir; - - if (!saved_dir) { - if (!dir || !*dir) { - char *tmp; - - tmp = read_w32_registry_string(NULL, "Software\\GNU\\GnuPG", - "HomeDir"); - if (tmp && !*tmp) { - free(tmp); - tmp = NULL; - } - if (tmp) { - saved_dir = tmp; - } - } - - if (!saved_dir) { - saved_dir = standard_homedir(); - } - } - dir = saved_dir; - } - if (!dir || !*dir) { - dir = strdup("C:\\gnupg"); - } - - return dir; -} diff --git a/src/utils/gnupg-registry.h b/src/utils/gnupg-registry.h index ce0e354db..aca71473e 100644 --- a/src/utils/gnupg-registry.h +++ b/src/utils/gnupg-registry.h @@ -1,43 +1,40 @@ /* registry.h - registry prototypes SPDX-FileCopyrightText: 2006, 2007 g 10 Code GmbH This file is part of GpgEX. SPDX-License-Identifier: LGPL-2.0-or-later */ /* keep this in sync with svn://cvs.gnupg.org/gpgex/trunk/src/registry.h (last checked against rev. 19) */ #ifndef REGISTRY_H #define REGISTRY_H #include #ifdef __cplusplus extern "C" { #if 0 } #endif #endif /* This is a helper function to load a Windows function from either of one DLLs. */ HRESULT w32_shgetfolderpath(HWND a, int b, HANDLE c, DWORD d, LPSTR e); /* Return a string from the Win32 Registry or NULL in case of error. Caller must release the return value. A NULL for root is an alias for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */ char *read_w32_registry_string(const char *root, const char *dir, const char *name); -/* Retrieve the default home directory. */ -char *default_homedir(void); - #ifdef __cplusplus #if 0 { #endif } #endif #endif /* ! REGISTRY_H */