File indexing completed on 2024-05-12 05:20:02

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     tests/test_uiserver.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 //
0011 // Usage: test_uiserver <socket> --verify-detached <signed data> <signature>
0012 //
0013 
0014 #include <config-kleopatra.h>
0015 
0016 #include <assuan.h>
0017 #include <gpg-error.h>
0018 
0019 #include <Libkleo/Hex>
0020 #include <Libkleo/KleoException>
0021 
0022 #include "utils/wsastarter.h"
0023 
0024 #ifndef Q_OS_WIN
0025 #include <errno.h>
0026 #include <fcntl.h>
0027 #include <sys/stat.h>
0028 #include <sys/types.h>
0029 #include <unistd.h>
0030 #endif
0031 
0032 #include <cstdlib>
0033 #include <iostream>
0034 #include <map>
0035 #include <string>
0036 #include <vector>
0037 
0038 using namespace Kleo;
0039 
0040 #ifdef Q_OS_WIN
0041 static const bool HAVE_FD_PASSING = false;
0042 #else
0043 static const bool HAVE_FD_PASSING = true;
0044 #endif
0045 
0046 static const unsigned int ASSUAN_CONNECT_FLAGS = HAVE_FD_PASSING ? 1 : 0;
0047 
0048 static std::vector<int> inFDs, outFDs, msgFDs;
0049 static std::vector<std::string> inFiles, outFiles, msgFiles;
0050 static std::map<std::string, std::string> inquireData;
0051 
0052 static void usage(const std::string &msg = std::string())
0053 {
0054     std::cerr << msg << std::endl
0055               << "\n"
0056                  "Usage: test_uiserver <socket> [<io>] [<options>] [<inquire>] command [<args>]\n"
0057                  "where:\n"
0058 #ifdef Q_OS_WIN
0059                  "      <io>: [--input[-fd] <file>] [--output[-fd] <file>] [--message[-fd] <file>]\n"
0060 #else
0061                  "      <io>: [--input <file>] [--output <file>] [--message <file>]\n"
0062 #endif
0063                  " <options>: *[--option name=value]\n"
0064                  " <inquire>: [--inquire keyword=<file>]\n";
0065     exit(1);
0066 }
0067 
0068 static gpg_error_t data(void *void_ctx, const void *buffer, size_t len)
0069 {
0070     (void)void_ctx;
0071     (void)buffer;
0072     (void)len;
0073     return 0; // ### implement me
0074 }
0075 
0076 static gpg_error_t status(void *void_ctx, const char *line)
0077 {
0078     (void)void_ctx;
0079     (void)line;
0080     return 0;
0081 }
0082 
0083 static gpg_error_t inquire(void *void_ctx, const char *keyword)
0084 {
0085     assuan_context_t ctx = (assuan_context_t)void_ctx;
0086     Q_ASSERT(ctx);
0087     const std::map<std::string, std::string>::const_iterator it = inquireData.find(keyword);
0088     if (it == inquireData.end()) {
0089         return gpg_error(GPG_ERR_UNKNOWN_COMMAND);
0090     }
0091 
0092     if (!it->second.empty() && it->second[0] == '@') {
0093         return gpg_error(GPG_ERR_NOT_IMPLEMENTED);
0094     }
0095 
0096     if (const gpg_error_t err = assuan_send_data(ctx, it->second.c_str(), it->second.size())) {
0097         qDebug("assuan_write_data: %s", gpg_strerror(err));
0098         return err;
0099     }
0100 
0101     return 0;
0102 }
0103 
0104 int main(int argc, char *argv[])
0105 {
0106     const Kleo::WSAStarter _wsastarter;
0107 
0108     assuan_set_gpg_err_source(GPG_ERR_SOURCE_DEFAULT);
0109 
0110     if (argc < 3) {
0111         usage(); // need socket and command, at least
0112     }
0113 
0114     const char *socket = argv[1];
0115 
0116     std::vector<const char *> options;
0117 
0118     std::string command;
0119     for (int optind = 2; optind < argc; ++optind) {
0120         const char *const arg = argv[optind];
0121         if (qstrcmp(arg, "--input") == 0) {
0122             const std::string file = argv[++optind];
0123             inFiles.push_back(file);
0124         } else if (qstrcmp(arg, "--output") == 0) {
0125             const std::string file = argv[++optind];
0126             outFiles.push_back(file);
0127         } else if (qstrcmp(arg, "--message") == 0) {
0128             const std::string file = argv[++optind];
0129             msgFiles.push_back(file);
0130 #ifndef Q_OS_WIN
0131         } else if (qstrcmp(arg, "--input-fd") == 0) {
0132             int inFD;
0133             if ((inFD = open(argv[++optind], O_RDONLY)) == -1) {
0134                 perror("--input-fd open()");
0135                 return 1;
0136             }
0137             inFDs.push_back(inFD);
0138         } else if (qstrcmp(arg, "--output-fd") == 0) {
0139             int outFD;
0140             if ((outFD = open(argv[++optind], O_WRONLY | O_CREAT, 0666)) == -1) {
0141                 perror("--output-fd open()");
0142                 return 1;
0143             }
0144             outFDs.push_back(outFD);
0145         } else if (qstrcmp(arg, "--message-fd") == 0) {
0146             int msgFD;
0147             if ((msgFD = open(argv[++optind], O_RDONLY)) == -1) {
0148                 perror("--message-fd open()");
0149                 return 1;
0150             }
0151             msgFDs.push_back(msgFD);
0152 #endif
0153         } else if (qstrcmp(arg, "--option") == 0) {
0154             options.push_back(argv[++optind]);
0155         } else if (qstrcmp(arg, "--inquire") == 0) {
0156             const std::string inqval = argv[++optind];
0157             const size_t pos = inqval.find('=');
0158             // ### implement indirection with "@file"...
0159             inquireData[inqval.substr(0, pos)] = inqval.substr(pos + 1);
0160         } else {
0161             while (optind < argc) {
0162                 if (!command.empty()) {
0163                     command += ' ';
0164                 }
0165                 command += argv[optind++];
0166             }
0167         }
0168     }
0169     if (command.empty()) {
0170         usage("Command expected, but only options found");
0171     }
0172 
0173     assuan_context_t ctx = nullptr;
0174 
0175     if (const gpg_error_t err = assuan_new(&ctx)) {
0176         qDebug("%s", Exception(err, "assuan_new").what());
0177         return 1;
0178     }
0179 
0180     if (const gpg_error_t err = assuan_socket_connect(ctx, socket, -1, ASSUAN_CONNECT_FLAGS)) {
0181         qDebug("%s", Exception(err, "assuan_socket_connect").what());
0182         return 1;
0183     }
0184 
0185     assuan_set_log_stream(ctx, stderr);
0186 
0187 #ifndef Q_OS_WIN
0188     for (std::vector<int>::const_iterator it = inFDs.begin(), end = inFDs.end(); it != end; ++it) {
0189         if (const gpg_error_t err = assuan_sendfd(ctx, *it)) {
0190             qDebug("%s", Exception(err, "assuan_sendfd( inFD )").what());
0191             return 1;
0192         }
0193 
0194         if (const gpg_error_t err = assuan_transact(ctx, "INPUT FD", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) {
0195             qDebug("%s", Exception(err, "INPUT FD").what());
0196             return 1;
0197         }
0198     }
0199 
0200     for (std::vector<int>::const_iterator it = msgFDs.begin(), end = msgFDs.end(); it != end; ++it) {
0201         if (const gpg_error_t err = assuan_sendfd(ctx, *it)) {
0202             qDebug("%s", Exception(err, "assuan_sendfd( msgFD )").what());
0203             return 1;
0204         }
0205 
0206         if (const gpg_error_t err = assuan_transact(ctx, "MESSAGE FD", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) {
0207             qDebug("%s", Exception(err, "MESSAGE FD").what());
0208             return 1;
0209         }
0210     }
0211 
0212     for (std::vector<int>::const_iterator it = outFDs.begin(), end = outFDs.end(); it != end; ++it) {
0213         if (const gpg_error_t err = assuan_sendfd(ctx, *it)) {
0214             qDebug("%s", Exception(err, "assuan_sendfd( outFD )").what());
0215             return 1;
0216         }
0217 
0218         if (const gpg_error_t err = assuan_transact(ctx, "OUTPUT FD", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) {
0219             qDebug("%s", Exception(err, "OUTPUT FD").what());
0220             return 1;
0221         }
0222     }
0223 #endif
0224 
0225     for (std::vector<std::string>::const_iterator it = inFiles.begin(), end = inFiles.end(); it != end; ++it) {
0226         char buffer[1024];
0227         sprintf(buffer, "INPUT FILE=%s", hexencode(*it).c_str());
0228 
0229         if (const gpg_error_t err = assuan_transact(ctx, buffer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) {
0230             qDebug("%s", Exception(err, buffer).what());
0231             return 1;
0232         }
0233     }
0234 
0235     for (std::vector<std::string>::const_iterator it = msgFiles.begin(), end = msgFiles.end(); it != end; ++it) {
0236         char buffer[1024];
0237         sprintf(buffer, "MESSAGE FILE=%s", hexencode(*it).c_str());
0238 
0239         if (const gpg_error_t err = assuan_transact(ctx, buffer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) {
0240             qDebug("%s", Exception(err, buffer).what());
0241             return 1;
0242         }
0243     }
0244 
0245     for (std::vector<std::string>::const_iterator it = outFiles.begin(), end = outFiles.end(); it != end; ++it) {
0246         char buffer[1024];
0247         sprintf(buffer, "OUTPUT FILE=%s", hexencode(*it).c_str());
0248 
0249         if (const gpg_error_t err = assuan_transact(ctx, buffer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) {
0250             qDebug("%s", Exception(err, buffer).what());
0251             return 1;
0252         }
0253     }
0254 
0255     for (const char *opt : std::as_const(options)) {
0256         std::string line = "OPTION ";
0257         line += opt;
0258         if (const gpg_error_t err = assuan_transact(ctx, line.c_str(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) {
0259             qDebug("%s", Exception(err, line).what());
0260             return 1;
0261         }
0262     }
0263 
0264     if (const gpg_error_t err = assuan_transact(ctx, command.c_str(), data, ctx, inquire, ctx, status, ctx)) {
0265         qDebug("%s", Exception(err, command).what());
0266         return 1;
0267     }
0268 
0269     assuan_release(ctx);
0270 
0271     return 0;
0272 }