<commit_before>/*
 * VapourSynth D2V Plugin
 *
 * Copyright (c) 2012 Derek Buitenhuis
 *
 * This file is part of d2vsource.
 *
 * d2vsource is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * d2vsource 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 d2vsource; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>

extern "C" {
#include <assert.h>
#include <stdlib.h>
#include <string.h>
}

#include "compat.hpp"
#include "d2v.hpp"
#include "gop.hpp"

#ifdef _WIN32
#include <windows.h>
#endif

using namespace std;

string d2vgetpath(const char *d2v_path, const string& file)
{
    string path;
    string d2v       = d2v_path;
    size_t delim_pos = d2v.rfind(PATH_DELIM) + 1;

    if ((file.substr(0, 1) == "/" || file.substr(1, 1) == ":") || (d2v.substr(0, 1) != "/" && d2v.substr(1, 1) != ":")) {
        path = file;
    } else {
        path  = d2v.substr(0, delim_pos);
        path += file;
    }

    return path;
}

/* Conditionally free all memebers of d2vcontext. */
void d2vfreep(d2vcontext **ctx)
{
    d2vcontext *lctx = *ctx;
    unsigned int i;

    if (!lctx)
        return;

    lctx->frames.clear();
    for(i = 0; i < lctx->gops.size(); i++) {
        gop g = lctx->gops[i];
        g.flags.clear();
    }
    lctx->gops.clear();

    if (lctx->files)
       delete [] lctx->files;

    delete lctx;

    *ctx = NULL;
}

/* Parse the entire D2V index and build the GOP and frame lists. */
d2vcontext *d2vparse(const char *filename, string& err)
{
    string line;
    ifstream input;
    d2vcontext *ret;
    int i;

    ret = new d2vcontext;

    /* Zero the context to aid in conditional freeing later. */
    memset(ret, 0, sizeof(*ret));
    ret->stream_type   = UNSET;
    ret->ts_pid        = -1;
    ret->loc.startfile = -1;

#ifdef _WIN32
    wchar_t wide_filename[_MAX_PATH];

    i = MultiByteToWideChar(CP_UTF8, 0, filename, -1, wide_filename, ARRAYSIZE(wide_filename));
    if (!i) {
        err  = "D2V filename is invalid: ";
        err += filename;
        goto fail;
    }

    input.open(wide_filename);
#else
    input.open(filename);
#endif

    if (input.fail()) {
        err = "D2V cannot be opened.";
        goto fail;
    }

    /* Check the DGIndexProjectFile version. */
    d2vgetline(input, line);
    if (line.substr(18, 2) != D2V_VERSION) {
        err = "D2V Version is unsupported!";
        goto fail;
    }

    /* Get the number of files. */
    d2vgetline(input, line);
    ret->num_files = atoi(line.c_str());
    if (!ret->num_files) {
        err = "Invalid D2V File.";
        goto fail;
    }

    /* Allocate files array. */
    ret->files = new string[ret->num_files];

    /* Read them all in. */
    for(i = 0; i < ret->num_files; i++) {
        d2vgetline(input, line);
        if (line.length()) {
            ret->files[i] = d2vgetpath(filename, line);
        } else {
            err = "Invalid file set in D2V.";
            goto fail;
        }
    }

    /* Next line should be empty. */
    d2vgetline(input, line);
    if (line.length()) {
        err = "Invalid D2V structure.";
        goto fail;
    }

    /* Iterate over the D2V header and fill the context members. */
    d2vgetline(input, line);
    while(line.length()) {
        size_t mid = line.find("=");
        string l   = line.substr(0, mid);
        string r   = line.substr(mid + 1, line.length() - 1);

        if (l == "Stream_Type") {
            int type = atoi(r.c_str());

            ret->stream_type = streamtype_conv[type];
        } else if (l == "MPEG2_Transport_PID") {
            size_t pos = r.find(",");

            ret->ts_pid = strtoul(r.substr(0, pos).c_str(), NULL, 16);
        } else if (l == "MPEG_Type") {
            ret->mpeg_type = atoi(r.c_str());
        } else if (l == "iDCT_Algorithm") {
            int algo = atoi(r.c_str());

            ret->idct_algo = idct_algo_conv[algo];
        } else if (l == "YUVRGB_Scale") {
            int type = atoi(r.c_str());

            ret->yuvrgb_scale = scaletype_conv[type];
        } else if (l == "Picture_Size") {
            size_t pos = r.find("x");

            ret->width  = atoi(r.substr(      0, pos           ).c_str());
            ret->height = atoi(r.substr(pos + 1, r.length() - 1).c_str());
        } else if (l == "Frame_Rate") {
            size_t start = r.find("(") + 1;
            size_t sep   = r.find("/");
            size_t end   = r.find(")");

            ret->fps_num = atoi(r.substr(  start, sep).c_str());
            ret->fps_den = atoi(r.substr(sep + 1, end).c_str());
        } else if (l == "Location") {
            size_t pos1 = r.find(",");
            size_t pos2 = r.find(",", pos1 + 1);
            size_t pos3 = r.find(",", pos2 + 1);

            ret->loc.startfile   = strtoul(r.substr(       0, pos1          ).c_str(), NULL, 16);
            ret->loc.startoffset = strtoul(r.substr(pos1 + 1, pos2          ).c_str(), NULL, 16);
            ret->loc.endfile     = strtoul(r.substr(pos2 + 1, pos3          ).c_str(), NULL, 16);
            ret->loc.endoffset   = strtoul(r.substr(pos3 + 1, r.length() - 1).c_str(), NULL, 16);
        }

        d2vgetline(input, line);
    }

    /* Some basic validation of input headers. */
    if (ret->fps_num <= 0 || ret->fps_den <= 0) {
        err = "Invalid framerate in D2V header.";
        goto fail;
    } else if (ret->mpeg_type != 1 && ret->mpeg_type != 2) {
        err = "Invalid MPEG type in D2V header.";
        goto fail;
    } else if (ret->width <= 0 || ret->width <= 0) {
        err = "Invalid dimentions in D2V header.";
        goto fail;
    } else if (ret->stream_type == TRANSPORT && ret->ts_pid < 0) {
        err = "Invalid PID in D2V header.";
        goto fail;
    } else if (ret->stream_type == UNSET) {
        err = "Invalid stream type in D2V header.";
        goto fail;
    } else if (ret->loc.startfile < 0 || ret->loc.startoffset < ret->loc.startfile ||
               ret->loc.endfile < ret->loc.startoffset || ret->loc.endoffset > ret->loc.endfile) {
        err = "Invalid location in D2V header.";
        goto fail;
    }

    /* Read in all GOPs. */
    i = 0;
    d2vgetline(input, line);
    while(line.length()) {
        string tok;
        istringstream ss(line);
        int offset;
        gop cur_gop;

        ss >> hex >> cur_gop.info;
        ss >> dec >> cur_gop.matrix;
        ss >> dec >> cur_gop.file;
        ss >> dec >> cur_gop.pos;
        ss >> dec >> cur_gop.skip;
        ss >> dec >> cur_gop.vob;
        ss >> dec >> cur_gop.cell;

        offset = 0;
        while(!ss.eof()) {
            uint16_t flags;
            frame f;

            ss >> hex >> flags;

            /*
             * We have to use a 16-bit int to force the stringstream to
             * read more than one character, so double check its size.
             */
            assert(flags <= 0xFF);

            cur_gop.flags.push_back((uint8_t) flags);

            f.gop    = i;
            f.offset = offset;
            ret->frames.push_back(f);

            offset++;
        }

        /* The last flag is always 'ff' to signify the end of the stream. */
        if (cur_gop.flags.back() == 0xFF) {
            cur_gop.flags.pop_back();
            ret->frames.pop_back();
        }

        ret->gops.push_back(cur_gop);
        i++;

        d2vgetline(input, line);
    }

    input.close();

    if (!ret->frames.size() || !ret->gops.size()) {
        err = "No frames in D2V file!";
        goto fail;
    }

    return ret;

fail:
    d2vfreep(&ret);
    return NULL;
}
<commit_msg>d2v: Fix dimensions check<commit_after>/*
 * VapourSynth D2V Plugin
 *
 * Copyright (c) 2012 Derek Buitenhuis
 *
 * This file is part of d2vsource.
 *
 * d2vsource is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * d2vsource 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 d2vsource; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>

extern "C" {
#include <assert.h>
#include <stdlib.h>
#include <string.h>
}

#include "compat.hpp"
#include "d2v.hpp"
#include "gop.hpp"

#ifdef _WIN32
#include <windows.h>
#endif

using namespace std;

string d2vgetpath(const char *d2v_path, const string& file)
{
    string path;
    string d2v       = d2v_path;
    size_t delim_pos = d2v.rfind(PATH_DELIM) + 1;

    if ((file.substr(0, 1) == "/" || file.substr(1, 1) == ":") || (d2v.substr(0, 1) != "/" && d2v.substr(1, 1) != ":")) {
        path = file;
    } else {
        path  = d2v.substr(0, delim_pos);
        path += file;
    }

    return path;
}

/* Conditionally free all memebers of d2vcontext. */
void d2vfreep(d2vcontext **ctx)
{
    d2vcontext *lctx = *ctx;
    unsigned int i;

    if (!lctx)
        return;

    lctx->frames.clear();
    for(i = 0; i < lctx->gops.size(); i++) {
        gop g = lctx->gops[i];
        g.flags.clear();
    }
    lctx->gops.clear();

    if (lctx->files)
       delete [] lctx->files;

    delete lctx;

    *ctx = NULL;
}

/* Parse the entire D2V index and build the GOP and frame lists. */
d2vcontext *d2vparse(const char *filename, string& err)
{
    string line;
    ifstream input;
    d2vcontext *ret;
    int i;

    ret = new d2vcontext;

    /* Zero the context to aid in conditional freeing later. */
    memset(ret, 0, sizeof(*ret));
    ret->stream_type   = UNSET;
    ret->ts_pid        = -1;
    ret->loc.startfile = -1;

#ifdef _WIN32
    wchar_t wide_filename[_MAX_PATH];

    i = MultiByteToWideChar(CP_UTF8, 0, filename, -1, wide_filename, ARRAYSIZE(wide_filename));
    if (!i) {
        err  = "D2V filename is invalid: ";
        err += filename;
        goto fail;
    }

    input.open(wide_filename);
#else
    input.open(filename);
#endif

    if (input.fail()) {
        err = "D2V cannot be opened.";
        goto fail;
    }

    /* Check the DGIndexProjectFile version. */
    d2vgetline(input, line);
    if (line.substr(18, 2) != D2V_VERSION) {
        err = "D2V Version is unsupported!";
        goto fail;
    }

    /* Get the number of files. */
    d2vgetline(input, line);
    ret->num_files = atoi(line.c_str());
    if (!ret->num_files) {
        err = "Invalid D2V File.";
        goto fail;
    }

    /* Allocate files array. */
    ret->files = new string[ret->num_files];

    /* Read them all in. */
    for(i = 0; i < ret->num_files; i++) {
        d2vgetline(input, line);
        if (line.length()) {
            ret->files[i] = d2vgetpath(filename, line);
        } else {
            err = "Invalid file set in D2V.";
            goto fail;
        }
    }

    /* Next line should be empty. */
    d2vgetline(input, line);
    if (line.length()) {
        err = "Invalid D2V structure.";
        goto fail;
    }

    /* Iterate over the D2V header and fill the context members. */
    d2vgetline(input, line);
    while(line.length()) {
        size_t mid = line.find("=");
        string l   = line.substr(0, mid);
        string r   = line.substr(mid + 1, line.length() - 1);

        if (l == "Stream_Type") {
            int type = atoi(r.c_str());

            ret->stream_type = streamtype_conv[type];
        } else if (l == "MPEG2_Transport_PID") {
            size_t pos = r.find(",");

            ret->ts_pid = strtoul(r.substr(0, pos).c_str(), NULL, 16);
        } else if (l == "MPEG_Type") {
            ret->mpeg_type = atoi(r.c_str());
        } else if (l == "iDCT_Algorithm") {
            int algo = atoi(r.c_str());

            ret->idct_algo = idct_algo_conv[algo];
        } else if (l == "YUVRGB_Scale") {
            int type = atoi(r.c_str());

            ret->yuvrgb_scale = scaletype_conv[type];
        } else if (l == "Picture_Size") {
            size_t pos = r.find("x");

            ret->width  = atoi(r.substr(      0, pos           ).c_str());
            ret->height = atoi(r.substr(pos + 1, r.length() - 1).c_str());
        } else if (l == "Frame_Rate") {
            size_t start = r.find("(") + 1;
            size_t sep   = r.find("/");
            size_t end   = r.find(")");

            ret->fps_num = atoi(r.substr(  start, sep).c_str());
            ret->fps_den = atoi(r.substr(sep + 1, end).c_str());
        } else if (l == "Location") {
            size_t pos1 = r.find(",");
            size_t pos2 = r.find(",", pos1 + 1);
            size_t pos3 = r.find(",", pos2 + 1);

            ret->loc.startfile   = strtoul(r.substr(       0, pos1          ).c_str(), NULL, 16);
            ret->loc.startoffset = strtoul(r.substr(pos1 + 1, pos2          ).c_str(), NULL, 16);
            ret->loc.endfile     = strtoul(r.substr(pos2 + 1, pos3          ).c_str(), NULL, 16);
            ret->loc.endoffset   = strtoul(r.substr(pos3 + 1, r.length() - 1).c_str(), NULL, 16);
        }

        d2vgetline(input, line);
    }

    /* Some basic validation of input headers. */
    if (ret->fps_num <= 0 || ret->fps_den <= 0) {
        err = "Invalid framerate in D2V header.";
        goto fail;
    } else if (ret->mpeg_type != 1 && ret->mpeg_type != 2) {
        err = "Invalid MPEG type in D2V header.";
        goto fail;
    } else if (ret->width <= 0 || ret->height <= 0) {
        err = "Invalid dimensions in D2V header.";
        goto fail;
    } else if (ret->stream_type == TRANSPORT && ret->ts_pid < 0) {
        err = "Invalid PID in D2V header.";
        goto fail;
    } else if (ret->stream_type == UNSET) {
        err = "Invalid stream type in D2V header.";
        goto fail;
    } else if (ret->loc.startfile < 0 || ret->loc.startoffset < ret->loc.startfile ||
               ret->loc.endfile < ret->loc.startoffset || ret->loc.endoffset > ret->loc.endfile) {
        err = "Invalid location in D2V header.";
        goto fail;
    }

    /* Read in all GOPs. */
    i = 0;
    d2vgetline(input, line);
    while(line.length()) {
        string tok;
        istringstream ss(line);
        int offset;
        gop cur_gop;

        ss >> hex >> cur_gop.info;
        ss >> dec >> cur_gop.matrix;
        ss >> dec >> cur_gop.file;
        ss >> dec >> cur_gop.pos;
        ss >> dec >> cur_gop.skip;
        ss >> dec >> cur_gop.vob;
        ss >> dec >> cur_gop.cell;

        offset = 0;
        while(!ss.eof()) {
            uint16_t flags;
            frame f;

            ss >> hex >> flags;

            /*
             * We have to use a 16-bit int to force the stringstream to
             * read more than one character, so double check its size.
             */
            assert(flags <= 0xFF);

            cur_gop.flags.push_back((uint8_t) flags);

            f.gop    = i;
            f.offset = offset;
            ret->frames.push_back(f);

            offset++;
        }

        /* The last flag is always 'ff' to signify the end of the stream. */
        if (cur_gop.flags.back() == 0xFF) {
            cur_gop.flags.pop_back();
            ret->frames.pop_back();
        }

        ret->gops.push_back(cur_gop);
        i++;

        d2vgetline(input, line);
    }

    input.close();

    if (!ret->frames.size() || !ret->gops.size()) {
        err = "No frames in D2V file!";
        goto fail;
    }

    return ret;

fail:
    d2vfreep(&ret);
    return NULL;
}
<|endoftext|>


<commit_before>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "NamespaceOptions.hxx"
#include "Config.hxx"
#include "mount_list.hxx"
#include "pool.hxx"
#include "system/pivot_root.h"
#include "system/bind_mount.h"
#include "pexpand.hxx"

#include <assert.h>
#include <sched.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/prctl.h>

#ifndef __linux
#error This library requires Linux
#endif

NamespaceOptions::NamespaceOptions(struct pool *pool,
                                   const NamespaceOptions &src)
    :enable_user(src.enable_user),
     enable_pid(src.enable_pid),
     enable_network(src.enable_network),
     enable_ipc(src.enable_ipc),
     enable_mount(src.enable_mount),
     mount_proc(src.mount_proc),
     pivot_root(p_strdup_checked(pool, src.pivot_root)),
     home(p_strdup_checked(pool, src.home)),
     expand_home(p_strdup_checked(pool, src.expand_home)),
     mount_home(p_strdup_checked(pool, src.mount_home)),
     mount_tmp_tmpfs(p_strdup_checked(pool, src.mount_tmp_tmpfs)),
     mount_tmpfs(p_strdup_checked(pool, src.mount_tmpfs)),
     mounts(MountList::CloneAll(*pool, src.mounts)),
     hostname(p_strdup_checked(pool, src.hostname))
{
}

void
NamespaceOptions::Init()
{
    enable_user = false;
    enable_pid = false;
    enable_network = false;
    enable_ipc = false;
    enable_mount = false;
    mount_proc = false;
    pivot_root = nullptr;
    home = nullptr;
    expand_home = nullptr;
    mount_home = nullptr;
    mount_tmp_tmpfs = nullptr;
    mount_tmpfs = nullptr;
    mounts = nullptr;
    hostname = nullptr;
}

void
NamespaceOptions::CopyFrom(struct pool &pool, const NamespaceOptions &src)
{
    *this = src;

    pivot_root = p_strdup_checked(&pool, src.pivot_root);
    home = p_strdup_checked(&pool, src.home);
    expand_home = p_strdup_checked(&pool, src.expand_home);
    mount_home = p_strdup_checked(&pool, src.mount_home);
    mount_tmpfs = p_strdup_checked(&pool, src.mount_tmpfs);
    mounts = MountList::CloneAll(pool, src.mounts);
    hostname = p_strdup_checked(&pool, src.hostname);
}

bool
NamespaceOptions::IsExpandable() const
{
    return expand_home != nullptr || MountList::IsAnyExpandable(mounts);
}

bool
NamespaceOptions::Expand(struct pool &pool, const MatchInfo &match_info,
                         Error &error_r)
{
    if (expand_home != nullptr) {
        home = expand_string_unescaped(&pool, expand_home, match_info,
                                       error_r);
        if (home == nullptr)
            return false;
    }

    return MountList::ExpandAll(pool, mounts, match_info, error_r);
}

int
NamespaceOptions::GetCloneFlags(const SpawnConfig &config, int flags) const
{
    // TODO: rewrite the namespace_superuser workaround
    if (enable_user && !config.ignore_userns)
        flags |= CLONE_NEWUSER;
    if (enable_pid)
        flags |= CLONE_NEWPID;
    if (enable_network)
        flags |= CLONE_NEWNET;
    if (enable_ipc)
        flags |= CLONE_NEWIPC;
    if (enable_mount)
        flags |= CLONE_NEWNS;
    if (hostname != nullptr)
        flags |= CLONE_NEWUTS;

    return flags;
}

static void
write_file(const char *path, const char *data)
{
    int fd = open(path, O_WRONLY|O_CLOEXEC);
    if (fd < 0) {
        fprintf(stderr, "open('%s') failed: %s\n",
                path, strerror(errno));
        _exit(2);
    }

    if (write(fd, data, strlen(data)) < 0) {
        fprintf(stderr, "write('%s') failed: %s\n",
                path, strerror(errno));
        _exit(2);
    }

    close(fd);
}

static bool
try_write_file(const char *path, const char *data)
{
    int fd = open(path, O_WRONLY|O_CLOEXEC);
    if (fd < 0)
        return false;

    if (write(fd, data, strlen(data)) < 0)
        return false;

    close(fd);
    return true;
}

static void
setup_uid_map(int uid)
{
    char buffer[64];
    sprintf(buffer, "%d %d 1", uid, uid);
    write_file("/proc/self/uid_map", buffer);
}

static void
setup_gid_map(int gid)
{
    char buffer[64];
    sprintf(buffer, "%d %d 1", gid, gid);
    write_file("/proc/self/gid_map", buffer);
}

/**
 * Write "deny" to /proc/self/setgroups which is necessary for
 * unprivileged processes to set up a gid_map.  See Linux commits
 * 9cc4651 and 66d2f33 for details.
 */
static void
deny_setgroups()
{
    try_write_file("/proc/self/setgroups", "deny");
}

void
NamespaceOptions::Setup(const SpawnConfig &config) const
{
    /* set up UID/GID mapping in the old /proc */
    if (enable_user && !config.ignore_userns) {
        // TODO: rewrite the namespace_superuser workaround
        deny_setgroups();
        setup_gid_map(config.default_uid_gid.gid);
        setup_uid_map(config.default_uid_gid.uid);
    }

    if (enable_mount)
        /* convert all "shared" mounts to "private" mounts */
        mount(nullptr, "/", nullptr, MS_PRIVATE|MS_REC, nullptr);

    const char *const new_root = pivot_root;
    const char *const put_old = "mnt";

    if (new_root != nullptr) {
        /* first bind-mount the new root onto itself to "unlock" the
           kernel's mount object (flag MNT_LOCKED) in our namespace;
           without this, the kernel would not allow an unprivileged
           process to pivot_root to it */
        bind_mount(new_root, new_root, MS_NOSUID|MS_RDONLY);

        /* release a reference to the old root */
        if (chdir(new_root) < 0) {
            fprintf(stderr, "chdir('%s') failed: %s\n",
                    new_root, strerror(errno));
            _exit(2);
        }

        /* enter the new root */
        int result = my_pivot_root(new_root, put_old);
        if (result < 0) {
            fprintf(stderr, "pivot_root('%s') failed: %s\n",
                    new_root, strerror(-result));
            _exit(2);
        }
    }

    if (mount_proc &&
        mount("none", "/proc", "proc", MS_NOEXEC|MS_NOSUID|MS_NODEV|MS_RDONLY, nullptr) < 0) {
        fprintf(stderr, "mount('/proc') failed: %s\n",
                strerror(errno));
        _exit(2);
    }

    if (mount_home != nullptr || mounts != nullptr) {
        /* go to /mnt so we can refer to the old directories with a
           relative path */

        const char *path = new_root != nullptr ? "/mnt" : "/";

        if (chdir(path) < 0) {
            fprintf(stderr, "chdir('%s') failed: %s\n", path, strerror(errno));
            _exit(2);
        }
    }

    if (mount_home != nullptr) {
        assert(home != nullptr);
        assert(*home == '/');

        bind_mount(home + 1, mount_home, MS_NOSUID|MS_NODEV);
    }

    MountList::ApplyAll(mounts);

    if (new_root != nullptr && (mount_home != nullptr || mounts != nullptr)) {
        /* back to the new root */
        if (chdir("/") < 0) {
            fprintf(stderr, "chdir('/') failed: %s\n", strerror(errno));
            _exit(2);
        }
    }

    if (new_root != nullptr) {
        /* get rid of the old root */
        if (umount2(put_old, MNT_DETACH) < 0) {
            fprintf(stderr, "umount('%s') failed: %s",
                    put_old, strerror(errno));
            _exit(2);
        }
    }

    if (mount_tmpfs != nullptr &&
        mount("none", mount_tmpfs, "tmpfs", MS_NODEV|MS_NOEXEC|MS_NOSUID,
              "size=16M,nr_inodes=256,mode=700") < 0) {
        fprintf(stderr, "mount(tmpfs, '%s') failed: %s\n",
                mount_tmpfs, strerror(errno));
        _exit(2);
    }

    if (mount_tmp_tmpfs != nullptr) {
        const char *options = "size=16M,nr_inodes=256,mode=1777";
        char buffer[256];
        if (*mount_tmp_tmpfs != 0) {
            snprintf(buffer, sizeof(buffer), "%s,%s", options, mount_tmp_tmpfs);
            options = buffer;
        }

        if (mount("none", "/tmp", "tmpfs", MS_NODEV|MS_NOEXEC|MS_NOSUID,
                  options) < 0) {
            fprintf(stderr, "mount('/tmp') failed: %s\n",
                    strerror(errno));
            _exit(2);
        }
    }

    if (hostname != nullptr &&
        sethostname(hostname, strlen(hostname)) < 0) {
        fprintf(stderr, "sethostname() failed: %s", strerror(errno));
        _exit(2);
    }
}

char *
NamespaceOptions::MakeId(char *p) const
{
    if (enable_user)
        p = (char *)mempcpy(p, ";uns", 4);

    if (enable_pid)
        p = (char *)mempcpy(p, ";pns", 4);

    if (enable_network)
        p = (char *)mempcpy(p, ";nns", 4);

    if (enable_ipc)
        p = (char *)mempcpy(p, ";ins", 4);

    if (enable_mount) {
        p = (char *)(char *)mempcpy(p, ";mns", 4);

        if (pivot_root != nullptr) {
            p = (char *)mempcpy(p, ";pvr=", 5);
            p = stpcpy(p, pivot_root);
        }

        if (mount_proc)
            p = (char *)mempcpy(p, ";proc", 5);

        if (mount_proc)
            p = (char *)mempcpy(p, ";tmpfs", 6);

        if (mount_home != nullptr) {
            p = (char *)mempcpy(p, ";h:", 3);
            p = stpcpy(p, home);
            *p++ = '=';
            p = stpcpy(p, mount_home);
        }

        if (mount_tmpfs != nullptr) {
            p = (char *)mempcpy(p, ";t:", 3);
            p = stpcpy(p, mount_tmpfs);
        }
    }

    if (hostname != nullptr) {
        p = (char *)mempcpy(p, ";uts=", 5);
        p = stpcpy(p, hostname);
    }

    return p;
}
<commit_msg>spawn/NamespaceOptions: fix mount_tmp_tmpfs use in MakeId()<commit_after>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "NamespaceOptions.hxx"
#include "Config.hxx"
#include "mount_list.hxx"
#include "pool.hxx"
#include "system/pivot_root.h"
#include "system/bind_mount.h"
#include "pexpand.hxx"

#include <assert.h>
#include <sched.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/prctl.h>

#ifndef __linux
#error This library requires Linux
#endif

NamespaceOptions::NamespaceOptions(struct pool *pool,
                                   const NamespaceOptions &src)
    :enable_user(src.enable_user),
     enable_pid(src.enable_pid),
     enable_network(src.enable_network),
     enable_ipc(src.enable_ipc),
     enable_mount(src.enable_mount),
     mount_proc(src.mount_proc),
     pivot_root(p_strdup_checked(pool, src.pivot_root)),
     home(p_strdup_checked(pool, src.home)),
     expand_home(p_strdup_checked(pool, src.expand_home)),
     mount_home(p_strdup_checked(pool, src.mount_home)),
     mount_tmp_tmpfs(p_strdup_checked(pool, src.mount_tmp_tmpfs)),
     mount_tmpfs(p_strdup_checked(pool, src.mount_tmpfs)),
     mounts(MountList::CloneAll(*pool, src.mounts)),
     hostname(p_strdup_checked(pool, src.hostname))
{
}

void
NamespaceOptions::Init()
{
    enable_user = false;
    enable_pid = false;
    enable_network = false;
    enable_ipc = false;
    enable_mount = false;
    mount_proc = false;
    pivot_root = nullptr;
    home = nullptr;
    expand_home = nullptr;
    mount_home = nullptr;
    mount_tmp_tmpfs = nullptr;
    mount_tmpfs = nullptr;
    mounts = nullptr;
    hostname = nullptr;
}

void
NamespaceOptions::CopyFrom(struct pool &pool, const NamespaceOptions &src)
{
    *this = src;

    pivot_root = p_strdup_checked(&pool, src.pivot_root);
    home = p_strdup_checked(&pool, src.home);
    expand_home = p_strdup_checked(&pool, src.expand_home);
    mount_home = p_strdup_checked(&pool, src.mount_home);
    mount_tmpfs = p_strdup_checked(&pool, src.mount_tmpfs);
    mounts = MountList::CloneAll(pool, src.mounts);
    hostname = p_strdup_checked(&pool, src.hostname);
}

bool
NamespaceOptions::IsExpandable() const
{
    return expand_home != nullptr || MountList::IsAnyExpandable(mounts);
}

bool
NamespaceOptions::Expand(struct pool &pool, const MatchInfo &match_info,
                         Error &error_r)
{
    if (expand_home != nullptr) {
        home = expand_string_unescaped(&pool, expand_home, match_info,
                                       error_r);
        if (home == nullptr)
            return false;
    }

    return MountList::ExpandAll(pool, mounts, match_info, error_r);
}

int
NamespaceOptions::GetCloneFlags(const SpawnConfig &config, int flags) const
{
    // TODO: rewrite the namespace_superuser workaround
    if (enable_user && !config.ignore_userns)
        flags |= CLONE_NEWUSER;
    if (enable_pid)
        flags |= CLONE_NEWPID;
    if (enable_network)
        flags |= CLONE_NEWNET;
    if (enable_ipc)
        flags |= CLONE_NEWIPC;
    if (enable_mount)
        flags |= CLONE_NEWNS;
    if (hostname != nullptr)
        flags |= CLONE_NEWUTS;

    return flags;
}

static void
write_file(const char *path, const char *data)
{
    int fd = open(path, O_WRONLY|O_CLOEXEC);
    if (fd < 0) {
        fprintf(stderr, "open('%s') failed: %s\n",
                path, strerror(errno));
        _exit(2);
    }

    if (write(fd, data, strlen(data)) < 0) {
        fprintf(stderr, "write('%s') failed: %s\n",
                path, strerror(errno));
        _exit(2);
    }

    close(fd);
}

static bool
try_write_file(const char *path, const char *data)
{
    int fd = open(path, O_WRONLY|O_CLOEXEC);
    if (fd < 0)
        return false;

    if (write(fd, data, strlen(data)) < 0)
        return false;

    close(fd);
    return true;
}

static void
setup_uid_map(int uid)
{
    char buffer[64];
    sprintf(buffer, "%d %d 1", uid, uid);
    write_file("/proc/self/uid_map", buffer);
}

static void
setup_gid_map(int gid)
{
    char buffer[64];
    sprintf(buffer, "%d %d 1", gid, gid);
    write_file("/proc/self/gid_map", buffer);
}

/**
 * Write "deny" to /proc/self/setgroups which is necessary for
 * unprivileged processes to set up a gid_map.  See Linux commits
 * 9cc4651 and 66d2f33 for details.
 */
static void
deny_setgroups()
{
    try_write_file("/proc/self/setgroups", "deny");
}

void
NamespaceOptions::Setup(const SpawnConfig &config) const
{
    /* set up UID/GID mapping in the old /proc */
    if (enable_user && !config.ignore_userns) {
        // TODO: rewrite the namespace_superuser workaround
        deny_setgroups();
        setup_gid_map(config.default_uid_gid.gid);
        setup_uid_map(config.default_uid_gid.uid);
    }

    if (enable_mount)
        /* convert all "shared" mounts to "private" mounts */
        mount(nullptr, "/", nullptr, MS_PRIVATE|MS_REC, nullptr);

    const char *const new_root = pivot_root;
    const char *const put_old = "mnt";

    if (new_root != nullptr) {
        /* first bind-mount the new root onto itself to "unlock" the
           kernel's mount object (flag MNT_LOCKED) in our namespace;
           without this, the kernel would not allow an unprivileged
           process to pivot_root to it */
        bind_mount(new_root, new_root, MS_NOSUID|MS_RDONLY);

        /* release a reference to the old root */
        if (chdir(new_root) < 0) {
            fprintf(stderr, "chdir('%s') failed: %s\n",
                    new_root, strerror(errno));
            _exit(2);
        }

        /* enter the new root */
        int result = my_pivot_root(new_root, put_old);
        if (result < 0) {
            fprintf(stderr, "pivot_root('%s') failed: %s\n",
                    new_root, strerror(-result));
            _exit(2);
        }
    }

    if (mount_proc &&
        mount("none", "/proc", "proc", MS_NOEXEC|MS_NOSUID|MS_NODEV|MS_RDONLY, nullptr) < 0) {
        fprintf(stderr, "mount('/proc') failed: %s\n",
                strerror(errno));
        _exit(2);
    }

    if (mount_home != nullptr || mounts != nullptr) {
        /* go to /mnt so we can refer to the old directories with a
           relative path */

        const char *path = new_root != nullptr ? "/mnt" : "/";

        if (chdir(path) < 0) {
            fprintf(stderr, "chdir('%s') failed: %s\n", path, strerror(errno));
            _exit(2);
        }
    }

    if (mount_home != nullptr) {
        assert(home != nullptr);
        assert(*home == '/');

        bind_mount(home + 1, mount_home, MS_NOSUID|MS_NODEV);
    }

    MountList::ApplyAll(mounts);

    if (new_root != nullptr && (mount_home != nullptr || mounts != nullptr)) {
        /* back to the new root */
        if (chdir("/") < 0) {
            fprintf(stderr, "chdir('/') failed: %s\n", strerror(errno));
            _exit(2);
        }
    }

    if (new_root != nullptr) {
        /* get rid of the old root */
        if (umount2(put_old, MNT_DETACH) < 0) {
            fprintf(stderr, "umount('%s') failed: %s",
                    put_old, strerror(errno));
            _exit(2);
        }
    }

    if (mount_tmpfs != nullptr &&
        mount("none", mount_tmpfs, "tmpfs", MS_NODEV|MS_NOEXEC|MS_NOSUID,
              "size=16M,nr_inodes=256,mode=700") < 0) {
        fprintf(stderr, "mount(tmpfs, '%s') failed: %s\n",
                mount_tmpfs, strerror(errno));
        _exit(2);
    }

    if (mount_tmp_tmpfs != nullptr) {
        const char *options = "size=16M,nr_inodes=256,mode=1777";
        char buffer[256];
        if (*mount_tmp_tmpfs != 0) {
            snprintf(buffer, sizeof(buffer), "%s,%s", options, mount_tmp_tmpfs);
            options = buffer;
        }

        if (mount("none", "/tmp", "tmpfs", MS_NODEV|MS_NOEXEC|MS_NOSUID,
                  options) < 0) {
            fprintf(stderr, "mount('/tmp') failed: %s\n",
                    strerror(errno));
            _exit(2);
        }
    }

    if (hostname != nullptr &&
        sethostname(hostname, strlen(hostname)) < 0) {
        fprintf(stderr, "sethostname() failed: %s", strerror(errno));
        _exit(2);
    }
}

char *
NamespaceOptions::MakeId(char *p) const
{
    if (enable_user)
        p = (char *)mempcpy(p, ";uns", 4);

    if (enable_pid)
        p = (char *)mempcpy(p, ";pns", 4);

    if (enable_network)
        p = (char *)mempcpy(p, ";nns", 4);

    if (enable_ipc)
        p = (char *)mempcpy(p, ";ins", 4);

    if (enable_mount) {
        p = (char *)(char *)mempcpy(p, ";mns", 4);

        if (pivot_root != nullptr) {
            p = (char *)mempcpy(p, ";pvr=", 5);
            p = stpcpy(p, pivot_root);
        }

        if (mount_proc)
            p = (char *)mempcpy(p, ";proc", 5);

        if (mount_home != nullptr) {
            p = (char *)mempcpy(p, ";h:", 3);
            p = stpcpy(p, home);
            *p++ = '=';
            p = stpcpy(p, mount_home);
        }

        if (mount_tmp_tmpfs != nullptr) {
            p = (char *)mempcpy(p, ";tt:", 3);
            p = stpcpy(p, mount_tmp_tmpfs);
        }

        if (mount_tmpfs != nullptr) {
            p = (char *)mempcpy(p, ";t:", 3);
            p = stpcpy(p, mount_tmpfs);
        }
    }

    if (hostname != nullptr) {
        p = (char *)mempcpy(p, ";uts=", 5);
        p = stpcpy(p, hostname);
    }

    return p;
}
<|endoftext|>


<commit_before>#pragma once
//=====================================================================//
/*!	@file
	@brief	PicoJPEG 画像クラス
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/glfw3_app/blob/master/LICENSE
*/
//=====================================================================//
#include <cstdio>
#include <cstdlib>
#include "graphics/img.hpp"
#include "graphics/picojpeg.h"
#include "common/file_io.hpp"
#include "common/format.hpp"

namespace img {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	PicoJPEG 形式ファイルクラス
		@param[in]	RENDER	描画ファンクタ
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	template <class RENDER>
	class picojpeg_in {

		RENDER&		render_;

		pjpeg_image_info_t	image_info_;

		uint8_t		status_;
		bool		reduce_;

		int16_t		width_;
		int16_t		height_;
		int16_t		ofs_x_;
		int16_t		ofs_y_;

		struct data_t {
			utils::file_io&	fin_;
			uint32_t		file_ofs_;
			uint32_t		file_size_;
			data_t(utils::file_io& fin) : fin_(fin), file_ofs_(0), file_size_(0) { }
		};

		void render_rgb_(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b)
		{
			auto c = RENDER::COLOR::rgb(r, g, b);
			render_.plot(ofs_x_ + x, ofs_y_ + y, c);
		}

		static uint8_t pjpeg_callback_(uint8_t* dst, uint8_t size, uint8_t* acr, void *pdata)
		{
			data_t* t = static_cast<data_t*>(pdata);
			auto len = std::min(t->file_size_ - t->file_ofs_, static_cast<uint32_t>(size));
			auto rl = t->fin_.read(dst, len);
			// utils::format("Read: %d (%d)\n") % len % rl;
			*acr = rl;
			t->file_ofs_ += rl;
			return 0;
		}

		bool pixel_(utils::file_io& fin) noexcept
		{
			int16_t xt = 0;
			int16_t yt = 0;
			while((status_ = pjpeg_decode_mcu()) == 0) {

				if(reduce_) {
					auto row_blocks = image_info_.m_MCUWidth  >> 3;
					auto col_blocks = image_info_.m_MCUHeight >> 3;
					int16_t xx = xt * row_blocks + ofs_x_;
					int16_t yy = yt * col_blocks + ofs_y_;
					if(image_info_.m_scanType == PJPG_GRAYSCALE) {
						auto gs = image_info_.m_pMCUBufR[0];
						render_rgb_(xx, yy, gs, gs, gs);
					} else {
						for(int16_t y = 0; y < col_blocks; ++y) {
							auto ofs = y * 128;
							for(int16_t x = 0; x < row_blocks; ++x) {
								auto r = image_info_.m_pMCUBufR[ofs];
								auto g = image_info_.m_pMCUBufG[ofs];
								auto b = image_info_.m_pMCUBufB[ofs];
								ofs += 64;
								render_rgb_(xx + x, yy + y, r, g, b);
							}
						}
					}
				} else {
					for(int16_t y = 0; y < image_info_.m_MCUHeight; y += 8) {
						auto yy = yt * image_info_.m_MCUHeight + ofs_y_;
						auto by_limit = std::min(8,
							image_info_.m_height - (yt * image_info_.m_MCUHeight + y));
						for(int16_t x = 0; x < image_info_.m_MCUWidth; x += 8) {
							auto bx_limit = std::min(8,
								image_info_.m_width - (xt * image_info_.m_MCUWidth + x));
							auto xx = xt * image_info_.m_MCUWidth + ofs_x_;
							auto ofs = (x * 8) + (y * 16);
							if(image_info_.m_scanType == PJPG_GRAYSCALE) {
								const uint8_t* pGS = image_info_.m_pMCUBufR + ofs;
								for(int16_t by = 0; by < by_limit; ++by) {
									for(int16_t bx = 0; bx < bx_limit; ++bx) {
										auto gs = *pGS++;
										render_rgb_(xx + bx, yy + by, gs, gs, gs);
									}
									pGS += (8 - bx_limit);
								}
							} else {
								const uint8_t* pR = image_info_.m_pMCUBufR + ofs;
								const uint8_t* pG = image_info_.m_pMCUBufG + ofs;
								const uint8_t* pB = image_info_.m_pMCUBufB + ofs;
								for(int16_t by = 0; by < by_limit; ++by) {
									for(int16_t bx = 0; bx < bx_limit; ++bx) {
										auto r = *pR++;
										auto g = *pG++;
										auto b = *pB++;
										render_rgb_(xx + bx, yy + by, r, g, b);
									}
									pR += (8 - bx_limit);
									pG += (8 - bx_limit);
									pB += (8 - bx_limit);
								}
							}
						}
					}
				}
				++xt;
				if(xt == image_info_.m_MCUSPerRow) {
					xt = 0;
					++yt;
				}
			}
			if(status_ != PJPG_NO_MORE_BLOCKS) {
				utils::format("pjpeg_decode_mcu() failed with status: %d\n") % status_;
				return false;
			}
			return true;
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
		*/
		//-----------------------------------------------------------------//
		picojpeg_in(RENDER& render) noexcept : render_(render),
			image_info_(),
			status_(0), reduce_(false),
			width_(0), height_(0), ofs_x_(0), ofs_y_(0)
		{ }

		//-----------------------------------------------------------------//
		/*!
			@brief	ステータスの取得（Error code）
			@return ステータス
		*/
		//-----------------------------------------------------------------//
		uint8_t get_status() const noexcept { return status_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	描画オフセットの設定
			@param[in]	x	X 軸オフセット
			@param[in]	y	Y 軸オフセット
		*/
		//-----------------------------------------------------------------//
		void set_draw_offset(int16_t x = 0, int16_t y = 0) noexcept
		{
			ofs_x_ = x;
			ofs_y_ = y;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	JPEG ファイルか確認する
			@param[in]	fin	file_io クラス
			@return エラーなら「false」を返す
		*/
		//-----------------------------------------------------------------//
		bool probe(utils::file_io& fin) noexcept
		{
			uint8_t sig[2];
			uint32_t pos = fin.tell();
			uint32_t l = fin.read(sig, 2);
			fin.seek(utils::file_io::SEEK::SET, pos);
			if(l == 2) {
				if(sig[0] == 0xff && sig[1] == 0xd8) {  // SOI Marker, Start image
					return true;
				}
			}
//			utils::format("Not JPEG file\n");
			return false;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	画像ファイルの情報を取得する
			@param[in]	fin	file_io クラス
			@param[in]	fo	情報を受け取る構造体
			@return エラーなら「false」を返す
		*/
		//-----------------------------------------------------------------//
		bool info(utils::file_io& fin, img::img_info& fo) noexcept
		{
			auto pos = fin.tell();

			data_t t(fin);
			t.file_ofs_  = 0;
			t.file_size_ = fin.get_file_size();
			status_ = pjpeg_decode_init(&image_info_, pjpeg_callback_, &t, false);
			if(status_) {
				fin.close();
				return false;
			}

			fin.seek(utils::file_io::SEEK::SET, pos);

			fo.width  = image_info_.m_width;
			fo.height = image_info_.m_height;
			switch(image_info_.m_comps) {
			case 1:
				fo.grayscale = true;
				fo.i_depth = 0;
				fo.r_depth = 8;
				fo.g_depth = 8;
				fo.b_depth = 8;
				fo.a_depth = 0;
				fo.clut_num = 0;
				break;
			case 3:
				fo.grayscale = false;
				fo.i_depth = 0;
				fo.r_depth = 8;
				fo.g_depth = 8;
				fo.b_depth = 8;
				fo.a_depth = 0;
				fo.clut_num = 0;
				break;
			case 4:
				fo.grayscale = false;
				fo.i_depth = 0;
				fo.r_depth = 8;
				fo.g_depth = 8;
				fo.b_depth = 8;
				fo.a_depth = 8;
				fo.clut_num = 0;
				break;
			default:
				fo.i_depth = 0;
				fo.r_depth = 0;
				fo.g_depth = 0;
				fo.b_depth = 0;
				fo.a_depth = 0;
				fo.clut_num = 0;
				return false;
				break;
			}
			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	JPEG ファイル、ロード
			@param[in]	fin	file_io クラス
			@param[in]	opt	フォーマット固有の設定文字列
			@return エラーなら「false」を返す
		*/
		//-----------------------------------------------------------------//
		bool load(utils::file_io& fin, const char* opt = nullptr)
		{
			// とりあえず、ヘッダーの検査
			if(!probe(fin)) {
				return false;
			}

			reduce_ = false;

			data_t t(fin);
			t.file_ofs_  = 0;
			t.file_size_ = fin.get_file_size();
			status_ = pjpeg_decode_init(&image_info_, pjpeg_callback_, &t, reduce_);
			if(status_) {
				if(status_ == PJPG_UNSUPPORTED_MODE) {
//					utils::format("Progressive JPEG files are not supported.\n");
				} else {
//					utils::format("pjpeg_decode_init() failed with status: %d\n") % status_;
				}
				fin.close();
				return false;
			}

			// In reduce mode output 1 pixel per 8x8 block.
			width_  = reduce_ ?
				(image_info_.m_MCUSPerRow * image_info_.m_MCUWidth)  / 8 : image_info_.m_width;
			height_ = reduce_ ?
				(image_info_.m_MCUSPerCol * image_info_.m_MCUHeight) / 8 : image_info_.m_height;

			auto ret = pixel_(fin);
			fin.close();

			return ret;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	JPEG ファイルをロードする
			@param[in]	fin	ファイル I/O クラス
			@param[in]	opt	フォーマット固有の設定文字列
			@return エラーがあれば「false」
		*/
		//-----------------------------------------------------------------//
		bool load(const char* fname, const char* opt = nullptr) noexcept
		{
			if(fname == nullptr) return false;

			utils::file_io fin;
			if(!fin.open(fname, "rb")) {
				return false;
			}
			auto ret = load(fin, opt);
			fin.close();
			return ret;
		}
	};
}
<commit_msg>update: cleanup<commit_after>#pragma once
//=====================================================================//
/*!	@file
	@brief	PicoJPEG クラス
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/glfw3_app/blob/master/LICENSE
*/
//=====================================================================//
#include <cstdio>
#include <cstdlib>
#include "graphics/img.hpp"
#include "graphics/picojpeg.h"
#include "common/file_io.hpp"
#include "common/format.hpp"

namespace img {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	PicoJPEG デコード・クラス
		@param[in]	RENDER	描画ファンクタ
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	template <class RENDER>
	class picojpeg_in {

		RENDER&		render_;

		pjpeg_image_info_t	image_info_;

		uint8_t		status_;
		bool		reduce_;

		int16_t		width_;
		int16_t		height_;
		int16_t		ofs_x_;
		int16_t		ofs_y_;

		struct data_t {
			utils::file_io&	fin_;
			uint32_t		file_ofs_;
			uint32_t		file_size_;
			data_t(utils::file_io& fin) : fin_(fin), file_ofs_(0), file_size_(0) { }
		};

		void render_rgb_(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b)
		{
			auto c = RENDER::COLOR::rgb(r, g, b);
			render_.plot(ofs_x_ + x, ofs_y_ + y, c);
		}

		static uint8_t pjpeg_callback_(uint8_t* dst, uint8_t size, uint8_t* acr, void *pdata)
		{
			data_t* t = static_cast<data_t*>(pdata);
			auto len = std::min(t->file_size_ - t->file_ofs_, static_cast<uint32_t>(size));
			auto rl = t->fin_.read(dst, len);
			// utils::format("Read: %d (%d)\n") % len % rl;
			*acr = rl;
			t->file_ofs_ += rl;
			return 0;
		}

		bool pixel_(utils::file_io& fin) noexcept
		{
			int16_t xt = 0;
			int16_t yt = 0;
			while((status_ = pjpeg_decode_mcu()) == 0) {

				if(reduce_) {
					auto row_blocks = image_info_.m_MCUWidth  >> 3;
					auto col_blocks = image_info_.m_MCUHeight >> 3;
					int16_t xx = xt * row_blocks + ofs_x_;
					int16_t yy = yt * col_blocks + ofs_y_;
					if(image_info_.m_scanType == PJPG_GRAYSCALE) {
						auto gs = image_info_.m_pMCUBufR[0];
						render_rgb_(xx, yy, gs, gs, gs);
					} else {
						for(int16_t y = 0; y < col_blocks; ++y) {
							auto ofs = y * 128;
							for(int16_t x = 0; x < row_blocks; ++x) {
								auto r = image_info_.m_pMCUBufR[ofs];
								auto g = image_info_.m_pMCUBufG[ofs];
								auto b = image_info_.m_pMCUBufB[ofs];
								ofs += 64;
								render_rgb_(xx + x, yy + y, r, g, b);
							}
						}
					}
				} else {
					for(int16_t y = 0; y < image_info_.m_MCUHeight; y += 8) {
						auto yy = yt * image_info_.m_MCUHeight + ofs_y_;
						auto by_limit = std::min(8,
							image_info_.m_height - (yt * image_info_.m_MCUHeight + y));
						for(int16_t x = 0; x < image_info_.m_MCUWidth; x += 8) {
							auto bx_limit = std::min(8,
								image_info_.m_width - (xt * image_info_.m_MCUWidth + x));
							auto xx = xt * image_info_.m_MCUWidth + ofs_x_;
							auto ofs = (x * 8) + (y * 16);
							if(image_info_.m_scanType == PJPG_GRAYSCALE) {
								const uint8_t* pGS = image_info_.m_pMCUBufR + ofs;
								for(int16_t by = 0; by < by_limit; ++by) {
									for(int16_t bx = 0; bx < bx_limit; ++bx) {
										auto gs = *pGS++;
										render_rgb_(xx + bx, yy + by, gs, gs, gs);
									}
									pGS += (8 - bx_limit);
								}
							} else {
								const uint8_t* pR = image_info_.m_pMCUBufR + ofs;
								const uint8_t* pG = image_info_.m_pMCUBufG + ofs;
								const uint8_t* pB = image_info_.m_pMCUBufB + ofs;
								for(int16_t by = 0; by < by_limit; ++by) {
									for(int16_t bx = 0; bx < bx_limit; ++bx) {
										auto r = *pR++;
										auto g = *pG++;
										auto b = *pB++;
										render_rgb_(xx + bx, yy + by, r, g, b);
									}
									pR += (8 - bx_limit);
									pG += (8 - bx_limit);
									pB += (8 - bx_limit);
								}
							}
						}
					}
				}
				++xt;
				if(xt == image_info_.m_MCUSPerRow) {
					xt = 0;
					++yt;
				}
			}
			if(status_ != PJPG_NO_MORE_BLOCKS) {
				utils::format("pjpeg_decode_mcu() failed with status: %d\n") % status_;
				return false;
			}
			return true;
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
		*/
		//-----------------------------------------------------------------//
		picojpeg_in(RENDER& render) noexcept : render_(render),
			image_info_(),
			status_(0), reduce_(false),
			width_(0), height_(0), ofs_x_(0), ofs_y_(0)
		{ }

		//-----------------------------------------------------------------//
		/*!
			@brief	ステータスの取得（Error code）
			@return ステータス
		*/
		//-----------------------------------------------------------------//
		uint8_t get_status() const noexcept { return status_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	描画オフセットの設定
			@param[in]	x	X 軸オフセット
			@param[in]	y	Y 軸オフセット
		*/
		//-----------------------------------------------------------------//
		void set_draw_offset(int16_t x = 0, int16_t y = 0) noexcept
		{
			ofs_x_ = x;
			ofs_y_ = y;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	JPEG ファイルか確認する
			@param[in]	fin	file_io クラス
			@return エラーなら「false」を返す
		*/
		//-----------------------------------------------------------------//
		bool probe(utils::file_io& fin) noexcept
		{
			uint8_t sig[2];
			uint32_t pos = fin.tell();
			uint32_t l = fin.read(sig, 2);
			fin.seek(utils::file_io::SEEK::SET, pos);
			if(l == 2) {
				if(sig[0] == 0xff && sig[1] == 0xd8) {  // SOI Marker, Start image
					return true;
				}
			}
//			utils::format("Not JPEG file\n");
			return false;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	画像ファイルの情報を取得する
			@param[in]	fin	file_io クラス
			@param[in]	fo	情報を受け取る構造体
			@return エラーなら「false」を返す
		*/
		//-----------------------------------------------------------------//
		bool info(utils::file_io& fin, img::img_info& fo) noexcept
		{
			auto pos = fin.tell();

			data_t t(fin);
			t.file_ofs_  = 0;
			t.file_size_ = fin.get_file_size();
			status_ = pjpeg_decode_init(&image_info_, pjpeg_callback_, &t, false);
			if(status_) {
				fin.close();
				return false;
			}

			fin.seek(utils::file_io::SEEK::SET, pos);

			fo.width  = image_info_.m_width;
			fo.height = image_info_.m_height;
			switch(image_info_.m_comps) {
			case 1:
				fo.grayscale = true;
				fo.i_depth = 0;
				fo.r_depth = 8;
				fo.g_depth = 8;
				fo.b_depth = 8;
				fo.a_depth = 0;
				fo.clut_num = 0;
				break;
			case 3:
				fo.grayscale = false;
				fo.i_depth = 0;
				fo.r_depth = 8;
				fo.g_depth = 8;
				fo.b_depth = 8;
				fo.a_depth = 0;
				fo.clut_num = 0;
				break;
			case 4:
				fo.grayscale = false;
				fo.i_depth = 0;
				fo.r_depth = 8;
				fo.g_depth = 8;
				fo.b_depth = 8;
				fo.a_depth = 8;
				fo.clut_num = 0;
				break;
			default:
				fo.i_depth = 0;
				fo.r_depth = 0;
				fo.g_depth = 0;
				fo.b_depth = 0;
				fo.a_depth = 0;
				fo.clut_num = 0;
				return false;
				break;
			}
			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	JPEG ファイル、ロード
			@param[in]	fin	file_io クラス
			@param[in]	opt	フォーマット固有の設定文字列
			@return エラーなら「false」を返す
		*/
		//-----------------------------------------------------------------//
		bool load(utils::file_io& fin, const char* opt = nullptr)
		{
			// とりあえず、ヘッダーの検査
			if(!probe(fin)) {
				return false;
			}

			reduce_ = false;

			data_t t(fin);
			t.file_ofs_  = 0;
			t.file_size_ = fin.get_file_size();
			status_ = pjpeg_decode_init(&image_info_, pjpeg_callback_, &t, reduce_);
			if(status_) {
				if(status_ == PJPG_UNSUPPORTED_MODE) {
//					utils::format("Progressive JPEG files are not supported.\n");
				} else {
//					utils::format("pjpeg_decode_init() failed with status: %d\n") % status_;
				}
				fin.close();
				return false;
			}

			// In reduce mode output 1 pixel per 8x8 block.
			width_  = reduce_ ?
				(image_info_.m_MCUSPerRow * image_info_.m_MCUWidth)  / 8 : image_info_.m_width;
			height_ = reduce_ ?
				(image_info_.m_MCUSPerCol * image_info_.m_MCUHeight) / 8 : image_info_.m_height;

			auto ret = pixel_(fin);
			fin.close();

			return ret;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	JPEG ファイルをロードする
			@param[in]	fin	ファイル I/O クラス
			@param[in]	opt	フォーマット固有の設定文字列
			@return エラーがあれば「false」
		*/
		//-----------------------------------------------------------------//
		bool load(const char* fname, const char* opt = nullptr) noexcept
		{
			if(fname == nullptr) return false;

			utils::file_io fin;
			if(!fin.open(fname, "rb")) {
				return false;
			}
			auto ret = load(fin, opt);
			fin.close();
			return ret;
		}
	};
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_IFMT_HPP
#define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_IFMT_HPP

#include <tao/pegtl.hpp>

#include "verify_meta.hpp"
#include "verify_rule.hpp"

namespace TAO_PEGTL_NAMESPACE
{
   template< template< typename, typename, typename > class S >
   void verify_ifmt( const result_type failure = result_type::local_failure )
   {
      verify_analyze< S< eof, eof, eof > >( __LINE__, __FILE__, false, false );
      verify_analyze< S< eof, eof, any > >( __LINE__, __FILE__, false, false );
      verify_analyze< S< eof, any, eof > >( __LINE__, __FILE__, false, false );
      verify_analyze< S< eof, any, any > >( __LINE__, __FILE__, true, false );
      verify_analyze< S< any, eof, eof > >( __LINE__, __FILE__, false, false );
      verify_analyze< S< any, eof, any > >( __LINE__, __FILE__, true, false );
      verify_analyze< S< any, any, eof > >( __LINE__, __FILE__, false, false );
      verify_analyze< S< any, any, any > >( __LINE__, __FILE__, true, false );

      verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "", failure, 0 );
      verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "b", failure, 1 );
      verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "c", result_type::success, 0 );
      verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "ab", result_type::success, 0 );
      verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "ac", failure, 2 );

      verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "", result_type::global_failure, 0 );
      verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "a", result_type::global_failure, 0 );
      verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "ac", result_type::global_failure, 1 );
      verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "b", result_type::global_failure, 1 );

      verify_rule< must< S< one< 'a' >, one< 'b' >, seq< one< 'c' >, one< 'd' > > > > >( __LINE__, __FILE__, "c", result_type::global_failure, 0 );
      verify_rule< must< S< one< 'a' >, one< 'b' >, seq< one< 'c' >, one< 'd' > > > > >( __LINE__, __FILE__, "cc", result_type::global_failure, 1 );
   }

}  // namespace TAO_PEGTL_NAMESPACE

#endif
<commit_msg>Allow disabling exceptions<commit_after>// Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_IFMT_HPP
#define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_IFMT_HPP

#include <tao/pegtl.hpp>

#include "verify_meta.hpp"
#include "verify_rule.hpp"

namespace TAO_PEGTL_NAMESPACE
{
   template< template< typename, typename, typename > class S >
   void verify_ifmt( const result_type failure = result_type::local_failure )
   {
      verify_analyze< S< eof, eof, eof > >( __LINE__, __FILE__, false, false );
      verify_analyze< S< eof, eof, any > >( __LINE__, __FILE__, false, false );
      verify_analyze< S< eof, any, eof > >( __LINE__, __FILE__, false, false );
      verify_analyze< S< eof, any, any > >( __LINE__, __FILE__, true, false );
      verify_analyze< S< any, eof, eof > >( __LINE__, __FILE__, false, false );
      verify_analyze< S< any, eof, any > >( __LINE__, __FILE__, true, false );
      verify_analyze< S< any, any, eof > >( __LINE__, __FILE__, false, false );
      verify_analyze< S< any, any, any > >( __LINE__, __FILE__, true, false );

      verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "", failure, 0 );
      verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "b", failure, 1 );
      verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "c", result_type::success, 0 );
      verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "ab", result_type::success, 0 );
      verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "ac", failure, 2 );

#if defined( __cxx_exceptions )
      verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "", result_type::global_failure, 0 );
      verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "a", result_type::global_failure, 0 );
      verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "ac", result_type::global_failure, 1 );
      verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "b", result_type::global_failure, 1 );

      verify_rule< must< S< one< 'a' >, one< 'b' >, seq< one< 'c' >, one< 'd' > > > > >( __LINE__, __FILE__, "c", result_type::global_failure, 0 );
      verify_rule< must< S< one< 'a' >, one< 'b' >, seq< one< 'c' >, one< 'd' > > > > >( __LINE__, __FILE__, "cc", result_type::global_failure, 1 );
#endif
   }

}  // namespace TAO_PEGTL_NAMESPACE

#endif
<|endoftext|>


<commit_before>#line 2 "togo/debug_constraints.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.

@file debug_constraints.hpp
@brief Definitions for constraints.
*/

#pragma once

#include <togo/config.hpp>

#if defined(TOGO_USE_CONSTRAINTS)
	#include <type_traits>

	#define TOGO_CONSTRAIN_IS_POD(T)								\
		static_assert(												\
			std::is_standard_layout<T>::value,						\
			"T is not a POD type"									\
		)

	#define TOGO_CONSTRAIN_SAME(T, U)								\
		static_assert(												\
			std::is_same<T, U>::value,								\
			"T and U are not the same types"						\
		)

	#define TOGO_CONSTRAIN_INTEGRAL(T)								\
		static_assert(												\
			std::is_integral<T>::value,								\
			"T is not an integral type"								\
		)

	#define TOGO_CONSTRAIN_ARITHMETIC(T)							\
		static_assert(												\
			std::is_arithmetic<T>::value,							\
			"T is not an arithmetic type"							\
		)

	#define TOGO_CONSTRAIN_INTEGRAL_ARITHMETIC(T)					\
		static_assert(												\
			std::is_integral<T>::value &&							\
			std::is_arithmetic<T>::value,							\
			"T is not an integral arithmetic type"					\
		)

	#define TOGO_CONSTRAIN_COMPARABLE(T)							\
		static_assert(												\
			std::is_pointer<T>::value ||							\
			(														\
				std::is_integral<T>::value &&						\
				std::is_arithmetic<T>::value						\
			),														\
			"T is not a permitted built-in comparable type"			\
		)
#else
	/// Statically assert that type T is of standard layout.
	#define TOGO_CONSTRAIN_IS_POD(T)

	/// Statically assert that type T is the same as type U.
	#define TOGO_CONSTRAIN_SAME(T, U)

	/// Statically assert that type T is an integral type.
	#define TOGO_CONSTRAIN_INTEGRAL(T)

	/// Statically assert that type T is an arithmetic type.
	#define TOGO_CONSTRAIN_ARITHMETIC(T)

	/// Statically assert that type T is an integral arithmetic type.
	#define TOGO_CONSTRAIN_INTEGRAL_ARITHMETIC(T)

	/// Statically assert that type T is a built-in comparable type.
	///
	/// Comparable types are arithmetic integrals and pointers.
	#define TOGO_CONSTRAIN_COMPARABLE(T)
#endif
<commit_msg>debug_constraints: added TOGO_CONSTRAIN_UNSIGNED(T).<commit_after>#line 2 "togo/debug_constraints.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.

@file debug_constraints.hpp
@brief Definitions for constraints.
*/

#pragma once

#include <togo/config.hpp>

#if defined(TOGO_USE_CONSTRAINTS)
	#include <type_traits>

	#define TOGO_CONSTRAIN_IS_POD(T)								\
		static_assert(												\
			std::is_standard_layout<T>::value,						\
			"T is not a POD type"									\
		)

	#define TOGO_CONSTRAIN_SAME(T, U)								\
		static_assert(												\
			std::is_same<T, U>::value,								\
			"T and U are not the same types"						\
		)

	#define TOGO_CONSTRAIN_INTEGRAL(T)								\
		static_assert(												\
			std::is_integral<T>::value,								\
			"T is not an integral type"								\
		)

	#define TOGO_CONSTRAIN_ARITHMETIC(T)							\
		static_assert(												\
			std::is_arithmetic<T>::value,							\
			"T is not an arithmetic type"							\
		)

	#define TOGO_CONSTRAIN_INTEGRAL_ARITHMETIC(T)					\
		static_assert(												\
			std::is_integral<T>::value &&							\
			std::is_arithmetic<T>::value,							\
			"T is not an integral arithmetic type"					\
		)

	#define TOGO_CONSTRAIN_UNSIGNED(T)								\
		static_assert(												\
			std::is_unsigned<T>::value,								\
			"T is not an unsigned arithmetic type"					\
		)

	#define TOGO_CONSTRAIN_COMPARABLE(T)							\
		static_assert(												\
			std::is_pointer<T>::value ||							\
			(														\
				std::is_integral<T>::value &&						\
				std::is_arithmetic<T>::value						\
			),														\
			"T is not a permitted built-in comparable type"			\
		)
#else
	/// Statically assert that type T is of standard layout.
	#define TOGO_CONSTRAIN_IS_POD(T)

	/// Statically assert that type T is the same as type U.
	#define TOGO_CONSTRAIN_SAME(T, U)

	/// Statically assert that type T is an integral type.
	#define TOGO_CONSTRAIN_INTEGRAL(T)

	/// Statically assert that type T is an arithmetic type.
	#define TOGO_CONSTRAIN_ARITHMETIC(T)

	/// Statically assert that type T is an integral arithmetic type.
	#define TOGO_CONSTRAIN_INTEGRAL_ARITHMETIC(T)

	/// Statically assert that type T is an unsigned arithmetic type.
	#define TOGO_CONSTRAIN_UNSIGNED(T)

	/// Statically assert that type T is a built-in comparable type.
	///
	/// Comparable types are arithmetic integrals and pointers.
	#define TOGO_CONSTRAIN_COMPARABLE(T)
#endif
<|endoftext|>


<commit_before>/**
 * @file
 *
 * @brief
 *
 * @copyright BSD License (see doc/COPYING or http://www.libelektra.org)
 */

#ifndef ELEKTRA_KDB_IO_HPP
#define ELEKTRA_KDB_IO_HPP

/*
 * @brief See examples/cpp_example_userio.cpp for how to use
 * USER_DEFINED_IO
 */
#define USER_DEFINED_IO

#include <key.hpp>

#include <iomanip>
#include <ostream>

#include "ansicolors.hpp"

namespace kdb
{

inline std::ostream & printError (std::ostream & os, kdb::Key const & error)
{
	try
	{
		if (!error.getMeta<const kdb::Key> ("error"))
		{
			// no error available
			return os;
		}

		os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::RED) << "Error (#"
		   << error.getMeta<std::string> ("error/number") << ") occurred!" << getErrorColor (ANSI_COLOR::RESET) << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Description: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/description") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Ingroup: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/ingroup") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Module: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/module") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "At: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/file") << ":" << error.getMeta<std::string> ("error/line") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Reason: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/reason") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Mountpoint: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/mountpoint") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Configfile: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/configfile") << std::endl;
	}
	catch (kdb::KeyTypeConversion const & e)
	{
		os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::RED)
		   << "Error metadata is not set correctly by a plugin: " << getErrorColor (ANSI_COLOR::RESET) << e.what () << std::endl;
	}

	return os;
}

inline std::ostream & printWarnings (std::ostream & os, kdb::Key const & error)
{
	try
	{
		if (!error.getMeta<const kdb::Key> ("warnings"))
		{
			// no warnings were issued
			return os;
		}

		int nr = error.getMeta<int> ("warnings");
		os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::MAGENTA) << nr + 1 << " Warning" << (!nr ? "" : "s")
		   << " were issued:" << getErrorColor (ANSI_COLOR::RESET) << std::endl;

		for (int i = 0; i <= nr; i++)
		{
			std::ostringstream name;
			name << "warnings/#" << std::setfill ('0') << std::setw (2) << i;
			// os << "\t" << name.str() << ": " << error.getMeta<std::string>(name.str()) << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::MAGENTA)
			   << " Warning number: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/number") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tDescription: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/description") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tIngroup: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/ingroup") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tModule: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/module") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tAt: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/file") << ":"
			   << error.getMeta<std::string> (name.str () + "/line") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tReason: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/reason") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tMountpoint: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/mountpoint") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tConfigfile: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/configfile") << std::endl;
		}
	}
	catch (kdb::KeyTypeConversion const & e)
	{
		os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::MAGENTA)
		   << "Warnings metadata not set correctly by a plugin: " << getErrorColor (ANSI_COLOR::RESET) << e.what () << std::endl;
	}

	return os;
}
}

#endif
<commit_msg>errormsg: add sorry<commit_after>/**
 * @file
 *
 * @brief
 *
 * @copyright BSD License (see doc/COPYING or http://www.libelektra.org)
 */

#ifndef ELEKTRA_KDB_IO_HPP
#define ELEKTRA_KDB_IO_HPP

/*
 * @brief See examples/cpp_example_userio.cpp for how to use
 * USER_DEFINED_IO
 */
#define USER_DEFINED_IO

#include <key.hpp>

#include <iomanip>
#include <ostream>

#include "ansicolors.hpp"

namespace kdb
{

inline std::ostream & printError (std::ostream & os, kdb::Key const & error)
{
	try
	{
		if (!error.getMeta<const kdb::Key> ("error"))
		{
			// no error available
			return os;
		}

		os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::RED) << "Sorry, the error (#"
		   << error.getMeta<std::string> ("error/number") << ") occurred ;(" << getErrorColor (ANSI_COLOR::RESET) << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Description: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/description") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Ingroup: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/ingroup") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Module: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/module") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "At: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/file") << ":" << error.getMeta<std::string> ("error/line") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Reason: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/reason") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Mountpoint: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/mountpoint") << std::endl;
		os << getErrorColor (ANSI_COLOR::BOLD) << "Configfile: " << getErrorColor (ANSI_COLOR::RESET)
		   << error.getMeta<std::string> ("error/configfile") << std::endl;
	}
	catch (kdb::KeyTypeConversion const & e)
	{
		os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::RED)
		   << "Sorry, error metadata is not set correctly by a plugin: " << getErrorColor (ANSI_COLOR::RESET) << e.what () << std::endl;
	}

	return os;
}

inline std::ostream & printWarnings (std::ostream & os, kdb::Key const & error)
{
	try
	{
		if (!error.getMeta<const kdb::Key> ("warnings"))
		{
			// no warnings were issued
			return os;
		}

		int nr = error.getMeta<int> ("warnings");
		os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::MAGENTA) << " Sorry, " << nr + 1 << " warning" << (!nr ? " was" : "s were")
		   << " issued:" << getErrorColor (ANSI_COLOR::RESET) << " ;(" << std::endl;

		for (int i = 0; i <= nr; i++)
		{
			std::ostringstream name;
			name << "warnings/#" << std::setfill ('0') << std::setw (2) << i;
			// os << "\t" << name.str() << ": " << error.getMeta<std::string>(name.str()) << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::MAGENTA)
			   << " Warning " << i << getErrorColor (ANSI_COLOR::RESET) << " (#"
			   << error.getMeta<std::string> (name.str () + "/number") << ")" << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tDescription: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/description") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tIngroup: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/ingroup") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tModule: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/module") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tAt: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/file") << ":"
			   << error.getMeta<std::string> (name.str () + "/line") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tReason: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/reason") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tMountpoint: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/mountpoint") << std::endl;
			os << getErrorColor (ANSI_COLOR::BOLD) << "\tConfigfile: " << getErrorColor (ANSI_COLOR::RESET)
			   << error.getMeta<std::string> (name.str () + "/configfile") << std::endl;
		}
	}
	catch (kdb::KeyTypeConversion const & e)
	{
		os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::MAGENTA)
		   << "Sorry, warnings metadata not set correctly by a plugin: " << getErrorColor (ANSI_COLOR::RESET) << e.what () << std::endl;
	}

	return os;
}
}

#endif
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>

#include "zlib.h"

#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/io.hpp"

namespace
{
	enum
	{
		udp_connection_retries = 4,
		udp_announce_retries = 15,
		udp_connect_timeout = 15,
		udp_announce_timeout = 10,
		udp_buffer_size = 2048
	};
}

using namespace boost::posix_time;

namespace libtorrent
{

	udp_tracker_connection::udp_tracker_connection(
		tracker_request const& req
		, std::string const& hostname
		, unsigned short port
		, boost::weak_ptr<request_callback> c
		, const http_settings& stn)
		: tracker_connection(c)
		, m_request_time(second_clock::universal_time())
		, m_request(req)
		, m_transaction_id(0)
		, m_connection_id(0)
		, m_settings(stn)
		, m_attempts(0)
	{
		// only announce is suppoerted at this time
		assert(req.kind == tracker_request::announce_request);

		// TODO: this is a problem. DNS-lookup is blocking!
		// (may block up to 5 seconds)
		address a(hostname.c_str(), port);
		if (has_requester()) requester().m_tracker_address = a;
		m_socket.reset(new socket(socket::udp, false));
		m_socket->connect(a);

		send_udp_connect();
	}

	bool udp_tracker_connection::send_finished() const
	{
		using namespace boost::posix_time;

		time_duration d = second_clock::universal_time() - m_request_time;
		return (m_transaction_id != 0
			&& m_connection_id != 0)
			|| d > seconds(m_settings.tracker_timeout);
	}

	bool udp_tracker_connection::tick()
	{
		using namespace boost::posix_time;

		time_duration d = second_clock::universal_time() - m_request_time;
		if (m_connection_id == 0
			&& d > seconds(udp_connect_timeout))
		{
			if (m_attempts >= udp_connection_retries)
			{
				if (has_requester())
					requester().tracker_request_timed_out(m_request);
				return true;
			}
			send_udp_connect();
			return false;
		}

		if (m_connection_id != 0
			&& d > seconds(udp_announce_timeout))
		{
			if (m_attempts >= udp_announce_retries)
			{
				if (has_requester())
					requester().tracker_request_timed_out(m_request);
				return true;
			}

			send_udp_announce();
			return false;
		}

		char buf[udp_buffer_size];
		int ret = m_socket->receive(buf, udp_buffer_size);

		// if there was nothing to receive, return
		if (ret == 0) return false;
		if (ret < 0)
		{
			socket::error_code err = m_socket->last_error();
			if (err == socket::would_block) return false;
			throw network_error(m_socket->last_error());
		}
		if (ret == udp_buffer_size)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, "tracker reply too big");
			return true;
		}

		if (m_connection_id == 0)
		{
			return parse_connect_response(buf, ret);
		}
		else
		{
			return parse_announce_response(buf, ret);
		}
	}

	void udp_tracker_connection::send_udp_announce()
	{
		if (m_transaction_id == 0)
			m_transaction_id = rand() | (rand() << 16);

		std::vector<char> buf;
		std::back_insert_iterator<std::vector<char> > out(buf);

		// connection_id
		detail::write_int64(m_connection_id, out);
		// action (announce)
		detail::write_int32(announce, out);
		// transaction_id
		detail::write_int32(m_transaction_id, out);
		// info_hash
		std::copy(m_request.info_hash.begin(), m_request.info_hash.end(), out);
		// peer_id
		std::copy(m_request.id.begin(), m_request.id.end(), out);
		// downloaded
		detail::write_int64(m_request.downloaded, out);
		// left
		detail::write_int64(m_request.left, out);
		// uploaded
		detail::write_int64(m_request.uploaded, out);
		// event
		detail::write_int32(m_request.event, out);
		// ip address
		detail::write_int32(0, out);
		// key
		detail::write_int32(m_request.key, out);
		// num_want
		detail::write_int32(m_request.num_want, out);
		// port
		detail::write_uint16(m_request.listen_port, out);

		m_socket->send(&buf[0], buf.size());
		m_request_time = second_clock::universal_time();
		++m_attempts;
	}

	void udp_tracker_connection::send_udp_connect()
	{
		char send_buf[16];
		char* ptr = send_buf;

		if (m_transaction_id == 0)
			m_transaction_id = rand() | (rand() << 16);

		// connection_id
		detail::write_int64(0, ptr);
		// action (connect)
		detail::write_int32(connect, ptr);
		// transaction_id
		detail::write_int32(m_transaction_id, ptr);

		m_socket->send(send_buf, 16);
		m_request_time = second_clock::universal_time();
		++m_attempts;
	}

	bool udp_tracker_connection::parse_announce_response(const char* buf, int len)
	{
		assert(buf != 0);
		assert(len > 0);

		if (len < 8)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 8, ignoring");
#endif
			return false;
		}

		int action = detail::read_int32(buf);
		int transaction = detail::read_int32(buf);
		if (transaction != m_transaction_id)
		{
			return false;
		}

		if (action == error)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, std::string(buf, buf + len - 8));
			return true;
		}
		if (action != announce) return false;

		if (len < 24)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 24, ignoring");
#endif
			return false;
		}
		int interval = detail::read_int32(buf);
		int incomplete = detail::read_int32(buf);
		int complete = detail::read_int32(buf);
		int num_peers = (len - 24) / 6;
		if ((len - 24) % 6 != 0)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, "invalid tracker response");
			return true;
		}

		if (!has_requester()) return true;

		std::vector<peer_entry> peer_list;
		for (int i = 0; i < num_peers; ++i)
		{
			peer_entry e;
			std::stringstream s;
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf);
			e.ip = s.str();
			e.port = detail::read_uint16(buf);
			e.id.clear();
			peer_list.push_back(e);
		}

		requester().tracker_response(m_request, peer_list, interval
			, complete, incomplete);
		return true;
	}

	bool udp_tracker_connection::parse_connect_response(const char* buf, int len)
	{
		assert(buf != 0);
		assert(len > 0);

		if (len < 8)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 8, ignoring");
#endif
			return false;
		}
		const char* ptr = buf;
		int action = detail::read_int32(ptr);
		int transaction = detail::read_int32(ptr);

		if (action == error)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, std::string(ptr, buf + len));
			return true;
		}
		if (action != connect) return false;
		if (m_transaction_id != transaction)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with incorrect transaction id, ignoring");
#endif
			return false;
		}

		if (len < 16)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a connection message size < 16, ignoring");
#endif
			return false;
		}
		// reset transaction
		m_transaction_id = 0;
		m_attempts = 0;
		m_connection_id = detail::read_int64(ptr);

		send_udp_announce();
		return false;
	}

}

<commit_msg>fixed udp-tracker connection bug<commit_after>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>

#include "zlib.h"

#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/io.hpp"

namespace
{
	enum
	{
		udp_connection_retries = 4,
		udp_announce_retries = 15,
		udp_connect_timeout = 15,
		udp_announce_timeout = 10,
		udp_buffer_size = 2048
	};
}

using namespace boost::posix_time;

namespace libtorrent
{

	udp_tracker_connection::udp_tracker_connection(
		tracker_request const& req
		, std::string const& hostname
		, unsigned short port
		, boost::weak_ptr<request_callback> c
		, const http_settings& stn)
		: tracker_connection(c)
		, m_request_time(second_clock::universal_time())
		, m_request(req)
		, m_transaction_id(0)
		, m_connection_id(0)
		, m_settings(stn)
		, m_attempts(0)
	{
		// only announce is suppoerted at this time
		assert(req.kind == tracker_request::announce_request);

		// TODO: this is a problem. DNS-lookup is blocking!
		// (may block up to 5 seconds)
		address a(hostname.c_str(), port);
		if (has_requester()) requester().m_tracker_address = a;
		m_socket.reset(new socket(socket::udp, false));
		m_socket->connect(a);

		send_udp_connect();
	}

	bool udp_tracker_connection::send_finished() const
	{
		using namespace boost::posix_time;

		time_duration d = second_clock::universal_time() - m_request_time;
		return (m_transaction_id != 0
			&& m_connection_id != 0)
			|| d > seconds(m_settings.tracker_timeout);
	}

	bool udp_tracker_connection::tick()
	{
		using namespace boost::posix_time;

		time_duration d = second_clock::universal_time() - m_request_time;
		if (m_connection_id == 0
			&& d > seconds(udp_connect_timeout))
		{
			if (m_attempts >= udp_connection_retries)
			{
				if (has_requester())
					requester().tracker_request_timed_out(m_request);
				return true;
			}
			send_udp_connect();
			return false;
		}

		if (m_connection_id != 0
			&& d > seconds(udp_announce_timeout))
		{
			if (m_attempts >= udp_announce_retries)
			{
				if (has_requester())
					requester().tracker_request_timed_out(m_request);
				return true;
			}

			send_udp_announce();
			return false;
		}

		char buf[udp_buffer_size];
		int ret = m_socket->receive(buf, udp_buffer_size);

		// if there was nothing to receive, return
		if (ret == 0) return false;
		if (ret < 0)
		{
			socket::error_code err = m_socket->last_error();
			if (err == socket::would_block) return false;
			throw network_error(m_socket->last_error());
		}
		if (ret == udp_buffer_size)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, "tracker reply too big");
			return true;
		}

		if (m_connection_id == 0)
		{
			return parse_connect_response(buf, ret);
		}
		else
		{
			return parse_announce_response(buf, ret);
		}
	}

	void udp_tracker_connection::send_udp_announce()
	{
		if (m_transaction_id == 0)
			m_transaction_id = rand() | (rand() << 16);

		std::vector<char> buf;
		std::back_insert_iterator<std::vector<char> > out(buf);

		// connection_id
		detail::write_int64(m_connection_id, out);
		// action (announce)
		detail::write_int32(announce, out);
		// transaction_id
		detail::write_int32(m_transaction_id, out);
		// info_hash
		std::copy(m_request.info_hash.begin(), m_request.info_hash.end(), out);
		// peer_id
		std::copy(m_request.id.begin(), m_request.id.end(), out);
		// downloaded
		detail::write_int64(m_request.downloaded, out);
		// left
		detail::write_int64(m_request.left, out);
		// uploaded
		detail::write_int64(m_request.uploaded, out);
		// event
		detail::write_int32(m_request.event, out);
		// ip address
		detail::write_int32(0, out);
		// key
		detail::write_int32(m_request.key, out);
		// num_want
		detail::write_int32(m_request.num_want, out);
		// port
		detail::write_uint16(m_request.listen_port, out);

		m_socket->send(&buf[0], buf.size());
		m_request_time = second_clock::universal_time();
		++m_attempts;
	}

	void udp_tracker_connection::send_udp_connect()
	{
		char send_buf[16];
		char* ptr = send_buf;

		if (m_transaction_id == 0)
			m_transaction_id = rand() | (rand() << 16);

		// connection_id
		detail::write_int64(0, ptr);
		// action (connect)
		detail::write_int32(connect, ptr);
		// transaction_id
		detail::write_int32(m_transaction_id, ptr);

		m_socket->send(send_buf, 16);
		m_request_time = second_clock::universal_time();
		++m_attempts;
	}

	bool udp_tracker_connection::parse_announce_response(const char* buf, int len)
	{
		assert(buf != 0);
		assert(len > 0);

		if (len < 8)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 8, ignoring");
#endif
			return false;
		}

		int action = detail::read_int32(buf);
		int transaction = detail::read_int32(buf);
		if (transaction != m_transaction_id)
		{
			return false;
		}

		if (action == error)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, std::string(buf, buf + len - 8));
			return true;
		}
		if (action != announce) return false;

		if (len < 24)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 24, ignoring");
#endif
			return false;
		}
		int interval = detail::read_int32(buf);
		int incomplete = detail::read_int32(buf);
		int complete = detail::read_int32(buf);
		int num_peers = (len - 20) / 6;
		if ((len - 20) % 6 != 0)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, "invalid tracker response");
			return true;
		}

		if (!has_requester()) return true;

		std::vector<peer_entry> peer_list;
		for (int i = 0; i < num_peers; ++i)
		{
			peer_entry e;
			std::stringstream s;
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf);
			e.ip = s.str();
			e.port = detail::read_uint16(buf);
			e.id.clear();
			peer_list.push_back(e);
		}

		requester().tracker_response(m_request, peer_list, interval
			, complete, incomplete);
		return true;
	}

	bool udp_tracker_connection::parse_connect_response(const char* buf, int len)
	{
		assert(buf != 0);
		assert(len > 0);

		if (len < 8)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 8, ignoring");
#endif
			return false;
		}
		const char* ptr = buf;
		int action = detail::read_int32(ptr);
		int transaction = detail::read_int32(ptr);

		if (action == error)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, std::string(ptr, buf + len));
			return true;
		}
		if (action != connect) return false;
		if (m_transaction_id != transaction)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with incorrect transaction id, ignoring");
#endif
			return false;
		}

		if (len < 16)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a connection message size < 16, ignoring");
#endif
			return false;
		}
		// reset transaction
		m_transaction_id = 0;
		m_attempts = 0;
		m_connection_id = detail::read_int64(ptr);

		send_udp_announce();
		return false;
	}

}

<|endoftext|>