<commit_before>#include <sstream>
#include <tgbot/methods/api.h>
#include <tgbot/utils/https.h>
#include <jsoncpp/json/json.h>
#include <tgbot/bot.h>

using namespace tgbot::methods;
using namespace tgbot::utils;

static inline void allowedUpdatesToString(const std::vector<api_types::UpdateType>& updates,
                                    std::stringstream& stream) {
    stream << "[";
    for (auto const &update : updates) {
        switch (update) {
            case api_types::UpdateType::MESSAGE:
                stream << "\"message\",";
                break;
            case api_types::UpdateType::CALLBACK_QUERY:
                stream << "\"callback_query\",";
                break;
            case api_types::UpdateType::INLINE_QUERY :
                stream << "\"inline_query\",";
                break;
            case api_types::UpdateType::CHOSEN_INLINE_RESULT:
                stream << "\"chosen_inline_result\",";
                break;
            case api_types::UpdateType::PRE_CHECKOUT_QUERY:
                stream << "\"pre_checkout_query\",";
                break;
            case api_types::UpdateType::SHIPPING_QUERY:
                stream << "\"shipping_query\",";
                break;
            case api_types::UpdateType::EDITED_CHANNEL_POST:
                stream << "\"edited_channel_post\",";
                break;
            case api_types::UpdateType::EDITED_MESSAGE:
                stream << "\"edited_message\",";
                break;
            case api_types::UpdateType::CHANNEL_POST:
                stream << "\"channel_post\",";
                break;
        }
    }
}

static inline void removeComma(const std::stringstream& stream, std::string& target) {
    std::string &&req = stream.str();
    char &endpos = req.at(req.size() - 1);
    if (endpos == ',')
        endpos = ']';
    target = req;
}

//Api constructors

//Webhook, no further action
tgbot::methods::Api::Api(const std::string& token) :
    baseApi("https://api.telegram.org/bot" + token)
{}

//Webhook
tgbot::methods::Api::Api(const std::string& token,
		const std::string& url,
		const int& maxConnections,
		const std::vector<api_types::UpdateType>& allowedUpdates) :
            baseApi("https://api.telegram.org/bot" + token) {

        if(!setWebhook(url,maxConnections,allowedUpdates))
            throw TelegramException("Unable to set webhook");
}

//Webhook with cert
tgbot::methods::Api::Api(const std::string& token,
		const std::string& url,
		const std::string& certificate,
		const int& maxConnections,
		const std::vector<api_types::UpdateType>& allowedUpdates) :
            baseApi("https://api.telegram.org/bot" + token) {

        if(!setWebhook(url,certificate,maxConnections,allowedUpdates))
            throw TelegramException("Unable to set webhook");
}

//HTTP Long polling
tgbot::methods::Api::Api(const std::string &token,
		const std::vector<api_types::UpdateType> &allowedUpdates,
		const int &timeout,
		const int &limit) :
	        baseApi("https://api.telegram.org/bot" + token),
	        currentOffset(0) {

		std::stringstream fullApiRequest;
		fullApiRequest << baseApi << "/getUpdates?limit=" << limit << "&timeout=" << timeout;

		if (allowedUpdates.size()) {
            fullApiRequest << "&allowed_updates=";
			allowedUpdatesToString(allowedUpdates,fullApiRequest);
			removeComma(fullApiRequest,updateApiRequest);
		} else
			updateApiRequest = fullApiRequest.str();
}

//
//API implementations
//

//getUpdates (internal usage)
int tgbot::methods::Api::getUpdates(void *c, std::vector<api_types::Update>& updates) {
	std::stringstream updatesRequest;
	updatesRequest << updateApiRequest << "&offset=" << currentOffset;

	Json::Value rootUpdate;
	Json::Reader parser;

	parser.parse(utils::http::get(c, updatesRequest.str()), rootUpdate);

	if (!rootUpdate.get("ok", "").asBool())
		throw TelegramException(rootUpdate.get("description", "").asCString());

	Json::Value valueUpdates = rootUpdate.get("result", "");
	const int& updatesCount = valueUpdates.size();
	if (!updatesCount)
		return 0;

	for (auto const &singleUpdate : valueUpdates)
		updates.emplace_back(singleUpdate);

	currentOffset = 1 + valueUpdates[updatesCount - 1].get("update_id", "").asInt64();

	return updatesCount;
}

//
// Availible API Methods
//

//setWebhook
bool tgbot::methods::Api::setWebhook(const std::string &url, const int &maxConnections,
                                     const std::vector<api_types::UpdateType> &allowedUpdates) {
    std::stringstream request;
    request << baseApi << "/setWebhook?url=" << url << "&max_connections=" << maxConnections;

    std::string setWebhookRequest;
    if (allowedUpdates.size()) {
        request << "&allowed_updates=";
        allowedUpdatesToString(allowedUpdates,request);
        removeComma(request,setWebhookRequest);
    } else
        setWebhookRequest = request.str();

    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,setWebhookRequest),value);
    curl_easy_cleanup(inst);

    bool isOk = value.get("ok","").asBool();
    if(isOk)
        urlWebhook = url;

    return isOk;
}

bool tgbot::methods::Api::setWebhook(const std::string &url, const std::string &certificate,
                                     const int &maxConnections,
                                     const std::vector<api_types::UpdateType> &allowedUpdates) {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    if(!allowedUpdates.size())
        reader.parse(http::multiPartUpload(inst, baseApi + "/setWebhook", certificate, url, maxConnections),
                     value);
    else {
        std::stringstream request;
        std::string final;

        allowedUpdatesToString(allowedUpdates, request);
        removeComma(request, final);

        reader.parse(http::multiPartUpload(inst, baseApi + "/setWebhook", certificate, url, maxConnections, final),
                     value);
    }

    curl_easy_cleanup(inst);

    bool isOk = value.get("ok","").asBool();
    if(isOk)
        urlWebhook = url;

    return isOk;
}

//deleteWebhook
bool tgbot::methods::Api::deleteWebhook() const {
    CURL* inst = http::curlEasyInit();
    bool isOk = (http::get(inst,baseApi + "/deleteWebhook").find("\"ok\":true") != std::string::npos);
    curl_easy_cleanup(inst);
    return isOk;
}

//getWebhookInfo
api_types::WebhookInfo tgbot::methods::Api::getWebhookInfo() const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,baseApi + "/getWebhookInfo"), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return api_types::WebhookInfo(value.get("result",""));
}

//getMe
api_types::User tgbot::methods::Api::getMe() const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,baseApi + "/getMe"), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return api_types::User(value.get("result",""));
}

//getChat
api_types::Chat tgbot::methods::Api::getChat(const std::string &chatId) const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,baseApi + "/getChat?chat_id=" + chatId), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return api_types::Chat(value.get("result",""));
}

//getChatMembersCount
unsigned tgbot::methods::Api::getChatMembersCount(const std::string &chatId) const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,baseApi + "/getChatMembersCount?chat_id=" + chatId), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return value.get("result","").asUInt();
}

//getFile
api_types::File tgbot::methods::Api::getFile(const std::string &fileId) const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,baseApi + "/getFile?file_id=" + fileId), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return api_types::File(value.get("result","").asUInt());
}

//getChatMember
api_types::ChatMember tgbot::methods::Api::getChatMember(const std::string &chatId, const int &userId) const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    std::stringstream url;
    url << baseApi << "/getFile?chat_id=" << chatId << "&user_id=" << userId;

    reader.parse(http::get(inst,url.str()), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return api_types::ChatMember(value.get("result","").asUInt());
}<commit_msg>Added some methods<commit_after>#include <sstream>
#include <tgbot/methods/api.h>
#include <tgbot/utils/https.h>
#include <jsoncpp/json/json.h>
#include <tgbot/bot.h>

using namespace tgbot::methods;
using namespace tgbot::utils;

static inline void allowedUpdatesToString(const std::vector<api_types::UpdateType>& updates,
                                    std::stringstream& stream) {
    stream << "[";
    for (auto const &update : updates) {
        switch (update) {
            case api_types::UpdateType::MESSAGE:
                stream << "\"message\",";
                break;
            case api_types::UpdateType::CALLBACK_QUERY:
                stream << "\"callback_query\",";
                break;
            case api_types::UpdateType::INLINE_QUERY :
                stream << "\"inline_query\",";
                break;
            case api_types::UpdateType::CHOSEN_INLINE_RESULT:
                stream << "\"chosen_inline_result\",";
                break;
            case api_types::UpdateType::PRE_CHECKOUT_QUERY:
                stream << "\"pre_checkout_query\",";
                break;
            case api_types::UpdateType::SHIPPING_QUERY:
                stream << "\"shipping_query\",";
                break;
            case api_types::UpdateType::EDITED_CHANNEL_POST:
                stream << "\"edited_channel_post\",";
                break;
            case api_types::UpdateType::EDITED_MESSAGE:
                stream << "\"edited_message\",";
                break;
            case api_types::UpdateType::CHANNEL_POST:
                stream << "\"channel_post\",";
                break;
        }
    }
}

static inline void removeComma(const std::stringstream& stream, std::string& target) {
    std::string &&req = stream.str();
    char &endpos = req.at(req.size() - 1);
    if (endpos == ',')
        endpos = ']';
    target = req;
}

//Api constructors

//Webhook, no further action
tgbot::methods::Api::Api(const std::string& token) :
    baseApi("https://api.telegram.org/bot" + token)
{}

//Webhook
tgbot::methods::Api::Api(const std::string& token,
		const std::string& url,
		const int& maxConnections,
		const std::vector<api_types::UpdateType>& allowedUpdates) :
            baseApi("https://api.telegram.org/bot" + token) {

        if(!setWebhook(url,maxConnections,allowedUpdates))
            throw TelegramException("Unable to set webhook");
}

//Webhook with cert
tgbot::methods::Api::Api(const std::string& token,
		const std::string& url,
		const std::string& certificate,
		const int& maxConnections,
		const std::vector<api_types::UpdateType>& allowedUpdates) :
            baseApi("https://api.telegram.org/bot" + token) {

        if(!setWebhook(url,certificate,maxConnections,allowedUpdates))
            throw TelegramException("Unable to set webhook");
}

//HTTP Long polling
tgbot::methods::Api::Api(const std::string &token,
		const std::vector<api_types::UpdateType> &allowedUpdates,
		const int &timeout,
		const int &limit) :
	        baseApi("https://api.telegram.org/bot" + token),
	        currentOffset(0) {

		std::stringstream fullApiRequest;
		fullApiRequest << baseApi << "/getUpdates?limit=" << limit << "&timeout=" << timeout;

		if (allowedUpdates.size()) {
            fullApiRequest << "&allowed_updates=";
			allowedUpdatesToString(allowedUpdates,fullApiRequest);
			removeComma(fullApiRequest,updateApiRequest);
		} else
			updateApiRequest = fullApiRequest.str();
}

//
//API implementations
//

//getUpdates (internal usage)
int tgbot::methods::Api::getUpdates(void *c, std::vector<api_types::Update>& updates) {
	std::stringstream updatesRequest;
	updatesRequest << updateApiRequest << "&offset=" << currentOffset;

	Json::Value rootUpdate;
	Json::Reader parser;

	parser.parse(utils::http::get(c, updatesRequest.str()), rootUpdate);

	if (!rootUpdate.get("ok", "").asBool())
		throw TelegramException(rootUpdate.get("description", "").asCString());

	Json::Value valueUpdates = rootUpdate.get("result", "");
	const int& updatesCount = valueUpdates.size();
	if (!updatesCount)
		return 0;

	for (auto const &singleUpdate : valueUpdates)
		updates.emplace_back(singleUpdate);

	currentOffset = 1 + valueUpdates[updatesCount - 1].get("update_id", "").asInt64();

	return updatesCount;
}

//
// Availible API Methods
//

//setWebhook
bool tgbot::methods::Api::setWebhook(const std::string &url, const int &maxConnections,
                                     const std::vector<api_types::UpdateType> &allowedUpdates) {
    std::stringstream request;
    request << baseApi << "/setWebhook?url=" << url << "&max_connections=" << maxConnections;

    std::string setWebhookRequest;
    if (allowedUpdates.size()) {
        request << "&allowed_updates=";
        allowedUpdatesToString(allowedUpdates,request);
        removeComma(request,setWebhookRequest);
    } else
        setWebhookRequest = request.str();

    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,setWebhookRequest),value);
    curl_easy_cleanup(inst);

    bool isOk = value.get("ok","").asBool();
    if(isOk)
        urlWebhook = url;

    return isOk;
}

bool tgbot::methods::Api::setWebhook(const std::string &url, const std::string &certificate,
                                     const int &maxConnections,
                                     const std::vector<api_types::UpdateType> &allowedUpdates) {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    if(!allowedUpdates.size())
        reader.parse(http::multiPartUpload(inst, baseApi + "/setWebhook", certificate, url, maxConnections),
                     value);
    else {
        std::stringstream request;
        std::string final;

        allowedUpdatesToString(allowedUpdates, request);
        removeComma(request, final);

        reader.parse(http::multiPartUpload(inst, baseApi + "/setWebhook", certificate, url, maxConnections, final),
                     value);
    }

    curl_easy_cleanup(inst);

    bool isOk = value.get("ok","").asBool();
    if(isOk)
        urlWebhook = url;

    return isOk;
}

//deleteWebhook
bool tgbot::methods::Api::deleteWebhook() const {
    CURL* inst = http::curlEasyInit();
    bool isOk = (http::get(inst,baseApi + "/deleteWebhook").find("\"ok\":true") != std::string::npos);
    curl_easy_cleanup(inst);
    return isOk;
}

//getWebhookInfo
api_types::WebhookInfo tgbot::methods::Api::getWebhookInfo() const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,baseApi + "/getWebhookInfo"), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return api_types::WebhookInfo(value.get("result",""));
}

//getMe
api_types::User tgbot::methods::Api::getMe() const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,baseApi + "/getMe"), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return api_types::User(value.get("result",""));
}

//getChat
api_types::Chat tgbot::methods::Api::getChat(const std::string &chatId) const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,baseApi + "/getChat?chat_id=" + chatId), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return api_types::Chat(value.get("result",""));
}

//getChatMembersCount
unsigned tgbot::methods::Api::getChatMembersCount(const std::string &chatId) const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,baseApi + "/getChatMembersCount?chat_id=" + chatId), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return value.get("result","").asUInt();
}

//getFile
api_types::File tgbot::methods::Api::getFile(const std::string &fileId) const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    reader.parse(http::get(inst,baseApi + "/getFile?file_id=" + fileId), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return api_types::File(value.get("result","").asUInt());
}

//getChatMember
api_types::ChatMember tgbot::methods::Api::getChatMember(const std::string &chatId, const int &userId) const {
    CURL* inst = http::curlEasyInit();
    Json::Value value;
    Json::Reader reader;

    std::stringstream url;
    url << baseApi << "/getChatMember?chat_id=" << chatId << "&user_id=" << userId;

    reader.parse(http::get(inst,url.str()), value);
    curl_easy_cleanup(inst);

    if(!value.get("ok","").asBool())
        throw TelegramException(value.get("description","").asCString());

    return api_types::ChatMember(value.get("result","").asUInt());
}<|endoftext|>


<commit_before>#include <string>
#include <iostream>
#include <functional>
#include <system_error>
#include <vector>

#include <cstring>

#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <libcgroup.h>

#include <fcntl.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <syscall.h>
#include <pwd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/mount.h>
#include <sys/wait.h>

#include "sandbox.h"
#include "utils.h"
#include "cgroup.h"
#include "semaphore.h"
#include "pipe.h"

namespace fs = boost::filesystem;
using std::string;
using std::vector;
using boost::format;

// Make sure fd 0,1,2 exists.
static void RedirectIO(const string &std_input, const string &std_output,
                       const string &std_error, int nullfd)
{
    int inputfd, outputfd, errorfd;
    if (std_input != "")
    {
        inputfd = Ensure(open(std_input.c_str(), O_RDONLY));
    }
    else
    {
        inputfd = nullfd;
    }
    Ensure(dup2(inputfd, STDIN_FILENO));

    if (std_output != "")
    {
        outputfd = Ensure(open(std_output.c_str(), O_WRONLY | O_TRUNC | O_CREAT,
                               S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP));
    }
    else
    {
        outputfd = nullfd;
    }
    Ensure(dup2(outputfd, STDOUT_FILENO));

    if (std_error != "")
    {
        if (std_error == std_output)
        {
            errorfd = outputfd;
        }
        else
        {
            errorfd = Ensure(open(std_error.c_str(), O_WRONLY | O_TRUNC | O_CREAT,
                                  S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP));
        }
    }
    else
    {
        errorfd = nullfd;
    }
    Ensure(dup2(errorfd, STDERR_FILENO));
}

struct ExecutionParameter
{
    const SandboxParameter &parameter;

    PosixSemaphore semaphore1, semaphore2;
    // This pipe is used to forward error message from the child process to the parent.
    PosixPipe pipefd;

    ExecutionParameter(const SandboxParameter &param, int pipeOptions) : parameter(param),
                                                                         semaphore1(true, 0),
                                                                         semaphore2(true, 0),
                                                                         pipefd(pipeOptions)
    {
    }
};

static int ChildProcess(void *param_ptr)
{
    ExecutionParameter &execParam = *reinterpret_cast<ExecutionParameter *>(param_ptr);
    // We obtain a full copy of parameters here. The arguments may be destoryed after some time.
    SandboxParameter parameter = execParam.parameter;

    try
    {
        Ensure(close(execParam.pipefd[0]));
        passwd *newUser = nullptr;
        if (parameter.userName != "")
        {
            // Get the user info before chroot, or it will be unable to open /etc/passwd
            newUser = CheckNull(getpwnam(parameter.userName.c_str()));
        }

        int nullfd = Ensure(open("/dev/null", O_RDWR));
        if (parameter.redirectBeforeChroot)
        {
            RedirectIO(parameter.stdinRedirection, parameter.stdoutRedirection,
                       parameter.stderrRedirection, nullfd);
        }

        // TODO: choose a better place for the temp path.
        fs::path tempRoot("/tmp");
        Ensure(mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)); // Make root private
        Ensure(mount(parameter.chrootDirectory.string().c_str(), tempRoot.string().c_str(), "", MS_BIND | MS_REC, ""));
        Ensure(mount("", tempRoot.string().c_str(), "", MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, ""));

        for (MountInfo &info : parameter.mounts)
        {
            fs::path target = tempRoot / info.dst;
            Ensure(mount(info.src.string().c_str(), target.string().c_str(), "", MS_BIND | MS_REC, ""));
            if (info.limit == 0)
            {
                Ensure(mount("", target.string().c_str(), "", MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, ""));
            }
            else if (info.limit != -1)
            {
                // TODO: implement.
            }
        }

        Ensure(chroot(tempRoot.string().c_str()));
        Ensure(chdir(parameter.workingDirectory.string().c_str()));

        if (parameter.mountProc)
        {
            Ensure(mount("proc", "/proc", "proc", 0, NULL));
        }
        if (!parameter.redirectBeforeChroot)
        {
            RedirectIO(parameter.stdinRedirection, parameter.stdoutRedirection,
                       parameter.stderrRedirection, nullfd);
        }

        const char *newHostname = "BraveNewWorld";
        Ensure(sethostname(newHostname, strlen(newHostname)));

        if (newUser != nullptr)
        {
            Ensure(setgid(newUser->pw_gid));
            Ensure(setuid(newUser->pw_uid));
        }

        vector<char *> params = StringToPtr(parameter.executableParameters),
                       envi = StringToPtr(parameter.environmentVariables);

        int temp = -1;
        // Inform the parent that no exception occurred.
        Ensure(write(execParam.pipefd[1], &temp, sizeof(int)));

        // Inform our parent that we are ready to go.
        execParam.semaphore1.Post();
        // Wait for parent's reply.
        execParam.semaphore2.Wait();

        Ensure(execve(parameter.executablePath.c_str(), &params[0], &envi[0]));

        // If execve returns, then we meet an error.
        raise(SIGABRT);
        return 255;
    }
    catch (std::exception &err)
    {
        // TODO: implement error handling
        // abort(); // This will cause segmentation fault.
        // throw;
        // return 222;

        const char *errMessage = err.what();
        int len = strlen(errMessage);
        try
        {
            Ensure(write(execParam.pipefd[1], &len, sizeof(int)));
            Ensure(write(execParam.pipefd[1], errMessage, len));
            Ensure(close(execParam.pipefd[1]));
            execParam.semaphore1.Post();
            return 126;
        }
        catch (...)
        {
            return 125;
        }
    }
    catch (...)
    {
        return 125;
    }
}

// The child stack is only used before `execve`, so it does not need much space.
const int childStackSize = 1024 * 700;
pid_t StartSandbox(const SandboxParameter &parameter
                   /* ,std::function<void(pid_t)> reportPid*/) // Let's use some fancy C++11 feature.
{
    pid_t container_pid = -1;
    try
    {
        // char* childStack = new char[childStackSize];
        std::vector<char> childStack(childStackSize); // I don't want to call `delete`

        ExecutionParameter execParam(parameter, O_CLOEXEC | O_NONBLOCK);

        container_pid = Ensure(clone(ChildProcess, &*childStack.end(),
                                     CLONE_NEWNET | CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD,
                                     const_cast<void *>(reinterpret_cast<const void *>(&execParam))));

        CgroupInfo memInfo("memory", parameter.cgroupName),
            cpuInfo("cpuacct", parameter.cgroupName),
            pidInfo("pids", parameter.cgroupName);

        vector<CgroupInfo *> infos = {&memInfo, &cpuInfo, &pidInfo};
        for (auto &item : infos)
        {
            CreateGroup(*item);
            KillGroupMembers(*item);
            WriteGroupProperty(*item, "tasks", container_pid);
        }

#define WRITE_WITH_CHECK(__where, __name, __value)                  \
    {                                                               \
        if ((__value) >= 0)                                         \
        {                                                           \
            WriteGroupProperty((__where), (__name), (__value));     \
        }                                                           \
        else                                                        \
        {                                                           \
            WriteGroupProperty((__where), (__name), string("max")); \
        }                                                           \
    }

        // Forcibly clear any memory usage by cache.
        WriteGroupProperty(memInfo, "memory.force_empty", 0);
        WriteGroupProperty(memInfo, "memory.memsw.limit_in_bytes", -1);
        WriteGroupProperty(memInfo, "memory.limit_in_bytes", -1);
        WRITE_WITH_CHECK(memInfo, "memory.limit_in_bytes", parameter.memoryLimit);
        WRITE_WITH_CHECK(memInfo, "memory.memsw.limit_in_bytes", parameter.memoryLimit);
        WRITE_WITH_CHECK(pidInfo, "pids.max", parameter.processLimit);

        // Wait for at most 100ms. If the child process hasn't posted the semaphore,
        // We will assume that the child has already dead.
        bool waitResult = execParam.semaphore1.TimedWait(100);

        int errLen, bytesRead = read(execParam.pipefd[0], &errLen, sizeof(int));
        // Child will be killed once the error has been thrown.
        if (!waitResult || bytesRead == 0 || bytesRead == -1)
        {
            // No information available.
            throw std::runtime_error("The child process has exited unexpectedly.");
        }
        else if (errLen != -1) // -1 indicates OK.
        {
            vector<char> buf(errLen);
            Ensure(read(execParam.pipefd[0], &*buf.begin(), errLen));
            string errstr(buf.begin(), buf.end());
            throw std::runtime_error((format("The child process has reported the following error: %1%") % errstr).str());
        }

        // Clear usage stats.
        WriteGroupProperty(memInfo, "memory.memsw.max_usage_in_bytes", 0);
        WriteGroupProperty(cpuInfo, "cpuacct.usage", 0);

        // Continue the child.
        execParam.semaphore2.Post();

        // Wait for 1ms to prevent the child stack deallocated before execve.
        // TODO: Find a better way to handle this.
        usleep(1000);

        return container_pid;
    }
    catch (std::exception &ex)
    {
        // Do the cleanups; we don't care whether these operations are successful.
        if (container_pid != -1)
        {
            (void)kill(container_pid, SIGKILL);
            (void)waitpid(container_pid, NULL, WNOHANG);
        }
        throw;
    }
}

ExecutionResult
SBWaitForProcess(pid_t pid)
{
    ExecutionResult result;
    int status;
    Ensure(waitpid(pid, &status, 0));
    if (WIFEXITED(status))
    {
        result.Status = EXITED;
        result.Code = WEXITSTATUS(status);
    }
    else if (WIFSIGNALED(status))
    {
        result.Status = SIGNALED;
        result.Code = WTERMSIG(status);
    }
    return result;
}<commit_msg>Fix potential execution freeze on `setuid'<commit_after>#include <string>
#include <iostream>
#include <functional>
#include <system_error>
#include <vector>

#include <cstring>

#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <libcgroup.h>

#include <fcntl.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <syscall.h>
#include <pwd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/mount.h>
#include <sys/wait.h>

#include "sandbox.h"
#include "utils.h"
#include "cgroup.h"
#include "semaphore.h"
#include "pipe.h"

namespace fs = boost::filesystem;
using std::string;
using std::vector;
using boost::format;

// Make sure fd 0,1,2 exists.
static void RedirectIO(const string &std_input, const string &std_output,
                       const string &std_error, int nullfd)
{
    int inputfd, outputfd, errorfd;
    if (std_input != "")
    {
        inputfd = Ensure(open(std_input.c_str(), O_RDONLY));
    }
    else
    {
        inputfd = nullfd;
    }
    Ensure(dup2(inputfd, STDIN_FILENO));

    if (std_output != "")
    {
        outputfd = Ensure(open(std_output.c_str(), O_WRONLY | O_TRUNC | O_CREAT,
                               S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP));
    }
    else
    {
        outputfd = nullfd;
    }
    Ensure(dup2(outputfd, STDOUT_FILENO));

    if (std_error != "")
    {
        if (std_error == std_output)
        {
            errorfd = outputfd;
        }
        else
        {
            errorfd = Ensure(open(std_error.c_str(), O_WRONLY | O_TRUNC | O_CREAT,
                                  S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP));
        }
    }
    else
    {
        errorfd = nullfd;
    }
    Ensure(dup2(errorfd, STDERR_FILENO));
}

struct ExecutionParameter
{
    const SandboxParameter &parameter;

    PosixSemaphore semaphore1, semaphore2;
    // This pipe is used to forward error message from the child process to the parent.
    PosixPipe pipefd;

    ExecutionParameter(const SandboxParameter &param, int pipeOptions) : parameter(param),
                                                                         semaphore1(true, 0),
                                                                         semaphore2(true, 0),
                                                                         pipefd(pipeOptions)
    {
    }
};

static int ChildProcess(void *param_ptr)
{
    ExecutionParameter &execParam = *reinterpret_cast<ExecutionParameter *>(param_ptr);
    // We obtain a full copy of parameters here. The arguments may be destoryed after some time.
    SandboxParameter parameter = execParam.parameter;

    try
    {
        Ensure(close(execParam.pipefd[0]));
        passwd *newUser = nullptr;
        if (parameter.userName != "")
        {
            // Get the user info before chroot, or it will be unable to open /etc/passwd
            newUser = CheckNull(getpwnam(parameter.userName.c_str()));
        }

        int nullfd = Ensure(open("/dev/null", O_RDWR));
        if (parameter.redirectBeforeChroot)
        {
            RedirectIO(parameter.stdinRedirection, parameter.stdoutRedirection,
                       parameter.stderrRedirection, nullfd);
        }

        // TODO: choose a better place for the temp path.
        fs::path tempRoot("/tmp");
        Ensure(mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)); // Make root private
        Ensure(mount(parameter.chrootDirectory.string().c_str(), tempRoot.string().c_str(), "", MS_BIND | MS_REC, ""));
        Ensure(mount("", tempRoot.string().c_str(), "", MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, ""));

        for (MountInfo &info : parameter.mounts)
        {
            fs::path target = tempRoot / info.dst;
            Ensure(mount(info.src.string().c_str(), target.string().c_str(), "", MS_BIND | MS_REC, ""));
            if (info.limit == 0)
            {
                Ensure(mount("", target.string().c_str(), "", MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, ""));
            }
            else if (info.limit != -1)
            {
                // TODO: implement.
            }
        }

        Ensure(chroot(tempRoot.string().c_str()));
        Ensure(chdir(parameter.workingDirectory.string().c_str()));

        if (parameter.mountProc)
        {
            Ensure(mount("proc", "/proc", "proc", 0, NULL));
        }
        if (!parameter.redirectBeforeChroot)
        {
            RedirectIO(parameter.stdinRedirection, parameter.stdoutRedirection,
                       parameter.stderrRedirection, nullfd);
        }

        const char *newHostname = "BraveNewWorld";
        Ensure(sethostname(newHostname, strlen(newHostname)));

        if (newUser != nullptr)
        {
            Ensure(syscall(SYS_setgid, newUser->pw_gid));
            Ensure(syscall(SYS_setuid, newUser->pw_uid));
        }

        vector<char *> params = StringToPtr(parameter.executableParameters),
                       envi = StringToPtr(parameter.environmentVariables);

        int temp = -1;
        // Inform the parent that no exception occurred.
        Ensure(write(execParam.pipefd[1], &temp, sizeof(int)));

        // Inform our parent that we are ready to go.
        execParam.semaphore1.Post();
        // Wait for parent's reply.
        execParam.semaphore2.Wait();

        Ensure(execve(parameter.executablePath.c_str(), &params[0], &envi[0]));

        // If execve returns, then we meet an error.
        raise(SIGABRT);
        return 255;
    }
    catch (std::exception &err)
    {
        // TODO: implement error handling
        // abort(); // This will cause segmentation fault.
        // throw;
        // return 222;

        const char *errMessage = err.what();
        int len = strlen(errMessage);
        try
        {
            Ensure(write(execParam.pipefd[1], &len, sizeof(int)));
            Ensure(write(execParam.pipefd[1], errMessage, len));
            Ensure(close(execParam.pipefd[1]));
            execParam.semaphore1.Post();
            return 126;
        }
        catch (...)
        {
            return 125;
        }
    }
    catch (...)
    {
        return 125;
    }
}

// The child stack is only used before `execve`, so it does not need much space.
const int childStackSize = 1024 * 700;
pid_t StartSandbox(const SandboxParameter &parameter
                   /* ,std::function<void(pid_t)> reportPid*/) // Let's use some fancy C++11 feature.
{
    pid_t container_pid = -1;
    try
    {
        // char* childStack = new char[childStackSize];
        std::vector<char> childStack(childStackSize); // I don't want to call `delete`

        ExecutionParameter execParam(parameter, O_CLOEXEC | O_NONBLOCK);

        container_pid = Ensure(clone(ChildProcess, &*childStack.end(),
                                     CLONE_NEWNET | CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD,
                                     const_cast<void *>(reinterpret_cast<const void *>(&execParam))));

        CgroupInfo memInfo("memory", parameter.cgroupName),
            cpuInfo("cpuacct", parameter.cgroupName),
            pidInfo("pids", parameter.cgroupName);

        vector<CgroupInfo *> infos = {&memInfo, &cpuInfo, &pidInfo};
        for (auto &item : infos)
        {
            CreateGroup(*item);
            KillGroupMembers(*item);
            WriteGroupProperty(*item, "tasks", container_pid);
        }

#define WRITE_WITH_CHECK(__where, __name, __value)                  \
    {                                                               \
        if ((__value) >= 0)                                         \
        {                                                           \
            WriteGroupProperty((__where), (__name), (__value));     \
        }                                                           \
        else                                                        \
        {                                                           \
            WriteGroupProperty((__where), (__name), string("max")); \
        }                                                           \
    }

        // Forcibly clear any memory usage by cache.
        WriteGroupProperty(memInfo, "memory.force_empty", 0);
        WriteGroupProperty(memInfo, "memory.memsw.limit_in_bytes", -1);
        WriteGroupProperty(memInfo, "memory.limit_in_bytes", -1);
        WRITE_WITH_CHECK(memInfo, "memory.limit_in_bytes", parameter.memoryLimit);
        WRITE_WITH_CHECK(memInfo, "memory.memsw.limit_in_bytes", parameter.memoryLimit);
        WRITE_WITH_CHECK(pidInfo, "pids.max", parameter.processLimit);

        // Wait for at most 100ms. If the child process hasn't posted the semaphore,
        // We will assume that the child has already dead.
        bool waitResult = execParam.semaphore1.TimedWait(100);

        int errLen, bytesRead = read(execParam.pipefd[0], &errLen, sizeof(int));
        // Child will be killed once the error has been thrown.
        if (!waitResult || bytesRead == 0 || bytesRead == -1)
        {
            // No information available.
            throw std::runtime_error("The child process has exited unexpectedly.");
        }
        else if (errLen != -1) // -1 indicates OK.
        {
            vector<char> buf(errLen);
            Ensure(read(execParam.pipefd[0], &*buf.begin(), errLen));
            string errstr(buf.begin(), buf.end());
            throw std::runtime_error((format("The child process has reported the following error: %1%") % errstr).str());
        }

        // Clear usage stats.
        WriteGroupProperty(memInfo, "memory.memsw.max_usage_in_bytes", 0);
        WriteGroupProperty(cpuInfo, "cpuacct.usage", 0);

        // Continue the child.
        execParam.semaphore2.Post();

        // Wait for 1ms to prevent the child stack deallocated before execve.
        // TODO: Find a better way to handle this.
        usleep(1000);

        return container_pid;
    }
    catch (std::exception &ex)
    {
        // Do the cleanups; we don't care whether these operations are successful.
        if (container_pid != -1)
        {
            (void)kill(container_pid, SIGKILL);
            (void)waitpid(container_pid, NULL, WNOHANG);
        }
        throw;
    }
}

ExecutionResult
SBWaitForProcess(pid_t pid)
{
    ExecutionResult result;
    int status;
    Ensure(waitpid(pid, &status, 0));
    if (WIFEXITED(status))
    {
        result.Status = EXITED;
        result.Code = WEXITSTATUS(status);
    }
    else if (WIFSIGNALED(status))
    {
        result.Status = SIGNALED;
        result.Code = WTERMSIG(status);
    }
    return result;
}<|endoftext|>


<commit_before>/*
 * This file is part of signon
 *
 * Copyright (C) 2009-2010 Nokia Corporation.
 *
 * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "pluginproxy.h"

#include <sys/types.h>
#include <pwd.h>

#include <QStringList>
#include <QThreadStorage>
#include <QThread>
#include <QDataStream>

#include "signond-common.h"
#include "SignOn/uisessiondata_priv.h"
#include "SignOn/signonplugincommon.h"

/*
 *   TODO: remove the "SignOn/authpluginif.h" include below after the removal
 *         of the deprecated error handling (needed here only for the deprecated
 *         AuthPluginError::PLUGIN_ERROR_GENERAL).
 */
#include "SignOn/authpluginif.h"

//TODO get this from config
#define REMOTEPLUGIN_BIN_PATH QLatin1String("/usr/bin/signonpluginprocess")
#define PLUGINPROCESS_START_TIMEOUT 5000
#define PLUGINPROCESS_STOP_TIMEOUT 1000

namespace SignonDaemonNS {

    /* ---------------------- PluginProcess ---------------------- */

    PluginProcess::PluginProcess(QObject *parent) : QProcess(parent)
    {
    }

    PluginProcess::~PluginProcess()
    {
    }

    void PluginProcess::setupChildProcess()
    {
        // Drop all root privileges in the child process and switch to signon user
#ifndef NO_SIGNON_USER
        //get uid and gid
        struct passwd *passwdRecord = getpwnam("signon");
        if ( !passwdRecord ){
            fprintf(stderr, "failed to get user: signon\n");
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
#ifdef SIGNOND_TRACE
        //this is run in remote plugin process, so trace should go to stderr
        fprintf(stderr, "got user: %s with uid: %d\n", passwdRecord->pw_name, passwdRecord->pw_uid);
#endif
        if (( ::setgid(passwdRecord->pw_gid))
                || (::setuid(passwdRecord->pw_uid))
                || (::getuid() != passwdRecord->pw_uid)
                ) {
            fprintf(stderr, "failed to set user: %s with uid: %d", passwdRecord->pw_name, passwdRecord->pw_uid);
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
 #endif
    }

    /* ---------------------- PluginProxy ---------------------- */

    PluginProxy::PluginProxy(QString type, QObject *parent)
            : QObject(parent)
    {
        TRACE();

        m_type = type;
        m_isProcessing = false;
        m_isResultObtained = false;
        m_currentResultOperation = -1;
        m_process = new PluginProcess(this);

        connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(onReadStandardError()));

        /*
         * TODO: some error handling should be added here, at least remove of current
         * request data from the top of the queue and reply an error code
         * */
        connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onExit(int, QProcess::ExitStatus)));
        connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));

        m_blobIOHandler = new BlobIOHandler(m_process, m_process, this);

        connect(m_blobIOHandler,
                SIGNAL(dataReceived(const QVariantMap &)),
                this,
                SLOT(sessionDataReceived(const QVariantMap &)));

        QSocketNotifier *readNotifier =
            new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read, this);

        readNotifier->setEnabled(false);
        m_blobIOHandler->setReadChannelSocketNotifier(readNotifier);
    }

    PluginProxy::~PluginProxy()
    {
        if (m_process != NULL &&
            m_process->state() != QProcess::NotRunning)
        {
            if (m_isProcessing)
                cancel();

            stop();

            if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT)) {
                qCritical() << "The signon plugin does not react on demand to stop: need to kill it!!!";
                m_process->kill();

                if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT))
                {
                    if (m_process->pid()) {
                        qCritical() << "The signon plugin seems to ignore kill(), killing it from command line";
                        QString killProcessCommand(QString::fromLatin1("kill -9 %1").arg(m_process->pid()));
                        QProcess::execute(killProcessCommand);
                    }
                }
            }
        }
    }

    PluginProxy* PluginProxy::createNewPluginProxy(const QString &type)
    {
        PluginProxy *pp = new PluginProxy(type);

        QStringList args = QStringList() << pp->m_type;
        pp->m_process->start(REMOTEPLUGIN_BIN_PATH, args);

        QByteArray tmp;

        if (!pp->waitForStarted(PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot be started";
            delete pp;
            return NULL;
        }

        if (!pp->readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot load plugin";
            delete pp;
            return NULL;
        }

        pp->m_type = pp->queryType();
        pp->m_mechanisms = pp->queryMechanisms();

        connect(pp->m_process, SIGNAL(readyRead()), pp, SLOT(onReadStandardOutput()));

        TRACE() << "The process is started";
        return pp;
    }

   bool PluginProxy::process(const QString &cancelKey, const QVariantMap &inData, const QString &mechanism)
   {
        if (!restartIfRequired())
            return false;

        m_isResultObtained = false;
        m_cancelKey = cancelKey;
        QVariant value = inData.value(SSOUI_KEY_UIPOLICY);
        m_uiPolicy = value.toInt();

        QDataStream in(m_process);
        in << (quint32)PLUGIN_OP_PROCESS;
        in << mechanism;

        m_blobIOHandler->sendData(inData);

        m_isProcessing = true;
        return true;
    }

   bool PluginProxy::processUi(const QString &cancelKey, const QVariantMap &inData)
   {
        TRACE();

        if (!restartIfRequired())
            return false;

        m_cancelKey = cancelKey;

        QDataStream in(m_process);

        in << (quint32)PLUGIN_OP_PROCESS_UI;

        m_blobIOHandler->sendData(inData);

        m_isProcessing = true;

        return true;
    }

   bool PluginProxy::processRefresh(const QString &cancelKey, const QVariantMap &inData)
   {
        TRACE();

        if (!restartIfRequired())
            return false;

        m_cancelKey = cancelKey;

        QDataStream in(m_process);

        in << (quint32)PLUGIN_OP_REFRESH;

        m_blobIOHandler->sendData(inData);

        m_isProcessing = true;

        return true;
    }

   void PluginProxy::cancel()
   {
       TRACE();
       QDataStream in(m_process);
       in << (quint32)PLUGIN_OP_CANCEL;
    }

   void PluginProxy::stop()
   {
       TRACE();
       QDataStream in(m_process);
       in << (quint32)PLUGIN_OP_STOP;
    }

    bool PluginProxy::readOnReady(QByteArray &buffer, int timeout)
    {
        bool ready = m_process->waitForReadyRead(timeout);

        if (ready) {
            if (!m_process->bytesAvailable())
                return false;

            while (m_process->bytesAvailable())
                buffer += m_process->readAllStandardOutput();
        }

        return ready;
    }

    bool PluginProxy::isProcessing()
    {
        return m_isProcessing;
    }

    void PluginProxy::blobIOError()
    {
        TRACE();
        disconnect(m_blobIOHandler, SIGNAL(error()), this, SLOT(blobIOError()));
        stop();

        connect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput()));
        emit processError(
            m_cancelKey,
            (int)Error::InternalServer,
            QLatin1String("Failed to I/O session data to/from the authentication plugin."));
    }

    bool PluginProxy::isResultOperationCodeValid(const int opCode) const
    {
        if (opCode == PLUGIN_RESPONSE_RESULT
            || opCode == PLUGIN_RESPONSE_STORE
            || opCode == PLUGIN_RESPONSE_ERROR
            || opCode == PLUGIN_RESPONSE_SIGNAL
            || opCode == PLUGIN_RESPONSE_UI
            || opCode == PLUGIN_RESPONSE_REFRESHED) return true;

        return false;
    }

    void PluginProxy::onReadStandardOutput()
    {
        disconnect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput()));

        if (!m_process->bytesAvailable()) {
            qCritical() << "No information available on process";
            m_isProcessing = false;
            emit processError(m_cancelKey, Error::InternalServer, QString());
            return;
        }

        QDataStream reader(m_process);
        reader >> m_currentResultOperation;

        TRACE() << "PROXY RESULT OPERATION:" << m_currentResultOperation;

        if (!isResultOperationCodeValid(m_currentResultOperation)) {
            TRACE() << "Unknown operation code - skipping.";

            //flushing the stdin channel
            Q_UNUSED(m_process->readAllStandardOutput());

            connect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput()));
            return;
        }

        if (m_currentResultOperation != PLUGIN_RESPONSE_SIGNAL
            && m_currentResultOperation != PLUGIN_RESPONSE_ERROR) {

            connect(m_blobIOHandler, SIGNAL(error()),
                    this, SLOT(blobIOError()));

            int expectedDataSize = 0;
            reader >> expectedDataSize;
            TRACE() << "PROXY EXPECTED DATA SIZE:" << expectedDataSize;

            m_blobIOHandler->receiveData(expectedDataSize);
        } else {
            handlePluginResponse(m_currentResultOperation);
        }
    }

    void PluginProxy::sessionDataReceived(const QVariantMap &map)
    {
        handlePluginResponse(m_currentResultOperation, map);
    }

    void PluginProxy::handlePluginResponse(const quint32 resultOperation,
                                           const QVariantMap &sessionDataMap)
    {
        TRACE() << resultOperation;

        if (resultOperation == PLUGIN_RESPONSE_RESULT) {
            TRACE() << "PLUGIN_RESPONSE_RESULT";

            m_isProcessing = false;

            if (!m_isResultObtained)
                emit processResultReply(m_cancelKey, sessionDataMap);
            else
                BLAME() << "Unexpected plugin response: ";

            m_isResultObtained = true;
        } else if (resultOperation == PLUGIN_RESPONSE_STORE) {
            TRACE() << "PLUGIN_RESPONSE_STORE";

            if (!m_isResultObtained)
                emit processStore(m_cancelKey, sessionDataMap);
            else
                BLAME() << "Unexpected plugin store: ";

        } else if (resultOperation == PLUGIN_RESPONSE_UI) {
            TRACE() << "PLUGIN_RESPONSE_UI";

            if (!m_isResultObtained) {
                bool allowed = true;

                if (m_uiPolicy == NoUserInteractionPolicy)
                    allowed = false;

                if (m_uiPolicy == ValidationPolicy) {
                    bool credentialsQueried =
                        (sessionDataMap.contains(SSOUI_KEY_QUERYUSERNAME)
                        || sessionDataMap.contains(SSOUI_KEY_QUERYPASSWORD));

                    bool captchaQueried  =
                        (sessionDataMap.contains(SSOUI_KEY_CAPTCHAIMG)
                         || sessionDataMap.contains(SSOUI_KEY_CAPTCHAURL));

                    if (credentialsQueried && !captchaQueried)
                        allowed = false;
                }

                if (!allowed) {
                    //set error and return;
                    TRACE() << "ui policy prevented ui launch";

                    QVariantMap nonConstMap = sessionDataMap;
                    nonConstMap.insert(SSOUI_KEY_ERROR, QUERY_ERROR_FORBIDDEN);
                    processUi(m_cancelKey, nonConstMap);
                } else {
                    TRACE() << "open ui";
                    emit processUiRequest(m_cancelKey, sessionDataMap);
                }
            } else {
                BLAME() << "Unexpected plugin ui response: ";
            }
        } else if (resultOperation == PLUGIN_RESPONSE_REFRESHED) {
            TRACE() << "PLUGIN_RESPONSE_REFRESHED";

            if (!m_isResultObtained)
                emit processRefreshRequest(m_cancelKey, sessionDataMap);
            else
                BLAME() << "Unexpected plugin ui response: ";
        } else if (resultOperation == PLUGIN_RESPONSE_ERROR) {
            TRACE() << "PLUGIN_RESPONSE_ERROR";
            quint32 err;
            QString errorMessage;

            QDataStream stream(m_process);
            stream >> err;
            stream >> errorMessage;
            m_isProcessing = false;

            if (!m_isResultObtained)
                emit processError(m_cancelKey, (int)err, errorMessage);
            else
                BLAME() << "Unexpected plugin error: " << errorMessage;

            m_isResultObtained = true;
        } else if (resultOperation == PLUGIN_RESPONSE_SIGNAL) {
            TRACE() << "PLUGIN_RESPONSE_SIGNAL";
            quint32 state;
            QString message;

            QDataStream stream(m_process);
            stream >> state;
            stream >> message;

            if (!m_isResultObtained)
                emit stateChanged(m_cancelKey, (int)state, message);
            else
                BLAME() << "Unexpected plugin signal: " << state << message;
        }

        connect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput()));
    }

    void PluginProxy::onReadStandardError()
    {
        QString ba = QString::fromLatin1(m_process->readAllStandardError());
    }

    void PluginProxy::onExit(int exitCode, QProcess::ExitStatus exitStatus)
    {
        TRACE() << "Plugin process exit with code " << exitCode << " : " << exitStatus;

        if (m_isProcessing || exitStatus == QProcess::CrashExit) {
            qCritical() << "Challenge produces CRASH!";
            emit processError(m_cancelKey, Error::InternalServer, QLatin1String("plugin processed crashed"));
        }
        if (exitCode == 2) {
            TRACE() << "plugin process terminated because cannot change user";
        }

        m_isProcessing = false;
    }

    void PluginProxy::onError(QProcess::ProcessError err)
    {
        TRACE() << "Error: " << err;
    }

    QString PluginProxy::queryType()
    {
        TRACE();

        if (!restartIfRequired())
            return QString();

        QDataStream ds(m_process);
        ds << (quint32)PLUGIN_OP_TYPE;

        QByteArray typeBa, buffer;
        bool result;

        if ((result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) {
            QDataStream out(buffer);
            out >> typeBa;
        } else
            qCritical("PluginProxy returned NULL result");

        return QString::fromLatin1(typeBa);
    }

    QStringList PluginProxy::queryMechanisms()
    {
        TRACE();

        if (!restartIfRequired())
            return QStringList();

        QDataStream in(m_process);
        in << (quint32)PLUGIN_OP_MECHANISMS;

        QByteArray buffer;
        QStringList strList;
        bool result;

        if ((result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) {
            QVariant mechanismsVar;
            QDataStream out(buffer);

            out >> mechanismsVar;
            QVariantList varList = mechanismsVar.toList();

            for (int i = 0; i < varList.count(); i++)
                    strList << varList.at(i).toString();

            TRACE() << strList;
        } else
            qCritical("PluginProxy returned NULL result");

        return strList;
    }

    bool PluginProxy::waitForStarted(int timeout)
    {
        return m_process->waitForStarted(timeout);
    }

    bool PluginProxy::waitForFinished(int timeout)
    {
        return m_process->waitForFinished(timeout);
    }

    bool PluginProxy::restartIfRequired()
    {
        if (m_process->state() == QProcess::NotRunning) {
            TRACE() << "RESTART REQUIRED";
            m_process->start(REMOTEPLUGIN_BIN_PATH, QStringList(m_type));

            QByteArray tmp;
            if (!waitForStarted(PLUGINPROCESS_START_TIMEOUT) || !readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT))
                return false;
        }
        return true;
    }

} //namespace SignonDaemonNS
<commit_msg>export SSO_DEBUG=1 when starting the plugins<commit_after>/*
 * This file is part of signon
 *
 * Copyright (C) 2009-2010 Nokia Corporation.
 *
 * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "pluginproxy.h"

#include <sys/types.h>
#include <pwd.h>

#include <QStringList>
#include <QThreadStorage>
#include <QThread>
#include <QDataStream>

#include "signond-common.h"
#include "SignOn/uisessiondata_priv.h"
#include "SignOn/signonplugincommon.h"

/*
 *   TODO: remove the "SignOn/authpluginif.h" include below after the removal
 *         of the deprecated error handling (needed here only for the deprecated
 *         AuthPluginError::PLUGIN_ERROR_GENERAL).
 */
#include "SignOn/authpluginif.h"

//TODO get this from config
#define REMOTEPLUGIN_BIN_PATH QLatin1String("/usr/bin/signonpluginprocess")
#define PLUGINPROCESS_START_TIMEOUT 5000
#define PLUGINPROCESS_STOP_TIMEOUT 1000

namespace SignonDaemonNS {

    /* ---------------------- PluginProcess ---------------------- */

    PluginProcess::PluginProcess(QObject *parent) : QProcess(parent)
    {
    }

    PluginProcess::~PluginProcess()
    {
    }

    void PluginProcess::setupChildProcess()
    {
        // Drop all root privileges in the child process and switch to signon user
#ifndef NO_SIGNON_USER
        //get uid and gid
        struct passwd *passwdRecord = getpwnam("signon");
        if ( !passwdRecord ){
            fprintf(stderr, "failed to get user: signon\n");
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
#ifdef SIGNOND_TRACE
        //this is run in remote plugin process, so trace should go to stderr
        fprintf(stderr, "got user: %s with uid: %d\n", passwdRecord->pw_name, passwdRecord->pw_uid);
#endif
        if (( ::setgid(passwdRecord->pw_gid))
                || (::setuid(passwdRecord->pw_uid))
                || (::getuid() != passwdRecord->pw_uid)
                ) {
            fprintf(stderr, "failed to set user: %s with uid: %d", passwdRecord->pw_name, passwdRecord->pw_uid);
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
 #endif
    }

    /* ---------------------- PluginProxy ---------------------- */

    PluginProxy::PluginProxy(QString type, QObject *parent)
            : QObject(parent)
    {
        TRACE();

        m_type = type;
        m_isProcessing = false;
        m_isResultObtained = false;
        m_currentResultOperation = -1;
        m_process = new PluginProcess(this);

#ifdef SIGNOND_TRACE
        if (debugEnabled()) {
            QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
            env.insert(QLatin1String("SSO_DEBUG"), QLatin1String("1"));
            m_process->setProcessEnvironment(env);
        }
#endif

        connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(onReadStandardError()));

        /*
         * TODO: some error handling should be added here, at least remove of current
         * request data from the top of the queue and reply an error code
         * */
        connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onExit(int, QProcess::ExitStatus)));
        connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));

        m_blobIOHandler = new BlobIOHandler(m_process, m_process, this);

        connect(m_blobIOHandler,
                SIGNAL(dataReceived(const QVariantMap &)),
                this,
                SLOT(sessionDataReceived(const QVariantMap &)));

        QSocketNotifier *readNotifier =
            new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read, this);

        readNotifier->setEnabled(false);
        m_blobIOHandler->setReadChannelSocketNotifier(readNotifier);
    }

    PluginProxy::~PluginProxy()
    {
        if (m_process != NULL &&
            m_process->state() != QProcess::NotRunning)
        {
            if (m_isProcessing)
                cancel();

            stop();

            if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT)) {
                qCritical() << "The signon plugin does not react on demand to stop: need to kill it!!!";
                m_process->kill();

                if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT))
                {
                    if (m_process->pid()) {
                        qCritical() << "The signon plugin seems to ignore kill(), killing it from command line";
                        QString killProcessCommand(QString::fromLatin1("kill -9 %1").arg(m_process->pid()));
                        QProcess::execute(killProcessCommand);
                    }
                }
            }
        }
    }

    PluginProxy* PluginProxy::createNewPluginProxy(const QString &type)
    {
        PluginProxy *pp = new PluginProxy(type);

        QStringList args = QStringList() << pp->m_type;
        pp->m_process->start(REMOTEPLUGIN_BIN_PATH, args);

        QByteArray tmp;

        if (!pp->waitForStarted(PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot be started";
            delete pp;
            return NULL;
        }

        if (!pp->readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot load plugin";
            delete pp;
            return NULL;
        }

        pp->m_type = pp->queryType();
        pp->m_mechanisms = pp->queryMechanisms();

        connect(pp->m_process, SIGNAL(readyRead()), pp, SLOT(onReadStandardOutput()));

        TRACE() << "The process is started";
        return pp;
    }

   bool PluginProxy::process(const QString &cancelKey, const QVariantMap &inData, const QString &mechanism)
   {
        if (!restartIfRequired())
            return false;

        m_isResultObtained = false;
        m_cancelKey = cancelKey;
        QVariant value = inData.value(SSOUI_KEY_UIPOLICY);
        m_uiPolicy = value.toInt();

        QDataStream in(m_process);
        in << (quint32)PLUGIN_OP_PROCESS;
        in << mechanism;

        m_blobIOHandler->sendData(inData);

        m_isProcessing = true;
        return true;
    }

   bool PluginProxy::processUi(const QString &cancelKey, const QVariantMap &inData)
   {
        TRACE();

        if (!restartIfRequired())
            return false;

        m_cancelKey = cancelKey;

        QDataStream in(m_process);

        in << (quint32)PLUGIN_OP_PROCESS_UI;

        m_blobIOHandler->sendData(inData);

        m_isProcessing = true;

        return true;
    }

   bool PluginProxy::processRefresh(const QString &cancelKey, const QVariantMap &inData)
   {
        TRACE();

        if (!restartIfRequired())
            return false;

        m_cancelKey = cancelKey;

        QDataStream in(m_process);

        in << (quint32)PLUGIN_OP_REFRESH;

        m_blobIOHandler->sendData(inData);

        m_isProcessing = true;

        return true;
    }

   void PluginProxy::cancel()
   {
       TRACE();
       QDataStream in(m_process);
       in << (quint32)PLUGIN_OP_CANCEL;
    }

   void PluginProxy::stop()
   {
       TRACE();
       QDataStream in(m_process);
       in << (quint32)PLUGIN_OP_STOP;
    }

    bool PluginProxy::readOnReady(QByteArray &buffer, int timeout)
    {
        bool ready = m_process->waitForReadyRead(timeout);

        if (ready) {
            if (!m_process->bytesAvailable())
                return false;

            while (m_process->bytesAvailable())
                buffer += m_process->readAllStandardOutput();
        }

        return ready;
    }

    bool PluginProxy::isProcessing()
    {
        return m_isProcessing;
    }

    void PluginProxy::blobIOError()
    {
        TRACE();
        disconnect(m_blobIOHandler, SIGNAL(error()), this, SLOT(blobIOError()));
        stop();

        connect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput()));
        emit processError(
            m_cancelKey,
            (int)Error::InternalServer,
            QLatin1String("Failed to I/O session data to/from the authentication plugin."));
    }

    bool PluginProxy::isResultOperationCodeValid(const int opCode) const
    {
        if (opCode == PLUGIN_RESPONSE_RESULT
            || opCode == PLUGIN_RESPONSE_STORE
            || opCode == PLUGIN_RESPONSE_ERROR
            || opCode == PLUGIN_RESPONSE_SIGNAL
            || opCode == PLUGIN_RESPONSE_UI
            || opCode == PLUGIN_RESPONSE_REFRESHED) return true;

        return false;
    }

    void PluginProxy::onReadStandardOutput()
    {
        disconnect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput()));

        if (!m_process->bytesAvailable()) {
            qCritical() << "No information available on process";
            m_isProcessing = false;
            emit processError(m_cancelKey, Error::InternalServer, QString());
            return;
        }

        QDataStream reader(m_process);
        reader >> m_currentResultOperation;

        TRACE() << "PROXY RESULT OPERATION:" << m_currentResultOperation;

        if (!isResultOperationCodeValid(m_currentResultOperation)) {
            TRACE() << "Unknown operation code - skipping.";

            //flushing the stdin channel
            Q_UNUSED(m_process->readAllStandardOutput());

            connect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput()));
            return;
        }

        if (m_currentResultOperation != PLUGIN_RESPONSE_SIGNAL
            && m_currentResultOperation != PLUGIN_RESPONSE_ERROR) {

            connect(m_blobIOHandler, SIGNAL(error()),
                    this, SLOT(blobIOError()));

            int expectedDataSize = 0;
            reader >> expectedDataSize;
            TRACE() << "PROXY EXPECTED DATA SIZE:" << expectedDataSize;

            m_blobIOHandler->receiveData(expectedDataSize);
        } else {
            handlePluginResponse(m_currentResultOperation);
        }
    }

    void PluginProxy::sessionDataReceived(const QVariantMap &map)
    {
        handlePluginResponse(m_currentResultOperation, map);
    }

    void PluginProxy::handlePluginResponse(const quint32 resultOperation,
                                           const QVariantMap &sessionDataMap)
    {
        TRACE() << resultOperation;

        if (resultOperation == PLUGIN_RESPONSE_RESULT) {
            TRACE() << "PLUGIN_RESPONSE_RESULT";

            m_isProcessing = false;

            if (!m_isResultObtained)
                emit processResultReply(m_cancelKey, sessionDataMap);
            else
                BLAME() << "Unexpected plugin response: ";

            m_isResultObtained = true;
        } else if (resultOperation == PLUGIN_RESPONSE_STORE) {
            TRACE() << "PLUGIN_RESPONSE_STORE";

            if (!m_isResultObtained)
                emit processStore(m_cancelKey, sessionDataMap);
            else
                BLAME() << "Unexpected plugin store: ";

        } else if (resultOperation == PLUGIN_RESPONSE_UI) {
            TRACE() << "PLUGIN_RESPONSE_UI";

            if (!m_isResultObtained) {
                bool allowed = true;

                if (m_uiPolicy == NoUserInteractionPolicy)
                    allowed = false;

                if (m_uiPolicy == ValidationPolicy) {
                    bool credentialsQueried =
                        (sessionDataMap.contains(SSOUI_KEY_QUERYUSERNAME)
                        || sessionDataMap.contains(SSOUI_KEY_QUERYPASSWORD));

                    bool captchaQueried  =
                        (sessionDataMap.contains(SSOUI_KEY_CAPTCHAIMG)
                         || sessionDataMap.contains(SSOUI_KEY_CAPTCHAURL));

                    if (credentialsQueried && !captchaQueried)
                        allowed = false;
                }

                if (!allowed) {
                    //set error and return;
                    TRACE() << "ui policy prevented ui launch";

                    QVariantMap nonConstMap = sessionDataMap;
                    nonConstMap.insert(SSOUI_KEY_ERROR, QUERY_ERROR_FORBIDDEN);
                    processUi(m_cancelKey, nonConstMap);
                } else {
                    TRACE() << "open ui";
                    emit processUiRequest(m_cancelKey, sessionDataMap);
                }
            } else {
                BLAME() << "Unexpected plugin ui response: ";
            }
        } else if (resultOperation == PLUGIN_RESPONSE_REFRESHED) {
            TRACE() << "PLUGIN_RESPONSE_REFRESHED";

            if (!m_isResultObtained)
                emit processRefreshRequest(m_cancelKey, sessionDataMap);
            else
                BLAME() << "Unexpected plugin ui response: ";
        } else if (resultOperation == PLUGIN_RESPONSE_ERROR) {
            TRACE() << "PLUGIN_RESPONSE_ERROR";
            quint32 err;
            QString errorMessage;

            QDataStream stream(m_process);
            stream >> err;
            stream >> errorMessage;
            m_isProcessing = false;

            if (!m_isResultObtained)
                emit processError(m_cancelKey, (int)err, errorMessage);
            else
                BLAME() << "Unexpected plugin error: " << errorMessage;

            m_isResultObtained = true;
        } else if (resultOperation == PLUGIN_RESPONSE_SIGNAL) {
            TRACE() << "PLUGIN_RESPONSE_SIGNAL";
            quint32 state;
            QString message;

            QDataStream stream(m_process);
            stream >> state;
            stream >> message;

            if (!m_isResultObtained)
                emit stateChanged(m_cancelKey, (int)state, message);
            else
                BLAME() << "Unexpected plugin signal: " << state << message;
        }

        connect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput()));
    }

    void PluginProxy::onReadStandardError()
    {
        QString ba = QString::fromLatin1(m_process->readAllStandardError());
    }

    void PluginProxy::onExit(int exitCode, QProcess::ExitStatus exitStatus)
    {
        TRACE() << "Plugin process exit with code " << exitCode << " : " << exitStatus;

        if (m_isProcessing || exitStatus == QProcess::CrashExit) {
            qCritical() << "Challenge produces CRASH!";
            emit processError(m_cancelKey, Error::InternalServer, QLatin1String("plugin processed crashed"));
        }
        if (exitCode == 2) {
            TRACE() << "plugin process terminated because cannot change user";
        }

        m_isProcessing = false;
    }

    void PluginProxy::onError(QProcess::ProcessError err)
    {
        TRACE() << "Error: " << err;
    }

    QString PluginProxy::queryType()
    {
        TRACE();

        if (!restartIfRequired())
            return QString();

        QDataStream ds(m_process);
        ds << (quint32)PLUGIN_OP_TYPE;

        QByteArray typeBa, buffer;
        bool result;

        if ((result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) {
            QDataStream out(buffer);
            out >> typeBa;
        } else
            qCritical("PluginProxy returned NULL result");

        return QString::fromLatin1(typeBa);
    }

    QStringList PluginProxy::queryMechanisms()
    {
        TRACE();

        if (!restartIfRequired())
            return QStringList();

        QDataStream in(m_process);
        in << (quint32)PLUGIN_OP_MECHANISMS;

        QByteArray buffer;
        QStringList strList;
        bool result;

        if ((result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) {
            QVariant mechanismsVar;
            QDataStream out(buffer);

            out >> mechanismsVar;
            QVariantList varList = mechanismsVar.toList();

            for (int i = 0; i < varList.count(); i++)
                    strList << varList.at(i).toString();

            TRACE() << strList;
        } else
            qCritical("PluginProxy returned NULL result");

        return strList;
    }

    bool PluginProxy::waitForStarted(int timeout)
    {
        return m_process->waitForStarted(timeout);
    }

    bool PluginProxy::waitForFinished(int timeout)
    {
        return m_process->waitForFinished(timeout);
    }

    bool PluginProxy::restartIfRequired()
    {
        if (m_process->state() == QProcess::NotRunning) {
            TRACE() << "RESTART REQUIRED";
            m_process->start(REMOTEPLUGIN_BIN_PATH, QStringList(m_type));

            QByteArray tmp;
            if (!waitForStarted(PLUGINPROCESS_START_TIMEOUT) || !readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT))
                return false;
        }
        return true;
    }

} //namespace SignonDaemonNS
<|endoftext|>


<commit_before>#include <vector>

#include "simulation.h"

#include "codes/bch.h"
#include "codes/rs.h"

static std::vector<decoder> decoders{
  cyclic::primitive_bch<5, dmin<7>, cyclic::berlekamp_massey_tag>(),
  cyclic::primitive_bch<5, dmin<7>, cyclic::peterson_gorenstein_zierler_tag>(),
  cyclic::primitive_bch<5, dmin<7>, cyclic::euklid_tag>(),
  cyclic::primitive_bch<5, dmin<7>, min_sum_tag<50> >(),
  cyclic::primitive_bch<5, dmin<7>,
                        normalized_min_sum_tag<50, std::ratio<8, 10> > >(),
  cyclic::primitive_bch<5, dmin<7>,
                        offset_min_sum_tag<50, std::ratio<1, 100> > >(),
  cyclic::primitive_bch<5, dmin<7>, self_correcting_1_min_sum_tag<50> >(),
  cyclic::primitive_bch<5, dmin<7>, self_correcting_2_min_sum_tag<50> >(),
  cyclic::primitive_bch<5, dmin<7>, normalized_2d_min_sum_tag<50> >(),
};

int main() {
  for (const auto &decoder : decoders) {
    std::cout << decoder.to_string() << std::endl;
  }
  thread_pool p;
  for (const auto &decoder : decoders)
    p.push(bitflip_simulation(decoder, 6));
}

<commit_msg>Ignore global-ctor/exit-time dtor warning<commit_after>#include <vector>

#include "simulation.h"

#include "codes/bch.h"
#include "codes/rs.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wexit-time-destructors"
#pragma clang diagnostic ignored "-Wglobal-constructors"

static std::vector<decoder> decoders{
  cyclic::primitive_bch<5, dmin<7>, cyclic::berlekamp_massey_tag>(),
  cyclic::primitive_bch<5, dmin<7>, cyclic::peterson_gorenstein_zierler_tag>(),
  cyclic::primitive_bch<5, dmin<7>, cyclic::euklid_tag>(),
  cyclic::primitive_bch<5, dmin<7>, min_sum_tag<50> >(),
  cyclic::primitive_bch<5, dmin<7>,
                        normalized_min_sum_tag<50, std::ratio<8, 10> > >(),
  cyclic::primitive_bch<5, dmin<7>,
                        offset_min_sum_tag<50, std::ratio<1, 100> > >(),
  cyclic::primitive_bch<5, dmin<7>, self_correcting_1_min_sum_tag<50> >(),
  cyclic::primitive_bch<5, dmin<7>, self_correcting_2_min_sum_tag<50> >(),
  cyclic::primitive_bch<5, dmin<7>, normalized_2d_min_sum_tag<50> >(),
};

#pragma clang diagnostic pop

int main() {
  for (const auto &decoder : decoders) {
    std::cout << decoder.to_string() << std::endl;
  }
  thread_pool p;
  for (const auto &decoder : decoders)
    p.push(bitflip_simulation(decoder, 6));
}

<|endoftext|>


<commit_before>// Copyright 2016-2017 John T. Foster, Katy L. Hanson

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//    http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "IGESControl_Reader.hxx"
#include "TColStd_HSequenceOfTransient.hxx"
#include "TopoDS_Shape.hxx"

#include "Teuchos_RCP.hpp"

#include "tadiga_IGES_geometry.h"

tadiga::IgesGeometry::IgesGeometry(
    const Teuchos::RCP<const Teuchos::Comm<int>>& kComm,
    const Teuchos::RCP<Teuchos::ParameterList>& kGeometryParameters)
    : kComm_(kComm) {
    const auto kFileName = kGeometryParameters->get<std::string>("File Name");

    // Open IGES Reader from OpenCASCADE
    const auto kIgesReader = Teuchos::rcp(new IGESControl_Reader);
    const auto status = kIgesReader->ReadFile(kFileName.c_str());

    // TODO(johntfoster@gmail.com): Check the status of the file

    Handle(TColStd_HSequenceOfTransient) myList =
        kIgesReader->GiveList("iges-faces");
    // selects all IGES faces in the file and puts them into a list  called
    // //MyList,

    const auto kIgesFaces = myList->Length();
    const auto kTransFaces = kIgesReader->TransferList(myList);
    // translates MyList,

    std::cout << "IGES Faces: " << kIgesFaces
              << "   Transferred:" << kTransFaces << std::endl;

    TopoDS_Shape sh = kIgesReader->OneShape();
};
<commit_msg>Change myList to myFacesList<commit_after>// Copyright 2016-2017 John T. Foster, Katy L. Hanson

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//    http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "IGESControl_Reader.hxx"
#include "TColStd_HSequenceOfTransient.hxx"
#include "TopoDS_Shape.hxx"

#include "Teuchos_RCP.hpp"

#include "tadiga_IGES_geometry.h"

tadiga::IgesGeometry::IgesGeometry(
    const Teuchos::RCP<const Teuchos::Comm<int>>& kComm,
    const Teuchos::RCP<Teuchos::ParameterList>& kGeometryParameters)
    : kComm_(kComm) {
    const auto kFileName = kGeometryParameters->get<std::string>("File Name");

    // Open IGES Reader from OpenCASCADE
    const auto kIgesReader = Teuchos::rcp(new IGESControl_Reader);
    const auto status = kIgesReader->ReadFile(kFileName.c_str());

    // TODO(johntfoster@gmail.com): Check the status of the file

    Handle(TColStd_HSequenceOfTransient) myFacesList =
        kIgesReader->GiveList("iges-faces");
    // selects all IGES faces in the file and puts them into a list  called
    // //MyList,

    const auto kIgesFaces = myFacesList->Length();
    const auto kTransFaces = kIgesReader->TransferList(myFacesList);
    // translates MyList,

    std::cout << "IGES Faces: " << kIgesFaces
              << "   Transferred:" << kTransFaces << std::endl;

    TopoDS_Shape sh = kIgesReader->OneShape();
};
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libetonyek project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <boost/make_shared.hpp>

#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>

#include "IWAMessage.h"
#include "IWORKMemoryStream.h"

using namespace libetonyek;

using std::size_t;
using std::string;

namespace test
{

namespace
{

RVNGInputStreamPtr_t makeStream(const unsigned char *const bytes, const unsigned long length)
{
  return boost::make_shared<IWORKMemoryStream>(bytes, length);
}

}

class IWAMessageTest : public CPPUNIT_NS::TestFixture
{
public:
  virtual void setUp();
  virtual void tearDown();

private:
  CPPUNIT_TEST_SUITE(IWAMessageTest);
  CPPUNIT_TEST(testSimple);
  CPPUNIT_TEST(testNestedMessage);
  CPPUNIT_TEST(testMissingFields);
  CPPUNIT_TEST(testRepeated);
  CPPUNIT_TEST(testPacked);
  CPPUNIT_TEST(testInvalidInput);
  CPPUNIT_TEST_SUITE_END();

private:
  void testSimple();
  void testNestedMessage();
  void testMissingFields();
  void testRepeated();
  void testPacked();
  void testInvalidInput();
};

void IWAMessageTest::setUp()
{
}

void IWAMessageTest::tearDown()
{
}

#define BYTES(b) (reinterpret_cast<const unsigned char *>(b)), (sizeof(b) - 1)

void IWAMessageTest::testSimple()
{
  {
    CPPUNIT_ASSERT_NO_THROW(IWAMessage(makeStream(BYTES("\x8\x4")), 2));
    IWAMessage msg(makeStream(BYTES("\x8\x4")), 2);
    CPPUNIT_ASSERT_NO_THROW(msg.uint64(1));
    CPPUNIT_ASSERT(msg.uint64(1));
    CPPUNIT_ASSERT_EQUAL(uint64_t(4), msg.uint64(1).get());
  }

  {
    const RVNGInputStreamPtr_t input(
      makeStream(BYTES(
                   "\x8\x4" // 1: uint64 = 4
                   "\x11\x2\x0\x0\x0\x0\x0\x0\x0" // 2: fixed64 = 2
                   "\x1a\x5hello" // 3: string = "hello"
                   "\x25\x20\x0\x0\x0" // 4: fixed32 = 32
                 ))
    );
    IWAMessage msg(input, 23);
    CPPUNIT_ASSERT_NO_THROW(msg.uint64(1));
    CPPUNIT_ASSERT(msg.uint64(1));
    CPPUNIT_ASSERT_EQUAL(uint64_t(4), msg.uint64(1).get());
    CPPUNIT_ASSERT_NO_THROW(msg.fixed64(2));
    CPPUNIT_ASSERT(msg.fixed64(2));
    CPPUNIT_ASSERT_EQUAL(uint64_t(2), msg.fixed64(2).get());
    CPPUNIT_ASSERT_NO_THROW(msg.string(3));
    CPPUNIT_ASSERT(msg.string(3));
    CPPUNIT_ASSERT_EQUAL(string("hello"), msg.string(3).get());
  }
}

void IWAMessageTest::testNestedMessage()
{
  // TODO: add
}

void IWAMessageTest::testMissingFields()
{
  IWAMessage msg(makeStream(BYTES("\x8\x4")), 2);
  CPPUNIT_ASSERT_NO_THROW(msg.string(4));
  CPPUNIT_ASSERT(!msg.string(4));
  CPPUNIT_ASSERT_NO_THROW(msg.fixed64(2));
  CPPUNIT_ASSERT(!msg.fixed64(2));
  CPPUNIT_ASSERT_NO_THROW(msg.message(8));
  CPPUNIT_ASSERT(!msg.message(8));
}

void IWAMessageTest::testRepeated()
{
  IWAMessage msg(makeStream(BYTES("\x8\x4\x8\x8\x8\x10")), 6);
  CPPUNIT_ASSERT_NO_THROW(msg.uint64(1));
  CPPUNIT_ASSERT_EQUAL(size_t(3), msg.uint64(1).size());
  CPPUNIT_ASSERT_EQUAL(uint64_t(4), msg.uint64(1)[0]);
  CPPUNIT_ASSERT_EQUAL(uint64_t(8), msg.uint64(1)[1]);
  CPPUNIT_ASSERT_EQUAL(uint64_t(16), msg.uint64(1)[2]);
}

void IWAMessageTest::testPacked()
{
  IWAMessage msg(makeStream(BYTES("\xa\x3\x1\x2\x3")), 5);
  CPPUNIT_ASSERT_NO_THROW(msg.uint64(1));
  CPPUNIT_ASSERT_EQUAL(size_t(3), msg.uint64(1).size());
  CPPUNIT_ASSERT_EQUAL(uint64_t(1), msg.uint64(1)[0]);
  CPPUNIT_ASSERT_EQUAL(uint64_t(2), msg.uint64(1)[1]);
  CPPUNIT_ASSERT_EQUAL(uint64_t(3), msg.uint64(1)[2]);
}

void IWAMessageTest::testInvalidInput()
{
  // TODO: add
}

#undef BYTES

CPPUNIT_TEST_SUITE_REGISTRATION(IWAMessageTest);

}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<commit_msg>add test for nested messages<commit_after>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libetonyek project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <boost/make_shared.hpp>

#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>

#include "IWAMessage.h"
#include "IWORKMemoryStream.h"

using namespace libetonyek;

using std::size_t;
using std::string;

namespace test
{

namespace
{

RVNGInputStreamPtr_t makeStream(const unsigned char *const bytes, const unsigned long length)
{
  return boost::make_shared<IWORKMemoryStream>(bytes, length);
}

}

class IWAMessageTest : public CPPUNIT_NS::TestFixture
{
public:
  virtual void setUp();
  virtual void tearDown();

private:
  CPPUNIT_TEST_SUITE(IWAMessageTest);
  CPPUNIT_TEST(testSimple);
  CPPUNIT_TEST(testNestedMessage);
  CPPUNIT_TEST(testMissingFields);
  CPPUNIT_TEST(testRepeated);
  CPPUNIT_TEST(testPacked);
  CPPUNIT_TEST(testInvalidInput);
  CPPUNIT_TEST_SUITE_END();

private:
  void testSimple();
  void testNestedMessage();
  void testMissingFields();
  void testRepeated();
  void testPacked();
  void testInvalidInput();
};

void IWAMessageTest::setUp()
{
}

void IWAMessageTest::tearDown()
{
}

#define BYTES(b) (reinterpret_cast<const unsigned char *>(b)), (sizeof(b) - 1)

void IWAMessageTest::testSimple()
{
  {
    CPPUNIT_ASSERT_NO_THROW(IWAMessage(makeStream(BYTES("\x8\x4")), 2));
    IWAMessage msg(makeStream(BYTES("\x8\x4")), 2);
    CPPUNIT_ASSERT_NO_THROW(msg.uint64(1));
    CPPUNIT_ASSERT(msg.uint64(1));
    CPPUNIT_ASSERT_EQUAL(uint64_t(4), msg.uint64(1).get());
  }

  {
    const RVNGInputStreamPtr_t input(
      makeStream(BYTES(
                   "\x8\x4" // 1: uint64 = 4
                   "\x11\x2\x0\x0\x0\x0\x0\x0\x0" // 2: fixed64 = 2
                   "\x1a\x5hello" // 3: string = "hello"
                   "\x25\x20\x0\x0\x0" // 4: fixed32 = 32
                 ))
    );
    IWAMessage msg(input, 23);
    CPPUNIT_ASSERT_NO_THROW(msg.uint64(1));
    CPPUNIT_ASSERT(msg.uint64(1));
    CPPUNIT_ASSERT_EQUAL(uint64_t(4), msg.uint64(1).get());
    CPPUNIT_ASSERT_NO_THROW(msg.fixed64(2));
    CPPUNIT_ASSERT(msg.fixed64(2));
    CPPUNIT_ASSERT_EQUAL(uint64_t(2), msg.fixed64(2).get());
    CPPUNIT_ASSERT_NO_THROW(msg.string(3));
    CPPUNIT_ASSERT(msg.string(3));
    CPPUNIT_ASSERT_EQUAL(string("hello"), msg.string(3).get());
  }
}

void IWAMessageTest::testNestedMessage()
{
  IWAMessage msg(makeStream(BYTES("\xa\x6\x8\x4\x12\x2\x10\xa")), 8); // {1: {1: uint32, 2: {2: uint32}}}
  CPPUNIT_ASSERT_NO_THROW(msg.message(1));
  CPPUNIT_ASSERT_NO_THROW(msg.message(1).get().uint32(1));
  CPPUNIT_ASSERT_EQUAL(4u, msg.message(1).get().uint32(1).get());
  CPPUNIT_ASSERT_NO_THROW(msg.message(1).get().message(2));
  CPPUNIT_ASSERT_NO_THROW(msg.message(1).get().message(2).get().uint32(2));
  CPPUNIT_ASSERT_EQUAL(10u, msg.message(1).get().message(2).get().uint32(2).get());
}

void IWAMessageTest::testMissingFields()
{
  IWAMessage msg(makeStream(BYTES("\x8\x4")), 2);
  CPPUNIT_ASSERT_NO_THROW(msg.string(4));
  CPPUNIT_ASSERT(!msg.string(4));
  CPPUNIT_ASSERT_NO_THROW(msg.fixed64(2));
  CPPUNIT_ASSERT(!msg.fixed64(2));
  CPPUNIT_ASSERT_NO_THROW(msg.message(8));
  CPPUNIT_ASSERT(!msg.message(8));
}

void IWAMessageTest::testRepeated()
{
  IWAMessage msg(makeStream(BYTES("\x8\x4\x8\x8\x8\x10")), 6);
  CPPUNIT_ASSERT_NO_THROW(msg.uint64(1));
  CPPUNIT_ASSERT_EQUAL(size_t(3), msg.uint64(1).size());
  CPPUNIT_ASSERT_EQUAL(uint64_t(4), msg.uint64(1)[0]);
  CPPUNIT_ASSERT_EQUAL(uint64_t(8), msg.uint64(1)[1]);
  CPPUNIT_ASSERT_EQUAL(uint64_t(16), msg.uint64(1)[2]);
}

void IWAMessageTest::testPacked()
{
  IWAMessage msg(makeStream(BYTES("\xa\x3\x1\x2\x3")), 5);
  CPPUNIT_ASSERT_NO_THROW(msg.uint64(1));
  CPPUNIT_ASSERT_EQUAL(size_t(3), msg.uint64(1).size());
  CPPUNIT_ASSERT_EQUAL(uint64_t(1), msg.uint64(1)[0]);
  CPPUNIT_ASSERT_EQUAL(uint64_t(2), msg.uint64(1)[1]);
  CPPUNIT_ASSERT_EQUAL(uint64_t(3), msg.uint64(1)[2]);
}

void IWAMessageTest::testInvalidInput()
{
  // TODO: add
}

#undef BYTES

CPPUNIT_TEST_SUITE_REGISTRATION(IWAMessageTest);

}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<|endoftext|>


<commit_before>#include <boost/test/unit_test.hpp>

#include "base58.h"

BOOST_AUTO_TEST_SUITE(base58_tests)

// TODO:
// EncodeBase58Check
// DecodeBase58Check
// CBase58Data
//    bool SetString(const char* psz)
    // bool SetString(const std::string& str)
    // std::string ToString() const
    // int CompareTo(const CBase58Data& b58) const
    // bool operator==(const CBase58Data& b58) const
    // bool operator<=(const CBase58Data& b58) const
    // bool operator>=(const CBase58Data& b58) const
    // bool operator< (const CBase58Data& b58) const
    // bool operator> (const CBase58Data& b58) const

// CBitcoinAddress
    // bool SetHash160(const uint160& hash160)
    // bool SetPubKey(const std::vector<unsigned char>& vchPubKey)
    // bool IsValid() const
    // CBitcoinAddress()
    // CBitcoinAddress(uint160 hash160In)
    // CBitcoinAddress(const std::vector<unsigned char>& vchPubKey)
    // CBitcoinAddress(const std::string& strAddress)
    // CBitcoinAddress(const char* pszAddress)
    // uint160 GetHash160() const

#define U(x) (reinterpret_cast<const unsigned char*>(x))
static struct {
    const unsigned char *data;
    int size;
} vstrIn[] = {
{U(""), 0},
{U("\x61"), 1},
{U("\x62\x62\x62"), 3},
{U("\x63\x63\x63"), 3},
{U("\x73\x69\x6d\x70\x6c\x79\x20\x61\x20\x6c\x6f\x6e\x67\x20\x73\x74\x72\x69\x6e\x67"), 20},
{U("\x00\xeb\x15\x23\x1d\xfc\xeb\x60\x92\x58\x86\xb6\x7d\x06\x52\x99\x92\x59\x15\xae\xb1\x72\xc0\x66\x47"), 25},
{U("\x51\x6b\x6f\xcd\x0f"), 5},
{U("\xbf\x4f\x89\x00\x1e\x67\x02\x74\xdd"), 9},
{U("\x57\x2e\x47\x94"), 4},
{U("\xec\xac\x89\xca\xd9\x39\x23\xc0\x23\x21"), 10},
{U("\x10\xc8\x51\x1e"), 4},
{U("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), 10},
};

const char *vstrOut[] = {
"",
"2g",
"a3gV",
"aPEr",
"2cFupjhnEsSn59qHXstmK2ffpLv2",
"1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L",
"ABnLTmg",
"3SEo3LWLoPntC",
"3EFU7m",
"EJDM8drfXA6uyA",
"Rt5zm",
"1111111111"
};

BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
{
    for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
    {
        BOOST_CHECK_EQUAL(EncodeBase58(vstrIn[i].data, vstrIn[i].data + vstrIn[i].size), vstrOut[i]);
    }
}

BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
{
    std::vector<unsigned char> result;
    for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
    {
        std::vector<unsigned char> expected(vstrIn[i].data, vstrIn[i].data + vstrIn[i].size);
        BOOST_CHECK(DecodeBase58(vstrOut[i], result));
        BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
    }
    BOOST_CHECK(!DecodeBase58("invalid", result));
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Delete base58_tests.cpp<commit_after><|endoftext|>