<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"

#include <stdint.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/poll.h>

#include "base/eintr_wrapper.h"
#include "base/process_util.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/pickle.h"
#include "base/string_util.h"
#include "base/unix_domain_socket_posix.h"
#include "chrome/common/sandbox_methods_linux.h"
#include "webkit/api/public/gtk/WebFontInfo.h"
#include "webkit/api/public/WebData.h"
#include "webkit/api/public/WebKit.h"
#include "webkit/api/public/WebKitClient.h"

#include "SkFontHost_fontconfig_direct.h"
#include "SkFontHost_fontconfig_ipc.h"

using WebKit::WebClipboard;
using WebKit::WebData;
using WebKit::WebFontInfo;
using WebKit::WebKitClient;
using WebKit::WebMimeRegistry;
using WebKit::WebPluginInfo;
using WebKit::WebPluginListBuilder;
using WebKit::WebSandboxSupport;
using WebKit::WebString;
using WebKit::WebThemeEngine;
using WebKit::WebUChar;
using WebKit::WebURL;
using WebKit::WebURLLoader;

// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC

// BEWARE: code in this file run across *processes* (not just threads).

// This code runs in a child process
class SandboxIPCProcess : public WebKitClient {
 public:
  // lifeline_fd: this is the read end of a pipe which the browser process
  //   holds the other end of. If the browser process dies, it's descriptors are
  //   closed and we will noticed an EOF on the pipe. That's our signal to exit.
  // browser_socket: the 'browser's end of the sandbox IPC socketpair. From the
  //   point of view of the renderer's, it's talking to the browser but this
  //   object actually services the requests.
  SandboxIPCProcess(int lifeline_fd, int browser_socket)
      : lifeline_fd_(lifeline_fd),
        browser_socket_(browser_socket),
        font_config_(new FontConfigDirect()) {
    WebKit::initialize(this);

    base::InjectiveMultimap multimap;
    multimap.push_back(base::InjectionArc(0, lifeline_fd, false));
    multimap.push_back(base::InjectionArc(0, browser_socket, false));

    base::CloseSuperfluousFds(multimap);
  }

  void Run() {
    struct pollfd pfds[2];
    pfds[0].fd = lifeline_fd_;
    pfds[0].events = POLLIN;
    pfds[1].fd = browser_socket_;
    pfds[1].events = POLLIN;

    bool failed_polls = 0;
    for (;;) {
      const int r = HANDLE_EINTR(poll(pfds, 2, -1));
      if (r < 1) {
        LOG(WARNING) << "poll errno:" << errno;
        if (failed_polls++ == 3) {
          LOG(FATAL) << "poll failing. Sandbox host aborting.";
          return;
        }
        continue;
      }

      failed_polls = 0;

      if (pfds[0].revents) {
        // our parent died so we should too.
        _exit(0);
      }

      if (pfds[1].revents) {
        HandleRequestFromRenderer(browser_socket_);
      }
    }
  }

  // ---------------------------------------------------------------------------
  // WebKitClient impl...

  virtual WebClipboard* clipboard() { return NULL; }
  virtual WebMimeRegistry* mimeRegistry() { return NULL; }
  virtual WebSandboxSupport* sandboxSupport() { return NULL; }
  virtual WebThemeEngine* themeEngine() { return NULL; }

  virtual unsigned long long visitedLinkHash(const char*, size_t) { return 0; }
  virtual bool isLinkVisited(unsigned long long) { return false; }

  virtual void setCookies(const WebURL&, const WebURL&, const WebString&) { }
  virtual WebString cookies(const WebURL&, const WebURL&) { return WebString(); }

  virtual void prefetchHostName(const WebString&) { }

  virtual bool getFileSize(const WebString& path, long long& result) {
    return false;
  }

  virtual WebURLLoader* createURLLoader() { return NULL; }

  virtual void getPluginList(bool refresh, WebPluginListBuilder*) { }

  virtual void decrementStatsCounter(const char*) { }
  virtual void incrementStatsCounter(const char*) { }

  virtual void traceEventBegin(const char* name, void*, const char*) { }
  virtual void traceEventEnd(const char* name, void*, const char*) { }

  virtual WebData loadResource(const char*) { return WebData(); }

  virtual void suddenTerminationChanged(bool) { }

  virtual WebString defaultLocale() { return WebString(); }

  virtual double currentTime() { return 0; }

  virtual void setSharedTimerFiredFunction(void (*)()) { }
  virtual void setSharedTimerFireTime(double) { }
  virtual void stopSharedTimer() { }

  virtual void callOnMainThread(void (*)()) { }

 private:
  // ---------------------------------------------------------------------------
  // Requests from the renderer...

  void HandleRequestFromRenderer(int fd) {
    std::vector<int> fds;
    static const unsigned kMaxMessageLength = 2048;
    char buf[kMaxMessageLength];
    const ssize_t len = base::RecvMsg(fd, buf, sizeof(buf), &fds);
    if (len == -1)
      return;
    if (fds.size() == 0)
      return;

    Pickle pickle(buf, len);
    void* iter = NULL;

    int kind;
    if (!pickle.ReadInt(&iter, &kind))
      goto error;

    if (kind == FontConfigIPC::METHOD_MATCH) {
      HandleFontMatchRequest(fd, pickle, iter, fds);
    } else if (kind == FontConfigIPC::METHOD_OPEN) {
      HandleFontOpenRequest(fd, pickle, iter, fds);
    } else if (kind == LinuxSandbox::METHOD_GET_FONT_FAMILY_FOR_CHARS) {
      HandleGetFontFamilyForChars(fd, pickle, iter, fds);
    }

  error:
    for (std::vector<int>::const_iterator
         i = fds.begin(); i != fds.end(); ++i) {
      close(*i);
    }
  }

  void HandleFontMatchRequest(int fd, Pickle& pickle, void* iter,
                              std::vector<int>& fds) {
    bool fileid_valid;
    uint32_t fileid;
    bool is_bold, is_italic;
    std::string family;

    if (!pickle.ReadBool(&iter, &fileid_valid))
      return;
    if (fileid_valid) {
      if (!pickle.ReadUInt32(&iter, &fileid))
        return;
    }
    if (!pickle.ReadBool(&iter, &is_bold) ||
        !pickle.ReadBool(&iter, &is_italic) ||
        !pickle.ReadString(&iter, &family)) {
      return;
    }

    std::string result_family;
    unsigned result_fileid;

    const bool r = font_config_->Match(
        &result_family, &result_fileid, fileid_valid, fileid, family, &is_bold,
        &is_italic);

    Pickle reply;
    if (!r) {
      reply.WriteBool(false);
    } else {
      reply.WriteBool(true);
      reply.WriteUInt32(result_fileid);
      reply.WriteString(result_family);
      reply.WriteBool(is_bold);
      reply.WriteBool(is_italic);
    }
    SendRendererReply(fds, reply, -1);
  }

  void HandleFontOpenRequest(int fd, Pickle& pickle, void* iter,
                             std::vector<int>& fds) {
    uint32_t fileid;
    if (!pickle.ReadUInt32(&iter, &fileid))
      return;
    const int result_fd = font_config_->Open(fileid);

    Pickle reply;
    if (result_fd == -1) {
      reply.WriteBool(false);
    } else {
      reply.WriteBool(true);
    }

    SendRendererReply(fds, reply, result_fd);
  }

  void HandleGetFontFamilyForChars(int fd, Pickle& pickle, void* iter,
                                   std::vector<int>& fds) {
    // The other side of this call is
    // chrome/renderer/renderer_sandbox_support_linux.cc

    int num_chars;
    if (!pickle.ReadInt(&iter, &num_chars))
      return;

    // We don't want a corrupt renderer asking too much of us, it might
    // overflow later in the code.
    static const int kMaxChars = 4096;
    if (num_chars < 1 || num_chars > kMaxChars) {
      LOG(WARNING) << "HandleGetFontFamilyForChars: too many chars: "
                   << num_chars;
      return;
    }

    scoped_array<WebUChar> chars(new WebUChar[num_chars]);

    for (int i = 0; i < num_chars; ++i) {
      uint32_t c;
      if (!pickle.ReadUInt32(&iter, &c)) {
        return;
      }

      chars[i] = c;
    }

    const WebString family = WebFontInfo::familyForChars(chars.get(), num_chars);
    const std::string family_utf8 = UTF16ToUTF8(family);

    Pickle reply;
    reply.WriteString(family_utf8);
    SendRendererReply(fds, reply, -1);
  }

  void SendRendererReply(const std::vector<int>& fds, const Pickle& reply,
                         int reply_fd) {
    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));
    struct iovec iov = {const_cast<void*>(reply.data()), reply.size()};
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    char control_buffer[CMSG_SPACE(sizeof(int))];

    if (reply_fd != -1) {
      struct cmsghdr *cmsg;

      msg.msg_control = control_buffer;
      msg.msg_controllen = sizeof(control_buffer);
      cmsg = CMSG_FIRSTHDR(&msg);
      cmsg->cmsg_level = SOL_SOCKET;
      cmsg->cmsg_type = SCM_RIGHTS;
      cmsg->cmsg_len = CMSG_LEN(sizeof(int));
      memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(int));
      msg.msg_controllen = cmsg->cmsg_len;
    }

    HANDLE_EINTR(sendmsg(fds[0], &msg, MSG_DONTWAIT));
  }

  // ---------------------------------------------------------------------------

  const int lifeline_fd_;
  const int browser_socket_;
  FontConfigDirect* const font_config_;
};

// -----------------------------------------------------------------------------

// Runs on the main thread at startup.
RenderSandboxHostLinux::RenderSandboxHostLinux() {
  int fds[2];
  CHECK(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == 0);

  renderer_socket_ = fds[0];
  const int browser_socket = fds[1];

  int pipefds[2];
  CHECK(0 == pipe(pipefds));
  const int child_lifeline_fd = pipefds[0];
  childs_lifeline_fd_ = pipefds[1];

  const pid_t child = fork();
  if (child == 0) {
    SandboxIPCProcess handler(child_lifeline_fd, browser_socket);
    handler.Run();
    _exit(0);
  }
}

RenderSandboxHostLinux::~RenderSandboxHostLinux() {
  HANDLE_EINTR(close(renderer_socket_));
  HANDLE_EINTR(close(childs_lifeline_fd_));
}
<commit_msg>Linux: don't leak file descriptors in the sandbox host.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"

#include <stdint.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/poll.h>

#include "base/eintr_wrapper.h"
#include "base/process_util.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/pickle.h"
#include "base/string_util.h"
#include "base/unix_domain_socket_posix.h"
#include "chrome/common/sandbox_methods_linux.h"
#include "webkit/api/public/gtk/WebFontInfo.h"
#include "webkit/api/public/WebData.h"
#include "webkit/api/public/WebKit.h"
#include "webkit/api/public/WebKitClient.h"

#include "SkFontHost_fontconfig_direct.h"
#include "SkFontHost_fontconfig_ipc.h"

using WebKit::WebClipboard;
using WebKit::WebData;
using WebKit::WebFontInfo;
using WebKit::WebKitClient;
using WebKit::WebMimeRegistry;
using WebKit::WebPluginInfo;
using WebKit::WebPluginListBuilder;
using WebKit::WebSandboxSupport;
using WebKit::WebString;
using WebKit::WebThemeEngine;
using WebKit::WebUChar;
using WebKit::WebURL;
using WebKit::WebURLLoader;

// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC

// BEWARE: code in this file run across *processes* (not just threads).

// This code runs in a child process
class SandboxIPCProcess : public WebKitClient {
 public:
  // lifeline_fd: this is the read end of a pipe which the browser process
  //   holds the other end of. If the browser process dies, it's descriptors are
  //   closed and we will noticed an EOF on the pipe. That's our signal to exit.
  // browser_socket: the 'browser's end of the sandbox IPC socketpair. From the
  //   point of view of the renderer's, it's talking to the browser but this
  //   object actually services the requests.
  SandboxIPCProcess(int lifeline_fd, int browser_socket)
      : lifeline_fd_(lifeline_fd),
        browser_socket_(browser_socket),
        font_config_(new FontConfigDirect()) {
    WebKit::initialize(this);

    base::InjectiveMultimap multimap;
    multimap.push_back(base::InjectionArc(0, lifeline_fd, false));
    multimap.push_back(base::InjectionArc(0, browser_socket, false));

    base::CloseSuperfluousFds(multimap);
  }

  void Run() {
    struct pollfd pfds[2];
    pfds[0].fd = lifeline_fd_;
    pfds[0].events = POLLIN;
    pfds[1].fd = browser_socket_;
    pfds[1].events = POLLIN;

    bool failed_polls = 0;
    for (;;) {
      const int r = HANDLE_EINTR(poll(pfds, 2, -1));
      if (r < 1) {
        LOG(WARNING) << "poll errno:" << errno;
        if (failed_polls++ == 3) {
          LOG(FATAL) << "poll failing. Sandbox host aborting.";
          return;
        }
        continue;
      }

      failed_polls = 0;

      if (pfds[0].revents) {
        // our parent died so we should too.
        _exit(0);
      }

      if (pfds[1].revents) {
        HandleRequestFromRenderer(browser_socket_);
      }
    }
  }

  // ---------------------------------------------------------------------------
  // WebKitClient impl...

  virtual WebClipboard* clipboard() { return NULL; }
  virtual WebMimeRegistry* mimeRegistry() { return NULL; }
  virtual WebSandboxSupport* sandboxSupport() { return NULL; }
  virtual WebThemeEngine* themeEngine() { return NULL; }

  virtual unsigned long long visitedLinkHash(const char*, size_t) { return 0; }
  virtual bool isLinkVisited(unsigned long long) { return false; }

  virtual void setCookies(const WebURL&, const WebURL&, const WebString&) { }
  virtual WebString cookies(const WebURL&, const WebURL&) { return WebString(); }

  virtual void prefetchHostName(const WebString&) { }

  virtual bool getFileSize(const WebString& path, long long& result) {
    return false;
  }

  virtual WebURLLoader* createURLLoader() { return NULL; }

  virtual void getPluginList(bool refresh, WebPluginListBuilder*) { }

  virtual void decrementStatsCounter(const char*) { }
  virtual void incrementStatsCounter(const char*) { }

  virtual void traceEventBegin(const char* name, void*, const char*) { }
  virtual void traceEventEnd(const char* name, void*, const char*) { }

  virtual WebData loadResource(const char*) { return WebData(); }

  virtual void suddenTerminationChanged(bool) { }

  virtual WebString defaultLocale() { return WebString(); }

  virtual double currentTime() { return 0; }

  virtual void setSharedTimerFiredFunction(void (*)()) { }
  virtual void setSharedTimerFireTime(double) { }
  virtual void stopSharedTimer() { }

  virtual void callOnMainThread(void (*)()) { }

 private:
  // ---------------------------------------------------------------------------
  // Requests from the renderer...

  void HandleRequestFromRenderer(int fd) {
    std::vector<int> fds;
    static const unsigned kMaxMessageLength = 2048;
    char buf[kMaxMessageLength];
    const ssize_t len = base::RecvMsg(fd, buf, sizeof(buf), &fds);
    if (len == -1)
      return;
    if (fds.size() == 0)
      return;

    Pickle pickle(buf, len);
    void* iter = NULL;

    int kind;
    if (!pickle.ReadInt(&iter, &kind))
      goto error;

    if (kind == FontConfigIPC::METHOD_MATCH) {
      HandleFontMatchRequest(fd, pickle, iter, fds);
    } else if (kind == FontConfigIPC::METHOD_OPEN) {
      HandleFontOpenRequest(fd, pickle, iter, fds);
    } else if (kind == LinuxSandbox::METHOD_GET_FONT_FAMILY_FOR_CHARS) {
      HandleGetFontFamilyForChars(fd, pickle, iter, fds);
    }

  error:
    for (std::vector<int>::const_iterator
         i = fds.begin(); i != fds.end(); ++i) {
      close(*i);
    }
  }

  void HandleFontMatchRequest(int fd, Pickle& pickle, void* iter,
                              std::vector<int>& fds) {
    bool fileid_valid;
    uint32_t fileid;
    bool is_bold, is_italic;
    std::string family;

    if (!pickle.ReadBool(&iter, &fileid_valid))
      return;
    if (fileid_valid) {
      if (!pickle.ReadUInt32(&iter, &fileid))
        return;
    }
    if (!pickle.ReadBool(&iter, &is_bold) ||
        !pickle.ReadBool(&iter, &is_italic) ||
        !pickle.ReadString(&iter, &family)) {
      return;
    }

    std::string result_family;
    unsigned result_fileid;

    const bool r = font_config_->Match(
        &result_family, &result_fileid, fileid_valid, fileid, family, &is_bold,
        &is_italic);

    Pickle reply;
    if (!r) {
      reply.WriteBool(false);
    } else {
      reply.WriteBool(true);
      reply.WriteUInt32(result_fileid);
      reply.WriteString(result_family);
      reply.WriteBool(is_bold);
      reply.WriteBool(is_italic);
    }
    SendRendererReply(fds, reply, -1);
  }

  void HandleFontOpenRequest(int fd, Pickle& pickle, void* iter,
                             std::vector<int>& fds) {
    uint32_t fileid;
    if (!pickle.ReadUInt32(&iter, &fileid))
      return;
    const int result_fd = font_config_->Open(fileid);

    Pickle reply;
    if (result_fd == -1) {
      reply.WriteBool(false);
    } else {
      reply.WriteBool(true);
    }

    SendRendererReply(fds, reply, result_fd);

    if (result_fd >= 0)
      close(result_fd);
  }

  void HandleGetFontFamilyForChars(int fd, Pickle& pickle, void* iter,
                                   std::vector<int>& fds) {
    // The other side of this call is
    // chrome/renderer/renderer_sandbox_support_linux.cc

    int num_chars;
    if (!pickle.ReadInt(&iter, &num_chars))
      return;

    // We don't want a corrupt renderer asking too much of us, it might
    // overflow later in the code.
    static const int kMaxChars = 4096;
    if (num_chars < 1 || num_chars > kMaxChars) {
      LOG(WARNING) << "HandleGetFontFamilyForChars: too many chars: "
                   << num_chars;
      return;
    }

    scoped_array<WebUChar> chars(new WebUChar[num_chars]);

    for (int i = 0; i < num_chars; ++i) {
      uint32_t c;
      if (!pickle.ReadUInt32(&iter, &c)) {
        return;
      }

      chars[i] = c;
    }

    const WebString family = WebFontInfo::familyForChars(chars.get(), num_chars);
    const std::string family_utf8 = UTF16ToUTF8(family);

    Pickle reply;
    reply.WriteString(family_utf8);
    SendRendererReply(fds, reply, -1);
  }

  void SendRendererReply(const std::vector<int>& fds, const Pickle& reply,
                         int reply_fd) {
    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));
    struct iovec iov = {const_cast<void*>(reply.data()), reply.size()};
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    char control_buffer[CMSG_SPACE(sizeof(int))];

    if (reply_fd != -1) {
      struct cmsghdr *cmsg;

      msg.msg_control = control_buffer;
      msg.msg_controllen = sizeof(control_buffer);
      cmsg = CMSG_FIRSTHDR(&msg);
      cmsg->cmsg_level = SOL_SOCKET;
      cmsg->cmsg_type = SCM_RIGHTS;
      cmsg->cmsg_len = CMSG_LEN(sizeof(int));
      memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(int));
      msg.msg_controllen = cmsg->cmsg_len;
    }

    HANDLE_EINTR(sendmsg(fds[0], &msg, MSG_DONTWAIT));
  }

  // ---------------------------------------------------------------------------

  const int lifeline_fd_;
  const int browser_socket_;
  FontConfigDirect* const font_config_;
};

// -----------------------------------------------------------------------------

// Runs on the main thread at startup.
RenderSandboxHostLinux::RenderSandboxHostLinux() {
  int fds[2];
  CHECK(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == 0);

  renderer_socket_ = fds[0];
  const int browser_socket = fds[1];

  int pipefds[2];
  CHECK(0 == pipe(pipefds));
  const int child_lifeline_fd = pipefds[0];
  childs_lifeline_fd_ = pipefds[1];

  const pid_t child = fork();
  if (child == 0) {
    SandboxIPCProcess handler(child_lifeline_fd, browser_socket);
    handler.Run();
    _exit(0);
  }
}

RenderSandboxHostLinux::~RenderSandboxHostLinux() {
  HANDLE_EINTR(close(renderer_socket_));
  HANDLE_EINTR(close(childs_lifeline_fd_));
}
<|endoftext|>


<commit_before>#include <QApplication>
#include <QObject>
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QPixmap>
#include <QFont>
#include <QSocketNotifier>
#include <QTextStream>
#include <QString>

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <arpa/inet.h>

using std::string;

struct Track {
    string title;
    string artist;
    string release;
    double progress; // between 0 and 1
} track;

string client_ip;
string client_name;
string file_type;

QLabel *title_label;
QLabel *artist_label;
QLabel *release_label;
QLabel *file_type_label;
QLabel *client_ip_label;

QFont* standard_font;
QFont* em_font;

// From Stack Overflow, with thanks:
// http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c
// minor mods to make independent of C99.
// more significant changes make it not malloc memory
// needs to initialise the docoding table first

static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3',
    '4', '5', '6', '7', '8', '9', '+', '/'};
static int decoding_table[256]; // an incoming char can range over ASCII, but by mistake could be all 8 bits.

static int mod_table[] = {0, 2, 1};

void initialise_decoding_table() {
    int i;
    for (i = 0; i < 64; i++)
        decoding_table[(unsigned char) encoding_table[i]] = i;
}

// pass in a pointer to the data, its length, a pointer to the output buffer and a pointer to an int containing its maximum length
// the actual length will be returned.

char *base64_encode(const unsigned char *data,
                    size_t input_length,
                    char *encoded_data,
                    size_t *output_length) {

    size_t calculated_output_length = 4 * ((input_length + 2) / 3);
    if (calculated_output_length> *output_length)
        return(NULL);
    *output_length = calculated_output_length;

    int i,j;
    for (i = 0, j = 0; i < input_length;) {

        uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
        uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
        uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;

        uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

        encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
        encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
        encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
        encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
    }

    for (i = 0; i < mod_table[input_length % 3]; i++)
        encoded_data[*output_length - 1 - i] = '=';

    return encoded_data;
}

// pass in a pointer to the data, its length, a pointer to the output buffer and a pointer to an int containing its maximum length
// the actual length will be returned.
int base64_decode(const char *data,
                  size_t input_length,
                  unsigned char *decoded_data,
                  size_t *output_length) {

    //remember somewhere to call initialise_decoding_table();

    if (input_length % 4 != 0) return -1;

    size_t calculated_output_length = input_length / 4 * 3;
    if (data[input_length - 1] == '=') calculated_output_length--;
    if (data[input_length - 2] == '=') calculated_output_length--;
    if (calculated_output_length> *output_length)
        return(-1);
    *output_length = calculated_output_length;

    int i,j;
    for (i = 0, j = 0; i < input_length;) {

        uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
        uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
        uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
        uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];

        uint32_t triple = (sextet_a << 3 * 6)
        + (sextet_b << 2 * 6)
        + (sextet_c << 1 * 6)
        + (sextet_d << 0 * 6);

        if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
        if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
        if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
    }

    return 0;
}

void initialise_track_object()
{
    track.title.clear();
    track.artist.clear();
    track.release.clear();
    track.progress = -1; // No metadata
    client_ip.clear();
    client_name.clear();
    file_type.clear();
}

void update_metadata()
{
    title_label->setText(QString::fromStdString(track.title));
    artist_label->setText(QString::fromStdString(track.artist));
    release_label->setText(QString::fromStdString(track.release));

    if (file_type.length() != 0) {
        file_type_label->setText("Fichier " + QString::fromStdString(file_type));
    } else {
        file_type_label->setText("");
    }

    if (client_ip.length() != 0) {
        client_ip_label->setText("En streaming depuis " + QString::fromStdString(client_ip));
        client_ip_label->setFont(*standard_font);
    } else {
        client_ip_label->setText("Pas de streaming en cours");
        client_ip_label->setFont(*em_font);
    }
}

void dataReceived(QString message)
{
    uint32_t type,code,length;
    char tagend[1024];
    int ret = sscanf(message.toAscii().data(),"<item><type>%8x</type><code>%8x</code><length>%u</length>",&type,&code,&length);
    if (ret==3) {
        // now, think about processing the tag.
        // basically, we need to get hold of the base-64 data, if any
        size_t outputlength=0;
        char payload[32769];
//        if (length>0) {
//            // get the next line, which should be a data tag
//            char datatagstart[64],datatagend[64];
//            memset(datatagstart,0,64);
//            int rc = fscanf(stdin,"<data encoding=\"base64\">");
//            if (rc==0) {
//                // now, read in that big (possibly) base64 buffer
//                int c = fgetc(stdin);
//                uint32_t b64size = 4*((length+2)/3);
//                char * b64buf = malloc(b64size+1);
//                memset(b64buf,0,b64size+1);
//                if (b64buf) {
//                    if (fgets(b64buf, b64size+1, stdin)!=NULL) {
//                        // it looks like we got it
//                        // printf("Looks like we got it, with a buffer size of %u.\n",b64size);
//                        //puts(b64buf);
//                        //printf("\n");
//                        // now, if it's not a picture, let's try to decode it.
//                        if (code!='PICT') {
//                            int inputlength=32678;
//                            if (b64size<inputlength)
//                                inputlength=b64size;
//                            outputlength=32768;
//                            if (base64_decode(b64buf,inputlength,payload,&outputlength)!=0) {
//                                printf("Failed to decode it.\n");
//                            }
//                        }
//                    }
//                    free(b64buf);
//                } else {
//                    printf("couldn't allocate memory for base-64 stuff\n");
//                }
//                rc = fscanf(stdin,"%64s",datatagend);
//                if (strcmp(datatagend,"</data></item>")!=0)
//                    printf("End data tag not seen, \"%s\" seen instead.\n",datatagend);
//            }
//        }
//
//        // printf("Got it decoded. Length of decoded string is %u bytes.\n",outputlength);
//        payload[outputlength]=0;

        // this has more information about tags, which might be relevant:
        // https://code.google.com/p/ytrack/wiki/DMAP
        switch (code) {
            case 'asal':
                track.release = payload;
                printf("Album Name: \"%s\".\n",payload);
                break;
            case 'asar':
                track.artist = payload;
                printf("Artist: \"%s\".\n",payload);
                break;
            case 'minm':
                track.title = payload;
                printf("Title: \"%s\".\n",payload);
                break;
            case 'asdt':
                file_type = payload;
                printf("File kind: \"%s\".\n",payload);
                break;
            case 'PICT':
                printf("Picture received, length %u bytes.\n",length);
                break;
            case 'clip':
                client_ip = payload;
                printf("Client's IP: \"%s\".\n",payload);
                break;
            case 'snam':
                client_name = payload;
                printf("Device Name: \"%s\".\n",payload);
                break;
            case 'snua':
                client_name = payload;
                printf("User Agent: \"%s\".\n",payload);
                break;
            default: if (type=='ssnc') {
                char typestring[5];
                *(uint32_t*)typestring = htonl(type);
                typestring[4]=0;
                char codestring[5];
                *(uint32_t*)codestring = htonl(code);
                codestring[4]=0;
                printf("\"%s\" \"%s\": \"%s\".\n",typestring,codestring,payload);
            }
        }
    } else {
        printf("\nXXX Could not decipher: \"%s\".\n",message.toAscii().data());
    }

    update_metadata();
}

void onData()
{
    QTextStream qin(stdin);
    QString line = qin.readLine();
    emit dataReceived(line);
}

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    // Initialize the Track object with empty strings and the like
    initialise_track_object();

    fd_set rfds;
    initialise_decoding_table();

    // Connect to STDIN signal
    QSocketNotifier *streamReader = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, qApp);
    QObject::connect(streamReader, SIGNAL(activated(int)), qApp, SLOT(onData()));
    streamReader->setEnabled(true);

    // Create the main fullscreen window
    QWidget main_window;
    main_window.setStyleSheet("background-color: black; color: white;");

    // QPixmap Image;
    // Image.load("1837.jpg");

    // Create Fonts
    standard_font = new QFont("Courier New");
    standard_font->setPixelSize(20);

    em_font = new QFont("Courier New");
    em_font->setPixelSize(20);
    em_font->setItalic(true);

    // Add labels
    title_label = new QLabel("Titre", &main_window);
    title_label->setFont(*standard_font);
    artist_label = new QLabel("Artiste", &main_window);
    artist_label->setFont(*standard_font);
    release_label = new QLabel("Album", &main_window);
    release_label->setFont(*standard_font);
    file_type_label = new QLabel("Fichier MP3", &main_window);
    file_type_label->setFont(*standard_font);
    client_ip_label = new QLabel("En streaming depuis 192.168.1.24", &main_window);
    client_ip_label->setFont(*standard_font);

    // Add "quit" button
    QPushButton *quit_button = new QPushButton("Quit" , &main_window);
    QObject::connect(quit_button , SIGNAL(clicked()), qApp , SLOT(quit()));

    QVBoxLayout *vbl = new QVBoxLayout(&main_window);
    vbl->addWidget(title_label);
    vbl->addWidget(artist_label);
    vbl->addWidget(release_label);
    vbl->addWidget(file_type_label);
    vbl->addWidget(client_ip_label);
    vbl->addWidget(quit_button);

    main_window.showFullScreen();

    return app.exec();
}
<commit_msg>Remove old file<commit_after><|endoftext|>


<commit_before>#include <cmath>
#include <iostream>
#include <stdexcept>

#include <pqxx/connection>
#include <pqxx/result>
#include <pqxx/transaction>

using namespace PGSTD;
using namespace pqxx;

// Test program for libpqxx.  Test fieldstream.
//
// Usage: test074 [connect-string]
//
// Where connect-string is a set of connection options in Postgresql's
// PQconnectdb() format, eg. "dbname=template1" to select from a database
// called template1, or "host=foo.bar.net user=smith" to connect to a backend
// running on host foo.bar.net, logging in as user smith.
int main(int, char *argv[])
{
  try
  {
    connection C(argv[1]);
    work W(C, "test74");
    result R = W.exec("SELECT * FROM pg_tables");
    const string sval = R.at(0).at(1).c_str();
    string sval2;
    fieldstream fs1(R[0][1]);
    fs1 >> sval2;
    if (sval2 != sval)
      throw logic_error("Got '" + sval + "' from field, "
	  "but '" + sval2 + "' from fieldstream");

    R = W.exec("SELECT count(*) FROM pg_tables");
    int ival;
    fieldstream fs2(R.at(0).at(0));
    fs2 >> ival;
    if (ival != R[0][0].as<int>())
      throw logic_error("Got int " + to_string(ival) + " from fieldstream, "
	  "but " + to_string(R[0][0].as<int>()) + " from field");

    double dval;
    (fieldstream(R.at(0).at(0))) >> dval;
    if (fabs(dval - R[0][0].as<double>()) > 0.1)
      throw logic_error("Got double " + to_string(dval) + " from fieldstream, "
	  "but " + to_string(R[0][0].as<double>()) + " from field");

    const float roughpi = 3.1415926435;
    R = W.exec("SELECT " + to_string(roughpi));
    float pival;
    (fieldstream(R.at(0).at(0))) >> pival;
    if (fabs(pival - roughpi) > 0.001)
      throw logic_error("Pi approximation came back as " + to_string(roughpi));

    if (to_string(R[0][0]) != R[0][0].c_str())
      throw logic_error("to_string(result::field) "
	  "inconsistent with to_string(const char[])");

    if (to_string(roughpi) != to_string(double(roughpi)))
      throw logic_error("to_string(float) inconsistent with to_string(double)");
    const long double ld = roughpi;
    if (to_string(roughpi) != to_string(ld))
      throw logic_error("to_string(float) "
	  "inconsistent with to_string(long double)");
  }
  catch (const sql_error &e)
  {
    cerr << "Database error: " << e.what() << endl
         << "Query was: " << e.query() << endl;
    return 2;
  }
  catch (const exception &e)
  {
    cerr << "Exception: " << e.what() << endl;
    return 2;
  }
  catch (...)
  {
    cerr << "Unhandled exception" << endl;
    return 100;
  }

  return 0;
}

<commit_msg>Test result::front(), result::tuple::front()<commit_after>#include <cmath>
#include <iostream>
#include <stdexcept>

#include <pqxx/connection>
#include <pqxx/result>
#include <pqxx/transaction>

using namespace PGSTD;
using namespace pqxx;

// Test program for libpqxx.  Test fieldstream.
//
// Usage: test074 [connect-string]
//
// Where connect-string is a set of connection options in Postgresql's
// PQconnectdb() format, eg. "dbname=template1" to select from a database
// called template1, or "host=foo.bar.net user=smith" to connect to a backend
// running on host foo.bar.net, logging in as user smith.
int main(int, char *argv[])
{
  try
  {
    connection C(argv[1]);
    work W(C, "test74");
    result R = W.exec("SELECT * FROM pg_tables");
    const string sval = R.at(0).at(1).c_str();
    string sval2;
    fieldstream fs1(R.front()[1]);
    fs1 >> sval2;
    if (sval2 != sval)
      throw logic_error("Got '" + sval + "' from field, "
	  "but '" + sval2 + "' from fieldstream");

    R = W.exec("SELECT count(*) FROM pg_tables");
    int ival;
    fieldstream fs2(R.at(0).at(0));
    fs2 >> ival;
    if (ival != R.front().front().as<int>())
      throw logic_error("Got int " + to_string(ival) + " from fieldstream, "
	  "but " + to_string(R.front().front().as<int>()) + " from field");

    double dval;
    (fieldstream(R.at(0).at(0))) >> dval;
    if (fabs(dval - R[0][0].as<double>()) > 0.1)
      throw logic_error("Got double " + to_string(dval) + " from fieldstream, "
	  "but " + to_string(R[0][0].as<double>()) + " from field");

    const float roughpi = 3.1415926435;
    R = W.exec("SELECT " + to_string(roughpi));
    float pival;
    (fieldstream(R.at(0).at(0))) >> pival;
    if (fabs(pival - roughpi) > 0.001)
      throw logic_error("Pi approximation came back as " + to_string(roughpi));

    if (to_string(R[0][0]) != R[0][0].c_str())
      throw logic_error("to_string(result::field) "
	  "inconsistent with to_string(const char[])");

    if (to_string(roughpi) != to_string(double(roughpi)))
      throw logic_error("to_string(float) inconsistent with to_string(double)");
    const long double ld = roughpi;
    if (to_string(roughpi) != to_string(ld))
      throw logic_error("to_string(float) "
	  "inconsistent with to_string(long double)");
  }
  catch (const sql_error &e)
  {
    cerr << "Database error: " << e.what() << endl
         << "Query was: " << e.query() << endl;
    return 2;
  }
  catch (const exception &e)
  {
    cerr << "Exception: " << e.what() << endl;
    return 2;
  }
  catch (...)
  {
    cerr << "Unhandled exception" << endl;
    return 100;
  }

  return 0;
}

<|endoftext|>


<commit_before>#include <nntpchan/event.hpp>
#include <sys/types.h>
#include <sys/event.h>

#include <iostream>

namespace nntpchan
{
namespace ev
{
    template<size_t bufsz>
    struct KqueueLoop : public Loop
    {
        int kfd;
        size_t conns;
        char readbuf[bufsz];

        KqueueLoop() : kfd(kqueue()), conns(0)
        {

        };

        virtual ~KqueueLoop()
        {
            ::close(kfd);
        }

        virtual bool TrackConn(ev::io * handler)
        {
            struct kevent event;
            short filter = 0;
            if(handler->readable() || handler->acceptable())
            {
                filter |= EVFILT_READ;
            }
            if(handler->writeable())
            {
                filter |= EVFILT_WRITE;
            }
            EV_SET(&event, handler->fd, filter, EV_ADD | EV_CLEAR, 0, 0, handler);
            int ret = kevent(kfd, &event, 1, nullptr, 0, nullptr);
            if(ret == -1) return false;
            if(event.flags & EV_ERROR)
            {
                std::cerr << "KqueueLoop::TrackConn() kevent failed: " << strerror(event.data) << std::endl;
                return false;
            }
            ++conns;
            return true;
        }

        virtual void UntrackConn(ev::io * handler)
        {
            struct kevent event;
            short filter = 0;
            if(handler->readable() || handler->acceptable())
            {
                filter |= EVFILT_READ;
            }
            if(handler->writeable())
            {
                filter |= EVFILT_WRITE;
            }
            EV_SET(&event, handler->fd, filter, EV_DELETE, 0, 0, handler);
            int ret = kevent(kfd, &event, 1, nullptr, 0, nullptr);
            if(ret == -1 || event.flags & EV_ERROR)
                std::cerr << "KqueueLoop::UntrackConn() kevent failed: " << strerror(event.data) << std::endl;
            else
                --conns;
        }

        virtual void Run()
        {
            struct kevent events[512];
            struct kevent * event;
            io * handler;
            int ret, idx;
            do
            {
                idx = 0;
                ret = kevent(kfd, nullptr, 0, events, 512, nullptr);
                if(ret > 0)
                {
                    while(idx < ret)
                    {
                        event = &events[idx++];
                        handler = static_cast<io *>(event->udata);
                        if(event->flags & EV_EOF)
                        {
                            handler->close();
                            delete handler;
                            continue;
                        }
                        if(event->filter & EVFILT_READ && handler->acceptable())
                        {
                            int backlog = event->data;
                            while(backlog)
                            {
                                handler->accept();
                                --backlog;
                            }
                        }

                        if(event->filter & EVFILT_READ && handler->readable())
                        {
                            int readed = 0;
                            size_t readnum = event->data;
                            while(readnum > sizeof(readbuf))
                            {
                                int r = handler->read(readbuf, sizeof(readbuf));
                                if(r > 0)
                                {
                                    readnum -= r;
                                    readed += r;
                                }
                                else
                                    readnum = 0;
                            }
                            if(readnum && readed != -1)
                            {
                               int r = handler->read(readbuf, readnum);
                               if(r > 0)
                                 readed += r;
                               else
                                 readed = r;
                            }
                        }
                        if(event->filter & EVFILT_WRITE && handler->writeable())
                        {
                            int writespace = event->data;
                            int written = handler->write(writespace);
                            std::cerr << "writen " << written << " of " >> writespace << std::endl;
                        }
                        if(!handler->keepalive())
                        {
                            handler->close();
                            delete handler;
                        }
                    }
                }
            }
            while(ret != -1);
        }
    };
}
}<commit_msg>debug<commit_after>#include <nntpchan/event.hpp>
#include <sys/types.h>
#include <sys/event.h>

#include <iostream>

namespace nntpchan
{
namespace ev
{
    template<size_t bufsz>
    struct KqueueLoop : public Loop
    {
        int kfd;
        size_t conns;
        char readbuf[bufsz];

        KqueueLoop() : kfd(kqueue()), conns(0)
        {

        };

        virtual ~KqueueLoop()
        {
            ::close(kfd);
        }

        virtual bool TrackConn(ev::io * handler)
        {
            struct kevent event;
            short filter = 0;
            if(handler->readable() || handler->acceptable())
            {
                filter |= EVFILT_READ;
            }
            if(handler->writeable())
            {
                filter |= EVFILT_WRITE;
            }
            EV_SET(&event, handler->fd, filter, EV_ADD | EV_CLEAR, 0, 0, handler);
            int ret = kevent(kfd, &event, 1, nullptr, 0, nullptr);
            if(ret == -1) return false;
            if(event.flags & EV_ERROR)
            {
                std::cerr << "KqueueLoop::TrackConn() kevent failed: " << strerror(event.data) << std::endl;
                return false;
            }
            ++conns;
            return true;
        }

        virtual void UntrackConn(ev::io * handler)
        {
            struct kevent event;
            short filter = 0;
            if(handler->readable() || handler->acceptable())
            {
                filter |= EVFILT_READ;
            }
            if(handler->writeable())
            {
                filter |= EVFILT_WRITE;
            }
            EV_SET(&event, handler->fd, filter, EV_DELETE, 0, 0, handler);
            int ret = kevent(kfd, &event, 1, nullptr, 0, nullptr);
            if(ret == -1 || event.flags & EV_ERROR)
                std::cerr << "KqueueLoop::UntrackConn() kevent failed: " << strerror(event.data) << std::endl;
            else
                --conns;
        }

        virtual void Run()
        {
            struct kevent events[512];
            struct kevent * event;
            io * handler;
            int ret, idx;
            do
            {
                idx = 0;
                ret = kevent(kfd, nullptr, 0, events, 512, nullptr);
                if(ret > 0)
                {
                    while(idx < ret)
                    {
                        event = &events[idx++];
                        handler = static_cast<io *>(event->udata);
                        if(event->flags & EV_EOF)
                        {
                            handler->close();
                            delete handler;
                            continue;
                        }
                        if(event->filter & EVFILT_READ && handler->acceptable())
                        {
                            int backlog = event->data;
                            while(backlog)
                            {
                                handler->accept();
                                --backlog;
                            }
                        }

                        if(event->filter & EVFILT_READ && handler->readable())
                        {
                            int readed = 0;
                            size_t readnum = event->data;
                            while(readnum > sizeof(readbuf))
                            {
                                int r = handler->read(readbuf, sizeof(readbuf));
                                if(r > 0)
                                {
                                    readnum -= r;
                                    readed += r;
                                }
                                else
                                    readnum = 0;
                            }
                            if(readnum && readed != -1)
                            {
                               int r = handler->read(readbuf, readnum);
                               if(r > 0)
                                 readed += r;
                               else
                                 readed = r;
                            }
                        }
                        if(event->filter & EVFILT_WRITE && handler->writeable())
                        {
                            int writespace = event->data;
                            int written = handler->write(writespace);
                            std::cerr << "writen " << written << " of " << writespace << std::endl;
                        }
                        if(!handler->keepalive())
                        {
                            handler->close();
                            delete handler;
                        }
                    }
                }
            }
            while(ret != -1);
        }
    };
}
}<|endoftext|>


<commit_before>#include "Players/Minimax_AI.h"

#include <iostream>

#include "Players/Game_Tree_Node_Result.h"
#include "Game/Board.h"
#include "Game/Clock.h"
#include "Game/Game_Result.h"
#include "Moves/Move.h"

#include "Utility.h"

const Move& Minimax_AI::choose_move(const Board& board, const Clock& clock) const
{
    // Erase data from previous board when starting new game
    if(board.game_record().size() <= 1)
    {
        principal_variation.clear();
        commentary.clear();
    }

    nodes_searched = 0;
    clock_start_time = clock.time_left(clock.running_for());
    maximum_depth = 0;

    nodes_evaluated = 0;
    total_evaluation_time = 0.0;
    time_at_last_output = clock.time_left(clock.running_for());

    const auto& legal_moves = board.legal_moves();
    if(legal_moves.size() == 1)
    {
        if(principal_variation.size() > 2 && principal_variation[1] == board.game_record().back())
        {
            // search_game_tree() assumes the principal variation starts
            // with the previous move of this player. If a move was forced,
            // then the principal variation needs to be updated to start with
            // the next move of this side after checking that the immediately
            // preceding move was the expected one.
            principal_variation.erase(principal_variation.begin(),
                                      principal_variation.begin() + 2);
        }
        else
        {
            principal_variation.clear();
        }

        commentary.push_back(principal_variation);

        return *legal_moves.front(); // If there's only one legal move, take it.
    }

    auto time_to_use = time_to_examine(board, clock);

    // alpha = highest score found that opponent will allow
    Game_Tree_Node_Result alpha_start = {Math::lose_score,
                                         board.whose_turn(),
                                         {}};

    // beta = score that will cause opponent to choose a different prior move
    Game_Tree_Node_Result beta_start = {Math::win_score,
                                        board.whose_turn(),
                                        {}};

    auto result = search_game_tree(board,
                                   time_to_use,
                                   clock,
                                   1,
                                   alpha_start,
                                   beta_start,
                                   ! principal_variation.empty());

    if(board.thinking_mode() == CECP)
    {
        output_thinking_cecp(result, clock, board.whose_turn());
    }

    if(result.depth() > 0)
    {
        commentary.push_back(result.variation);
    }
    else
    {
        commentary.push_back({});
    }

    if(result.depth() > 1)
    {
        principal_variation = result.variation;
    }
    else
    {
        principal_variation.clear();
    }

    if(nodes_evaluated > 0)
    {
        evaluation_speed = nodes_evaluated / total_evaluation_time;
    }

    return *result.variation.front();
}

Game_Tree_Node_Result Minimax_AI::search_game_tree(const Board& board,
                                                   const double time_to_examine,
                                                   const Clock& clock,
                                                   const size_t depth,
                                                   Game_Tree_Node_Result alpha,
                                                   const Game_Tree_Node_Result& beta,
                                                   bool still_on_principal_variation) const
{
    const auto time_start = clock.time_left(clock.running_for());
    maximum_depth = std::max(maximum_depth, depth);
    auto all_legal_moves = board.legal_moves();

    // The two items in the principal variation are the last two moves of
    // the non-hypothetical board. So, the first item in the principal variation to
    // consider is at index depth + 1 (since depth starts at 1).
    if(still_on_principal_variation && principal_variation.size() > depth + 1)
    {
        auto next_principal_variation_move = principal_variation[depth + 1];
        auto move_iter = std::find(all_legal_moves.begin(),
                                   all_legal_moves.end(),
                                   next_principal_variation_move);

        if(move_iter != all_legal_moves.end())
        {
            // Put principal variation move at start of list to allow
            // the most pruning later.
            std::iter_swap(all_legal_moves.begin(), move_iter);
        }
    }
    else
    {
        still_on_principal_variation = false;
    }

    // Consider capturing moves first after principal variation move
    auto partition_start = std::next(all_legal_moves.begin(), still_on_principal_variation ? 1 : 0);
    std::partition(partition_start, all_legal_moves.end(),
                   [&board](auto move){ return board.move_captures(*move); });

    const auto perspective = board.whose_turn();
    auto moves_left = all_legal_moves.size();

    Game_Tree_Node_Result best_result = {Math::lose_score,
                                         perspective,
                                         {all_legal_moves.front()}};

    for(const auto& move : all_legal_moves)
    {
        auto evaluate_start_time = clock.time_left(clock.running_for());
        ++nodes_searched;

        auto next_board = board;

        auto move_result = next_board.submit_move(*move);
        if(move_result.winner() != NONE)
        {
            // This move results in checkmate, no other move can be better.
            return create_result(next_board, perspective, move_result, depth);
        }

        if(alpha.depth() <= depth + 2 && alpha.is_winning_for(perspective))
        {
            // This move will take a longer path to victory
            // than one already found. Use "depth + 2" since,
            // if this move isn't winning (and it isn't, since
            // we're here), then the earliest move that can
            // win is the next one, which is two away (after
            // opponent's move).
            continue;
        }

        double time_left = time_to_examine - (time_start - clock.time_left(clock.running_for()));
        double time_allotted_for_this_move = (time_left / moves_left)*speculation_time_factor(next_board);
        time_allotted_for_this_move = std::min(time_allotted_for_this_move, clock.time_left(clock.running_for()));

        bool recurse;
        if(move_result.game_has_ended())
        {
            recurse = false;
        }
        else if(still_on_principal_variation)
        {
            recurse = true;
        }
        else if(depth > 500)
        {
            recurse = false; // prevent stack overflow
        }
        else
        {
            auto minimum_time_to_recurse = next_board.legal_moves().size() / evaluation_speed;
            recurse = (time_allotted_for_this_move > minimum_time_to_recurse);
        }

        Game_Tree_Node_Result result;
        if(recurse)
        {
            result = search_game_tree(next_board,
                                      time_allotted_for_this_move,
                                      clock,
                                      depth + 1,
                                      beta,
                                      alpha,
                                      still_on_principal_variation);
        }
        else
        {
            // Record immediate result without looking ahead further
            result = create_result(next_board, perspective, move_result, depth);
        }

        if(better_than(result, best_result, perspective))
        {
            best_result = result;

            if(better_than(best_result, alpha, perspective))
            {
                alpha = best_result;
                if(better_than(alpha, beta, perspective) || alpha == beta)
                {
                    break;
                }
                else if(board.thinking_mode() == CECP && time_since_last_output(clock) > 0.1)
                {
                    output_thinking_cecp(alpha, clock,
                                         depth % 2 == 1 ? perspective : opposite(perspective));
                    time_at_last_output = clock.time_left(clock.running_for());
                }
            }
        }

        --moves_left;
        still_on_principal_variation = false; // only the first move is part of the principal variation

        if(clock.time_left(clock.running_for()) < 0)
        {
            break;
        }

        if( ! recurse) // This move was scored by genome.evaluate().
        {
            ++nodes_evaluated;
            total_evaluation_time += evaluate_start_time - clock.time_left(clock.running_for());
        }
    }

    return best_result;
}

void Minimax_AI::output_thinking_cecp(const Game_Tree_Node_Result& thought,
                                      const Clock& clock,
                                      Color perspective) const
{
    auto score = thought.corrected_score(perspective) / centipawn_value();

    // Indicate "mate in N moves" where N == thought.depth
    if(thought.is_winning_for(perspective))
    {
        score = 10000.0 - thought.depth() + 1;
    }
    else if(thought.is_losing_for(perspective))
    {
        score = -(10000.0 - thought.depth() + 1);
    }

    auto time_so_far = clock_start_time - clock.time_left(clock.running_for());
    std::cout << thought.depth() // ply
        << " "
        << int(score) // score in what should be centipawns
        << " "
        << int(time_so_far * 100) // search time in centiseconds
        << " "
        << nodes_searched
        << " "
        << maximum_depth
        << " "
        << int(nodes_searched / time_so_far)
        << '\t';

    // Principal variation
    for(const auto& move : thought.variation)
    {
        std::cout << move->coordinate_move() << ' ';
    }

    std::cout << std::endl;
}

double Minimax_AI::time_since_last_output(const Clock& clock) const
{
    return time_at_last_output - clock.time_left(clock.running_for());
}

Game_Tree_Node_Result Minimax_AI::create_result(const Board& board,
                                                Color perspective,
                                                Game_Result move_result,
                                                size_t depth) const
{
    return {evaluate(board, move_result, perspective, depth),
            perspective,
            {board.game_record().end() - depth,
            board.game_record().end()}};
}

void Minimax_AI::calibrate_thinking_speed() const
{
    evaluation_speed = 100; // very conservative initial guess
    auto calibration_time = 1.0; // seconds
    Board board;
    Clock clock(calibration_time, 1, 0.0);
    clock.start();
    choose_move(board, clock);
    // choose_move() keeps track of the time it takes and the number of positions
    // it sees, so this practice move will update the positions_per_second to a
    // more reasonable value.
}

double Minimax_AI::evaluate(const Board & board, Game_Result move_result, Color perspective, size_t depth) const
{
    if(move_result.game_has_ended())
    {
        if(move_result.winner() == NONE) // stalemate
        {
            return 0;
        }
        else if(move_result.winner() == perspective) // checkmate win
        {
            return Math::win_score;
        }
        else // checkmate loss
        {
            return Math::lose_score;
        }
    }

    return internal_evaluate(board, perspective, depth);
}

double Minimax_AI::centipawn_value() const
{
    return value_of_centipawn;
}

void Minimax_AI::calculate_centipawn_value()
{
    auto board_with_pawns = Board("4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1");
    auto board_with_no_white_pawns = Board("4k3/pppppppp/8/8/8/8/8/4K3 w - - 0 1");
    value_of_centipawn = std::abs(evaluate(board_with_pawns, {}, WHITE, 0) - evaluate(board_with_no_white_pawns, {}, WHITE, 0)) / 800;
}

std::string Minimax_AI::commentary_for_move(size_t move_number) const
{
    std::string result;
    if(move_number < commentary.size() && !commentary.at(move_number).empty())
    {
        result = commentary.at(move_number).front()->coordinate_move();
        for(size_t i = 1; i < commentary.at(move_number).size(); ++i)
        {
            result += " " + commentary.at(move_number).at(i)->coordinate_move();
        }
    }

    return result;
}
<commit_msg>Change move reordering logic<commit_after>#include "Players/Minimax_AI.h"

#include <iostream>

#include "Players/Game_Tree_Node_Result.h"
#include "Game/Board.h"
#include "Game/Clock.h"
#include "Game/Game_Result.h"
#include "Moves/Move.h"

#include "Utility.h"

const Move& Minimax_AI::choose_move(const Board& board, const Clock& clock) const
{
    // Erase data from previous board when starting new game
    if(board.game_record().size() <= 1)
    {
        principal_variation.clear();
        commentary.clear();
    }

    nodes_searched = 0;
    clock_start_time = clock.time_left(clock.running_for());
    maximum_depth = 0;

    nodes_evaluated = 0;
    total_evaluation_time = 0.0;
    time_at_last_output = clock.time_left(clock.running_for());

    const auto& legal_moves = board.legal_moves();
    if(legal_moves.size() == 1)
    {
        if(principal_variation.size() > 2 && principal_variation[1] == board.game_record().back())
        {
            // search_game_tree() assumes the principal variation starts
            // with the previous move of this player. If a move was forced,
            // then the principal variation needs to be updated to start with
            // the next move of this side after checking that the immediately
            // preceding move was the expected one.
            principal_variation.erase(principal_variation.begin(),
                                      principal_variation.begin() + 2);
        }
        else
        {
            principal_variation.clear();
        }

        commentary.push_back(principal_variation);

        return *legal_moves.front(); // If there's only one legal move, take it.
    }

    auto time_to_use = time_to_examine(board, clock);

    // alpha = highest score found that opponent will allow
    Game_Tree_Node_Result alpha_start = {Math::lose_score,
                                         board.whose_turn(),
                                         {}};

    // beta = score that will cause opponent to choose a different prior move
    Game_Tree_Node_Result beta_start = {Math::win_score,
                                        board.whose_turn(),
                                        {}};

    auto result = search_game_tree(board,
                                   time_to_use,
                                   clock,
                                   1,
                                   alpha_start,
                                   beta_start,
                                   ! principal_variation.empty());

    if(board.thinking_mode() == CECP)
    {
        output_thinking_cecp(result, clock, board.whose_turn());
    }

    if(result.depth() > 0)
    {
        commentary.push_back(result.variation);
    }
    else
    {
        commentary.push_back({});
    }

    if(result.depth() > 1)
    {
        principal_variation = result.variation;
    }
    else
    {
        principal_variation.clear();
    }

    if(nodes_evaluated > 0)
    {
        evaluation_speed = nodes_evaluated / total_evaluation_time;
    }

    return *result.variation.front();
}

Game_Tree_Node_Result Minimax_AI::search_game_tree(const Board& board,
                                                   const double time_to_examine,
                                                   const Clock& clock,
                                                   const size_t depth,
                                                   Game_Tree_Node_Result alpha,
                                                   const Game_Tree_Node_Result& beta,
                                                   bool still_on_principal_variation) const
{
    const auto time_start = clock.time_left(clock.running_for());
    maximum_depth = std::max(maximum_depth, depth);
    auto all_legal_moves = board.legal_moves();

    // The two items in the principal variation are the last two moves of
    // the non-hypothetical board. So, the first item in the principal variation to
    // consider is at index depth + 1 (since depth starts at 1).
    still_on_principal_variation = (still_on_principal_variation && principal_variation.size() > depth + 1);
    auto found_principal_variation_move = still_on_principal_variation;
    if(still_on_principal_variation)
    {
        auto next_principal_variation_move = principal_variation[depth + 1];
        auto move_iter = std::find(all_legal_moves.begin(),
                                   all_legal_moves.end(),
                                   next_principal_variation_move);

        found_principal_variation_move = (move_iter != all_legal_moves.end());
        if(found_principal_variation_move)
        {
            // Put principal variation move at start of list to allow
            // the most pruning later.
            std::iter_swap(all_legal_moves.begin(), move_iter);
        }
    }

    // Consider capturing moves first after principal variation move
    auto partition_start = std::next(all_legal_moves.begin(), found_principal_variation_move ? 1 : 0);
    std::partition(partition_start, all_legal_moves.end(),
                   [&board](auto move){ return board.move_captures(*move); });

    const auto perspective = board.whose_turn();
    auto moves_left = all_legal_moves.size();

    Game_Tree_Node_Result best_result = {Math::lose_score,
                                         perspective,
                                         {all_legal_moves.front()}};

    for(const auto& move : all_legal_moves)
    {
        auto evaluate_start_time = clock.time_left(clock.running_for());
        ++nodes_searched;

        auto next_board = board;

        auto move_result = next_board.submit_move(*move);
        if(move_result.winner() != NONE)
        {
            // This move results in checkmate, no other move can be better.
            return create_result(next_board, perspective, move_result, depth);
        }

        if(alpha.depth() <= depth + 2 && alpha.is_winning_for(perspective))
        {
            // This move will take a longer path to victory
            // than one already found. Use "depth + 2" since,
            // if this move isn't winning (and it isn't, since
            // we're here), then the earliest move that can
            // win is the next one, which is two away (after
            // opponent's move).
            continue;
        }

        double time_left = time_to_examine - (time_start - clock.time_left(clock.running_for()));
        double time_allotted_for_this_move = (time_left / moves_left)*speculation_time_factor(next_board);
        time_allotted_for_this_move = std::min(time_allotted_for_this_move, clock.time_left(clock.running_for()));

        bool recurse;
        if(move_result.game_has_ended())
        {
            recurse = false;
        }
        else if(still_on_principal_variation)
        {
            recurse = true;
        }
        else if(depth > 500)
        {
            recurse = false; // prevent stack overflow
        }
        else
        {
            auto minimum_time_to_recurse = next_board.legal_moves().size() / evaluation_speed;
            recurse = (time_allotted_for_this_move > minimum_time_to_recurse);
        }

        Game_Tree_Node_Result result;
        if(recurse)
        {
            result = search_game_tree(next_board,
                                      time_allotted_for_this_move,
                                      clock,
                                      depth + 1,
                                      beta,
                                      alpha,
                                      still_on_principal_variation);
        }
        else
        {
            // Record immediate result without looking ahead further
            result = create_result(next_board, perspective, move_result, depth);
        }

        if(better_than(result, best_result, perspective))
        {
            best_result = result;

            if(better_than(best_result, alpha, perspective))
            {
                alpha = best_result;
                if(better_than(alpha, beta, perspective) || alpha == beta)
                {
                    break;
                }
                else if(board.thinking_mode() == CECP && time_since_last_output(clock) > 0.1)
                {
                    output_thinking_cecp(alpha, clock,
                                         depth % 2 == 1 ? perspective : opposite(perspective));
                    time_at_last_output = clock.time_left(clock.running_for());
                }
            }
        }

        --moves_left;
        still_on_principal_variation = false; // only the first move is part of the principal variation

        if(clock.time_left(clock.running_for()) < 0)
        {
            break;
        }

        if( ! recurse) // This move was scored by genome.evaluate().
        {
            ++nodes_evaluated;
            total_evaluation_time += evaluate_start_time - clock.time_left(clock.running_for());
        }
    }

    return best_result;
}

void Minimax_AI::output_thinking_cecp(const Game_Tree_Node_Result& thought,
                                      const Clock& clock,
                                      Color perspective) const
{
    auto score = thought.corrected_score(perspective) / centipawn_value();

    // Indicate "mate in N moves" where N == thought.depth
    if(thought.is_winning_for(perspective))
    {
        score = 10000.0 - thought.depth() + 1;
    }
    else if(thought.is_losing_for(perspective))
    {
        score = -(10000.0 - thought.depth() + 1);
    }

    auto time_so_far = clock_start_time - clock.time_left(clock.running_for());
    std::cout << thought.depth() // ply
        << " "
        << int(score) // score in what should be centipawns
        << " "
        << int(time_so_far * 100) // search time in centiseconds
        << " "
        << nodes_searched
        << " "
        << maximum_depth
        << " "
        << int(nodes_searched / time_so_far)
        << '\t';

    // Principal variation
    for(const auto& move : thought.variation)
    {
        std::cout << move->coordinate_move() << ' ';
    }

    std::cout << std::endl;
}

double Minimax_AI::time_since_last_output(const Clock& clock) const
{
    return time_at_last_output - clock.time_left(clock.running_for());
}

Game_Tree_Node_Result Minimax_AI::create_result(const Board& board,
                                                Color perspective,
                                                Game_Result move_result,
                                                size_t depth) const
{
    return {evaluate(board, move_result, perspective, depth),
            perspective,
            {board.game_record().end() - depth,
            board.game_record().end()}};
}

void Minimax_AI::calibrate_thinking_speed() const
{
    evaluation_speed = 100; // very conservative initial guess
    auto calibration_time = 1.0; // seconds
    Board board;
    Clock clock(calibration_time, 1, 0.0);
    clock.start();
    choose_move(board, clock);
    // choose_move() keeps track of the time it takes and the number of positions
    // it sees, so this practice move will update the positions_per_second to a
    // more reasonable value.
}

double Minimax_AI::evaluate(const Board & board, Game_Result move_result, Color perspective, size_t depth) const
{
    if(move_result.game_has_ended())
    {
        if(move_result.winner() == NONE) // stalemate
        {
            return 0;
        }
        else if(move_result.winner() == perspective) // checkmate win
        {
            return Math::win_score;
        }
        else // checkmate loss
        {
            return Math::lose_score;
        }
    }

    return internal_evaluate(board, perspective, depth);
}

double Minimax_AI::centipawn_value() const
{
    return value_of_centipawn;
}

void Minimax_AI::calculate_centipawn_value()
{
    auto board_with_pawns = Board("4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1");
    auto board_with_no_white_pawns = Board("4k3/pppppppp/8/8/8/8/8/4K3 w - - 0 1");
    value_of_centipawn = std::abs(evaluate(board_with_pawns, {}, WHITE, 0) - evaluate(board_with_no_white_pawns, {}, WHITE, 0)) / 800;
}

std::string Minimax_AI::commentary_for_move(size_t move_number) const
{
    std::string result;
    if(move_number < commentary.size() && !commentary.at(move_number).empty())
    {
        result = commentary.at(move_number).front()->coordinate_move();
        for(size_t i = 1; i < commentary.at(move_number).size(); ++i)
        {
            result += " " + commentary.at(move_number).at(i)->coordinate_move();
        }
    }

    return result;
}
<|endoftext|>


<commit_before>#ifndef DISSENT_TUNNEL_TUNNEL_PACKET_H_GUARD
#define DISSENT_TUNNEL_TUNNEL_PACKET_H_GUARD

#include <QByteArray>
#include <QDataStream>
#include <QHash>
#include <QMetaEnum>
#include <QObject>
#include <QVariant>

namespace Dissent {
namespace Tunnel {
  class TunnelPacketHack : public QObject
  {
    Q_OBJECT
    Q_ENUMS(Types);
    Q_ENUMS(OptionalFields);
  };

  /**
   * Used for holding information used in TunnelPackets
   */
  class TunnelPacket {
    public:
      /**
       * Packet types
       */
      enum Types {
        UDP_START = 0,
        UDP_REQUEST,
        UDP_RESPONSE,
        TCP_START,
        TCP_REQUEST,
        TCP_RESPONSE,
        FINISHED,
      };

      /**
       * Option fields/
       */
      enum OptionalFields {
        KEY = 0,
        MESSAGE,
        HOST,
        PORT
      };

      static const int MAX_MESSAGE_SIZE = 64000;

      /**
       * Converts a field enum into a string
       */
      static inline QString FieldToString(int field)
      {
        int index = QObject::staticMetaObject.indexOfEnumerator("OptionalFields");
        return QObject::staticMetaObject.enumerator(index).valueToKey(field);
      }

      /**
       * Converts a type enum into a string
       */
      static inline QString TypeToString(int type)
      {
        int index = QObject::staticMetaObject.indexOfEnumerator("Types");
        return QObject::staticMetaObject.enumerator(index).valueToKey(type);
      }

      /**
       * Builds a UDP_START packet
       * @param connection_id unique identifier for conn
       * @param key public key for this connection
       */
      static inline TunnelPacket BuildUdpStart(const QByteArray &connection_id,
          const QByteArray &key)
      {
        QHash<int, QVariant> options;
        options[KEY] = key;
        return TunnelPacket(UDP_START, connection_id, options);
      }

      /**
       * Builds a UDP_REQUEST packet
       * @param connection_id unique identifier for conn
       * @param host the host to connect to
       * @param port the port on that host
       * @param message the message to transmit to the host
       */
      static inline TunnelPacket BuildUdpRequest(const QByteArray &connection_id,
          const QString &host, quint16 port, const QByteArray &message)
      {
        QHash<int, QVariant> options;
        options[HOST] = host;
        options[PORT] = port;
        options[MESSAGE] = message;
        return TunnelPacket(UDP_REQUEST, connection_id, options);
      }

      /**
       * Builds a UDP_RESPONSE packet
       * @param connection_id unique identifier for conn
       * @param host the remote connected host
       * @param port the port on that host
       * @param message the message received from that host
       */
      static inline TunnelPacket BuildUdpResponse(const QByteArray &connection_id,
          const QString &host, quint16 port, const QByteArray &message)
      {
        QHash<int, QVariant> options;
        options[HOST] = host;
        options[PORT] = port;
        options[MESSAGE] = message;
        return TunnelPacket(UDP_RESPONSE, connection_id, options);
      }

      /**
       * Builds a TCP_START packet
       * @param connection_id unique identifier for conn
       * @param host the remote connected host
       * @param port the port on that host
       * @param key public key for this connection
       */
      static inline TunnelPacket BuildTcpStart(const QByteArray &connection_id,
          const QString &host, quint16 port, const QByteArray &key)
      {
        QHash<int, QVariant> options;
        options[HOST] = host;
        options[PORT] = port;
        options[KEY] = key;
        return TunnelPacket(TCP_START, connection_id, options);
      }

      /**
       * Builds a TCP_REQUEST packet
       * @param connection_id unique identifier for conn
       * @param message the mesasge to transmit to the host
       */
      static inline TunnelPacket BuildTcpRequest(const QByteArray &connection_id,
          const QByteArray &message)
      {
        QHash<int, QVariant> options;
        options[MESSAGE] = message;
        return TunnelPacket(TCP_REQUEST, connection_id, options);
      }

      /**
       * Builds a TCP_REQUEST packet
       * @param connection_id unique identifier for conn
       * @param message the mesasge received from the host
       */
      static inline TunnelPacket BuildTcpResponse(const QByteArray &connection_id,
          const QByteArray &message)
      {
        QHash<int, QVariant> options;
        options[MESSAGE] = message;
        return TunnelPacket(TCP_RESPONSE, connection_id, options);
      }

      /**
       * @param connection_id unique identifier for conn
       */
      static inline TunnelPacket BuildFinished(const QByteArray &connection_id)
      {
        return TunnelPacket(FINISHED, connection_id);
      }

      /**
       * Remote constructor
       * @param packet serialized packet
       */
      TunnelPacket(const QByteArray &packet) :
        m_packet(packet)
      {
        QDataStream stream0(packet);
        stream0 >> m_unsigned_packet >> m_signature;

        QDataStream stream1(m_unsigned_packet);
        int type;
        stream1 >> type >> m_connection_id;
        m_type = static_cast<Types>(type);

        int option_count;
        stream1 >> option_count;

        if(option_count > 0) {
          int option_field;
          QVariant option;
          while(!stream1.atEnd() && m_options.size() < option_count) {
            stream1 >> option_field >> option;
            m_options[option_field] = option;
          }
        }

        m_valid = Validate();
      }

      TunnelPacket() : m_valid(false)
      {
      }

      /**
       * Returns the full packet
       */
      QByteArray GetPacket() const { return m_packet; }

      /**
       * Returns the unsigned portion of the packet
       */
      QByteArray GetUnsignedPacket() const { return m_unsigned_packet; }

      /**
       * Returns the packets type
       */
      Types GetType() const { return m_type; }

      /**
       * Returns the packets connection id
       */
      QByteArray GetConnectionId() const { return m_connection_id; }

      /**
       * Returns the address
       */
      QString GetHost() const
      {
        return m_options.value(HOST).value<QString>();
      }

      /**
       * Returns the port
       */
      quint16 GetPort() const
      {
        return m_options.value(PORT).value<quint16>();
      }

      /**
       * Returns the remote sides key
       */
      QByteArray GetKey() const
      {
        return m_options.value(KEY).value<QByteArray>();
      }

      /**
       * Returns the internal message
       */
      QByteArray GetMessage() const
      {
        return m_options.value(MESSAGE).value<QByteArray>();
      }

      /**
       * Returns the signature component
       */
      QByteArray GetSignature() const { return m_signature; }

      bool IsValid() const { return m_valid; }

      /**
       * Sets the signature and updates the packet
       */
      void SetSignature(const QByteArray &signature)
      {
        m_signature = signature;
        QDataStream stream(&m_packet, QIODevice::WriteOnly);
        stream << m_unsigned_packet << m_signature;
      }

    private:
      /**
       * Local constructor, called only from static constructors
       * @param type type of packet
       * @param connection_id unique identifier for conn
       * @param optional optional fields
       */
      TunnelPacket(Types type, const QByteArray &connection_id,
          const QHash<int, QVariant> &options = QHash<int, QVariant>()) :
        m_type(type),
        m_connection_id(connection_id),
        m_options(options)
      {
        QDataStream stream(&m_unsigned_packet, QIODevice::WriteOnly);
        stream << m_type << m_connection_id << options.size();
        foreach(int option, options.keys()) {
          stream << option << options[option];
        }

        QDataStream istream(&m_packet, QIODevice::WriteOnly);
        istream << m_unsigned_packet << m_signature;
        m_valid = Validate();
      }

      bool m_valid;
      QByteArray m_packet;
      QByteArray m_unsigned_packet;
      Types m_type;
      QByteArray m_connection_id;
      QHash<int, QVariant> m_options;
      QByteArray m_message;
      QByteArray m_signature;

      typedef QList<OptionalFields> RequiredFields;

      bool Validate()
      {
        if(m_connection_id.isEmpty()) {
          qDebug() << "TunnelPacket -- missing connection id";
          return false;
        }

        RequiredFields fields;
        if(!GetRequiredFields(m_type, fields)) {
          qDebug() << "TunnelPacket -- invalid type" << m_type;
          return false;
        }

        foreach(OptionalFields field, fields) {
          if(!m_options.contains(field)) {
            qDebug() << "TunnelPacket -- missing field" << FieldToString(field);
            return false;
          }

          if(!m_options.value(field).canConvert(GetType(field))) {
            qDebug() << "TunnelPacket -- bad field" << FieldToString(field);
            return false;
          }
        }

        if(m_options.contains(PORT)) {
          quint32 port = m_options.value(PORT).toUInt();
          if(port > 65535) {
            qDebug() << "TunnelPacket -- invalid port";
            return false;
          }
        }

        return true;
      }

      static inline QVariant::Type GetType(OptionalFields field)
      {
        switch(field) {
          case KEY:
            return QVariant::ByteArray;
          case MESSAGE:
            return QVariant::ByteArray;
          case HOST:
            return QVariant::String;
          case PORT:
            return QVariant::UInt;
          default:
            return QVariant::Invalid;
        }
      }

      static inline RequiredFields BuildRequiredFields(Types type)
      {
        RequiredFields fields;
        switch(type) {
          case UDP_START:
            fields.append(KEY);
            break;
          case UDP_REQUEST:
          case UDP_RESPONSE:
            fields.append(HOST);
            fields.append(PORT);
            fields.append(MESSAGE);
            break;
          case TCP_START:
            fields.append(HOST);
            fields.append(PORT);
            fields.append(KEY);
            break;
          case TCP_REQUEST:
          case TCP_RESPONSE:
            fields.append(MESSAGE);
            break;
          case FINISHED:
            break;
        }
        return fields;
      }

      static inline bool GetRequiredFields(Types type, RequiredFields &required)
      {
        switch(type) {
          case UDP_START:
            static RequiredFields udp_start = BuildRequiredFields(type);
            required = udp_start;
            return true;
          case UDP_REQUEST:
          case UDP_RESPONSE:
            static RequiredFields udp_request = BuildRequiredFields(type);
            required = udp_request;
            return true;
          case TCP_START:
            static RequiredFields tcp_start = BuildRequiredFields(type);
            required = tcp_start;
            return true;
          case TCP_REQUEST:
          case TCP_RESPONSE:
            static RequiredFields tcp_response = BuildRequiredFields(type);
            required = tcp_response;
            return true;
          case FINISHED:
            static RequiredFields finished = BuildRequiredFields(type);
            required = finished;
            return true;
          default:
            return false;
        }
      }
  };

  inline QDataStream &operator<<(QDataStream &stream, const TunnelPacket &packet)
  {
    stream << packet.GetPacket();
    return stream;
  }

  inline QDataStream &operator>>(QDataStream &stream, TunnelPacket &packet)
  {
    QByteArray data;
    stream >> data;
    packet = TunnelPacket(data);
    return stream;
  }
}
}

#endif
<commit_msg>[TunnelPacket] Include a length header on TunnelPackets This allows a Tcp stream to not have to include an additional length field for parsing out TunnelPackets<commit_after>#ifndef DISSENT_TUNNEL_TUNNEL_PACKET_H_GUARD
#define DISSENT_TUNNEL_TUNNEL_PACKET_H_GUARD

#include <QByteArray>
#include <QDataStream>
#include <QHash>
#include <QMetaEnum>
#include <QObject>
#include <QVariant>

namespace Dissent {
namespace Tunnel {
  class TunnelPacketHack : public QObject
  {
    Q_OBJECT
    Q_ENUMS(Types);
    Q_ENUMS(OptionalFields);
  };

  /**
   * Used for holding information used in TunnelPackets
   */
  class TunnelPacket {
    public:
      /**
       * Packet types
       */
      enum Types {
        UDP_START = 0,
        UDP_REQUEST,
        UDP_RESPONSE,
        TCP_START,
        TCP_REQUEST,
        TCP_RESPONSE,
        FINISHED,
      };

      /**
       * Option fields/
       */
      enum OptionalFields {
        KEY = 0,
        MESSAGE,
        HOST,
        PORT
      };

      static const int MAX_MESSAGE_SIZE = 64000;

      /**
       * Converts a field enum into a string
       */
      static inline QString FieldToString(int field)
      {
        int index = QObject::staticMetaObject.indexOfEnumerator("OptionalFields");
        return QObject::staticMetaObject.enumerator(index).valueToKey(field);
      }

      /**
       * Converts a type enum into a string
       */
      static inline QString TypeToString(int type)
      {
        int index = QObject::staticMetaObject.indexOfEnumerator("Types");
        return QObject::staticMetaObject.enumerator(index).valueToKey(type);
      }

      /**
       * Builds a UDP_START packet
       * @param connection_id unique identifier for conn
       * @param key public key for this connection
       */
      static inline TunnelPacket BuildUdpStart(const QByteArray &connection_id,
          const QByteArray &key)
      {
        QHash<int, QVariant> options;
        options[KEY] = key;
        return TunnelPacket(UDP_START, connection_id, options);
      }

      /**
       * Builds a UDP_REQUEST packet
       * @param connection_id unique identifier for conn
       * @param host the host to connect to
       * @param port the port on that host
       * @param message the message to transmit to the host
       */
      static inline TunnelPacket BuildUdpRequest(const QByteArray &connection_id,
          const QString &host, quint16 port, const QByteArray &message)
      {
        QHash<int, QVariant> options;
        options[HOST] = host;
        options[PORT] = port;
        options[MESSAGE] = message;
        return TunnelPacket(UDP_REQUEST, connection_id, options);
      }

      /**
       * Builds a UDP_RESPONSE packet
       * @param connection_id unique identifier for conn
       * @param host the remote connected host
       * @param port the port on that host
       * @param message the message received from that host
       */
      static inline TunnelPacket BuildUdpResponse(const QByteArray &connection_id,
          const QString &host, quint16 port, const QByteArray &message)
      {
        QHash<int, QVariant> options;
        options[HOST] = host;
        options[PORT] = port;
        options[MESSAGE] = message;
        return TunnelPacket(UDP_RESPONSE, connection_id, options);
      }

      /**
       * Builds a TCP_START packet
       * @param connection_id unique identifier for conn
       * @param host the remote connected host
       * @param port the port on that host
       * @param key public key for this connection
       */
      static inline TunnelPacket BuildTcpStart(const QByteArray &connection_id,
          const QString &host, quint16 port, const QByteArray &key)
      {
        QHash<int, QVariant> options;
        options[HOST] = host;
        options[PORT] = port;
        options[KEY] = key;
        return TunnelPacket(TCP_START, connection_id, options);
      }

      /**
       * Builds a TCP_REQUEST packet
       * @param connection_id unique identifier for conn
       * @param message the mesasge to transmit to the host
       */
      static inline TunnelPacket BuildTcpRequest(const QByteArray &connection_id,
          const QByteArray &message)
      {
        QHash<int, QVariant> options;
        options[MESSAGE] = message;
        return TunnelPacket(TCP_REQUEST, connection_id, options);
      }

      /**
       * Builds a TCP_REQUEST packet
       * @param connection_id unique identifier for conn
       * @param message the mesasge received from the host
       */
      static inline TunnelPacket BuildTcpResponse(const QByteArray &connection_id,
          const QByteArray &message)
      {
        QHash<int, QVariant> options;
        options[MESSAGE] = message;
        return TunnelPacket(TCP_RESPONSE, connection_id, options);
      }

      /**
       * @param connection_id unique identifier for conn
       */
      static inline TunnelPacket BuildFinished(const QByteArray &connection_id)
      {
        return TunnelPacket(FINISHED, connection_id);
      }

      /**
       * Remote constructor
       * @param packet serialized packet
       */
      TunnelPacket(const QByteArray &packet)
      {
        QDataStream stream0(packet);
        stream0 >> m_length >> m_unsigned_packet >> m_signature;

        qint32 expected = m_unsigned_packet.size() + m_signature.size() + 12;
        if(expected != m_length) {
          qDebug() << "TunnelPacket: Packet length does not match" <<
            "expected packet length --" << expected << "!=" << m_length;
          m_valid = false;
          return;
        }

        QDataStream stream1(m_unsigned_packet);
        int type;
        stream1 >> type >> m_connection_id;
        m_type = static_cast<Types>(type);

        int option_count;
        stream1 >> option_count;

        if(option_count > 0) {
          int option_field;
          QVariant option;
          while(!stream1.atEnd() && m_options.size() < option_count) {
            stream1 >> option_field >> option;
            m_options[option_field] = option;
          }
        }

        m_packet = packet.mid(0, m_length);
        m_valid = Validate();
      }

      TunnelPacket() : m_valid(false)
      {
      }

      /**
       * Returns the full packet length
       */
      qint32 GetLength() const { return m_length; }

      /**
       * Returns the full packet
       */
      QByteArray GetPacket() const { return m_packet; }

      /**
       * Returns the unsigned portion of the packet
       */
      QByteArray GetUnsignedPacket() const { return m_unsigned_packet; }

      /**
       * Returns the packets type
       */
      Types GetType() const { return m_type; }

      /**
       * Returns the packets connection id
       */
      QByteArray GetConnectionId() const { return m_connection_id; }

      /**
       * Returns the address
       */
      QString GetHost() const
      {
        return m_options.value(HOST).value<QString>();
      }

      /**
       * Returns the port
       */
      quint16 GetPort() const
      {
        return m_options.value(PORT).value<quint16>();
      }

      /**
       * Returns the remote sides key
       */
      QByteArray GetKey() const
      {
        return m_options.value(KEY).value<QByteArray>();
      }

      /**
       * Returns the internal message
       */
      QByteArray GetMessage() const
      {
        return m_options.value(MESSAGE).value<QByteArray>();
      }

      /**
       * Returns the signature component
       */
      QByteArray GetSignature() const { return m_signature; }

      bool IsValid() const { return m_valid; }

      /**
       * Sets the signature and updates the packet
       */
      void SetSignature(const QByteArray &signature)
      {
        m_signature = signature;
        m_length = m_unsigned_packet.size() + m_signature.size() + 12;
        QDataStream stream(&m_packet, QIODevice::WriteOnly);
        stream << m_length << m_unsigned_packet << m_signature;
      }

    private:
      /**
       * Local constructor, called only from static constructors
       * @param type type of packet
       * @param connection_id unique identifier for conn
       * @param optional optional fields
       */
      TunnelPacket(Types type, const QByteArray &connection_id,
          const QHash<int, QVariant> &options = QHash<int, QVariant>()) :
        m_type(type),
        m_connection_id(connection_id),
        m_options(options)
      {
        QDataStream stream(&m_unsigned_packet, QIODevice::WriteOnly);
        stream << m_type << m_connection_id << options.size();
        foreach(int option, options.keys()) {
          stream << option << options[option];
        }

        m_length = m_unsigned_packet.size() + m_signature.size() + 12;
        QDataStream istream(&m_packet, QIODevice::WriteOnly);
        istream << m_length << m_unsigned_packet << m_signature;
        m_valid = Validate();
      }

      bool m_valid;
      QByteArray m_packet;
      QByteArray m_unsigned_packet;
      Types m_type;
      QByteArray m_connection_id;
      QHash<int, QVariant> m_options;
      QByteArray m_message;
      QByteArray m_signature;
      qint32 m_length;

      typedef QList<OptionalFields> RequiredFields;

      bool Validate()
      {
        if(m_connection_id.isEmpty()) {
          qDebug() << "TunnelPacket -- missing connection id";
          return false;
        }

        RequiredFields fields;
        if(!GetRequiredFields(m_type, fields)) {
          qDebug() << "TunnelPacket -- invalid type" << m_type;
          return false;
        }

        foreach(OptionalFields field, fields) {
          if(!m_options.contains(field)) {
            qDebug() << "TunnelPacket -- missing field" << FieldToString(field);
            return false;
          }

          if(!m_options.value(field).canConvert(GetType(field))) {
            qDebug() << "TunnelPacket -- bad field" << FieldToString(field);
            return false;
          }
        }

        if(m_options.contains(PORT)) {
          quint32 port = m_options.value(PORT).toUInt();
          if(port > 65535) {
            qDebug() << "TunnelPacket -- invalid port";
            return false;
          }
        }

        return true;
      }

      static inline QVariant::Type GetType(OptionalFields field)
      {
        switch(field) {
          case KEY:
            return QVariant::ByteArray;
          case MESSAGE:
            return QVariant::ByteArray;
          case HOST:
            return QVariant::String;
          case PORT:
            return QVariant::UInt;
          default:
            return QVariant::Invalid;
        }
      }

      static inline RequiredFields BuildRequiredFields(Types type)
      {
        RequiredFields fields;
        switch(type) {
          case UDP_START:
            fields.append(KEY);
            break;
          case UDP_REQUEST:
          case UDP_RESPONSE:
            fields.append(HOST);
            fields.append(PORT);
            fields.append(MESSAGE);
            break;
          case TCP_START:
            fields.append(HOST);
            fields.append(PORT);
            fields.append(KEY);
            break;
          case TCP_REQUEST:
          case TCP_RESPONSE:
            fields.append(MESSAGE);
            break;
          case FINISHED:
            break;
        }
        return fields;
      }

      static inline bool GetRequiredFields(Types type, RequiredFields &required)
      {
        switch(type) {
          case UDP_START:
            static RequiredFields udp_start = BuildRequiredFields(type);
            required = udp_start;
            return true;
          case UDP_REQUEST:
          case UDP_RESPONSE:
            static RequiredFields udp_request = BuildRequiredFields(type);
            required = udp_request;
            return true;
          case TCP_START:
            static RequiredFields tcp_start = BuildRequiredFields(type);
            required = tcp_start;
            return true;
          case TCP_REQUEST:
          case TCP_RESPONSE:
            static RequiredFields tcp_response = BuildRequiredFields(type);
            required = tcp_response;
            return true;
          case FINISHED:
            static RequiredFields finished = BuildRequiredFields(type);
            required = finished;
            return true;
          default:
            return false;
        }
      }
  };

  inline QDataStream &operator<<(QDataStream &stream, const TunnelPacket &packet)
  {
    stream << packet.GetPacket();
    return stream;
  }

  inline QDataStream &operator>>(QDataStream &stream, TunnelPacket &packet)
  {
    QByteArray data;
    stream >> data;
    packet = TunnelPacket(data);
    return stream;
  }
}
}

#endif
<|endoftext|>