File indexing completed on 2024-06-23 05:14:13

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     uiserver/echocommand.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <config-kleopatra.h>
0011 
0012 #include "echocommand.h"
0013 
0014 #include <utils/input.h>
0015 #include <utils/output.h>
0016 
0017 #include <Libkleo/KleoException>
0018 
0019 #include <gpg-error.h>
0020 
0021 #include <KLocalizedString>
0022 
0023 #include <QByteArray>
0024 #include <QIODevice>
0025 
0026 #include <algorithm>
0027 #include <string>
0028 
0029 using namespace Kleo;
0030 
0031 static const char option_prefix[] = "prefix";
0032 
0033 class EchoCommand::Private
0034 {
0035 public:
0036     int operationsInFlight = 0;
0037     QByteArray buffer;
0038 };
0039 
0040 EchoCommand::EchoCommand()
0041     : QObject()
0042     , AssuanCommandMixin<EchoCommand>()
0043     , d(new Private)
0044 {
0045 }
0046 
0047 EchoCommand::~EchoCommand()
0048 {
0049 }
0050 
0051 int EchoCommand::doStart()
0052 {
0053     const std::vector<std::shared_ptr<Input>> in = inputs(), msg = messages();
0054     const std::vector<std::shared_ptr<Output>> out = outputs();
0055 
0056     if (!in.empty() && out.empty()) {
0057         return makeError(GPG_ERR_NOT_SUPPORTED);
0058     }
0059 
0060     if (!msg.empty()) {
0061         return makeError(GPG_ERR_NOT_SUPPORTED);
0062     }
0063 
0064     if (hasOption(option_prefix) && !option(option_prefix).toByteArray().isEmpty()) {
0065         return makeError(GPG_ERR_NOT_IMPLEMENTED);
0066     }
0067 
0068     std::string keyword;
0069     if (hasOption("inquire")) {
0070         keyword = option("inquire").toString().toStdString();
0071         if (keyword.empty()) {
0072             return makeError(GPG_ERR_INV_ARG);
0073         }
0074     }
0075 
0076     const std::string output = option("text").toString().toStdString();
0077 
0078     // aaand ACTION:
0079 
0080     // 1. echo the command line though the status channel
0081     sendStatus("ECHO", output.empty() ? QString() : QLatin1StringView(output.c_str()));
0082 
0083     // 2. if --inquire was given, inquire more data from the client:
0084     if (!keyword.empty()) {
0085         if (const int err = inquire(keyword.c_str(), this, SLOT(slotInquireData(int, QByteArray)))) {
0086             return err;
0087         } else {
0088             ++d->operationsInFlight;
0089         }
0090     }
0091 
0092     // 3. if INPUT was given, start the data pump for input->output
0093     if (const std::shared_ptr<QIODevice> i = in.at(0)->ioDevice()) {
0094         const std::shared_ptr<QIODevice> o = out.at(0)->ioDevice();
0095 
0096         ++d->operationsInFlight;
0097 
0098         connect(i.get(), &QIODevice::readyRead, this, &EchoCommand::slotInputReadyRead);
0099         connect(o.get(), &QIODevice::bytesWritten, this, &EchoCommand::slotOutputBytesWritten);
0100 
0101         if (i->bytesAvailable()) {
0102             slotInputReadyRead();
0103         }
0104     }
0105 
0106     if (!d->operationsInFlight) {
0107         done();
0108     }
0109     return 0;
0110 }
0111 
0112 void EchoCommand::doCanceled()
0113 {
0114 }
0115 
0116 void EchoCommand::slotInquireData(int rc, const QByteArray &data)
0117 {
0118     --d->operationsInFlight;
0119 
0120     if (rc) {
0121         done(rc);
0122         return;
0123     }
0124 
0125     try {
0126         sendStatus("ECHOINQ", QLatin1StringView(data));
0127         if (!d->operationsInFlight) {
0128             done();
0129         }
0130     } catch (const Exception &e) {
0131         done(e.error(), e.message());
0132     } catch (const std::exception &e) {
0133         done(makeError(GPG_ERR_UNEXPECTED),
0134              i18n("Caught unexpected exception in SignCommand::Private::slotMicAlgDetermined: %1", QString::fromLocal8Bit(e.what())));
0135     } catch (...) {
0136         done(makeError(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in SignCommand::Private::slotMicAlgDetermined"));
0137     }
0138 }
0139 
0140 void EchoCommand::slotInputReadyRead()
0141 {
0142     const std::shared_ptr<QIODevice> in = inputs().at(0)->ioDevice();
0143     Q_ASSERT(in);
0144 
0145     QByteArray buffer;
0146     buffer.resize(in->bytesAvailable());
0147     const qint64 read = in->read(buffer.data(), buffer.size());
0148     if (read == -1) {
0149         done(makeError(GPG_ERR_EIO));
0150         return;
0151     }
0152     if (read == 0 || (!in->isSequential() && read == in->size())) {
0153         in->close();
0154     }
0155 
0156     buffer.resize(read);
0157     d->buffer += buffer;
0158 
0159     slotOutputBytesWritten();
0160 }
0161 
0162 void EchoCommand::slotOutputBytesWritten()
0163 {
0164     const std::shared_ptr<QIODevice> out = outputs().at(0)->ioDevice();
0165     Q_ASSERT(out);
0166 
0167     if (!d->buffer.isEmpty()) {
0168         if (out->bytesToWrite()) {
0169             return;
0170         }
0171 
0172         const qint64 written = out->write(d->buffer);
0173         if (written == -1) {
0174             done(makeError(GPG_ERR_EIO));
0175             return;
0176         }
0177         d->buffer.remove(0, written);
0178     }
0179 
0180     if (out->isOpen() && d->buffer.isEmpty() && !inputs().at(0)->ioDevice()->isOpen()) {
0181         out->close();
0182         if (!--d->operationsInFlight) {
0183             done();
0184         }
0185     }
0186 }
0187 
0188 #include "moc_echocommand.cpp"