<commit_before>/*
* Copyright (C) 2010  Toni Gundogdu <legatvs@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "internal.h"

#include <fstream>
#include <stdexcept>
#include <sstream>
#include <iomanip>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif

#if defined (HAVE_SIGNAL_H) && defined (HAVE_SIGNAL)
#define WITH_SIGNAL
#endif

#include <boost/format.hpp>
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>

#ifndef foreach
#define foreach BOOST_FOREACH
#endif

#include <curl/curl.h>

#include "quvicpp/quvicpp.h"

#include "cclive/options.h"
#include "cclive/progressbar.h"
#include "cclive/re.h"
#include "cclive/error.h"
#include "cclive/log.h"
#include "cclive/file.h"

namespace cclive
{

file::file ()
  : _initial_length(0)
{ }

file::file (
  const quvicpp::media& media,
  const quvicpp::url& url,
  const int n,
  const options& opts)
  : _initial_length(0)
{
  _init (media, url, n, opts);
}

file::file (const file& f)
  : _initial_length(0)
{
  _swap(f);
}

file&
file::operator=(const file& f)
{
  if (this != &f) _swap(f);
  return *this;
}

void
file::_swap (const file& f)
{
  _name           = f._name;
  _path           = f._path;
  _initial_length = f._initial_length;
}

#define E "server response code %ld, expected 200 or 206 (conn_code=%ld)"

static std::string
format_unexpected_http_error (const long resp_code, const long conn_code)
{
  return (boost::format (E) % resp_code % conn_code).str ();
}

#undef E

#define E "%s (curl_code=%ld, resp_code=%ld, conn_code=%ld)"

static std::string
format_error (
  const CURLcode curl_code,
  const long resp_code,
  const long conn_code)
{
  const std::string e = curl_easy_strerror (curl_code);
  return (boost::format (E) % e % curl_code % resp_code % conn_code).str ();
}

#undef E

static size_t
write_cb (void *data, size_t size, size_t nmemb, void *ptr)
{
  std::ofstream *o   = reinterpret_cast<std::ofstream*>(ptr);
  const size_t rsize = size*nmemb;
  o->write (static_cast<char*>(data), rsize);
  o->flush ();
  return rsize;
}

#ifdef WITH_SIGNAL
static volatile sig_atomic_t recv_usr1;

static void
handle_usr1 (int s)
{
  if (s == SIGUSR1)
    recv_usr1 = 1;
}
#endif

static int
progress_cb (void *ptr, double, double now, double, double)
{
#ifdef WITH_SIGNAL
  if (recv_usr1)
    {
      recv_usr1 = 0;
      return 1; // Return a non-zero value to abort this transfer.
    }
#endif
  reinterpret_cast<progressbar*>(ptr)->update (now);
  return 0;
}

namespace po = boost::program_options;

bool
file::write (
  const quvicpp::query& q,
  const quvicpp::url& u,
  const options& opts) const
{
  CURL *curl = q.curlHandle();

  curl_easy_setopt (curl, CURLOPT_URL, u.media_url().c_str());
  curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_cb);

  const po::variables_map map  = opts.map();
  std::ios_base::openmode mode = std::ofstream::binary;

  if ( map.count ("overwrite") )
    mode |= std::ofstream::trunc;
  else
    {
      if ( _should_continue() )
        mode |= std::ofstream::app;
    }

  std::ofstream out( _path.c_str(), mode );

  if ( out.fail() )
    {
      std::string s = _path + ": ";

      if (errno)
        s += cclive::perror ();
      else
        s += "unknown file open error";

      throw std::runtime_error (s);
    }

  curl_easy_setopt (curl, CURLOPT_WRITEDATA, &out);

  curl_easy_setopt (curl, CURLOPT_ENCODING, "identity");
  curl_easy_setopt (curl, CURLOPT_HEADER, 0L);

  progressbar pb (*this, u, opts);
  curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, &pb);
  curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0L);
  curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_cb);

  curl_off_t resume_from = static_cast<curl_off_t>(_initial_length);
  curl_easy_setopt (curl, CURLOPT_RESUME_FROM_LARGE, resume_from);

  curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT,
                    map["connect-timeout"].as<int>());

  curl_easy_setopt (curl, CURLOPT_TIMEOUT,
                    map["transfer-timeout"].as<int>());

  curl_easy_setopt (curl, CURLOPT_DNS_CACHE_TIMEOUT,
                    map["dns-cache-timeout"].as<int>());

  curl_off_t throttle = map["throttle"].as<int>()*1024;
  curl_easy_setopt (curl, CURLOPT_MAX_RECV_SPEED_LARGE, throttle);

#ifdef WITH_SIGNAL
  recv_usr1 = 0;
  if (signal(SIGUSR1, handle_usr1) == SIG_ERR)
    {
      cclive::log << "warning: ";
      if (errno)
        cclive::log << cclive::perror ();
      else
        cclive::log << "unable to catch SIGUSR1";
      cclive::log << std::endl;
    }
#endif

  const CURLcode rc = curl_easy_perform (curl);

  // Restore curl settings.

  curl_easy_setopt (curl, CURLOPT_HEADER, 1L);
  curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1L);
  curl_easy_setopt (curl, CURLOPT_RESUME_FROM_LARGE, 0L);
  curl_easy_setopt (curl, CURLOPT_MAX_RECV_SPEED_LARGE,
                    static_cast<curl_off_t>(0L));

  out.flush();
  out.close();

  long resp_code = 0;
  long conn_code = 0;

  curl_easy_getinfo (curl,
                     CURLINFO_RESPONSE_CODE, &resp_code);

  curl_easy_getinfo (curl,
                     CURLINFO_HTTP_CONNECTCODE, &conn_code);

  std::string error;

  if (CURLE_OK == rc)
    {
      if (resp_code != 200 && resp_code != 206)
        error = format_unexpected_http_error (resp_code, conn_code);
    }
  else
    {
      error = format_error (rc, resp_code, conn_code);
    }

  if ( !error.empty() )
    {
      cclive::log << std::endl;

      if (resp_code >= 400 && resp_code <= 500)
        throw std::runtime_error (error);
      else
        {
#ifdef WITH_SIGNAL
          if (rc == 42)
            {
              // 42 = Operation aborted by callback.
              error = "sigusr1 received: interrupt current download";
            }
#endif
          cclive::log << "error: " << error << std::endl;
        }

      return false; // Retry.
    }

  pb.finish();

  cclive::log << std::endl;

  return true;
}

double file::initial_length   () const
{
  return _initial_length;
}
const std::string& file::path () const
{
  return _path;
}
bool file::_should_continue   () const
{
  return _initial_length > 0;
}

static double to_mb (const double bytes)
{
  return bytes/(1024*1024);
}

std::string
file::to_s (const quvicpp::url& url) const
{
  const double length = to_mb(url.content_length());

  boost::format fmt = boost::format("%s  %.2fM  [%s]")
                      % _name % length % url.content_type ();

  return fmt.str();
}

#define E_NOTHINGTODO "media retrieved completely already"

namespace fs = boost::filesystem;

static fs::path
output_dir (const po::variables_map& map)
{
  fs::path dir;

  if (map.count ("output-dir"))
    dir = map["output-dir"].as<std::string>();

#ifdef HAVE_GETCWD
  else
    {
      char *p = getcwd(NULL, 0);
      if (p)
        {
          dir = p;
          free(p);
          p = NULL;
        }
    }
#endif

  return fs::system_complete (dir);
}

void
file::_init (
  const quvicpp::media& media,
  const quvicpp::url& url,
  const int n,
  const options& opts)
{
  const po::variables_map map = opts.map();

  if (map.count("output-file"))
    {
      // Overrides --filename-format.

      fs::path p = output_dir (map);

      p /= map["output-file"].as<std::string>();

#if BOOST_FILESYSTEM_VERSION > 2
      _name           = p.filename().string();
#else
      _name           = p.filename();
#endif
      _path           = p.string();
      _initial_length = file::exists (_path);

      if ( _initial_length >= url.content_length() && !map.count("overwrite") )
        throw std::runtime_error (E_NOTHINGTODO);
    }

  else
    {
      std::string title  = media.title();

      // Apply --regexp to title.

      cclive::re::match (map["regexp"].as<std::string>(), title);

      cclive::re::trim (title);

      // --filename-format

      std::string fname_format =
        map["filename-format"].as<std::string>();

      boost::format fmt;

      fmt = boost::format("s{%%i}{%1%}g") % media.id();
      cclive::re::subst (fmt.str(), fname_format);

      fmt = boost::format("s{%%t}{%1%}g") % title;
      cclive::re::subst (fmt.str(), fname_format);

      fmt = boost::format("s{%%s}{%1%}g") % url.suffix();
      cclive::re::subst (fmt.str(), fname_format);

      fmt = boost::format("s{%%h}{%1%}g") % media.host();
      cclive::re::subst (fmt.str(), fname_format);

      if (map.count("subst"))
        {
          std::istringstream iss(map["subst"].as<std::string>());
          std::vector<std::string> v;

          std::copy(
            std::istream_iterator<std::string >(iss),
            std::istream_iterator<std::string >(),
            std::back_inserter<std::vector<std::string> >(v)
          );

          foreach (std::string s, v)
          cclive::re::subst (s, fname_format);
        }

      std::stringstream b;

      b << fname_format;

      // A multi-segment media.

      if (n > 1) b << "_" << n;

      // Output dir.

      const fs::path out_dir = output_dir (map);
      fs::path templ_path    = out_dir;

      templ_path /= b.str ();

      // Path, name.

      fs::path p = fs::system_complete (templ_path);

#if BOOST_FILESYSTEM_VERSION > 2
      _name = p.filename().string();
#else
      _name = p.filename();
#endif
      _path = p.string();

      if ( !map.count ("overwrite") )
        {
          for (int i=0; i<INT_MAX; ++i)
            {
              _initial_length = file::exists (_path);

              if (!_initial_length)
                break;

              else if (_initial_length >= url.content_length())
                throw std::runtime_error (E_NOTHINGTODO);

              else
                {
                  if (map.count("continue"))
                    break;
                }

              boost::format fmt =
                boost::format ("%1%.%2%") % templ_path.string () % i;

              p     = fs::system_complete (fmt.str ());

#if BOOST_FILESYSTEM_VERSION > 2
              _name = p.filename().string();
#else
              _name = p.filename();
#endif
              _path = p.string();
            }
        }
    }

  if ( map.count("overwrite") )
    _initial_length = 0;
}

#undef E_NOTHINGTODO

double
file::exists (const std::string& path)
{
  fs::path p( fs::system_complete(path) );

  double size = 0;

  if (fs::exists(p))
    size = static_cast<double>(fs::file_size(p));

  return size;
}

} // End namespace.

// vim: set ts=2 sw=2 tw=72 expandtab:
<commit_msg>file.cpp: Improve readability<commit_after>/*
* Copyright (C) 2010  Toni Gundogdu <legatvs@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "internal.h"

#include <fstream>
#include <stdexcept>
#include <sstream>
#include <iomanip>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif

#if defined (HAVE_SIGNAL_H) && defined (HAVE_SIGNAL)
#define WITH_SIGNAL
#endif

#include <boost/format.hpp>
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>

#ifndef foreach
#define foreach BOOST_FOREACH
#endif

#include <curl/curl.h>

#include "quvicpp/quvicpp.h"

#include "cclive/options.h"
#include "cclive/progressbar.h"
#include "cclive/re.h"
#include "cclive/error.h"
#include "cclive/log.h"
#include "cclive/file.h"

namespace cclive
{

file::file ()
  : _initial_length(0)
{ }

file::file (
  const quvicpp::media& media,
  const quvicpp::url& url,
  const int n,
  const options& opts)
  : _initial_length(0)
{
  _init (media, url, n, opts);
}

file::file (const file& f)
  : _initial_length(0)
{
  _swap(f);
}

file&
file::operator=(const file& f)
{
  if (this != &f) _swap(f);
  return *this;
}

void
file::_swap (const file& f)
{
  _name           = f._name;
  _path           = f._path;
  _initial_length = f._initial_length;
}

#define E "server response code %ld, expected 200 or 206 (conn_code=%ld)"

static std::string
format_unexpected_http_error (const long resp_code, const long conn_code)
{
  return (boost::format (E) % resp_code % conn_code).str ();
}

#undef E

#define E "%s (curl_code=%ld, resp_code=%ld, conn_code=%ld)"

static std::string
format_error (
  const CURLcode curl_code,
  const long resp_code,
  const long conn_code)
{
  const std::string e = curl_easy_strerror (curl_code);
  return (boost::format (E) % e % curl_code % resp_code % conn_code).str ();
}

#undef E

static size_t
write_cb (void *data, size_t size, size_t nmemb, void *ptr)
{
  std::ofstream *o   = reinterpret_cast<std::ofstream*>(ptr);
  const size_t rsize = size*nmemb;
  o->write (static_cast<char*>(data), rsize);
  o->flush ();
  return rsize;
}

#ifdef WITH_SIGNAL
static volatile sig_atomic_t recv_usr1;

static void
handle_usr1 (int s)
{
  if (s == SIGUSR1)
    recv_usr1 = 1;
}
#endif

static int
progress_cb (void *ptr, double, double now, double, double)
{
#ifdef WITH_SIGNAL
  if (recv_usr1)
    {
      recv_usr1 = 0;
      return 1; // Return a non-zero value to abort this transfer.
    }
#endif
  reinterpret_cast<progressbar*>(ptr)->update (now);
  return 0;
}

namespace po = boost::program_options;

bool
file::write (
  const quvicpp::query& q,
  const quvicpp::url& u,
  const options& opts) const
{
  CURL *curl = q.curlHandle();

  curl_easy_setopt (curl, CURLOPT_URL, u.media_url().c_str());
  curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_cb);

  const po::variables_map map  = opts.map();
  std::ios_base::openmode mode = std::ofstream::binary;

  if ( map.count ("overwrite") )
    mode |= std::ofstream::trunc;
  else
    {
      if ( _should_continue() )
        mode |= std::ofstream::app;
    }

  std::ofstream out( _path.c_str(), mode );

  if ( out.fail() )
    {
      std::string s = _path + ": ";

      if (errno)
        s += cclive::perror ();
      else
        s += "unknown file open error";

      throw std::runtime_error (s);
    }

  curl_easy_setopt (curl, CURLOPT_WRITEDATA, &out);

  curl_easy_setopt (curl, CURLOPT_ENCODING, "identity");
  curl_easy_setopt (curl, CURLOPT_HEADER, 0L);

  progressbar pb (*this, u, opts);
  curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, &pb);
  curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0L);
  curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_cb);

  curl_off_t resume_from = static_cast<curl_off_t>(_initial_length);
  curl_easy_setopt (curl, CURLOPT_RESUME_FROM_LARGE, resume_from);

  curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT,
                    map["connect-timeout"].as<int>());

  curl_easy_setopt (curl, CURLOPT_TIMEOUT,
                    map["transfer-timeout"].as<int>());

  curl_easy_setopt (curl, CURLOPT_DNS_CACHE_TIMEOUT,
                    map["dns-cache-timeout"].as<int>());

  curl_off_t throttle = map["throttle"].as<int>()*1024;
  curl_easy_setopt (curl, CURLOPT_MAX_RECV_SPEED_LARGE, throttle);

#ifdef WITH_SIGNAL
  recv_usr1 = 0;
  if (signal(SIGUSR1, handle_usr1) == SIG_ERR)
    {
      cclive::log << "warning: ";
      if (errno)
        cclive::log << cclive::perror ();
      else
        cclive::log << "unable to catch SIGUSR1";
      cclive::log << std::endl;
    }
#endif

  const CURLcode rc = curl_easy_perform (curl);

  // Restore curl settings.

  curl_easy_setopt (curl, CURLOPT_HEADER, 1L);
  curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1L);
  curl_easy_setopt (curl, CURLOPT_RESUME_FROM_LARGE, 0L);
  curl_easy_setopt (curl, CURLOPT_MAX_RECV_SPEED_LARGE,
                    static_cast<curl_off_t>(0L));

  out.flush();
  out.close();

  long resp_code = 0;
  long conn_code = 0;

  curl_easy_getinfo (curl,
                     CURLINFO_RESPONSE_CODE, &resp_code);

  curl_easy_getinfo (curl,
                     CURLINFO_HTTP_CONNECTCODE, &conn_code);

  std::string error;

  if (CURLE_OK == rc)
    {
      if (resp_code != 200 && resp_code != 206)
        error = format_unexpected_http_error (resp_code, conn_code);
    }
  else
    {
      error = format_error (rc, resp_code, conn_code);
    }

  if ( !error.empty() )
    {
      cclive::log << std::endl;

      if (resp_code >= 400 && resp_code <= 500)
        throw std::runtime_error (error);
      else
        {
#ifdef WITH_SIGNAL
          if (rc == 42)
            {
              // 42 = Operation aborted by callback.
              error = "sigusr1 received: interrupt current download";
            }
#endif
          cclive::log << "error: " << error << std::endl;
        }

      return false; // Retry.
    }

  pb.finish();

  cclive::log << std::endl;

  return true;
}

double file::initial_length   () const
{
  return _initial_length;
}
const std::string& file::path () const
{
  return _path;
}
bool file::_should_continue   () const
{
  return _initial_length > 0;
}

static double to_mb (const double bytes)
{
  return bytes/(1024*1024);
}

std::string
file::to_s (const quvicpp::url& url) const
{
  const double length = to_mb(url.content_length());

  boost::format fmt = boost::format("%s  %.2fM  [%s]")
                      % _name % length % url.content_type ();

  return fmt.str();
}

#define E_NOTHINGTODO "media retrieved completely already"

namespace fs = boost::filesystem;

static fs::path
output_dir (const po::variables_map& map)
{
  fs::path dir;

  if (map.count ("output-dir"))
    dir = map["output-dir"].as<std::string>();

#ifdef HAVE_GETCWD
  else
    {
      char *p = getcwd(NULL, 0);
      if (p)
        {
          dir = p;
          free(p);
          p = NULL;
        }
    }
#endif

  return fs::system_complete (dir);
}

void
file::_init (
  const quvicpp::media& media,
  const quvicpp::url& url,
  const int n,
  const options& opts)
{
  const po::variables_map map = opts.map();

  if (map.count("output-file"))
    {
      // Overrides --filename-format.

      fs::path p = output_dir (map);

      p /= map["output-file"].as<std::string>();

#if BOOST_FILESYSTEM_VERSION > 2
      _name           = p.filename().string();
#else
      _name           = p.filename();
#endif
      _path           = p.string();
      _initial_length = file::exists (_path);

      if ( _initial_length >= url.content_length() && !map.count("overwrite") )
        throw std::runtime_error (E_NOTHINGTODO);
    }

  else
    {
      std::string title  = media.title();

      // Apply --regexp to title.

      cclive::re::match (map["regexp"].as<std::string>(), title);

      cclive::re::trim (title);

      // --filename-format

      std::string fname_format =
        map["filename-format"].as<std::string>();

      boost::format fmt;

      fmt = boost::format("s{%%i}{%1%}g") % media.id();
      cclive::re::subst (fmt.str(), fname_format);

      fmt = boost::format("s{%%t}{%1%}g") % title;
      cclive::re::subst (fmt.str(), fname_format);

      fmt = boost::format("s{%%s}{%1%}g") % url.suffix();
      cclive::re::subst (fmt.str(), fname_format);

      fmt = boost::format("s{%%h}{%1%}g") % media.host();
      cclive::re::subst (fmt.str(), fname_format);

      if (map.count("subst"))
        {
          std::istringstream iss(map["subst"].as<std::string>());
          std::vector<std::string> v;

          std::copy(
            std::istream_iterator<std::string >(iss),
            std::istream_iterator<std::string >(),
            std::back_inserter<std::vector<std::string> >(v)
          );

          foreach (std::string s, v)
          {
            cclive::re::subst (s, fname_format);
          }
        }

      std::stringstream b;

      b << fname_format;

      // A multi-segment media.

      if (n > 1) b << "_" << n;

      // Output dir.

      const fs::path out_dir = output_dir (map);
      fs::path templ_path    = out_dir;

      templ_path /= b.str ();

      // Path, name.

      fs::path p = fs::system_complete (templ_path);

#if BOOST_FILESYSTEM_VERSION > 2
      _name = p.filename().string();
#else
      _name = p.filename();
#endif
      _path = p.string();

      if ( !map.count ("overwrite") )
        {
          for (int i=0; i<INT_MAX; ++i)
            {
              _initial_length = file::exists (_path);

              if (!_initial_length)
                break;

              else if (_initial_length >= url.content_length())
                throw std::runtime_error (E_NOTHINGTODO);

              else
                {
                  if (map.count("continue"))
                    break;
                }

              boost::format fmt =
                boost::format ("%1%.%2%") % templ_path.string () % i;

              p     = fs::system_complete (fmt.str ());

#if BOOST_FILESYSTEM_VERSION > 2
              _name = p.filename().string();
#else
              _name = p.filename();
#endif
              _path = p.string();
            }
        }
    }

  if ( map.count("overwrite") )
    _initial_length = 0;
}

#undef E_NOTHINGTODO

double
file::exists (const std::string& path)
{
  fs::path p( fs::system_complete(path) );

  double size = 0;

  if (fs::exists(p))
    size = static_cast<double>(fs::file_size(p));

  return size;
}

} // End namespace.

// vim: set ts=2 sw=2 tw=72 expandtab:
<|endoftext|>


<commit_before>#include "Config.hxx"
#include "CertDatabase.hxx"
#include "Wildcard.hxx"
#include "ssl/ssl_init.hxx"
#include "ssl/Util.hxx"
#include "ssl/AltName.hxx"
#include "ssl/Name.hxx"
#include "ssl/MemBio.hxx"
#include "ssl/Unique.hxx"
#include "ssl/Error.hxx"
#include "pg/Error.hxx"
#include "util/ConstBuffer.hxx"

#include <inline/compiler.h>

#include <openssl/ts.h>

#include <stdexcept>

#include <stdio.h>
#include <stdlib.h>
#include <poll.h>

static const CertDatabaseConfig config{"dbname=lb", std::string()};

class SslBuffer {
    unsigned char *data = nullptr;
    size_t size;

public:
    explicit SslBuffer(X509 *cert) {
        int result = i2d_X509(cert, &data);
        if (result < 0)
            throw SslError("Failed to encode certificate");

        size = result;
    }

    explicit SslBuffer(EVP_PKEY *key) {
        int result = i2d_PrivateKey(key, &data);
        if (result < 0)
            throw SslError("Failed to encode key");

        size = result;
    }

    SslBuffer(SslBuffer &&src):data(src.data), size(src.size) {
        src.data = nullptr;
    }

    ~SslBuffer() {
        if (data != nullptr)
            OPENSSL_free(data);
    }

    PgBinaryValue ToPg() const {
        return {data, size};
    }
};

static UniqueX509
LoadCertFile(const char *path)
{
    auto cert = TS_CONF_load_cert(path);
    if (cert == nullptr)
        throw SslError("Failed to load certificate");

    return UniqueX509(cert);
}

static UniqueEVP_PKEY
LoadKeyFile(const char *path)
{
    auto key = TS_CONF_load_key(path, nullptr);
    if (key == nullptr)
        throw SslError("Failed to load key");

    return UniqueEVP_PKEY(key);
}

static PgResult
CheckError(PgResult &&result)
{
    if (result.IsError())
        throw PgError(std::move(result));

    return std::move(result);
}

gcc_pure
static AllocatedString<>
GetCommonName(X509_NAME &name)
{
    return NidToString(name, NID_commonName);
}

gcc_pure
static AllocatedString<>
GetCommonName(X509 *cert)
{
    X509_NAME *subject = X509_get_subject_name(cert);
    return subject != nullptr
        ? GetCommonName(*subject)
        : nullptr;
}

gcc_pure
static AllocatedString<>
FormatTime(ASN1_TIME *t)
{
    if (t == nullptr)
        return nullptr;

    return BioWriterToString([t](BIO &bio){
            ASN1_TIME_print(&bio, t);
        });
}

/**
 * @return true when new certificate has been inserted, false when an
 * existing certificate has been updated
 */
static bool
LoadCertificate(CertDatabase &db, X509 &cert, EVP_PKEY &key)
{
    const auto common_name = GetCommonName(&cert);
    assert(common_name != nullptr);

    const SslBuffer cert_buffer(&cert);
    const auto cert_der = cert_buffer.ToPg();

    const SslBuffer key_buffer(&key);
    const auto key_der = key_buffer.ToPg();

    const auto alt_names = GetSubjectAltNames(cert);

    const auto not_before = FormatTime(X509_get_notBefore(&cert));
    if (not_before == nullptr)
        throw "Certificate does not have a notBefore time stamp";

    const auto not_after = FormatTime(X509_get_notAfter(&cert));
    if (not_after == nullptr)
        throw "Certificate does not have a notAfter time stamp";

    auto result = CheckError(db.UpdateServerCertificate(common_name.c_str(),
                                                        alt_names,
                                                        not_before.c_str(),
                                                        not_after.c_str(),
                                                        cert_der, key_der));
    if (result.GetAffectedRows() > 0) {
        return false;
    } else {
        CheckError(db.InsertServerCertificate(common_name.c_str(),
                                              alt_names,
                                              not_before.c_str(),
                                              not_after.c_str(),
                                              cert_der, key_der));
        return true;
    }
}

static void
LoadCertificate(const char *cert_path, const char *key_path)
{
    const ScopeSslGlobalInit ssl_init;

    const auto cert = LoadCertFile(cert_path);
    const auto common_name = GetCommonName(cert.get());
    if (common_name == nullptr)
        throw "Certificate has no common name";

    const auto key = LoadKeyFile(key_path);
    if (!MatchModulus(*cert, *key))
        throw "Key and certificate do not match.";

    CertDatabase db(config);

    bool inserted = LoadCertificate(db, *cert, *key);
    printf("%s: %s\n", inserted ? "insert" : "update", common_name.c_str());
    db.NotifyModified();
}

static void
DeleteCertificate(const char *host)
{
    CertDatabase db(config);

    const auto result = CheckError(db.DeleteServerCertificateByName(host));
    if (result.GetAffectedRows() == 0)
        throw "Certificate not found";

    db.NotifyModified();
}

static UniqueX509
FindCertByName(CertDatabase &db, const char *common_name)
{
    auto result = CheckError(db.FindServerCertificateByName(common_name));
    if (result.GetRowCount() == 0)
        return nullptr;

    if (!result.IsColumnBinary(0) || result.IsValueNull(0, 0))
        throw "Unexpected result";

    auto cert_der = result.GetBinaryValue(0, 0);

    auto data = (const unsigned char *)cert_der.value;
    UniqueX509 cert(d2i_X509(nullptr, &data, cert_der.size));
    if (!cert)
        throw "d2i_X509() failed";

    return cert;
}

static UniqueX509
FindCertByHost(const char *host)
{
    CertDatabase db(config);

    auto cert = FindCertByName(db, host);
    if (!cert) {
        auto wildcard = MakeCommonNameWildcard(host);
        if (!wildcard.empty())
            cert = FindCertByName(db, wildcard.c_str());

        if (!cert)
            throw "Certificate not found";
    }

    return cert;
}

static void
FindCertificate(const char *host)
{
    const ScopeSslGlobalInit ssl_init;

    auto cert = FindCertByHost(host);
    X509_print_fp(stdout, cert.get());
    PEM_write_X509(stdout, cert.get());
}

gcc_noreturn
static void
Monitor()
{
    CertDatabase db(config);
    CheckError(db.ListenModified());

    std::string last_modified = db.GetLastModified();
    if (last_modified.empty()) {
        last_modified = db.GetCurrentTimestamp();
        if (last_modified.empty())
            throw "CURRENT_TIMESTAMP failed";
    }

    struct pollfd pfd = {
        .fd = db.GetSocket(),
        .events = POLLIN,
    };

    while (true) {
        if (poll(&pfd, 1, -1) < 0)
            throw "poll() failed";

        db.ConsumeInput();
        while (db.GetNextNotify()) {}

        std::string new_last_modified = db.GetLastModified();
        if (new_last_modified.empty())
            throw "No MAX(modified) found";

        for (auto &row : CheckError(db.GetModifiedServerCertificatesMeta(last_modified.c_str())))
            printf("%s %s %s\n",
                   row.GetValue(1),
                   *row.GetValue(0) == 't' ? "deleted" : "modified",
                   row.GetValue(2));

        last_modified = std::move(new_last_modified);
    }
}

static void
Tail()
{
    CertDatabase db(config);

    for (auto &row : CheckError(db.TailModifiedServerCertificatesMeta()))
        printf("%s %s %s\n",
               row.GetValue(1),
               *row.GetValue(0) == 't' ? "deleted" : "modified",
               row.GetValue(2));
}

static void
AddExt(X509 *cert, int nid, const char *value)
{
    UniqueX509_EXTENSION ext(X509V3_EXT_conf_nid(nullptr, nullptr, nid,
                                                 const_cast<char *>(value)));
    if (ext == nullptr)
        throw SslError("X509V3_EXT_conf_nid() failed");

    X509_add_ext(cert, ext.get(), -1);
}

static UniqueX509
MakeSelfSignedDummyCert(EVP_PKEY &key, const char *common_name)
{
    UniqueX509 cert(X509_new());
    if (cert == nullptr)
        throw "X509_new() failed";

    auto *name = X509_get_subject_name(cert.get());

    if (!X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
                                    const_cast<unsigned char *>((const unsigned char *)common_name),
                                    -1, -1, 0))
        throw SslError("X509_NAME_add_entry_by_NID() failed");

    X509_set_issuer_name(cert.get(), name);

    X509_set_version(cert.get(), 2);
    ASN1_INTEGER_set(X509_get_serialNumber(cert.get()), 1);
    X509_gmtime_adj(X509_get_notBefore(cert.get()), 0);
    X509_gmtime_adj(X509_get_notAfter(cert.get()), 60 * 60);
    X509_set_pubkey(cert.get(), &key);

    AddExt(cert.get(), NID_basic_constraints, "critical,CA:TRUE");
    AddExt(cert.get(), NID_key_usage, "critical,keyCertSign");

    if (!X509_sign(cert.get(), &key, EVP_sha1()))
        throw SslError("X509_sign() failed");

    return cert;
}

static void
Populate(CertDatabase &db, EVP_PKEY *key, PgBinaryValue key_der,
         const char *common_name)
{
    (void)key;

    // TODO: fake time stamps
    const char *not_before = "1971-01-01";
    const char *not_after = "1971-01-01";

    auto cert = MakeSelfSignedDummyCert(*key, common_name);
    const SslBuffer cert_buffer(cert.get());
    const auto cert_der = cert_buffer.ToPg();

    const auto alt_names = GetSubjectAltNames(*cert);

    CheckError(db.InsertServerCertificate(common_name, alt_names,
                                          not_before, not_after,
                                          cert_der, key_der));
}

static void
Populate(const char *key_path, const char *suffix, unsigned n)
{
    const ScopeSslGlobalInit ssl_init;

    const auto key = LoadKeyFile(key_path);

    const SslBuffer key_buffer(key.get());
    const auto key_der = key_buffer.ToPg();

    CertDatabase db(config);

    if (n == 0) {
        Populate(db, key.get(), key_der, suffix);
    } else {
        if (!db.BeginSerializable())
            throw "BEGIN failed";

        for (unsigned i = 1; i <= n; ++i) {
            char buffer[256];
            snprintf(buffer, sizeof(buffer), "%u%s", i, suffix);
            Populate(db, key.get(), key_der, buffer);
        }

        if (!db.Commit())
            throw "COMMIT failed";
    }

    db.NotifyModified();
}

int
main(int argc, char **argv)
{
    ConstBuffer<const char *> args(argv + 1, argc - 1);

    if (args.IsEmpty()) {
        fprintf(stderr, "Usage: %s COMMAND ...\n"
                "\n"
                "Commands:\n"
                "  load CERT KEY\n"
                "  delete HOST\n"
                "  find HOST\n"
                "  monitor\n"
                "  tail\n"
                "  populate KEY COUNT SUFFIX\n"
                "\n", argv[0]);
        return EXIT_FAILURE;
    }

    const auto cmd = args.shift();

    try {
        if (strcmp(cmd, "load") == 0) {
            if (args.size != 2) {
                fprintf(stderr, "Usage: %s load CERT KEY\n", argv[0]);
                return EXIT_FAILURE;
            }

            LoadCertificate(args[0], args[1]);
        } else if (strcmp(cmd, "delete") == 0) {
            if (args.size != 1) {
                fprintf(stderr, "Usage: %s delete HOST\n", argv[0]);
                return EXIT_FAILURE;
            }

            DeleteCertificate(args[0]);
        } else if (strcmp(cmd, "find") == 0) {
            if (args.size != 1) {
                fprintf(stderr, "Usage: %s find HOST\n", argv[0]);
                return EXIT_FAILURE;
            }

            FindCertificate(args[0]);
        } else if (strcmp(cmd, "monitor") == 0) {
            if (args.size != 0) {
                fprintf(stderr, "Usage: %s monitor\n", argv[0]);
                return EXIT_FAILURE;
            }

            Monitor();
        } else if (strcmp(cmd, "tail") == 0) {
            if (args.size != 0) {
                fprintf(stderr, "Usage: %s tail\n", argv[0]);
                return EXIT_FAILURE;
            }

            Tail();
        } else if (strcmp(cmd, "populate") == 0) {
            if (args.size < 2 || args.size > 3) {
                fprintf(stderr, "Usage: %s populate KEY {HOST|SUFFIX COUNT}\n",
                        argv[0]);
                return EXIT_FAILURE;
            }

            const char *key = args[0];
            const char *suffix = args[1];
            unsigned count = 0;

            if (args.size == 3) {
                count = strtoul(args[2], nullptr, 10);
                if (count == 0) {
                    fprintf(stderr, "Invalid COUNT parameter\n");
                    return EXIT_FAILURE;
                }
            }

            Populate(key, suffix, count);
        } else {
            fprintf(stderr, "Unknown command: %s\n", cmd);
            return EXIT_FAILURE;
        }

        return EXIT_SUCCESS;
    } catch (const std::exception &e) {
        fprintf(stderr, "%s\n", e.what());
        return EXIT_FAILURE;
    } catch (const char *msg) {
        fprintf(stderr, "%s\n", msg);
        return EXIT_FAILURE;
    }
}
<commit_msg>certdb/Main: rebase SslBuffer on WritableBuffer<commit_after>#include "Config.hxx"
#include "CertDatabase.hxx"
#include "Wildcard.hxx"
#include "ssl/ssl_init.hxx"
#include "ssl/Util.hxx"
#include "ssl/AltName.hxx"
#include "ssl/Name.hxx"
#include "ssl/MemBio.hxx"
#include "ssl/Unique.hxx"
#include "ssl/Error.hxx"
#include "pg/Error.hxx"
#include "util/ConstBuffer.hxx"
#include "util/WritableBuffer.hxx"

#include <inline/compiler.h>

#include <openssl/ts.h>

#include <stdexcept>

#include <stdio.h>
#include <stdlib.h>
#include <poll.h>

static const CertDatabaseConfig config{"dbname=lb", std::string()};

class SslBuffer : WritableBuffer<unsigned char> {
public:
    explicit SslBuffer(X509 *cert) {
        data = nullptr;
        int result = i2d_X509(cert, &data);
        if (result < 0)
            throw SslError("Failed to encode certificate");

        size = result;
    }

    explicit SslBuffer(EVP_PKEY *key) {
        data = nullptr;
        int result = i2d_PrivateKey(key, &data);
        if (result < 0)
            throw SslError("Failed to encode key");

        size = result;
    }

    SslBuffer(SslBuffer &&src):WritableBuffer<unsigned char>(src) {
        src.data = nullptr;
    }

    ~SslBuffer() {
        if (data != nullptr)
            OPENSSL_free(data);
    }

    PgBinaryValue ToPg() const {
        return {data, size};
    }
};

static UniqueX509
LoadCertFile(const char *path)
{
    auto cert = TS_CONF_load_cert(path);
    if (cert == nullptr)
        throw SslError("Failed to load certificate");

    return UniqueX509(cert);
}

static UniqueEVP_PKEY
LoadKeyFile(const char *path)
{
    auto key = TS_CONF_load_key(path, nullptr);
    if (key == nullptr)
        throw SslError("Failed to load key");

    return UniqueEVP_PKEY(key);
}

static PgResult
CheckError(PgResult &&result)
{
    if (result.IsError())
        throw PgError(std::move(result));

    return std::move(result);
}

gcc_pure
static AllocatedString<>
GetCommonName(X509_NAME &name)
{
    return NidToString(name, NID_commonName);
}

gcc_pure
static AllocatedString<>
GetCommonName(X509 *cert)
{
    X509_NAME *subject = X509_get_subject_name(cert);
    return subject != nullptr
        ? GetCommonName(*subject)
        : nullptr;
}

gcc_pure
static AllocatedString<>
FormatTime(ASN1_TIME *t)
{
    if (t == nullptr)
        return nullptr;

    return BioWriterToString([t](BIO &bio){
            ASN1_TIME_print(&bio, t);
        });
}

/**
 * @return true when new certificate has been inserted, false when an
 * existing certificate has been updated
 */
static bool
LoadCertificate(CertDatabase &db, X509 &cert, EVP_PKEY &key)
{
    const auto common_name = GetCommonName(&cert);
    assert(common_name != nullptr);

    const SslBuffer cert_buffer(&cert);
    const auto cert_der = cert_buffer.ToPg();

    const SslBuffer key_buffer(&key);
    const auto key_der = key_buffer.ToPg();

    const auto alt_names = GetSubjectAltNames(cert);

    const auto not_before = FormatTime(X509_get_notBefore(&cert));
    if (not_before == nullptr)
        throw "Certificate does not have a notBefore time stamp";

    const auto not_after = FormatTime(X509_get_notAfter(&cert));
    if (not_after == nullptr)
        throw "Certificate does not have a notAfter time stamp";

    auto result = CheckError(db.UpdateServerCertificate(common_name.c_str(),
                                                        alt_names,
                                                        not_before.c_str(),
                                                        not_after.c_str(),
                                                        cert_der, key_der));
    if (result.GetAffectedRows() > 0) {
        return false;
    } else {
        CheckError(db.InsertServerCertificate(common_name.c_str(),
                                              alt_names,
                                              not_before.c_str(),
                                              not_after.c_str(),
                                              cert_der, key_der));
        return true;
    }
}

static void
LoadCertificate(const char *cert_path, const char *key_path)
{
    const ScopeSslGlobalInit ssl_init;

    const auto cert = LoadCertFile(cert_path);
    const auto common_name = GetCommonName(cert.get());
    if (common_name == nullptr)
        throw "Certificate has no common name";

    const auto key = LoadKeyFile(key_path);
    if (!MatchModulus(*cert, *key))
        throw "Key and certificate do not match.";

    CertDatabase db(config);

    bool inserted = LoadCertificate(db, *cert, *key);
    printf("%s: %s\n", inserted ? "insert" : "update", common_name.c_str());
    db.NotifyModified();
}

static void
DeleteCertificate(const char *host)
{
    CertDatabase db(config);

    const auto result = CheckError(db.DeleteServerCertificateByName(host));
    if (result.GetAffectedRows() == 0)
        throw "Certificate not found";

    db.NotifyModified();
}

static UniqueX509
FindCertByName(CertDatabase &db, const char *common_name)
{
    auto result = CheckError(db.FindServerCertificateByName(common_name));
    if (result.GetRowCount() == 0)
        return nullptr;

    if (!result.IsColumnBinary(0) || result.IsValueNull(0, 0))
        throw "Unexpected result";

    auto cert_der = result.GetBinaryValue(0, 0);

    auto data = (const unsigned char *)cert_der.value;
    UniqueX509 cert(d2i_X509(nullptr, &data, cert_der.size));
    if (!cert)
        throw "d2i_X509() failed";

    return cert;
}

static UniqueX509
FindCertByHost(const char *host)
{
    CertDatabase db(config);

    auto cert = FindCertByName(db, host);
    if (!cert) {
        auto wildcard = MakeCommonNameWildcard(host);
        if (!wildcard.empty())
            cert = FindCertByName(db, wildcard.c_str());

        if (!cert)
            throw "Certificate not found";
    }

    return cert;
}

static void
FindCertificate(const char *host)
{
    const ScopeSslGlobalInit ssl_init;

    auto cert = FindCertByHost(host);
    X509_print_fp(stdout, cert.get());
    PEM_write_X509(stdout, cert.get());
}

gcc_noreturn
static void
Monitor()
{
    CertDatabase db(config);
    CheckError(db.ListenModified());

    std::string last_modified = db.GetLastModified();
    if (last_modified.empty()) {
        last_modified = db.GetCurrentTimestamp();
        if (last_modified.empty())
            throw "CURRENT_TIMESTAMP failed";
    }

    struct pollfd pfd = {
        .fd = db.GetSocket(),
        .events = POLLIN,
    };

    while (true) {
        if (poll(&pfd, 1, -1) < 0)
            throw "poll() failed";

        db.ConsumeInput();
        while (db.GetNextNotify()) {}

        std::string new_last_modified = db.GetLastModified();
        if (new_last_modified.empty())
            throw "No MAX(modified) found";

        for (auto &row : CheckError(db.GetModifiedServerCertificatesMeta(last_modified.c_str())))
            printf("%s %s %s\n",
                   row.GetValue(1),
                   *row.GetValue(0) == 't' ? "deleted" : "modified",
                   row.GetValue(2));

        last_modified = std::move(new_last_modified);
    }
}

static void
Tail()
{
    CertDatabase db(config);

    for (auto &row : CheckError(db.TailModifiedServerCertificatesMeta()))
        printf("%s %s %s\n",
               row.GetValue(1),
               *row.GetValue(0) == 't' ? "deleted" : "modified",
               row.GetValue(2));
}

static void
AddExt(X509 *cert, int nid, const char *value)
{
    UniqueX509_EXTENSION ext(X509V3_EXT_conf_nid(nullptr, nullptr, nid,
                                                 const_cast<char *>(value)));
    if (ext == nullptr)
        throw SslError("X509V3_EXT_conf_nid() failed");

    X509_add_ext(cert, ext.get(), -1);
}

static UniqueX509
MakeSelfSignedDummyCert(EVP_PKEY &key, const char *common_name)
{
    UniqueX509 cert(X509_new());
    if (cert == nullptr)
        throw "X509_new() failed";

    auto *name = X509_get_subject_name(cert.get());

    if (!X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
                                    const_cast<unsigned char *>((const unsigned char *)common_name),
                                    -1, -1, 0))
        throw SslError("X509_NAME_add_entry_by_NID() failed");

    X509_set_issuer_name(cert.get(), name);

    X509_set_version(cert.get(), 2);
    ASN1_INTEGER_set(X509_get_serialNumber(cert.get()), 1);
    X509_gmtime_adj(X509_get_notBefore(cert.get()), 0);
    X509_gmtime_adj(X509_get_notAfter(cert.get()), 60 * 60);
    X509_set_pubkey(cert.get(), &key);

    AddExt(cert.get(), NID_basic_constraints, "critical,CA:TRUE");
    AddExt(cert.get(), NID_key_usage, "critical,keyCertSign");

    if (!X509_sign(cert.get(), &key, EVP_sha1()))
        throw SslError("X509_sign() failed");

    return cert;
}

static void
Populate(CertDatabase &db, EVP_PKEY *key, PgBinaryValue key_der,
         const char *common_name)
{
    (void)key;

    // TODO: fake time stamps
    const char *not_before = "1971-01-01";
    const char *not_after = "1971-01-01";

    auto cert = MakeSelfSignedDummyCert(*key, common_name);
    const SslBuffer cert_buffer(cert.get());
    const auto cert_der = cert_buffer.ToPg();

    const auto alt_names = GetSubjectAltNames(*cert);

    CheckError(db.InsertServerCertificate(common_name, alt_names,
                                          not_before, not_after,
                                          cert_der, key_der));
}

static void
Populate(const char *key_path, const char *suffix, unsigned n)
{
    const ScopeSslGlobalInit ssl_init;

    const auto key = LoadKeyFile(key_path);

    const SslBuffer key_buffer(key.get());
    const auto key_der = key_buffer.ToPg();

    CertDatabase db(config);

    if (n == 0) {
        Populate(db, key.get(), key_der, suffix);
    } else {
        if (!db.BeginSerializable())
            throw "BEGIN failed";

        for (unsigned i = 1; i <= n; ++i) {
            char buffer[256];
            snprintf(buffer, sizeof(buffer), "%u%s", i, suffix);
            Populate(db, key.get(), key_der, buffer);
        }

        if (!db.Commit())
            throw "COMMIT failed";
    }

    db.NotifyModified();
}

int
main(int argc, char **argv)
{
    ConstBuffer<const char *> args(argv + 1, argc - 1);

    if (args.IsEmpty()) {
        fprintf(stderr, "Usage: %s COMMAND ...\n"
                "\n"
                "Commands:\n"
                "  load CERT KEY\n"
                "  delete HOST\n"
                "  find HOST\n"
                "  monitor\n"
                "  tail\n"
                "  populate KEY COUNT SUFFIX\n"
                "\n", argv[0]);
        return EXIT_FAILURE;
    }

    const auto cmd = args.shift();

    try {
        if (strcmp(cmd, "load") == 0) {
            if (args.size != 2) {
                fprintf(stderr, "Usage: %s load CERT KEY\n", argv[0]);
                return EXIT_FAILURE;
            }

            LoadCertificate(args[0], args[1]);
        } else if (strcmp(cmd, "delete") == 0) {
            if (args.size != 1) {
                fprintf(stderr, "Usage: %s delete HOST\n", argv[0]);
                return EXIT_FAILURE;
            }

            DeleteCertificate(args[0]);
        } else if (strcmp(cmd, "find") == 0) {
            if (args.size != 1) {
                fprintf(stderr, "Usage: %s find HOST\n", argv[0]);
                return EXIT_FAILURE;
            }

            FindCertificate(args[0]);
        } else if (strcmp(cmd, "monitor") == 0) {
            if (args.size != 0) {
                fprintf(stderr, "Usage: %s monitor\n", argv[0]);
                return EXIT_FAILURE;
            }

            Monitor();
        } else if (strcmp(cmd, "tail") == 0) {
            if (args.size != 0) {
                fprintf(stderr, "Usage: %s tail\n", argv[0]);
                return EXIT_FAILURE;
            }

            Tail();
        } else if (strcmp(cmd, "populate") == 0) {
            if (args.size < 2 || args.size > 3) {
                fprintf(stderr, "Usage: %s populate KEY {HOST|SUFFIX COUNT}\n",
                        argv[0]);
                return EXIT_FAILURE;
            }

            const char *key = args[0];
            const char *suffix = args[1];
            unsigned count = 0;

            if (args.size == 3) {
                count = strtoul(args[2], nullptr, 10);
                if (count == 0) {
                    fprintf(stderr, "Invalid COUNT parameter\n");
                    return EXIT_FAILURE;
                }
            }

            Populate(key, suffix, count);
        } else {
            fprintf(stderr, "Unknown command: %s\n", cmd);
            return EXIT_FAILURE;
        }

        return EXIT_SUCCESS;
    } catch (const std::exception &e) {
        fprintf(stderr, "%s\n", e.what());
        return EXIT_FAILURE;
    } catch (const char *msg) {
        fprintf(stderr, "%s\n", msg);
        return EXIT_FAILURE;
    }
}
<|endoftext|>


<commit_before>/*
 * SessionPanmirrorDataCite.cpp
 *
 * Copyright (C) 2009-16 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionPanmirrorDataCite.hpp"

#include <shared_core/Error.hpp>
#include <shared_core/json/Json.hpp>

#include <core/Exec.hpp>
#include <core/json/JsonRpc.hpp>
#include <core/http/Util.hpp>

#include <session/SessionModuleContext.hpp>
#include <session/SessionAsyncDownloadFile.hpp>

#include "SessionPanmirrorUtils.hpp"

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace panmirror {
namespace datacite {

namespace {

const char * const kDataCiteApiHost = "https://api.datacite.org";

// convert json returned by API to json record expected by the client
json::Object asDataCiteRecord(const json::Object resultJson)
{
   resultJson.writeFormatted(std::cerr);

   // get attributes object
   json::Object attributesJson;
   json::readObject(resultJson, "attributes", attributesJson);

   // read some basic attributes
   std::string doi;
   json::Array titlesJson;
   json::readObject(attributesJson, "doi", doi, "titles", titlesJson);

   // bail if we don't have a doi and a title
   if (doi.empty() || titlesJson.isEmpty())
      return json::Object();

   // return a record w/ doi + other fields
   json::Object recordJson;
   recordJson["doi"] = doi;

   // read title
   if (titlesJson[0].isObject() && titlesJson[0].getObject().hasMember("title"))
      recordJson["title"] = titlesJson[0].getObject()["title"];

   // read publication info
   std::string publisher;
   json::readObject(attributesJson, "publisher", publisher);
   if (!publisher.empty())
      recordJson["publisher"] = publisher;
   if (attributesJson.hasMember("publicationYear"))
      recordJson["publicationYear"] = attributesJson["publicationYear"];

   // read creators
   json::Array creatorsJson;
   json::readObject(attributesJson, "creators", creatorsJson);
   if (!creatorsJson.isEmpty())
   {
      json::Array targetCreatorsJson;
      for (auto creatorJson : creatorsJson)
      {
         if (creatorJson.isObject() && creatorJson.getObject().hasMember("name"))
         {
             std::string familyName, givenName, fullName;
             json::readObject(creatorJson.getObject(), "name", fullName);
             json::readObject(creatorJson.getObject(), "givenName", givenName);
             json::readObject(creatorJson.getObject(), "familyName", familyName);

             json::Object targetJson;
             if (!fullName.empty())
                 targetJson["fullName"] = fullName;
             if (!givenName.empty())
                 targetJson["givenName"] = givenName;
             if (!familyName.empty())
                 targetJson["familyName"] = familyName;
             targetCreatorsJson.push_back(targetJson);

         }
      }
      recordJson["creators"] = targetCreatorsJson;
   }

   // read type
   json::Object typesJson;
   json::readObject(attributesJson, "types", typesJson);
   if (typesJson.hasMember("citeproc"))
      recordJson["type"] = typesJson["citeproc"];

   return recordJson;
}

void dataCiteRequest(const std::string& resource,
                     http::Fields params,
                     const json::JsonRpcFunctionContinuation& cont,
                     const JsonHandler& handler)
{
   // build query string
   std::string queryString;
   core::http::util::buildQueryString(params, &queryString);
   if (queryString.length() > 0)
      queryString = "?" + queryString;

   // build the url and make the request
   boost::format fmt("%s/%s%s");
   const std::string url = boost::str(fmt % kDataCiteApiHost % resource % queryString);

   asyncDownloadFile(url, boost::bind(jsonRpcDownloadHandler, cont, _1, ERROR_LOCATION,
                                      [handler](const core::json::JsonRpcFunctionContinuation& cont,
                                      core::json::Value json) {
      // validate top level JSON:API response
      if (!json.isObject() ||
          (!json.getObject().hasMember("data") && !json.getObject().hasMember("errors")))
      {
         resolveJsonRpcContinuation(cont, kStatusError, json::Value(),
                                    "Unexpected data format returned by DataCite API");
      }
      else if (json.getObject().hasMember("errors"))
      {
         json::Array errorsJson;
         json::readObject(json.getObject(), "errors", errorsJson);
         std::string code, title;
         if (errorsJson.getSize() > 0 && errorsJson[0].isObject())
         {
            json::readObject(errorsJson[0].getObject(),
                             "code", code,
                             "title", title);
         }
         if (code.empty())
            code = "-1";
         if (title.empty())
            title = "(Unknown Error)";

         resolveJsonRpcContinuation(cont, kStatusError, json::Value(),
                                    "DataCite API Error: " + code + " - " + title);
      }
      else
      {
         handler(cont, json.getObject()["data"]);
      }
   }));
}

void dataCiteSearch(const json::JsonRpcRequest& request,
                    const json::JsonRpcFunctionContinuation& cont)
{
   // extract query
   std::string query;
   Error error = json::readParams(request.params, &query);
   if (error)
   {
     json::JsonRpcResponse response;
     setErrorResponse(error, &response);
     cont(Success(), &response);
     return;
   }

   // build params
   core::http::Fields params;
   params.push_back(std::make_pair("query", query));

   // make request
   dataCiteRequest("dois", params, cont,
                   [](const core::json::JsonRpcFunctionContinuation& cont, core::json::Value json) {

      if (!json.isArray())
      {
         resolveJsonRpcContinuation(cont, kStatusError, json::Value(),
                                    "DataCite API returned incorrectly formatted response");
      }
      else
      {
         json::Array resultsJson = json.getArray();
         json::Array recordsJson;
         for (auto resultJson : resultsJson)
         {
            if (resultJson.isObject())
            {
                 json::Object jsonRecord = asDataCiteRecord(resultJson.getObject());
                 if (jsonRecord.getSize() > 0)
                    recordsJson.push_back(jsonRecord);
            }
         }
         resolveJsonRpcContinuation(cont, kStatusOK, recordsJson);
      }
   });
}

} // end anonymous namespace

Error initialize()
{
   ExecBlock initBlock;
   initBlock.addFunctions()
      (boost::bind(module_context::registerAsyncRpcMethod, "datacite_search", dataCiteSearch))
   ;
   return initBlock.execute();
}

} // end namespace datacite
} // end namespace panmirror
} // end namespace modules
} // end namespace session
} // end namespace rstudio
<commit_msg>Remove extraneous debug output in Datacite<commit_after>/*
 * SessionPanmirrorDataCite.cpp
 *
 * Copyright (C) 2009-16 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionPanmirrorDataCite.hpp"

#include <shared_core/Error.hpp>
#include <shared_core/json/Json.hpp>

#include <core/Exec.hpp>
#include <core/json/JsonRpc.hpp>
#include <core/http/Util.hpp>

#include <session/SessionModuleContext.hpp>
#include <session/SessionAsyncDownloadFile.hpp>

#include "SessionPanmirrorUtils.hpp"

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace panmirror {
namespace datacite {

namespace {

const char * const kDataCiteApiHost = "https://api.datacite.org";

// convert json returned by API to json record expected by the client
json::Object asDataCiteRecord(const json::Object resultJson)
{
   // get attributes object
   json::Object attributesJson;
   json::readObject(resultJson, "attributes", attributesJson);

   // read some basic attributes
   std::string doi;
   json::Array titlesJson;
   json::readObject(attributesJson, "doi", doi, "titles", titlesJson);

   // bail if we don't have a doi and a title
   if (doi.empty() || titlesJson.isEmpty())
      return json::Object();

   // return a record w/ doi + other fields
   json::Object recordJson;
   recordJson["doi"] = doi;

   // read title
   if (titlesJson[0].isObject() && titlesJson[0].getObject().hasMember("title"))
      recordJson["title"] = titlesJson[0].getObject()["title"];

   // read publication info
   std::string publisher;
   json::readObject(attributesJson, "publisher", publisher);
   if (!publisher.empty())
      recordJson["publisher"] = publisher;
   if (attributesJson.hasMember("publicationYear"))
      recordJson["publicationYear"] = attributesJson["publicationYear"];

   // read creators
   json::Array creatorsJson;
   json::readObject(attributesJson, "creators", creatorsJson);
   if (!creatorsJson.isEmpty())
   {
      json::Array targetCreatorsJson;
      for (auto creatorJson : creatorsJson)
      {
         if (creatorJson.isObject() && creatorJson.getObject().hasMember("name"))
         {
             std::string familyName, givenName, fullName;
             json::readObject(creatorJson.getObject(), "name", fullName);
             json::readObject(creatorJson.getObject(), "givenName", givenName);
             json::readObject(creatorJson.getObject(), "familyName", familyName);

             json::Object targetJson;
             if (!fullName.empty())
                 targetJson["fullName"] = fullName;
             if (!givenName.empty())
                 targetJson["givenName"] = givenName;
             if (!familyName.empty())
                 targetJson["familyName"] = familyName;
             targetCreatorsJson.push_back(targetJson);

         }
      }
      recordJson["creators"] = targetCreatorsJson;
   }

   // read type
   json::Object typesJson;
   json::readObject(attributesJson, "types", typesJson);
   if (typesJson.hasMember("citeproc"))
      recordJson["type"] = typesJson["citeproc"];

   return recordJson;
}

void dataCiteRequest(const std::string& resource,
                     http::Fields params,
                     const json::JsonRpcFunctionContinuation& cont,
                     const JsonHandler& handler)
{
   // build query string
   std::string queryString;
   core::http::util::buildQueryString(params, &queryString);
   if (queryString.length() > 0)
      queryString = "?" + queryString;

   // build the url and make the request
   boost::format fmt("%s/%s%s");
   const std::string url = boost::str(fmt % kDataCiteApiHost % resource % queryString);

   asyncDownloadFile(url, boost::bind(jsonRpcDownloadHandler, cont, _1, ERROR_LOCATION,
                                      [handler](const core::json::JsonRpcFunctionContinuation& cont,
                                      core::json::Value json) {
      // validate top level JSON:API response
      if (!json.isObject() ||
          (!json.getObject().hasMember("data") && !json.getObject().hasMember("errors")))
      {
         resolveJsonRpcContinuation(cont, kStatusError, json::Value(),
                                    "Unexpected data format returned by DataCite API");
      }
      else if (json.getObject().hasMember("errors"))
      {
         json::Array errorsJson;
         json::readObject(json.getObject(), "errors", errorsJson);
         std::string code, title;
         if (errorsJson.getSize() > 0 && errorsJson[0].isObject())
         {
            json::readObject(errorsJson[0].getObject(),
                             "code", code,
                             "title", title);
         }
         if (code.empty())
            code = "-1";
         if (title.empty())
            title = "(Unknown Error)";

         resolveJsonRpcContinuation(cont, kStatusError, json::Value(),
                                    "DataCite API Error: " + code + " - " + title);
      }
      else
      {
         handler(cont, json.getObject()["data"]);
      }
   }));
}

void dataCiteSearch(const json::JsonRpcRequest& request,
                    const json::JsonRpcFunctionContinuation& cont)
{
   // extract query
   std::string query;
   Error error = json::readParams(request.params, &query);
   if (error)
   {
     json::JsonRpcResponse response;
     setErrorResponse(error, &response);
     cont(Success(), &response);
     return;
   }

   // build params
   core::http::Fields params;
   params.push_back(std::make_pair("query", query));

   // make request
   dataCiteRequest("dois", params, cont,
                   [](const core::json::JsonRpcFunctionContinuation& cont, core::json::Value json) {

      if (!json.isArray())
      {
         resolveJsonRpcContinuation(cont, kStatusError, json::Value(),
                                    "DataCite API returned incorrectly formatted response");
      }
      else
      {
         json::Array resultsJson = json.getArray();
         json::Array recordsJson;
         for (auto resultJson : resultsJson)
         {
            if (resultJson.isObject())
            {
                 json::Object jsonRecord = asDataCiteRecord(resultJson.getObject());
                 if (jsonRecord.getSize() > 0)
                    recordsJson.push_back(jsonRecord);
            }
         }
         resolveJsonRpcContinuation(cont, kStatusOK, recordsJson);
      }
   });
}

} // end anonymous namespace

Error initialize()
{
   ExecBlock initBlock;
   initBlock.addFunctions()
      (boost::bind(module_context::registerAsyncRpcMethod, "datacite_search", dataCiteSearch))
   ;
   return initBlock.execute();
}

} // end namespace datacite
} // end namespace panmirror
} // end namespace modules
} // end namespace session
} // end namespace rstudio
<|endoftext|>


<commit_before>
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin developers
// Copyright (c) 2017 Empinel/The Ion Developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "assert.h"

#include "chainparams.h"
#include "main.h"
#include "util.h"
#include "amount.h"

#include <boost/assign/list_of.hpp>

using namespace boost::assign;

struct SeedSpec6 {
    uint8_t addr[16];
    uint16_t port;
};

#include "chainparamsseeds.h"

/**
 * Main network
 */

//! Convert the pnSeeds6 array into usable address objects.
static void convertSeed6(std::vector<CAddress> &vSeedsOut, const SeedSpec6 *data, unsigned int count)
{
    // It'll only connect to one or two seed nodes because once it connects,
    // it'll get a pile of addresses with newer timestamps.
    // Seed nodes are given a random 'last seen time' of between one and two
    // weeks ago.
    const int64_t nOneWeek = 7*24*60*60;
    for (unsigned int i = 0; i < count; i++)
    {
        struct in6_addr ip;
        memcpy(&ip, data[i].addr, sizeof(ip));
        CAddress addr(CService(ip, data[i].port));
        addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;
        vSeedsOut.push_back(addr);
    }
}

static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, const uint64_t nTime, const uint32_t nNonce, const uint32_t nBits, const int32_t nVersion, const CAmount& genesisReward)
{
    std::vector<CTxIn> vin;
    vin.resize(1);
    vin[0].scriptSig = CScript() << nTime << CScriptNum(4) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
    std::vector<CTxOut> vout;
    vout.resize(1);
    vout[0].nValue = genesisReward;
    vout[0].scriptPubKey = genesisOutputScript;
    CTransaction txNew(1, nTime, vin, vout, 0);

    CBlock genesis;
    genesis.nTime    = nTime;
    genesis.nBits    = nBits;
    genesis.nNonce   = nNonce;
    genesis.nVersion = nVersion;
    genesis.vtx.push_back(txNew);
    genesis.hashPrevBlock.SetNull();
    genesis.hashMerkleRoot = genesis.BuildMerkleTree();

    return genesis;
}

static CBlock CreateGenesisBlock(uint64_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
{
    const char* pszTimestamp = "MTNC genesis block 2017 8 8";
    const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
    return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
}

class CMainParams : public CChainParams {
public:
    CMainParams() {
        // The message start string is designed to be unlikely to occur in normal data.
        // The characters are rarely used upper ASCII, not valid as UTF-8, and produce
        // a large 4-byte int at any alignment.
        pchMessageStart[0] = 0xd5;
        pchMessageStart[1] = 0xce;
        pchMessageStart[2] = 0x8c;
        pchMessageStart[3] = 0x5e;
        vAlertPubKey = ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f");
        nDefaultPort = 10086;
        nRPCPort = 10085;
        nProofOfWorkLimit = uint256S("000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
        nProofOfStakeLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");

        genesis = CreateGenesisBlock(1502272349, 3217844, 0x1e00ffff, 1, (1 * COIN));

        if (true && (genesis.GetHash() != hashGenesisBlock)) {

        // This will figure out a valid hash and Nonce if you're
        // creating a different genesis block:
            uint256 hashTarget = uint256().SetCompact(genesis.nBits);
            while (genesis.GetHash() > hashTarget)
               {
                   ++genesis.nNonce;
                   if (genesis.nNonce == 0)
                   {
                       printf("NONCE WRAPPED, incrementing time");
                       ++genesis.nTime;
                   }
               }
                           printf("genesis.nTime = %u \n", genesis.nTime);
                           printf("genesis.nNonce = %u \n", genesis.nNonce);
                           printf("genesis.nVersion = %u \n", genesis.nVersion);
                           printf("genesis.GetHash = %s\n", genesis.GetHash().ToString().c_str()); //first this, then comment this line out and uncomment the one under.
                           printf("genesis.hashMerkleRoot = %s \n", genesis.hashMerkleRoot.ToString().c_str()); //improvised. worked for me, to find merkle root/
        }

        hashGenesisBlock = genesis.GetHash();

        assert(hashGenesisBlock == uint256("0x00000016046213f1d00cf060b74be707ee78cf9fadb6e5717935295c1ca3d9a4"));
        assert(genesis.hashMerkleRoot == uint256("0x10604c2780ecd24d1fdd729cf30a7f18d85be6215cbfd8948459b1c887dc4179"));

        base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,50);
        base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5);
        base58Prefixes[SECRET_KEY] =     std::vector<unsigned char>(1,178);
        base58Prefixes[STEALTH_ADDRESS] = std::vector<unsigned char>(1,40);
        base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container<std::vector<unsigned char> >();
        base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container<std::vector<unsigned char> >();

        vSeeds.push_back(CDNSSeedData("47.52.46.158", "47.52.46.158"));
        vSeeds.push_back(CDNSSeedData("23.247.2.51", "23.247.2.51"));
        vSeeds.push_back(CDNSSeedData("50.117.86.76", "50.117.86.76"));
        convertSeed6(vFixedSeeds, pnSeed6_main, ARRAYLEN(pnSeed6_main));

        nPoolMaxTransactions = 3;
        strDarksendPoolDummyAddress = "MArGStjK9iAS9uEavGyZNp8W6A9PbhRXWJ";
        nLastPOWBlock 	= 20000;
    }

    virtual const CBlock& GenesisBlock() const { return genesis; }
    virtual Network NetworkID() const { return CChainParams::MAIN; }

    virtual const vector<CAddress>& FixedSeeds() const {
        return vFixedSeeds;
    }
protected:
    CBlock genesis;
    vector<CAddress> vFixedSeeds;
};
static CMainParams mainParams;

//
// Testnet
//

class CTestNetParams : public CMainParams {
public:
    CTestNetParams() {
        // The message start string is designed to be unlikely to occur in normal data.
        // The characters are rarely used upper ASCII, not valid as UTF-8, and produce
        // a large 4-byte int at any alignment.
        pchMessageStart[0] = 0xf2;
        pchMessageStart[1] = 0xae;
        pchMessageStart[2] = 0xe7;
        pchMessageStart[3] = 0xe4;
        vAlertPubKey = ParseHex("");
        nDefaultPort = 11111;
        nRPCPort = 11110;
        strDataDir = "testnet";

        nProofOfWorkLimit = uint256S("000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
        nProofOfStakeLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");

        genesis = CreateGenesisBlock(1502272349, 3217844, 0x1e00ffff, 1, (1 * COIN));

        if (true && (genesis.GetHash() != hashGenesisBlock)) {

        // This will figure out a valid hash and Nonce if you're
        // creating a different genesis block:
            uint256 hashTarget = uint256().SetCompact(genesis.nBits);
            while (genesis.GetHash() > hashTarget)
               {
                   ++genesis.nNonce;
                   if (genesis.nNonce == 0)
                   {
                       printf("NONCE WRAPPED, incrementing time");
                       ++genesis.nTime;
                   }
               }
                           printf("genesis.nTime = %u \n", genesis.nTime);
                           printf("genesis.nNonce = %u \n", genesis.nNonce);
                           printf("genesis.nVersion = %u \n", genesis.nVersion);
                           printf("genesis.GetHash = %s\n", genesis.GetHash().ToString().c_str()); //first this, then comment this line out and uncomment the one under.
                           printf("genesis.hashMerkleRoot = %s \n", genesis.hashMerkleRoot.ToString().c_str()); //improvised. worked for me, to find merkle root/
        }

        hashGenesisBlock = genesis.GetHash();

        assert(hashGenesisBlock == uint256("0x00000016046213f1d00cf060b74be707ee78cf9fadb6e5717935295c1ca3d9a4"));
        assert(genesis.hashMerkleRoot == uint256("0x10604c2780ecd24d1fdd729cf30a7f18d85be6215cbfd8948459b1c887dc4179"));

        vFixedSeeds.clear();
        vSeeds.clear();

        base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,97);
        base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
        base58Prefixes[SECRET_KEY]     = std::vector<unsigned char>(1,239);
        base58Prefixes[STEALTH_ADDRESS] = std::vector<unsigned char>(1,40);
        base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container<std::vector<unsigned char> >();
        base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x35)(0x83)(0x94).convert_to_container<std::vector<unsigned char> >();

        nLastPOWBlock = 0x7fffffff;
    }
    virtual Network NetworkID() const { return CChainParams::TESTNET; }
};
static CTestNetParams testNetParams;

static CChainParams *pCurrentParams = &mainParams;

const CChainParams &Params() {
    return *pCurrentParams;
}

void SelectParams(CChainParams::Network network) {
    switch (network) {
        case CChainParams::MAIN:
            pCurrentParams = &mainParams;
            break;
        case CChainParams::TESTNET:
            pCurrentParams = &testNetParams;
            break;
        default:
            assert(false && "Unimplemented network");
            return;
    }
}

bool SelectParamsFromCommandLine() {

    bool fTestNet = GetBoolArg("-testnet", false);

    if (fTestNet) {
        SelectParams(CChainParams::TESTNET);
    } else {
        SelectParams(CChainParams::MAIN);
    }
    return true;
}
<commit_msg>seed update<commit_after>
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin developers
// Copyright (c) 2017 Empinel/The Ion Developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "assert.h"

#include "chainparams.h"
#include "main.h"
#include "util.h"
#include "amount.h"

#include <boost/assign/list_of.hpp>

using namespace boost::assign;

struct SeedSpec6 {
    uint8_t addr[16];
    uint16_t port;
};

#include "chainparamsseeds.h"

/**
 * Main network
 */

//! Convert the pnSeeds6 array into usable address objects.
static void convertSeed6(std::vector<CAddress> &vSeedsOut, const SeedSpec6 *data, unsigned int count)
{
    // It'll only connect to one or two seed nodes because once it connects,
    // it'll get a pile of addresses with newer timestamps.
    // Seed nodes are given a random 'last seen time' of between one and two
    // weeks ago.
    const int64_t nOneWeek = 7*24*60*60;
    for (unsigned int i = 0; i < count; i++)
    {
        struct in6_addr ip;
        memcpy(&ip, data[i].addr, sizeof(ip));
        CAddress addr(CService(ip, data[i].port));
        addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;
        vSeedsOut.push_back(addr);
    }
}

static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, const uint64_t nTime, const uint32_t nNonce, const uint32_t nBits, const int32_t nVersion, const CAmount& genesisReward)
{
    std::vector<CTxIn> vin;
    vin.resize(1);
    vin[0].scriptSig = CScript() << nTime << CScriptNum(4) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
    std::vector<CTxOut> vout;
    vout.resize(1);
    vout[0].nValue = genesisReward;
    vout[0].scriptPubKey = genesisOutputScript;
    CTransaction txNew(1, nTime, vin, vout, 0);

    CBlock genesis;
    genesis.nTime    = nTime;
    genesis.nBits    = nBits;
    genesis.nNonce   = nNonce;
    genesis.nVersion = nVersion;
    genesis.vtx.push_back(txNew);
    genesis.hashPrevBlock.SetNull();
    genesis.hashMerkleRoot = genesis.BuildMerkleTree();

    return genesis;
}

static CBlock CreateGenesisBlock(uint64_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
{
    const char* pszTimestamp = "MTNC genesis block 2017 8 8";
    const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
    return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
}

class CMainParams : public CChainParams {
public:
    CMainParams() {
        // The message start string is designed to be unlikely to occur in normal data.
        // The characters are rarely used upper ASCII, not valid as UTF-8, and produce
        // a large 4-byte int at any alignment.
        pchMessageStart[0] = 0xd5;
        pchMessageStart[1] = 0xce;
        pchMessageStart[2] = 0x8c;
        pchMessageStart[3] = 0x5e;
        vAlertPubKey = ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f");
        nDefaultPort = 10086;
        nRPCPort = 10085;
        nProofOfWorkLimit = uint256S("000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
        nProofOfStakeLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");

        genesis = CreateGenesisBlock(1502272349, 3217844, 0x1e00ffff, 1, (1 * COIN));

        if (true && (genesis.GetHash() != hashGenesisBlock)) {

        // This will figure out a valid hash and Nonce if you're
        // creating a different genesis block:
            uint256 hashTarget = uint256().SetCompact(genesis.nBits);
            while (genesis.GetHash() > hashTarget)
               {
                   ++genesis.nNonce;
                   if (genesis.nNonce == 0)
                   {
                       printf("NONCE WRAPPED, incrementing time");
                       ++genesis.nTime;
                   }
               }
                           printf("genesis.nTime = %u \n", genesis.nTime);
                           printf("genesis.nNonce = %u \n", genesis.nNonce);
                           printf("genesis.nVersion = %u \n", genesis.nVersion);
                           printf("genesis.GetHash = %s\n", genesis.GetHash().ToString().c_str()); //first this, then comment this line out and uncomment the one under.
                           printf("genesis.hashMerkleRoot = %s \n", genesis.hashMerkleRoot.ToString().c_str()); //improvised. worked for me, to find merkle root/
        }

        hashGenesisBlock = genesis.GetHash();

        assert(hashGenesisBlock == uint256("0x00000016046213f1d00cf060b74be707ee78cf9fadb6e5717935295c1ca3d9a4"));
        assert(genesis.hashMerkleRoot == uint256("0x10604c2780ecd24d1fdd729cf30a7f18d85be6215cbfd8948459b1c887dc4179"));

        base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,50);
        base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5);
        base58Prefixes[SECRET_KEY] =     std::vector<unsigned char>(1,178);
        base58Prefixes[STEALTH_ADDRESS] = std::vector<unsigned char>(1,40);
        base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container<std::vector<unsigned char> >();
        base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container<std::vector<unsigned char> >();

        vSeeds.push_back(CDNSSeedData("47.52.46.158", "47.52.46.158"));
        vSeeds.push_back(CDNSSeedData("seed.masternodecoin.org", "seed.masternodecoin.org"));
        vSeeds.push_back(CDNSSeedData("50.118.255.28", "50.118.255.28"));
        convertSeed6(vFixedSeeds, pnSeed6_main, ARRAYLEN(pnSeed6_main));

        nPoolMaxTransactions = 3;
        strDarksendPoolDummyAddress = "MArGStjK9iAS9uEavGyZNp8W6A9PbhRXWJ";
        nLastPOWBlock 	= 20000;
    }

    virtual const CBlock& GenesisBlock() const { return genesis; }
    virtual Network NetworkID() const { return CChainParams::MAIN; }

    virtual const vector<CAddress>& FixedSeeds() const {
        return vFixedSeeds;
    }
protected:
    CBlock genesis;
    vector<CAddress> vFixedSeeds;
};
static CMainParams mainParams;

//
// Testnet
//

class CTestNetParams : public CMainParams {
public:
    CTestNetParams() {
        // The message start string is designed to be unlikely to occur in normal data.
        // The characters are rarely used upper ASCII, not valid as UTF-8, and produce
        // a large 4-byte int at any alignment.
        pchMessageStart[0] = 0xf2;
        pchMessageStart[1] = 0xae;
        pchMessageStart[2] = 0xe7;
        pchMessageStart[3] = 0xe4;
        vAlertPubKey = ParseHex("");
        nDefaultPort = 11111;
        nRPCPort = 11110;
        strDataDir = "testnet";

        nProofOfWorkLimit = uint256S("000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
        nProofOfStakeLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");

        genesis = CreateGenesisBlock(1502272349, 3217844, 0x1e00ffff, 1, (1 * COIN));

        if (true && (genesis.GetHash() != hashGenesisBlock)) {

        // This will figure out a valid hash and Nonce if you're
        // creating a different genesis block:
            uint256 hashTarget = uint256().SetCompact(genesis.nBits);
            while (genesis.GetHash() > hashTarget)
               {
                   ++genesis.nNonce;
                   if (genesis.nNonce == 0)
                   {
                       printf("NONCE WRAPPED, incrementing time");
                       ++genesis.nTime;
                   }
               }
                           printf("genesis.nTime = %u \n", genesis.nTime);
                           printf("genesis.nNonce = %u \n", genesis.nNonce);
                           printf("genesis.nVersion = %u \n", genesis.nVersion);
                           printf("genesis.GetHash = %s\n", genesis.GetHash().ToString().c_str()); //first this, then comment this line out and uncomment the one under.
                           printf("genesis.hashMerkleRoot = %s \n", genesis.hashMerkleRoot.ToString().c_str()); //improvised. worked for me, to find merkle root/
        }

        hashGenesisBlock = genesis.GetHash();

        assert(hashGenesisBlock == uint256("0x00000016046213f1d00cf060b74be707ee78cf9fadb6e5717935295c1ca3d9a4"));
        assert(genesis.hashMerkleRoot == uint256("0x10604c2780ecd24d1fdd729cf30a7f18d85be6215cbfd8948459b1c887dc4179"));

        vFixedSeeds.clear();
        vSeeds.clear();

        base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,97);
        base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
        base58Prefixes[SECRET_KEY]     = std::vector<unsigned char>(1,239);
        base58Prefixes[STEALTH_ADDRESS] = std::vector<unsigned char>(1,40);
        base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container<std::vector<unsigned char> >();
        base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x35)(0x83)(0x94).convert_to_container<std::vector<unsigned char> >();

        nLastPOWBlock = 0x7fffffff;
    }
    virtual Network NetworkID() const { return CChainParams::TESTNET; }
};
static CTestNetParams testNetParams;

static CChainParams *pCurrentParams = &mainParams;

const CChainParams &Params() {
    return *pCurrentParams;
}

void SelectParams(CChainParams::Network network) {
    switch (network) {
        case CChainParams::MAIN:
            pCurrentParams = &mainParams;
            break;
        case CChainParams::TESTNET:
            pCurrentParams = &testNetParams;
            break;
        default:
            assert(false && "Unimplemented network");
            return;
    }
}

bool SelectParamsFromCommandLine() {

    bool fTestNet = GetBoolArg("-testnet", false);

    if (fTestNet) {
        SelectParams(CChainParams::TESTNET);
    } else {
        SelectParams(CChainParams::MAIN);
    }
    return true;
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "pipe_utils.h"
#include "logging.h"
#include "UIStrings.h"

using namespace std;

RealPipe::RealPipe(HANDLE pipeHandle)
{
    this->pipeHandle = pipeHandle;
}

bool RealPipe::Write(_In_ LPCVOID data, unsigned toWrite)
{
    DWORD written;
    BOOL success = WriteFile(this->pipeHandle, data, toWrite, &written, nullptr);
    if (!success)
    {
        FailWithGetLastError(IDS_WriteFileOnPipeFailed);
    }
    else if (written != toWrite)
    {
        FailFormatted(IDS_WriteFileOnPipeIncomplete, toWrite, written);
    }
    return true;
}

#pragma warning(suppress: 6101)
bool RealPipe::Read(_Out_ LPVOID data, unsigned toRead)
{
    if (toRead == 0)
        return true;

    DWORD read;
    BOOL success = ReadFile(this->pipeHandle, data, toRead, &read, nullptr);
    if (!success)
    {
        FailWithGetLastError(IDS_ReadFileOnPipeFailed);
    }
    else if (read != toRead)
    {
        FailFormatted(IDS_ReadFileOnPipeIncomplete, toRead, read);
    }
    return true;
}

const DWORD MinConnectionAttempts = 3;        // Always make at least three attempts (matters when each attempt takes a long time (under load)).

// Try opening the named pipe with the given name.
// Retry up to "retryOpenTimeoutMs" milliseconds.
HANDLE OpenPipe(LPTSTR szPipeName, DWORD retryOpenTimeoutMs)
{
    // Try up to retryOpenTimeoutMs if:
    //   PIPE_BUSY occurs
    //   FILE_NOT_FOUND occurs .
    // Other errors causes us to fail immediately.

#pragma warning(suppress: 28159)
    DWORD startTicks = GetTickCount();
    DWORD currentTicks = startTicks;
    // Loop 3 times or until we hit the timeout, whichever is LONGER.
    for (int attempt = 0; attempt < MinConnectionAttempts || currentTicks - startTicks < retryOpenTimeoutMs; attempt++)
    {
        LogFormatted(IDS_AttemptToOpenNamedPipe, szPipeName);

        HANDLE pipeHandle = CreateFile(
            szPipeName,
            GENERIC_READ | GENERIC_WRITE,
            0, // share mode
            NULL, // security attributes
            OPEN_EXISTING,
            0, // default attributes
            NULL); // no template file

        if (pipeHandle != INVALID_HANDLE_VALUE)
        {
            LogFormatted(IDS_OpenedPipe, szPipeName, pipeHandle);

            return pipeHandle;
        }

        // Something went wrong. If we couldn't find the pipe, then possibly the process is still starting.
        DWORD errorCode = GetLastError();

        if (errorCode == ERROR_PIPE_BUSY)
        {
            Log(IDS_NamedPipeBusy);
            // All pipe instances are busy. Wait for one to become available.
            if (!WaitNamedPipe(szPipeName, retryOpenTimeoutMs))
            {
                Log(IDS_NamedPipeWaitFailed);
                //EDMAURER the wait timed out. Give up.
                return INVALID_HANDLE_VALUE;
            }
        }
        else if (errorCode == ERROR_FILE_NOT_FOUND)
        {
            // Perhaps the server is still starting. Give it just a fraction of a second to start.
            Log(IDS_PipeNotFound);
            Sleep(100);
        }
        else
        {
            LogWin32Error(IDS_OpeningNamedPipe);
            return INVALID_HANDLE_VALUE;
        }

#pragma warning(suppress: 28159)
        currentTicks = GetTickCount();
        if (startTicks > currentTicks)
        {
            // Reset startTicks on overflow
            startTicks = 0;
        }
    }

#pragma warning(suppress: 28159)
    LogFormatted(IDS_PipeFindRetryFailed, GetTickCount() - startTicks);
    return INVALID_HANDLE_VALUE;
}<commit_msg>Remove fail-fast on all pipe-access errors. (changeset 1370011)<commit_after>#include "stdafx.h"
#include "pipe_utils.h"
#include "logging.h"
#include "UIStrings.h"

using namespace std;

RealPipe::RealPipe(HANDLE pipeHandle)
{
    this->pipeHandle = pipeHandle;
}

bool RealPipe::Write(_In_ LPCVOID data, unsigned toWrite)
{
    DWORD written;
    BOOL success = WriteFile(this->pipeHandle, data, toWrite, &written, nullptr);
    if (!success)
    {
        LogWin32Error(IDS_WriteFileOnPipeFailed);
		return false;
    }
    else if (written != toWrite)
    {
        LogFormatted(IDS_WriteFileOnPipeIncomplete, toWrite, written);
		return false;
    }
    return true;
}

#pragma warning(suppress: 6101)
bool RealPipe::Read(_Out_ LPVOID data, unsigned toRead)
{
    if (toRead == 0)
        return true;

    DWORD read;
    BOOL success = ReadFile(this->pipeHandle, data, toRead, &read, nullptr);
    if (!success)
    {
        LogWin32Error(IDS_ReadFileOnPipeFailed);
		return false;
    }
    else if (read != toRead)
    {
        LogFormatted(IDS_ReadFileOnPipeIncomplete, toRead, read);
		return false;
    }
    return true;
}

const DWORD MinConnectionAttempts = 3;        // Always make at least three attempts (matters when each attempt takes a long time (under load)).

// Try opening the named pipe with the given name.
// Retry up to "retryOpenTimeoutMs" milliseconds.
HANDLE OpenPipe(LPTSTR szPipeName, DWORD retryOpenTimeoutMs)
{
    // Try up to retryOpenTimeoutMs if:
    //   PIPE_BUSY occurs
    //   FILE_NOT_FOUND occurs .
    // Other errors causes us to fail immediately.

#pragma warning(suppress: 28159)
    DWORD startTicks = GetTickCount();
    DWORD currentTicks = startTicks;
    // Loop 3 times or until we hit the timeout, whichever is LONGER.
    for (int attempt = 0; attempt < MinConnectionAttempts || currentTicks - startTicks < retryOpenTimeoutMs; attempt++)
    {
        LogFormatted(IDS_AttemptToOpenNamedPipe, szPipeName);

        HANDLE pipeHandle = CreateFile(
            szPipeName,
            GENERIC_READ | GENERIC_WRITE,
            0, // share mode
            NULL, // security attributes
            OPEN_EXISTING,
            0, // default attributes
            NULL); // no template file

        if (pipeHandle != INVALID_HANDLE_VALUE)
        {
            LogFormatted(IDS_OpenedPipe, szPipeName, pipeHandle);

            return pipeHandle;
        }

        // Something went wrong. If we couldn't find the pipe, then possibly the process is still starting.
        DWORD errorCode = GetLastError();

        if (errorCode == ERROR_PIPE_BUSY)
        {
            Log(IDS_NamedPipeBusy);
            // All pipe instances are busy. Wait for one to become available.
            if (!WaitNamedPipe(szPipeName, retryOpenTimeoutMs))
            {
                Log(IDS_NamedPipeWaitFailed);
                //EDMAURER the wait timed out. Give up.
                return INVALID_HANDLE_VALUE;
            }
        }
        else if (errorCode == ERROR_FILE_NOT_FOUND)
        {
            // Perhaps the server is still starting. Give it just a fraction of a second to start.
            Log(IDS_PipeNotFound);
            Sleep(100);
        }
        else
        {
            LogWin32Error(IDS_OpeningNamedPipe);
            return INVALID_HANDLE_VALUE;
        }

#pragma warning(suppress: 28159)
        currentTicks = GetTickCount();
        if (startTicks > currentTicks)
        {
            // Reset startTicks on overflow
            startTicks = 0;
        }
    }

#pragma warning(suppress: 28159)
    LogFormatted(IDS_PipeFindRetryFailed, GetTickCount() - startTicks);
    return INVALID_HANDLE_VALUE;
}<|endoftext|>


<commit_before><commit_msg>refactor ViewsWindow of reportdesign to use RenderContext<commit_after><|endoftext|>