<commit_before>// Copyright (c) 2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "bench.h"

#include <iomanip>
#include <iostream>
#include <sys/time.h>

using namespace benchmark;

std::map<std::string, BenchFunction> BenchRunner::benchmarks;

static double gettimedouble(void) {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_usec * 0.000001 + tv.tv_sec;
}

BenchRunner::BenchRunner(std::string name, BenchFunction func)
{
    benchmarks.insert(std::make_pair(name, func));
}

void
BenchRunner::RunAll(double elapsedTimeForOne)
{
    std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "\n";

    for (std::map<std::string,BenchFunction>::iterator it = benchmarks.begin();
         it != benchmarks.end(); ++it) {

        State state(it->first, elapsedTimeForOne);
        BenchFunction& func = it->second;
        func(state);
    }
}

bool State::KeepRunning()
{
    if (count & countMask) {
      ++count;
      return true;
    }
    double now;
    if (count == 0) {
        beginTime = now = gettimedouble();
    }
    else {
        now = gettimedouble();
        double elapsed = now - lastTime;
        double elapsedOne = elapsed * countMaskInv;
        if (elapsedOne < minTime) minTime = elapsedOne;
        if (elapsedOne > maxTime) maxTime = elapsedOne;
        if (elapsed*128 < maxElapsed) {
          // If the execution was much too fast (1/128th of maxElapsed), increase the count mask by 8x and restart timing.
          // The restart avoids including the overhead of this code in the measurement.
          countMask = ((countMask<<3)|7) & ((1LL<<60)-1);
          countMaskInv = 1./(countMask+1);
          count = 0;
          minTime = std::numeric_limits<double>::max();
          maxTime = std::numeric_limits<double>::min();
          return true;
        }
        if (elapsed*16 < maxElapsed) {
          countMask = ((countMask<<1)|1) & ((1LL<<60)-1);
          countMaskInv = 1./(countMask+1);
        }
    }
    lastTime = now;
    ++count;

    if (now - beginTime < maxElapsed) return true; // Keep going

    --count;

    // Output results
    double average = (now-beginTime)/count;
    std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << "\n";

    return false;
}
<commit_msg>bench: Fix subtle counting issue when rescaling iteration count<commit_after>// Copyright (c) 2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "bench.h"

#include <iomanip>
#include <iostream>
#include <sys/time.h>

using namespace benchmark;

std::map<std::string, BenchFunction> BenchRunner::benchmarks;

static double gettimedouble(void) {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_usec * 0.000001 + tv.tv_sec;
}

BenchRunner::BenchRunner(std::string name, BenchFunction func)
{
    benchmarks.insert(std::make_pair(name, func));
}

void
BenchRunner::RunAll(double elapsedTimeForOne)
{
    std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "\n";

    for (std::map<std::string,BenchFunction>::iterator it = benchmarks.begin();
         it != benchmarks.end(); ++it) {

        State state(it->first, elapsedTimeForOne);
        BenchFunction& func = it->second;
        func(state);
    }
}

bool State::KeepRunning()
{
    if (count & countMask) {
      ++count;
      return true;
    }
    double now;
    if (count == 0) {
        beginTime = now = gettimedouble();
    }
    else {
        now = gettimedouble();
        double elapsed = now - lastTime;
        double elapsedOne = elapsed * countMaskInv;
        if (elapsedOne < minTime) minTime = elapsedOne;
        if (elapsedOne > maxTime) maxTime = elapsedOne;
        if (elapsed*128 < maxElapsed) {
          // If the execution was much too fast (1/128th of maxElapsed), increase the count mask by 8x and restart timing.
          // The restart avoids including the overhead of this code in the measurement.
          countMask = ((countMask<<3)|7) & ((1LL<<60)-1);
          countMaskInv = 1./(countMask+1);
          count = 0;
          minTime = std::numeric_limits<double>::max();
          maxTime = std::numeric_limits<double>::min();
          return true;
        }
        if (elapsed*16 < maxElapsed) {
            uint64_t newCountMask = ((countMask<<1)|1) & ((1LL<<60)-1);
            if ((count & newCountMask)==0) {
                countMask = newCountMask;
                countMaskInv = 1./(countMask+1);
            }
        }
    }
    lastTime = now;
    ++count;

    if (now - beginTime < maxElapsed) return true; // Keep going

    --count;

    // Output results
    double average = (now-beginTime)/count;
    std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << "\n";

    return false;
}
<|endoftext|>


<commit_before>/**
 *  @file bigfixstats.cpp
 *  @brief Converts BigFix deployment reports into Atlassian Confluence tables
 */

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Michael Maraya
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <algorithm>
#include <cmath>
#include <fstream>  // NOLINT
#include <map>
#include <string>
#include <vector>
#include "bigfix/bigfixstats.h"

ComputerGroup::ComputerGroup() {
}

ComputerGroup::ComputerGroup(const std::string name) {
  name_ = name;
}

uint8_t ComputerGroup::widest() const {
  uint8_t top {0};
  uint8_t name = name_.length();
  uint8_t current = bf::format(current_).length();
  uint8_t target = bf::format(target_).length();
  uint8_t percent = bf::format(this->percent()).length() + 2;
  std::vector<uint8_t> vector = {name, current, target, percent};
  for (auto it : vector) {
    if (it > top) {
      top = it;
    }
  }
  return top;
}

std::string ComputerGroup::name() const {
  return name_;
}

std::string ComputerGroup::formatted_name() const {
  std::string ret;
  if (name_ == "OS") {
    ret = name_ + "*" + std::string(this->widest() - name_.length() - 1, ' ');
  } else {
    ret = name_ + std::string(this->widest() - name_.length(), ' ');
  }
  return ret;
}

uint32_t ComputerGroup::current() const {
  return current_;
}

std::string ComputerGroup::formatted_current() const {
  std::string output = bf::format(current_);
  return output + std::string(this->widest() - output.length() + 1, ' ');
}

uint32_t ComputerGroup::target() const {
  return target_;
}

std::string ComputerGroup::formatted_target() const {
  std::string output = bf::format(target_);
  return output + std::string(this->widest() - output.length() + 1, ' ');
}

uint8_t ComputerGroup::percent() const {
  if (target_ != 0) {
    return round(static_cast<double>(current_) / target_ * 100);
  } else {
    return 0;
  }
}

std::string ComputerGroup::formatted_percent() const {
  std::string output = "*" + std::to_string(this->percent()) + "*";
  return output + std::string(this->widest() - output.length() + 1, ' ');
}

void ComputerGroup::set_name(std::string name) {
  name_ = name;
}

void ComputerGroup::set_current(uint32_t current) {
  current_ = current;
}

void ComputerGroup::set_target(uint32_t target) {
  target_ = target;
}

/**
 *  @details format the supplied number into comma-separated groupings since
 *           there apparently is no portable way of doing this
 */
std::string bf::format(const uint32_t number) {
  std::string output = std::to_string(number);
  if (output.length() > 3) {
    for (int i = static_cast<int>(output.length() - 3); i > 0; i -= 3) {
      output.insert(i, ",");
    }
  }
  return output;
}

/**
 *  @brief Converts BigFix deployment reports into text for updating Atlassian
 *         Confluence tables
 *  @param argc number of command-line arguments
 *  @param argv array of command-line arguments
 *  @retval int returns 0 upon successful program completion, non-zero otherwise
 */
int main(int argc, const char * argv[]) {
  // load arguments into vector<string> so we can use std::find
  std::vector<std::string> args;
  std::vector<std::string>::iterator it;
  for (int i = 1; i < argc; ++i) {
    args.push_back(argv[i]);
  }
  // display -h help
  it = std::find(args.begin(), args.end(), "-h");
  if ((argc == 1) || (it != args.end())) {
    usage();
    return 0;
  }
  // use -c current file
  std::string current_file {};
  it = std::find(args.begin(), args.end(), "-c");
  if (it != args.end()) {
    if (next(it) != args.end()) {
      current_file = *next(it);
    } else {
      printf("%s: option -c requires an argument\n", bf::kProgramName.c_str());
      usage();
      return 1;
    }
  }
  // use -t target file
  std::string target_file {};
  it = std::find(args.begin(), args.end(), "-t");
  if (it != args.end()) {
    if (next(it) != args.end()) {
      target_file = *next(it);
    } else {
      printf("%s: option -t requires an argument\n", bf::kProgramName.c_str());
      usage();
      return 1;
    }
  }
  std::map<std::string, uint32_t> raw;
  std::vector<ComputerGroup> final;
  loadTarget(target_file, &final);
  loadCurrent(current_file, &raw, &final);
  display(current_file, &raw, &final);
}

/**
 *  @details Load computer groups and target deployment counts
 */
void loadTarget(std::string filename, std::vector<ComputerGroup>* final) {
  std::ifstream fs(filename);
  if (fs.is_open()) {
    std::string line {};
    while (std::getline(fs, line)) {
      std::string group {};
      uint16_t target {0};
      std::size_t delim = line.find(bf::kDelim, 0);
      // read computer group and target
      group = line.substr(0, delim);
      target = std::stoi(line.substr(delim + 1, line.length()));
      // create new computer group
      ComputerGroup cg = ComputerGroup(group);
      cg.set_target(target);
      final->push_back(cg);
    }
    fs.close();
  } else {
    printf("Error: Could not open file %s", filename.c_str());
  }
}

/**
 *  @details Load current deployment counts
 */
void loadCurrent(std::string filename, std::map<std::string, uint32_t>* raw,
                 std::vector<ComputerGroup>* final) {
  std::ifstream fs(filename);
  if (fs.is_open()) {
    std::string line {};
    while (std::getline(fs, line)) {
      if (line.compare(0, bf::kRecord.length(), bf::kRecord) == 0) {
        // read records
        std::size_t start = line.find(bf::kStart, 0), end {0};
        while (start != std::string::npos) {
          std::string group {};
          uint32_t count {0};
          // read computer group
          end = line.find(bf::kEnd, start);
          if (end != std::string::npos) {
            start += bf::kStart.length();
            group = line.substr(start, end - start);
          }
          // read computer count
          start = line.find(bf::kStart, start + bf::kStart.length());
          end = line.find(bf::kEnd, start);
          if (end != std::string::npos) {
            start += bf::kStart.length();
            count = std::stoi(line.substr(start, end - start));
          }
          // populate collection
          raw->emplace(group, count);
          // read next computer group
          start = line.find(bf::kStart, start + bf::kStart.length());
        }
      }
    }
    fs.close();
    // update computer group collection
    std::map<std::string, uint32_t>::iterator it;
    for (auto &cg : *final) {
      it = raw->find(cg.name());
      if (it != raw->end()) {
        cg.set_current(it->second);
        // add MBDA current deployment stats to OS
        if (cg.name() == "OS") {
          it = raw->find("MBDA");
          cg.set_current(cg.current() + it->second);
        }
      }
    }
  } else {
    printf("Error: Could not open file %s", filename.c_str());
  }
}

/**
 *  @details Display computer group, current, target and percentage
 */
void display(std::string filename, std::map<std::string, uint32_t>* raw,
             std::vector<ComputerGroup>* final) {
  // extract date from filename
  size_t begin = filename.length() - bf::kExt.length() - bf::kDate.length();
  std::string date = filename.substr(begin, bf::kDate.length());
  // store raw results
  std::string raw_display[2] {"||   Date   || ", "|  " + date + "  | "};
  // compute raw totals
  uint32_t raw_total {0};
  for (auto cg : *raw) {
    raw_total += cg.second;
    if (cg.first != "CBS" && cg.first != "HCHB") {
      raw_display[0] += cg.first + " || ";
      raw_display[1] += bf::format(cg.second) + " | ";
    }
  }
  raw_display[0] += "TOTAL ||";
  raw_display[1] += bf::format(raw_total) + " |";
  printf("%s\n%s\n\n", raw_display[0].c_str(), raw_display[1].c_str());
  // compute final totals
  uint32_t current_total {0}, target_total {0};
  for (auto cg : *final) {
    current_total += cg.current();
    target_total += cg.target();
  }
  ComputerGroup total = ComputerGroup("TOTAL");
  total.set_current(current_total);
  total.set_target(target_total);
  final->push_back(total);
  // populate rows
  std::string header = "|| Nodes       || ";
  std::string current = "| *Current*    | ";
  std::string target = "| *Target*     | ";
  std::string percent = "| *% Comp*     | ";
  // display results
  for (auto cg : *final) {
    header += cg.formatted_name() + " || ";
    current += cg.formatted_current() + " | ";
    target += cg.formatted_target() + " | ";
    percent += cg.formatted_percent() + " | ";
  }
  printf("%s\n%s\n%s\n%s\n", header.c_str(), current.c_str(), target.c_str(),
         percent.c_str());
}

/**
 *  @details Display program name, version, and usage
 */
void usage() {
  printf("%s, version %u.%u\n\n", bf::kProgramName.c_str(), bf::kMajorVersion,
         bf::kMinorVersion);
  printf("usage: %s [-h] -t target -c current \n", bf::kProgramName.c_str());
  printf("-h display usage\n");
  printf("-t filename of the comma-separated computer group targets\n");
  printf("-c filename of the current computer group deployment statistics\n\n");
}

<commit_msg>tighten up the display output<commit_after>/**
 *  @file bigfixstats.cpp
 *  @brief Converts BigFix deployment reports into Atlassian Confluence tables
 */

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Michael Maraya
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <algorithm>
#include <cmath>
#include <fstream>  // NOLINT
#include <map>
#include <string>
#include <vector>
#include "bigfix/bigfixstats.h"

ComputerGroup::ComputerGroup() {
}

ComputerGroup::ComputerGroup(const std::string name) {
  name_ = name;
}

uint8_t ComputerGroup::widest() const {
  uint8_t top {0};
  uint8_t name = name_.length();
  uint8_t current = bf::format(current_).length();
  uint8_t target = bf::format(target_).length();
  uint8_t percent = bf::format(this->percent()).length() + 2;
  std::vector<uint8_t> vector = {name, current, target, percent};
  for (auto it : vector) {
    if (it > top) {
      top = it;
    }
  }
  return top;
}

std::string ComputerGroup::name() const {
  return name_;
}

std::string ComputerGroup::formatted_name() const {
  std::string ret;
  if (name_ == "OS") {
    ret = name_ + "*" + std::string(this->widest() - name_.length() - 1, ' ');
  } else {
    ret = name_ + std::string(this->widest() - name_.length(), ' ');
  }
  return ret;
}

uint32_t ComputerGroup::current() const {
  return current_;
}

std::string ComputerGroup::formatted_current() const {
  std::string output = bf::format(current_);
  return output + std::string(this->widest() - output.length() + 1, ' ');
}

uint32_t ComputerGroup::target() const {
  return target_;
}

std::string ComputerGroup::formatted_target() const {
  std::string output = bf::format(target_);
  return output + std::string(this->widest() - output.length() + 1, ' ');
}

uint8_t ComputerGroup::percent() const {
  if (target_ != 0) {
    return round(static_cast<double>(current_) / target_ * 100);
  } else {
    return 0;
  }
}

std::string ComputerGroup::formatted_percent() const {
  std::string output = "*" + std::to_string(this->percent()) + "*";
  return output + std::string(this->widest() - output.length() + 1, ' ');
}

void ComputerGroup::set_name(std::string name) {
  name_ = name;
}

void ComputerGroup::set_current(uint32_t current) {
  current_ = current;
}

void ComputerGroup::set_target(uint32_t target) {
  target_ = target;
}

/**
 *  @details format the supplied number into comma-separated groupings since
 *           there apparently is no portable way of doing this
 */
std::string bf::format(const uint32_t number) {
  std::string output = std::to_string(number);
  if (output.length() > 3) {
    for (int i = static_cast<int>(output.length() - 3); i > 0; i -= 3) {
      output.insert(i, ",");
    }
  }
  return output;
}

/**
 *  @brief Converts BigFix deployment reports into text for updating Atlassian
 *         Confluence tables
 *  @param argc number of command-line arguments
 *  @param argv array of command-line arguments
 *  @retval int returns 0 upon successful program completion, non-zero otherwise
 */
int main(int argc, const char * argv[]) {
  // load arguments into vector<string> so we can use std::find
  std::vector<std::string> args;
  std::vector<std::string>::iterator it;
  for (int i = 1; i < argc; ++i) {
    args.push_back(argv[i]);
  }
  // display -h help
  it = std::find(args.begin(), args.end(), "-h");
  if ((argc == 1) || (it != args.end())) {
    usage();
    return 0;
  }
  // use -c current file
  std::string current_file {};
  it = std::find(args.begin(), args.end(), "-c");
  if (it != args.end()) {
    if (next(it) != args.end()) {
      current_file = *next(it);
    } else {
      printf("%s: option -c requires an argument\n", bf::kProgramName.c_str());
      usage();
      return 1;
    }
  }
  // use -t target file
  std::string target_file {};
  it = std::find(args.begin(), args.end(), "-t");
  if (it != args.end()) {
    if (next(it) != args.end()) {
      target_file = *next(it);
    } else {
      printf("%s: option -t requires an argument\n", bf::kProgramName.c_str());
      usage();
      return 1;
    }
  }
  std::map<std::string, uint32_t> raw;
  std::vector<ComputerGroup> final;
  loadTarget(target_file, &final);
  loadCurrent(current_file, &raw, &final);
  display(current_file, &raw, &final);
}

/**
 *  @details Load computer groups and target deployment counts
 */
void loadTarget(std::string filename, std::vector<ComputerGroup>* final) {
  std::ifstream fs(filename);
  if (fs.is_open()) {
    std::string line {};
    while (std::getline(fs, line)) {
      std::string group {};
      uint16_t target {0};
      std::size_t delim = line.find(bf::kDelim, 0);
      // read computer group and target
      group = line.substr(0, delim);
      target = std::stoi(line.substr(delim + 1, line.length()));
      // create new computer group
      ComputerGroup cg = ComputerGroup(group);
      cg.set_target(target);
      final->push_back(cg);
    }
    fs.close();
  } else {
    printf("Error: Could not open file %s", filename.c_str());
  }
}

/**
 *  @details Load current deployment counts
 */
void loadCurrent(std::string filename, std::map<std::string, uint32_t>* raw,
                 std::vector<ComputerGroup>* final) {
  std::ifstream fs(filename);
  if (fs.is_open()) {
    std::string line {};
    while (std::getline(fs, line)) {
      if (line.compare(0, bf::kRecord.length(), bf::kRecord) == 0) {
        // read records
        std::size_t start = line.find(bf::kStart, 0), end {0};
        while (start != std::string::npos) {
          std::string group {};
          uint32_t count {0};
          // read computer group
          end = line.find(bf::kEnd, start);
          if (end != std::string::npos) {
            start += bf::kStart.length();
            group = line.substr(start, end - start);
          }
          // read computer count
          start = line.find(bf::kStart, start + bf::kStart.length());
          end = line.find(bf::kEnd, start);
          if (end != std::string::npos) {
            start += bf::kStart.length();
            count = std::stoi(line.substr(start, end - start));
          }
          // populate collection
          raw->emplace(group, count);
          // read next computer group
          start = line.find(bf::kStart, start + bf::kStart.length());
        }
      }
    }
    fs.close();
    // update computer group collection
    std::map<std::string, uint32_t>::iterator it;
    for (auto &cg : *final) {
      it = raw->find(cg.name());
      if (it != raw->end()) {
        cg.set_current(it->second);
        // add MBDA current deployment stats to OS
        if (cg.name() == "OS") {
          it = raw->find("MBDA");
          cg.set_current(cg.current() + it->second);
        }
      }
    }
  } else {
    printf("Error: Could not open file %s", filename.c_str());
  }
}

/**
 *  @details Display computer group, current, target and percentage
 */
void display(std::string filename, std::map<std::string, uint32_t>* raw,
             std::vector<ComputerGroup>* final) {
  // extract date from filename
  size_t begin = filename.length() - bf::kExt.length() - bf::kDate.length();
  std::string date = filename.substr(begin, bf::kDate.length());
  // store raw results
  std::string raw_display[2] {"||   Date   || ", "|  " + date + "  | "};
  // compute raw totals
  uint32_t raw_total {0};
  for (auto cg : *raw) {
    raw_total += cg.second;
    if (cg.first != "CBS" && cg.first != "HCHB") {
      raw_display[0] += cg.first + " || ";
      raw_display[1] += bf::format(cg.second) + " | ";
    }
  }
  raw_display[0] += "TOTAL ||";
  raw_display[1] += bf::format(raw_total) + " |";
  printf("%s\n%s\n\n", raw_display[0].c_str(), raw_display[1].c_str());
  // compute final totals
  uint32_t current_total {0}, target_total {0};
  for (auto cg : *final) {
    current_total += cg.current();
    target_total += cg.target();
  }
  ComputerGroup total = ComputerGroup("TOTAL");
  total.set_current(current_total);
  total.set_target(target_total);
  final->push_back(total);
  // populate rows
  std::string header = "|| Nodes    || ";
  std::string current = "| *Current* | ";
  std::string target = "| *Target*  | ";
  std::string percent = "| *%Comp*   | ";
  // display results
  for (auto cg : *final) {
    header += cg.formatted_name() + " || ";
    current += cg.formatted_current() + " | ";
    target += cg.formatted_target() + " | ";
    percent += cg.formatted_percent() + " | ";
  }
  printf("%s\n%s\n%s\n%s\n", header.c_str(), current.c_str(), target.c_str(),
         percent.c_str());
}

/**
 *  @details Display program name, version, and usage
 */
void usage() {
  printf("%s, version %u.%u\n\n", bf::kProgramName.c_str(), bf::kMajorVersion,
         bf::kMinorVersion);
  printf("usage: %s [-h] -t target -c current \n", bf::kProgramName.c_str());
  printf("-h display usage\n");
  printf("-t filename of the comma-separated computer group targets\n");
  printf("-c filename of the current computer group deployment statistics\n\n");
}

<|endoftext|>


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

#include "chainparamsbase.h"
#include "clientversion.h"
#include "rpcclient.h"
#include "rpcprotocol.h"
#include "util.h"
#include "utilstrencodings.h"

#include <boost/filesystem/operations.hpp>
#include <stdio.h>

#include <event2/event.h>
#include <event2/http.h>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>

#include <univalue.h>

using namespace std;

static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;

std::string HelpMessageCli()
{
    string strUsage;
    strUsage += HelpMessageGroup(_("Options:"));
    strUsage += HelpMessageOpt("-?", _("This help message"));
    strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
    strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
    AppendParamsHelpMessages(strUsage);
    strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
    strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), BaseParams(CBaseChainParams::MAIN).RPCPort(), BaseParams(CBaseChainParams::TESTNET).RPCPort()));
    strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
    strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
    strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
    strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));

    return strUsage;
}

//////////////////////////////////////////////////////////////////////////////
//
// Start
//

//
// Exception thrown on connection error.  This error is used to determine
// when to wait if -rpcwait is given.
//
class CConnectionFailed : public std::runtime_error
{
public:

    explicit inline CConnectionFailed(const std::string& msg) :
        std::runtime_error(msg)
    {}

};

static bool AppInitRPC(int argc, char* argv[])
{
    //
    // Parameters
    //
    ParseParameters(argc, argv);
    if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) {

        std::string strUsage = _("Bitcoin MVF-BU RPC client version") + " " + FormatFullVersion() + "\n";  // MVF-BU client name (MVHF-BU-DES-IDME-1)
        if (!mapArgs.count("-version")) {
            strUsage += "\n" + _("Usage:") + "\n" +
                  "  bitcoin-cli [options] <command> [params]  " + _("Send command to Bitcoin") + "\n" +   // BU remove branding
                  "  bitcoin-cli [options] help                " + _("List commands") + "\n" +
                  "  bitcoin-cli [options] help <command>      " + _("Get help for a command") + "\n";

            strUsage += "\n" + HelpMessageCli();
        }

        fprintf(stdout, "%s", strUsage.c_str());
        return false;
    }
    if (!boost::filesystem::is_directory(GetDataDir(false))) {
        fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
        return false;
    }
    try {
        ReadConfigFile(mapArgs, mapMultiArgs);
    } catch (const std::exception& e) {
        fprintf(stderr,"Error reading configuration file: %s\n", e.what());
        return false;
    }
    // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
    try {
        SelectBaseParams(ChainNameFromCommandLine());
    } catch (const std::exception& e) {
        fprintf(stderr, "Error: %s\n", e.what());
        return false;
    }
    if (GetBoolArg("-rpcssl", false))
    {
        fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
        return false;
    }
    return true;
}

/** Reply structure for request_done to fill in */
struct HTTPReply
{
    int status;
    std::string body;
};

static void http_request_done(struct evhttp_request *req, void *ctx)
{
    HTTPReply *reply = static_cast<HTTPReply*>(ctx);

    if (req == NULL) {
        /* If req is NULL, it means an error occurred while connecting, but
         * I'm not sure how to find out which one. We also don't really care.
         */
        reply->status = 0;
        return;
    }

    reply->status = evhttp_request_get_response_code(req);

    struct evbuffer *buf = evhttp_request_get_input_buffer(req);
    if (buf)
    {
        size_t size = evbuffer_get_length(buf);
        const char *data = (const char*)evbuffer_pullup(buf, size);
        if (data)
            reply->body = std::string(data, size);
        evbuffer_drain(buf, size);
    }
}

UniValue CallRPC(const string& strMethod, const UniValue& params)
{
    std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
    int port = GetArg("-rpcport", BaseParams().RPCPort());

    // Create event base
    struct event_base *base = event_base_new(); // TODO RAII
    if (!base)
        throw runtime_error("cannot create event_base");

    // Synchronously look up hostname
    struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII
    if (evcon == NULL)
        throw runtime_error("create connection failed");
    evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));

    HTTPReply response;
    struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
    if (req == NULL)
        throw runtime_error("create http request failed");

    // Get credentials
    std::string strRPCUserColonPass;
    if (mapArgs["-rpcpassword"] == "") {
        // Try fall back to cookie-based authentication if no password is provided
        if (!GetAuthCookie(&strRPCUserColonPass)) {
            throw runtime_error(strprintf(
                _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"),
                    GetConfigFile().string().c_str()));

        }
    } else {
        strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
    }

    struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
    assert(output_headers);
    evhttp_add_header(output_headers, "Host", host.c_str());
    evhttp_add_header(output_headers, "Connection", "close");
    evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());

    // Attach request data
    std::string strRequest = JSONRPCRequest(strMethod, params, 1);
    struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
    assert(output_buffer);
    evbuffer_add(output_buffer, strRequest.data(), strRequest.size());

    int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
    if (r != 0) {
        evhttp_connection_free(evcon);
        event_base_free(base);
        throw CConnectionFailed("send http request failed");
    }

    event_base_dispatch(base);
    evhttp_connection_free(evcon);
    event_base_free(base);

    if (response.status == 0)
        throw CConnectionFailed("couldn't connect to server");
    else if (response.status == HTTP_UNAUTHORIZED)
        throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
    else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
        throw runtime_error(strprintf("server returned HTTP error %d", response.status));
    else if (response.body.empty())
        throw runtime_error("no response from server");

    // Parse reply
    UniValue valReply(UniValue::VSTR);
    if (!valReply.read(response.body))
        throw runtime_error("couldn't parse reply from server");
    const UniValue& reply = valReply.get_obj();
    if (reply.empty())
        throw runtime_error("expected reply to have result, error and id properties");

    return reply;
}

int CommandLineRPC(int argc, char *argv[])
{
    string strPrint;
    int nRet = 0;
    try {
        // Skip switches
        while (argc > 1 && IsSwitchChar(argv[1][0])) {
            argc--;
            argv++;
        }

        // Method
        if (argc < 2)
            throw runtime_error("too few parameters");
        string strMethod = argv[1];

        // Parameters default to strings
        std::vector<std::string> strParams(&argv[2], &argv[argc]);
        UniValue params = RPCConvertValues(strMethod, strParams);

        // Execute and handle connection failures with -rpcwait
        const bool fWait = GetBoolArg("-rpcwait", false);
        do {
            try {
                const UniValue reply = CallRPC(strMethod, params);

                // Parse reply
                const UniValue& result = find_value(reply, "result");
                const UniValue& error  = find_value(reply, "error");

                if (!error.isNull()) {
                    // Error
                    int code = error["code"].get_int();
                    if (fWait && code == RPC_IN_WARMUP)
                        throw CConnectionFailed("server in warmup");
                    strPrint = "error: " + error.write();
                    nRet = abs(code);
                    if (error.isObject())
                    {
                        UniValue errCode = find_value(error, "code");
                        UniValue errMsg  = find_value(error, "message");
                        strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";

                        if (errMsg.isStr())
                            strPrint += "error message:\n"+errMsg.get_str();
                    }
                } else {
                    // Result
                    if (result.isNull())
                        strPrint = "";
                    else if (result.isStr())
                        strPrint = result.get_str();
                    else
                        strPrint = result.write(2);
                }
                // Connection succeeded, no need to retry.
                break;
            }
            catch (const CConnectionFailed&) {
                if (fWait)
                    MilliSleep(1000);
                else
                    throw;
            }
        } while (fWait);
    }
    catch (const boost::thread_interrupted&) {
        throw;
    }
    catch (const std::exception& e) {
        strPrint = string("error: ") + e.what();
        nRet = EXIT_FAILURE;
    }
    catch (...) {
        PrintExceptionContinue(NULL, "CommandLineRPC()");
        throw;
    }

    if (strPrint != "") {
        fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
    }
    return nRet;
}

int main(int argc, char* argv[])
{
    SetupEnvironment();
    if (!SetupNetworking()) {
        fprintf(stderr, "Error: Initializing networking failed\n");
        exit(1);
    }

    try {
        if(!AppInitRPC(argc, argv))
            return EXIT_FAILURE;
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "AppInitRPC()");
        return EXIT_FAILURE;
    } catch (...) {
        PrintExceptionContinue(NULL, "AppInitRPC()");
        return EXIT_FAILURE;
    }

    int ret = EXIT_FAILURE;
    try {
        ret = CommandLineRPC(argc, argv);
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "CommandLineRPC()");
    } catch (...) {
        PrintExceptionContinue(NULL, "CommandLineRPC()");
    }
    return ret;
}
<commit_msg>add missing Copyright (c) 2016 The Bitcoin developers<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Copyright (c) 2015-2016 The Bitcoin Unlimited developers
// Copyright (c) 2016 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "chainparamsbase.h"
#include "clientversion.h"
#include "rpcclient.h"
#include "rpcprotocol.h"
#include "util.h"
#include "utilstrencodings.h"

#include <boost/filesystem/operations.hpp>
#include <stdio.h>

#include <event2/event.h>
#include <event2/http.h>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>

#include <univalue.h>

using namespace std;

static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;

std::string HelpMessageCli()
{
    string strUsage;
    strUsage += HelpMessageGroup(_("Options:"));
    strUsage += HelpMessageOpt("-?", _("This help message"));
    strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
    strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
    AppendParamsHelpMessages(strUsage);
    strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
    strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), BaseParams(CBaseChainParams::MAIN).RPCPort(), BaseParams(CBaseChainParams::TESTNET).RPCPort()));
    strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
    strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
    strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
    strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));

    return strUsage;
}

//////////////////////////////////////////////////////////////////////////////
//
// Start
//

//
// Exception thrown on connection error.  This error is used to determine
// when to wait if -rpcwait is given.
//
class CConnectionFailed : public std::runtime_error
{
public:

    explicit inline CConnectionFailed(const std::string& msg) :
        std::runtime_error(msg)
    {}

};

static bool AppInitRPC(int argc, char* argv[])
{
    //
    // Parameters
    //
    ParseParameters(argc, argv);
    if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) {

        std::string strUsage = _("Bitcoin MVF-BU RPC client version") + " " + FormatFullVersion() + "\n";  // MVF-BU client name (MVHF-BU-DES-IDME-1)
        if (!mapArgs.count("-version")) {
            strUsage += "\n" + _("Usage:") + "\n" +
                  "  bitcoin-cli [options] <command> [params]  " + _("Send command to Bitcoin") + "\n" +   // BU remove branding
                  "  bitcoin-cli [options] help                " + _("List commands") + "\n" +
                  "  bitcoin-cli [options] help <command>      " + _("Get help for a command") + "\n";

            strUsage += "\n" + HelpMessageCli();
        }

        fprintf(stdout, "%s", strUsage.c_str());
        return false;
    }
    if (!boost::filesystem::is_directory(GetDataDir(false))) {
        fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
        return false;
    }
    try {
        ReadConfigFile(mapArgs, mapMultiArgs);
    } catch (const std::exception& e) {
        fprintf(stderr,"Error reading configuration file: %s\n", e.what());
        return false;
    }
    // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
    try {
        SelectBaseParams(ChainNameFromCommandLine());
    } catch (const std::exception& e) {
        fprintf(stderr, "Error: %s\n", e.what());
        return false;
    }
    if (GetBoolArg("-rpcssl", false))
    {
        fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
        return false;
    }
    return true;
}

/** Reply structure for request_done to fill in */
struct HTTPReply
{
    int status;
    std::string body;
};

static void http_request_done(struct evhttp_request *req, void *ctx)
{
    HTTPReply *reply = static_cast<HTTPReply*>(ctx);

    if (req == NULL) {
        /* If req is NULL, it means an error occurred while connecting, but
         * I'm not sure how to find out which one. We also don't really care.
         */
        reply->status = 0;
        return;
    }

    reply->status = evhttp_request_get_response_code(req);

    struct evbuffer *buf = evhttp_request_get_input_buffer(req);
    if (buf)
    {
        size_t size = evbuffer_get_length(buf);
        const char *data = (const char*)evbuffer_pullup(buf, size);
        if (data)
            reply->body = std::string(data, size);
        evbuffer_drain(buf, size);
    }
}

UniValue CallRPC(const string& strMethod, const UniValue& params)
{
    std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
    int port = GetArg("-rpcport", BaseParams().RPCPort());

    // Create event base
    struct event_base *base = event_base_new(); // TODO RAII
    if (!base)
        throw runtime_error("cannot create event_base");

    // Synchronously look up hostname
    struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII
    if (evcon == NULL)
        throw runtime_error("create connection failed");
    evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));

    HTTPReply response;
    struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
    if (req == NULL)
        throw runtime_error("create http request failed");

    // Get credentials
    std::string strRPCUserColonPass;
    if (mapArgs["-rpcpassword"] == "") {
        // Try fall back to cookie-based authentication if no password is provided
        if (!GetAuthCookie(&strRPCUserColonPass)) {
            throw runtime_error(strprintf(
                _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"),
                    GetConfigFile().string().c_str()));

        }
    } else {
        strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
    }

    struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
    assert(output_headers);
    evhttp_add_header(output_headers, "Host", host.c_str());
    evhttp_add_header(output_headers, "Connection", "close");
    evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());

    // Attach request data
    std::string strRequest = JSONRPCRequest(strMethod, params, 1);
    struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
    assert(output_buffer);
    evbuffer_add(output_buffer, strRequest.data(), strRequest.size());

    int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
    if (r != 0) {
        evhttp_connection_free(evcon);
        event_base_free(base);
        throw CConnectionFailed("send http request failed");
    }

    event_base_dispatch(base);
    evhttp_connection_free(evcon);
    event_base_free(base);

    if (response.status == 0)
        throw CConnectionFailed("couldn't connect to server");
    else if (response.status == HTTP_UNAUTHORIZED)
        throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
    else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
        throw runtime_error(strprintf("server returned HTTP error %d", response.status));
    else if (response.body.empty())
        throw runtime_error("no response from server");

    // Parse reply
    UniValue valReply(UniValue::VSTR);
    if (!valReply.read(response.body))
        throw runtime_error("couldn't parse reply from server");
    const UniValue& reply = valReply.get_obj();
    if (reply.empty())
        throw runtime_error("expected reply to have result, error and id properties");

    return reply;
}

int CommandLineRPC(int argc, char *argv[])
{
    string strPrint;
    int nRet = 0;
    try {
        // Skip switches
        while (argc > 1 && IsSwitchChar(argv[1][0])) {
            argc--;
            argv++;
        }

        // Method
        if (argc < 2)
            throw runtime_error("too few parameters");
        string strMethod = argv[1];

        // Parameters default to strings
        std::vector<std::string> strParams(&argv[2], &argv[argc]);
        UniValue params = RPCConvertValues(strMethod, strParams);

        // Execute and handle connection failures with -rpcwait
        const bool fWait = GetBoolArg("-rpcwait", false);
        do {
            try {
                const UniValue reply = CallRPC(strMethod, params);

                // Parse reply
                const UniValue& result = find_value(reply, "result");
                const UniValue& error  = find_value(reply, "error");

                if (!error.isNull()) {
                    // Error
                    int code = error["code"].get_int();
                    if (fWait && code == RPC_IN_WARMUP)
                        throw CConnectionFailed("server in warmup");
                    strPrint = "error: " + error.write();
                    nRet = abs(code);
                    if (error.isObject())
                    {
                        UniValue errCode = find_value(error, "code");
                        UniValue errMsg  = find_value(error, "message");
                        strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";

                        if (errMsg.isStr())
                            strPrint += "error message:\n"+errMsg.get_str();
                    }
                } else {
                    // Result
                    if (result.isNull())
                        strPrint = "";
                    else if (result.isStr())
                        strPrint = result.get_str();
                    else
                        strPrint = result.write(2);
                }
                // Connection succeeded, no need to retry.
                break;
            }
            catch (const CConnectionFailed&) {
                if (fWait)
                    MilliSleep(1000);
                else
                    throw;
            }
        } while (fWait);
    }
    catch (const boost::thread_interrupted&) {
        throw;
    }
    catch (const std::exception& e) {
        strPrint = string("error: ") + e.what();
        nRet = EXIT_FAILURE;
    }
    catch (...) {
        PrintExceptionContinue(NULL, "CommandLineRPC()");
        throw;
    }

    if (strPrint != "") {
        fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
    }
    return nRet;
}

int main(int argc, char* argv[])
{
    SetupEnvironment();
    if (!SetupNetworking()) {
        fprintf(stderr, "Error: Initializing networking failed\n");
        exit(1);
    }

    try {
        if(!AppInitRPC(argc, argv))
            return EXIT_FAILURE;
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "AppInitRPC()");
        return EXIT_FAILURE;
    } catch (...) {
        PrintExceptionContinue(NULL, "AppInitRPC()");
        return EXIT_FAILURE;
    }

    int ret = EXIT_FAILURE;
    try {
        ret = CommandLineRPC(argc, argv);
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "CommandLineRPC()");
    } catch (...) {
        PrintExceptionContinue(NULL, "CommandLineRPC()");
    }
    return ret;
}
<|endoftext|>


<commit_before>/**
 * The MIT License
 *
 * Copyright (C) 2017 Kiyofumi Kondoh
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "stdafx.h"
#include "test_cef_offscreen.h"

extern int main_win(HINSTANCE hInstance);
extern int main_win_term();

#include "include/base/cef_lock.h"

#define MAX_LOADSTRING 100

HWND        s_hWnd = NULL;
HDC         s_memDC = NULL;
HBITMAP     s_memBitmap = NULL;
void*       s_memBitmapPixel = NULL;
base::Lock  s_memBitmapPixelLock;
HBITMAP     s_memBitmapPrev = NULL;

HINSTANCE hInst;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];

ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    {
        const int nRet = main_win(hInstance);
        if ( 0 <= nRet )
        {
            return nRet;
        }
    }

    MSG msg;
    HACCEL hAccelTable;

    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_TEST_CEF_OFFSCREEN, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST_CEF_OFFSCREEN));

    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    {
        main_win_term();
    }

    return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST_CEF_OFFSCREEN));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_TEST_CEF_OFFSCREEN);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance;

   DWORD dwStyle = WS_OVERLAPPEDWINDOW;
   dwStyle ^= WS_MINIMIZEBOX;
   dwStyle ^= WS_MAXIMIZEBOX;
   dwStyle ^= WS_THICKFRAME;

   DWORD dwExStyle = 0;

   int width = CW_USEDEFAULT;
   int height = CW_USEDEFAULT;
   {
       RECT     rect;

       rect.left = 0;
       rect.top = 0;
       rect.bottom = 720;
       rect.right = 1280;

       ::AdjustWindowRectEx(&rect, dwStyle, TRUE, dwExStyle);

       width = rect.right - rect.left;
       height = rect.bottom - rect.top;
   }

   hWnd = CreateWindow(szWindowClass, szTitle, dwStyle,
      CW_USEDEFAULT, 0, width, height, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   s_hWnd = hWnd;

   return TRUE;
}

#include "../test_cef_app/cef_client.h"

extern CefRefPtr<Client> s_client;

#include <windowsx.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_CREATE:
        {
            hdc = GetDC( hWnd );

            s_memDC = CreateCompatibleDC( hdc );
            {
                BITMAPINFO bi;

                ZeroMemory( &bi, sizeof(bi) );
                bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                bi.bmiHeader.biBitCount = 32;
                bi.bmiHeader.biPlanes = 1;
                bi.bmiHeader.biWidth = 1280;
                bi.bmiHeader.biHeight = -720;

                {
                    base::AutoLock  lock_scope(s_memBitmapPixelLock);

                    s_memBitmap = CreateDIBSection( NULL, &bi, DIB_RGB_COLORS, &s_memBitmapPixel, NULL, 0 );
                    if ( NULL == s_memBitmap )
                    {
                        ReleaseDC( hWnd, hdc );
                        return -1;
                    }

                    {
                        DWORD* p = reinterpret_cast<DWORD*>(s_memBitmapPixel);
                        for ( int x = 0; x < 1280; ++x )
                        {
                            for ( int y = 0; y < 720; ++y )
                            {
                                // A,R,G,B
                                //p[y*1280 + x] = 0xffffffffUL;
                                p[y*1280 + x] = 0x0000ff00UL;
                            }
                        }
                    }
                }
            }
            s_memBitmapPrev = (HBITMAP)SelectObject( s_memDC, s_memBitmap );

            ReleaseDC( hWnd, hdc );
        }
        break;

    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        BitBlt( hdc, 0, 0, 1280, 720, s_memDC, 0, 0, SRCCOPY );
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        {
            if ( NULL != s_memDC )
            {
                if ( NULL != s_memBitmap )
                {
                    base::AutoLock  lock_scope(s_memBitmapPixelLock);

                    SelectObject( s_memDC, s_memBitmapPrev );
                    s_memBitmapPrev = NULL;
                    DeleteObject( s_memBitmap );
                    s_memBitmap = NULL;
                    s_memBitmapPixel = NULL;
                }

                DeleteDC( s_memDC );
                s_memDC = NULL;
            }
        }
        PostQuitMessage(0);
        break;

    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                CefRefPtr<CefBrowserHost> host = browser->GetHost();
                if ( NULL != host )
                {
                    CefMouseEvent event;
                    event.x = GET_X_LPARAM(lParam);
                    event.y = GET_Y_LPARAM(lParam);
                    event.modifiers = 0;
                    const bool btnUp = (WM_LBUTTONUP == message)?(true):(false);
                    host->SendMouseClickEvent( event, MBT_LEFT, btnUp, 1 );
                }
            }
        }
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

<commit_msg>add mouse move<commit_after>/**
 * The MIT License
 *
 * Copyright (C) 2017 Kiyofumi Kondoh
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "stdafx.h"
#include "test_cef_offscreen.h"

extern int main_win(HINSTANCE hInstance);
extern int main_win_term();

#include "include/base/cef_lock.h"

#define MAX_LOADSTRING 100

HWND        s_hWnd = NULL;
HDC         s_memDC = NULL;
HBITMAP     s_memBitmap = NULL;
void*       s_memBitmapPixel = NULL;
base::Lock  s_memBitmapPixelLock;
HBITMAP     s_memBitmapPrev = NULL;

HINSTANCE hInst;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];

ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    {
        const int nRet = main_win(hInstance);
        if ( 0 <= nRet )
        {
            return nRet;
        }
    }

    MSG msg;
    HACCEL hAccelTable;

    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_TEST_CEF_OFFSCREEN, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST_CEF_OFFSCREEN));

    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    {
        main_win_term();
    }

    return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST_CEF_OFFSCREEN));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_TEST_CEF_OFFSCREEN);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance;

   DWORD dwStyle = WS_OVERLAPPEDWINDOW;
   dwStyle ^= WS_MINIMIZEBOX;
   dwStyle ^= WS_MAXIMIZEBOX;
   dwStyle ^= WS_THICKFRAME;

   DWORD dwExStyle = 0;

   int width = CW_USEDEFAULT;
   int height = CW_USEDEFAULT;
   {
       RECT     rect;

       rect.left = 0;
       rect.top = 0;
       rect.bottom = 720;
       rect.right = 1280;

       ::AdjustWindowRectEx(&rect, dwStyle, TRUE, dwExStyle);

       width = rect.right - rect.left;
       height = rect.bottom - rect.top;
   }

   hWnd = CreateWindow(szWindowClass, szTitle, dwStyle,
      CW_USEDEFAULT, 0, width, height, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   s_hWnd = hWnd;

   return TRUE;
}

#include "../test_cef_app/cef_client.h"

extern CefRefPtr<Client> s_client;

#include <windowsx.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_CREATE:
        {
            hdc = GetDC( hWnd );

            s_memDC = CreateCompatibleDC( hdc );
            {
                BITMAPINFO bi;

                ZeroMemory( &bi, sizeof(bi) );
                bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                bi.bmiHeader.biBitCount = 32;
                bi.bmiHeader.biPlanes = 1;
                bi.bmiHeader.biWidth = 1280;
                bi.bmiHeader.biHeight = -720;

                {
                    base::AutoLock  lock_scope(s_memBitmapPixelLock);

                    s_memBitmap = CreateDIBSection( NULL, &bi, DIB_RGB_COLORS, &s_memBitmapPixel, NULL, 0 );
                    if ( NULL == s_memBitmap )
                    {
                        ReleaseDC( hWnd, hdc );
                        return -1;
                    }

                    {
                        DWORD* p = reinterpret_cast<DWORD*>(s_memBitmapPixel);
                        for ( int x = 0; x < 1280; ++x )
                        {
                            for ( int y = 0; y < 720; ++y )
                            {
                                // A,R,G,B
                                //p[y*1280 + x] = 0xffffffffUL;
                                p[y*1280 + x] = 0x0000ff00UL;
                            }
                        }
                    }
                }
            }
            s_memBitmapPrev = (HBITMAP)SelectObject( s_memDC, s_memBitmap );

            ReleaseDC( hWnd, hdc );
        }
        break;

    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        BitBlt( hdc, 0, 0, 1280, 720, s_memDC, 0, 0, SRCCOPY );
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        {
            if ( NULL != s_memDC )
            {
                if ( NULL != s_memBitmap )
                {
                    base::AutoLock  lock_scope(s_memBitmapPixelLock);

                    SelectObject( s_memDC, s_memBitmapPrev );
                    s_memBitmapPrev = NULL;
                    DeleteObject( s_memBitmap );
                    s_memBitmap = NULL;
                    s_memBitmapPixel = NULL;
                }

                DeleteDC( s_memDC );
                s_memDC = NULL;
            }
        }
        PostQuitMessage(0);
        break;

    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                CefRefPtr<CefBrowserHost> host = browser->GetHost();
                if ( NULL != host )
                {
                    CefMouseEvent event;
                    event.x = GET_X_LPARAM(lParam);
                    event.y = GET_Y_LPARAM(lParam);
                    event.modifiers = 0;
                    const bool btnUp = (WM_LBUTTONUP == message)?(true):(false);
                    host->SendMouseClickEvent( event, MBT_LEFT, btnUp, 1 );
                }
            }
        }
        break;

    case WM_MOUSEMOVE:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                CefRefPtr<CefBrowserHost> host = browser->GetHost();
                if ( NULL != host )
                {
                    CefMouseEvent event;
                    event.x = GET_X_LPARAM(lParam);
                    event.y = GET_Y_LPARAM(lParam);
                    event.modifiers = 0;
                    const bool mouseLeave = false;
                    host->SendMouseMoveEvent( event, mouseLeave );
                }
            }
        }
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

<|endoftext|>


<commit_before>// ===============================
//  PC-BSD SysAdm Bridge Server
// Available under the 3-clause BSD License
// Written by: Ken Moore <ken@pcbsd.org> May 2016
// =================================
#include "globals.h"

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

#include "BridgeServer.h"

#define DEBUG 0

//Create any global classes/settings
QSettings *CONFIG = new QSettings(SETTINGSFILE, QSettings::IniFormat);
AuthorizationManager *AUTHSYSTEM = new AuthorizationManager();

//Set the defail values for the global config variables
/*int BlackList_BlockMinutes = 60;
int BlackList_AuthFailsToBlock = 5;
int BlackList_AuthFailResetMinutes = 10;*/

//Create the default logfile
/*QFile logfile;
void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg){
  QString txt;
  switch(type){
  case QtDebugMsg:
  	  txt = msg;
  	  break;
  case QtWarningMsg:
  	  txt = QString("WARNING: %1").arg(msg);
          txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
  	  break;
  case QtCriticalMsg:
  	  txt = QString("CRITICAL: %1").arg(msg);
	  txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
  	  break;
  case QtFatalMsg:
  	  txt = QString("FATAL: %1").arg(msg);
	  txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
  	  break;
  }

  QTextStream out(&logfile);
  out << txt;
  if(!txt.endsWith("\n")){ out << "\n"; }
} */

int main( int argc, char ** argv )
{
    //Evaluate input arguments
    quint16 port = 12149; //port number
    bool settingchange = false;
    for(int i=1; i<argc; i++){
      if(settingchange){
        QString info = QString(argv[i]);
	if(!info.contains("=")){ continue; } //invalid format
	QString var = info.section("=",0,0); QString val = info.section("=",1,-1);
	qDebug() << "Changing bridge setting:" << info;
        if(var=="blacklist/block_minutes"){ CONFIG->setValue("blacklist_settings/block_minutes",val.toInt()); }
        else if(var=="blacklist/fails_to_block"){ CONFIG->setValue("blacklist_settings/fails_to_block",val.toInt()); }
        //else if(var=="blacklist_settings/block_minutes"){ CONFIG->setValue("blacklist_settings/block_minutes",val.toInt()); }
      }
      else if( (QString(argv[i])=="-port" || QString(argv[i])=="-p") && (i+1<argc)){ i++; port = QString(argv[i]).toUInt(); }
      else if( QString(argv[i])=="-set" && i+1<argc){ settingchange = true; }
    }
    if(settingchange){ return 0; }

     QCoreApplication a(argc, argv);

    //Setup the log file
    /*logfile.setFileName("/var/log/sysadm-bridge.log");
    if(DEBUG){ qDebug() << "Log File:" << logfile.fileName(); }
      if(QFile::exists(logfile.fileName()+".old")){ QFile::remove(logfile.fileName()+".old"); }
      if(logfile.exists()){ QFile::rename(logfile.fileName(), logfile.fileName()+".old"); }
      //Make sure the parent directory exists
      if(!QFile::exists("/var/log")){
        QDir dir;
        dir.mkpath("/var/log");
      }
      logfile.open(QIODevice::WriteOnly | QIODevice::Append);
      qInstallMessageHandler(MessageOutput);
      */
    //Create the server
    qDebug() << "Starting the PC-BSD sysadm bridge....";
    BridgeServer server;

    //Start the servers
    int ret = 1; //error return value
    if(!server.startServer(port)){ qDebug() << "Could not start bridge server on port:" << port; }
    else{
      //Now start the main event loop
      qDebug() << "Bridge Started:" << QDateTime::currentDateTime().toString(Qt::ISODate);
      ret = a.exec();
      qDebug() << "Bridge Stopped:" << QDateTime::currentDateTime().toString(Qt::ISODate);
    }

    //Cleanup any globals
    delete CONFIG;
    //logfile.close();

    //Return
    return ret;
}
<commit_msg>Add a couple CLI options to the sysadm-bridge: 1) "-import_ssl_file <filepath>": This will import any .crt or .key file such that the designated public key may be used for authentication with this bridge. 2) "-import_ssl_pubkey <pubkey>": This will import the designated pubkey such that it may be used for authentication with this bridge.<commit_after>// ===============================
//  PC-BSD SysAdm Bridge Server
// Available under the 3-clause BSD License
// Written by: Ken Moore <ken@pcbsd.org> May 2016
// =================================
#include "globals.h"

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

#include "BridgeServer.h"

#define DEBUG 0
#define USELOG 1 //turn this to "0" for dumping all output to the CLI (for debugging)

//Create any global classes/settings
QSettings *CONFIG = new QSettings(SETTINGSFILE, QSettings::IniFormat);
AuthorizationManager *AUTHSYSTEM = new AuthorizationManager();

//Set the defail values for the global config variables
/*int BlackList_BlockMinutes = 60;
int BlackList_AuthFailsToBlock = 5;
int BlackList_AuthFailResetMinutes = 10;*/

//Create the default logfile
QFile logfile;
void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg){
  QString txt;
  switch(type){
  case QtDebugMsg:
  	  txt = msg;
  	  break;
  case QtWarningMsg:
  	  txt = QString("WARNING: %1").arg(msg);
          txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
  	  break;
  case QtCriticalMsg:
  	  txt = QString("CRITICAL: %1").arg(msg);
	  txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
  	  break;
  case QtFatalMsg:
  	  txt = QString("FATAL: %1").arg(msg);
	  txt += "\n Context: "+QString(context.file)+" Line: "+QString(context.line)+" Function: "+QString(context.function);
  	  break;
  }

  QTextStream out(&logfile);
  out << txt;
  if(!txt.endsWith("\n")){ out << "\n"; }
}

int main( int argc, char ** argv )
{
    //Evaluate input arguments
    quint16 port = 12149; //port number
    bool settingchange = false;
    for(int i=1; i<argc; i++){
      if(settingchange){
        QString info = QString(argv[i]);
	if(!info.contains("=")){ continue; } //invalid format
	QString var = info.section("=",0,0); QString val = info.section("=",1,-1);
	qDebug() << "Changing bridge setting:" << info;
        if(var=="blacklist/block_minutes"){ CONFIG->setValue("blacklist_settings/block_minutes",val.toInt()); }
        else if(var=="blacklist/fails_to_block"){ CONFIG->setValue("blacklist_settings/fails_to_block",val.toInt()); }
        //else if(var=="blacklist_settings/block_minutes"){ CONFIG->setValue("blacklist_settings/block_minutes",val.toInt()); }
      }
      else if( (QString(argv[i])=="-port" || QString(argv[i])=="-p") && (i+1<argc)){ i++; port = QString(argv[i]).toUInt(); }
      else if( QString(argv[i])=="-set" && i+1<argc){ settingchange = true; }
      else if( QString(argv[i])=="-import_ssl_file" && i+1<argc){
        i++;
        QFile file(argv[i]);
 	if(!file.open(QIODevice::ReadOnly)){ qDebug() << "Could not open file:" << file.fileName(); }
        else{
          QString enc_key;
          if(file.fileName().endsWith(".crt")){ QSslCertificate cert(&file); enc_key = QString(cert.publicKey().toPem().toBase64()); }
          else if(file.fileName().endsWith(".key")){ QSslKey key(&file); enc_key = QString(key.toPem().toBase64());  }
          if(enc_key.isEmpty()){ qDebug() << "Could not read key (need .crt or .key file)"; }
          else{
            qDebug() << "Registered Key:" << enc_key << "(base64)";
            CONFIG->setValue("RegisteredCerts/cli-import/"+enc_key, "Date Registered: "+QDateTime::currentDateTime().toString(Qt::ISODate) );
          }
	}
        settingchange=true;
      }else if( QString(argv[i])=="-import_ssl_pubkey" && i+1<argc){
        i++;
        QString enc_key = QByteArray(argv[i]).toBase64();
        CONFIG->setValue("RegisteredCerts/cli-import/"+enc_key, "Date Registered: "+QDateTime::currentDateTime().toString(Qt::ISODate) );
        qDebug() << "Registered Key:" << enc_key << "(base64)";
        settingchange=true;
      }
    } //end loop over argc
    if(settingchange){ return 0; }

     QCoreApplication a(argc, argv);

    //Setup the log file
    if(USELOG){
      logfile.setFileName("/var/log/sysadm-bridge.log");
      if(DEBUG){ qDebug() << "Log File:" << logfile.fileName(); }
      if(QFile::exists(logfile.fileName()+".old")){ QFile::remove(logfile.fileName()+".old"); }
      if(logfile.exists()){ QFile::rename(logfile.fileName(), logfile.fileName()+".old"); }
      //Make sure the parent directory exists
      if(!QFile::exists("/var/log")){
        QDir dir;
        dir.mkpath("/var/log");
      }
      logfile.open(QIODevice::WriteOnly | QIODevice::Append);
      qInstallMessageHandler(MessageOutput);
    }
    //Create the server
    qDebug() << "Starting the PC-BSD sysadm bridge....";
    BridgeServer server;

    //Start the servers
    int ret = 1; //error return value
    if(!server.startServer(port)){ qDebug() << "Could not start bridge server on port:" << port; }
    else{
      //Now start the main event loop
      qDebug() << "Bridge Started:" << QDateTime::currentDateTime().toString(Qt::ISODate);
      ret = a.exec();
      qDebug() << "Bridge Stopped:" << QDateTime::currentDateTime().toString(Qt::ISODate);
    }

    //Cleanup any globals
    delete CONFIG;
    if(USELOG){ logfile.close(); }

    //Return
    return ret;
}
<|endoftext|>


<commit_before>/*
 * jsonRpcWebsockets.cpp
 *
 *  Created on: 9.6.2015
 *      Author: ondra
 */

#include "jsonRpcWebsockets.h"
#include "../httpserver/abstractWebSockets.tcc"
#include <lightspeed/base/interface.tcc>
#include "lightspeed/base/actions/promise.tcc"
#include "lightspeed/base/debug/dbglog.h"
#include "lightspeed/base/exceptions/httpStatusException.h"
#include "lightspeed/base/containers/map.tcc"

namespace jsonsrv {

class JsonRpcWebsocketsConnection::HttpRequestWrapper: public BredyHttpSrv::IHttpRequest {
public:
	HttpRequestWrapper(BredyHttpSrv::IHttpRequest *r, JsonRpcWebsocketsConnection *owner):r(r),owner(owner) {}

	BredyHttpSrv::IHttpRequest  * const r;
	JsonRpcWebsocketsConnection * const owner;

protected:
	virtual ConstStrA getMethod() const {return "WS";}
	virtual ConstStrA getPath() const {return r->getPath();}
	virtual ConstStrA getProtocol() const {return r->getProtocol();}
	virtual HeaderValue getHeaderField(ConstStrA field) const {return r->getHeaderField(field);}
	virtual HeaderValue getHeaderField(HeaderField field) const {return r->getHeaderField(field);}
	virtual bool enumHeader(HdrEnumFn fn) const {return r->enumHeader(fn);}
	virtual void sendHeaders() {}
	virtual void header(ConstStrA , ConstStrA ) {}
	virtual void header(HeaderField , ConstStrA ) {}
	virtual void status(natural , ConstStrA = ConstStrA()) {}
	virtual void errorPage(natural , ConstStrA = ConstStrA(), ConstStrA = ConstStrA()) {}
	virtual void redirect(ConstStrA , int = 0) {}
	virtual ConstStrA getBaseUrl() const {return r->getBaseUrl();}
	virtual void useHTTP11(bool ) {}
	virtual bool isField(ConstStrA text, HeaderField fld) const {return r->isField(text,fld);}
	virtual natural read(void *,  natural ) {return 0;}
    virtual natural write(const void *,  natural )  {return 0;}
	virtual natural peek(void *, natural ) const {return 0;}
	virtual bool canRead() const {return false;}
	virtual bool canWrite() const {return false;}
	virtual void flush() {}
	virtual natural dataReady() const {return 0;}
	virtual void finish() {}
	virtual bool headersSent() const {return true;}
	virtual natural callHandler(ConstStrA , IHttpHandler ** = 0) {return 0;}
	virtual natural forwardRequest(ConstStrA, IHttpHandler ** = 0) { return 0; }
	virtual bool keepAlive() const {return true;}
	virtual PNetworkStream getConnection() {return nil;}
	virtual void setRequestContext(IHttpHandlerContext *context) {
		rctx = context;
	}
	virtual void setConnectionContext(IHttpHandlerContext *context) {
		owner->jsonrpc_context = context;
	}
	virtual IHttpHandlerContext *getRequestContext() const {
		return rctx;
	}
	virtual IHttpHandlerContext *getConnectionContext() const {
		return owner->jsonrpc_context;
	}
	virtual natural getPostBodySize() const {
		return 0;
	}
	virtual void beginIO() {r->beginIO();}
	virtual void endIO()   {r->endIO();}
	virtual void setMaxPostSize(natural ) {}
	virtual void attachThread(natural ) {}
	virtual void closeOutput() {}
	virtual void setRequestName(ConstStrA ) {}
	virtual void recordRequestDuration(natural x) {r->recordRequestDuration(x);}

	virtual void *proxyInterface(IInterfaceRequest &p) {
		void *x = IInterface::proxyInterface(p);
		if (!x) x = r->proxyInterface(p);
		return x;
	}
	virtual const void *proxyInterface(const IInterfaceRequest &p) const {
		const void *x = IInterface::proxyInterface(p);
		if (!x) x = r->proxyInterface(p);
		return x;
	}

	virtual StringA getAbsoluteUrl() const { return r->getAbsoluteUrl(); }
	virtual StringA getAbsoluteUrl(ConstStrA relpath) const { return r->getAbsoluteUrl(relpath); }

	AllocPointer<IHttpHandlerContext> rctx;

};

JsonRpcWebsocketsConnection::JsonRpcWebsocketsConnection(IHttpRequest &request, IJsonRpc& handler, StringA openMethod)
	:WebSocketConnection(request),handler(handler),nextPromiseId(0),http(request),openMethod(openMethod.getMT()) {

	logobject = handler.getIfcPtr<IJsonRpcLogObject>();
}

void JsonRpcWebsocketsConnection::sendNotification(ConstStrA name, JSON::PNode arguments) {
	Synchronized<FastLock> _(lock);
	JSON::PNode req = json("method",name)
			("params",arguments)
			("id",nil);
	ConstStrA msg = json.factory->toString(*req);
	this->sendTextMessage(msg,true);
}

Future<JSON::PNode> JsonRpcWebsocketsConnection::callMethod(ConstStrA name, JSON::PNode arguments) {
	Synchronized<FastLock> _(lock);
	natural promiseId = nextPromiseId++;
	JSON::PNode req = json("method",name)
			("params",arguments)
			("id",promiseId);
	ConstStrA msg = json.factory->toString(*req);
	this->sendTextMessage(msg,true);

	Future<JSON::PNode> promise;
	waitingPromises.insert(promiseId,promise.getPromise());
	return promise;
}

JsonRpcWebsocketsConnection* JsonRpcWebsocketsConnection::getConnection(IHttpRequest& request) {
	HttpRequestWrapper *rq = request.getIfcPtr<HttpRequestWrapper>();
	if (rq == 0) return 0;
	return rq->owner;
}

void JsonRpcWebsocketsConnection::setContext(IHttpHandlerContext* context) {
	this->context = context;
}

IHttpHandlerContext* JsonRpcWebsocketsConnection::getContext() const {
	return context;
}

void JsonRpcWebsocketsConnection::onTextMessage(ConstStrA msg) {
	Synchronized<FastLock> _(lock);
	LS_LOGOBJ(lg);

	JSON::PNode req = json.factory->fromString(msg);
	if (req->getPtr("result")) {
		natural id = req["id"]->getUInt();
		const Promise<JSON::PNode> *pres = waitingPromises.find(id);
		if (pres == 0) return;
		Promise<JSON::PNode> p = *pres;
		waitingPromises.erase(id);
		if (req["error"]->isNull()) p.resolve(req["result"]);
		else p.reject(RpcError(THISLOCATION,req["error"]));
	} else {
		TimeStamp beginTime = TimeStamp::now();
		ConstStrA method = req["method"]->getStringUtf8();
		JSON::PNode params = req["params"];
		JSON::PNode id = req["id"];
		JSON::PNode context = req->getPtr("context");
		IJsonRpc::CallResult callRes;
		HttpRequestWrapper httpwrp(&http,this);
		try
		{
			SyncReleased<FastLock> _(lock);
			callRes = handler.callMethod(&httpwrp,method,params,context,id);
			if (logobject)
				logobject->logMethod(http,method,params,context,callRes.logOutput);
		} catch (std::exception &e) {
			callRes.error = json.factory->newValue(e.what());
			callRes.id = id;
			callRes.logOutput = callRes.error;
		}
		if (!id->isNull()) {
			JSON::Builder::Object response = json("result",callRes.result)
					("error",callRes.error)
					("id", callRes.id);
			if (callRes.newContext != nil) {
				response("context",callRes.newContext);
			}
			ConstStrA msg = json.factory->toString(*response);
			sendTextMessage(msg,true);
		}
		TimeStamp endTime = TimeStamp::now();
		natural ms = (endTime - beginTime).getMilis();
		lg.progress("%1 - POST %2 WSRPC/1.0 (%3 ms)") << http.getIfc<IHttpPeerInfo>().getPeerRealAddr()
						<< method << ms;
		http.recordRequestDuration(ms);
	}

}
JsonRpcWebsockets::JsonRpcWebsockets(IJsonRpc& handler,StringA openMethod):handler(handler),openMethod(openMethod.getMT()) {
}

WebSocketConnection* JsonRpcWebsockets::onNewConnection(
		IRuntimeAlloc& alloc, IHttpRequest& request,
		ConstStrA ) {

	IHttpRequest::HeaderValue origin =  request.getHeaderField(IHttpRequest::fldOrigin);
	bool allow = true;
	if (origin.defined) {
		Optional<bool> allowed = handler.isAllowedOrigin(origin);
		if (allowed == null) {
			ConstStrA corig = origin;

			if (corig.head(7) == ConstStrA("http://")) corig = corig.offset(7);
			else if (corig.head(8) == ConstStrA("https://")) corig = corig.offset(8);

			ConstStrA host = request.getHeaderField(IHttpRequest::fldHost);
			if (host.tail(corig.length()) != corig) allow = false;
		} else {
			allow = allowed;
		}
	}
	if (allow)
		return new(alloc) JsonRpcWebsocketsConnection(request,handler,openMethod);
	else
		throw HttpStatusException(THISLOCATION,request.getAbsoluteUrl(),403,"Forbidden");
}

void JsonRpcWebsocketsConnection::onCloseOutput(natural ) {
	Synchronized<FastLock> _(lock);
	context = nil;
}

JsonRpcWebsocketsConnection::~JsonRpcWebsocketsConnection() {
	Synchronized<FastLock> _(lock);
	context = nil;
}

void JsonRpcWebsocketsConnection::onConnect() {
	if (!openMethod.empty()) {
		IJsonRpc::CallResult callRes;
		{
			JSON::PNode nullNode = json.factory->newNullNode();
			JSON::PNode emptyArr = json.array();
			SyncReleased<FastLock> _(lock);
			callRes = handler.callMethod(&http,openMethod,emptyArr,nullNode,nullNode);
			if (logobject)
				logobject->logMethod(http,openMethod,emptyArr,nullNode,callRes.logOutput);
			if (callRes.error != nil && !callRes.error->isNull()) {
				this->closeConnection(closeAbnormal);
			}
		}

	}
}

void JsonRpcWebsocketsConnection::sendNotification(const PreparedNtf& ntf) {
	this->sendTextMessage(ntf,true);
}

JsonRpcWebsocketsConnection::PreparedNtf JsonRpcWebsocketsConnection::prepareNotification(
		LightSpeed::ConstStrA name, LightSpeed::JSON::PNode arguments) {
	Synchronized<FastLock> _(lock);
	JSON::PNode req = json("method",name)
			("params",arguments)
			("id",nil);
	return  IRpcNotify::prepare(json.factory->toString(*req));
}

IRpcNotify *IRpcNotify::fromRequest(RpcRequest *r) {
	JsonRpcWebsocketsConnection *conn = JsonRpcWebsocketsConnection::getConnection(*r->httpRequest);
	return conn;
}

} /* namespace jsonsrv */

<commit_msg>fix uncaught exception closes websocket connection<commit_after>/*
 * jsonRpcWebsockets.cpp
 *
 *  Created on: 9.6.2015
 *      Author: ondra
 */

#include "jsonRpcWebsockets.h"
#include "../httpserver/abstractWebSockets.tcc"
#include <lightspeed/base/interface.tcc>
#include "lightspeed/base/actions/promise.tcc"
#include "lightspeed/base/debug/dbglog.h"
#include "lightspeed/base/exceptions/httpStatusException.h"
#include "lightspeed/base/containers/map.tcc"

namespace jsonsrv {

class JsonRpcWebsocketsConnection::HttpRequestWrapper: public BredyHttpSrv::IHttpRequest {
public:
	HttpRequestWrapper(BredyHttpSrv::IHttpRequest *r, JsonRpcWebsocketsConnection *owner):r(r),owner(owner) {}

	BredyHttpSrv::IHttpRequest  * const r;
	JsonRpcWebsocketsConnection * const owner;

protected:
	virtual ConstStrA getMethod() const {return "WS";}
	virtual ConstStrA getPath() const {return r->getPath();}
	virtual ConstStrA getProtocol() const {return r->getProtocol();}
	virtual HeaderValue getHeaderField(ConstStrA field) const {return r->getHeaderField(field);}
	virtual HeaderValue getHeaderField(HeaderField field) const {return r->getHeaderField(field);}
	virtual bool enumHeader(HdrEnumFn fn) const {return r->enumHeader(fn);}
	virtual void sendHeaders() {}
	virtual void header(ConstStrA , ConstStrA ) {}
	virtual void header(HeaderField , ConstStrA ) {}
	virtual void status(natural , ConstStrA = ConstStrA()) {}
	virtual void errorPage(natural , ConstStrA = ConstStrA(), ConstStrA = ConstStrA()) {}
	virtual void redirect(ConstStrA , int = 0) {}
	virtual ConstStrA getBaseUrl() const {return r->getBaseUrl();}
	virtual void useHTTP11(bool ) {}
	virtual bool isField(ConstStrA text, HeaderField fld) const {return r->isField(text,fld);}
	virtual natural read(void *,  natural ) {return 0;}
    virtual natural write(const void *,  natural )  {return 0;}
	virtual natural peek(void *, natural ) const {return 0;}
	virtual bool canRead() const {return false;}
	virtual bool canWrite() const {return false;}
	virtual void flush() {}
	virtual natural dataReady() const {return 0;}
	virtual void finish() {}
	virtual bool headersSent() const {return true;}
	virtual natural callHandler(ConstStrA , IHttpHandler ** = 0) {return 0;}
	virtual natural forwardRequest(ConstStrA, IHttpHandler ** = 0) { return 0; }
	virtual bool keepAlive() const {return true;}
	virtual PNetworkStream getConnection() {return nil;}
	virtual void setRequestContext(IHttpHandlerContext *context) {
		rctx = context;
	}
	virtual void setConnectionContext(IHttpHandlerContext *context) {
		owner->jsonrpc_context = context;
	}
	virtual IHttpHandlerContext *getRequestContext() const {
		return rctx;
	}
	virtual IHttpHandlerContext *getConnectionContext() const {
		return owner->jsonrpc_context;
	}
	virtual natural getPostBodySize() const {
		return 0;
	}
	virtual void beginIO() {r->beginIO();}
	virtual void endIO()   {r->endIO();}
	virtual void setMaxPostSize(natural ) {}
	virtual void attachThread(natural ) {}
	virtual void closeOutput() {}
	virtual void setRequestName(ConstStrA ) {}
	virtual void recordRequestDuration(natural x) {r->recordRequestDuration(x);}

	virtual void *proxyInterface(IInterfaceRequest &p) {
		void *x = IInterface::proxyInterface(p);
		if (!x) x = r->proxyInterface(p);
		return x;
	}
	virtual const void *proxyInterface(const IInterfaceRequest &p) const {
		const void *x = IInterface::proxyInterface(p);
		if (!x) x = r->proxyInterface(p);
		return x;
	}

	virtual StringA getAbsoluteUrl() const { return r->getAbsoluteUrl(); }
	virtual StringA getAbsoluteUrl(ConstStrA relpath) const { return r->getAbsoluteUrl(relpath); }

	AllocPointer<IHttpHandlerContext> rctx;

};

JsonRpcWebsocketsConnection::JsonRpcWebsocketsConnection(IHttpRequest &request, IJsonRpc& handler, StringA openMethod)
	:WebSocketConnection(request),handler(handler),nextPromiseId(0),http(request),openMethod(openMethod.getMT()) {

	logobject = handler.getIfcPtr<IJsonRpcLogObject>();
}

void JsonRpcWebsocketsConnection::sendNotification(ConstStrA name, JSON::PNode arguments) {
	Synchronized<FastLock> _(lock);
	JSON::PNode req = json("method",name)
			("params",arguments)
			("id",nil);
	ConstStrA msg = json.factory->toString(*req);
	this->sendTextMessage(msg,true);
}

Future<JSON::PNode> JsonRpcWebsocketsConnection::callMethod(ConstStrA name, JSON::PNode arguments) {
	Synchronized<FastLock> _(lock);
	natural promiseId = nextPromiseId++;
	JSON::PNode req = json("method",name)
			("params",arguments)
			("id",promiseId);
	ConstStrA msg = json.factory->toString(*req);
	this->sendTextMessage(msg,true);

	Future<JSON::PNode> promise;
	waitingPromises.insert(promiseId,promise.getPromise());
	return promise;
}

JsonRpcWebsocketsConnection* JsonRpcWebsocketsConnection::getConnection(IHttpRequest& request) {
	HttpRequestWrapper *rq = request.getIfcPtr<HttpRequestWrapper>();
	if (rq == 0) return 0;
	return rq->owner;
}

void JsonRpcWebsocketsConnection::setContext(IHttpHandlerContext* context) {
	this->context = context;
}

IHttpHandlerContext* JsonRpcWebsocketsConnection::getContext() const {
	return context;
}

void JsonRpcWebsocketsConnection::onTextMessage(ConstStrA msg) {
	Synchronized<FastLock> _(lock);
	LS_LOGOBJ(lg);

	JSON::PNode req = json.factory->fromString(msg);
	if (req->getPtr("result")) {
		natural id = req["id"]->getUInt();
		const Promise<JSON::PNode> *pres = waitingPromises.find(id);
		if (pres == 0) return;
		Promise<JSON::PNode> p = *pres;
		waitingPromises.erase(id);
		if (req["error"]->isNull()) p.resolve(req["result"]);
		else p.reject(RpcError(THISLOCATION,req["error"]));
	} else {
		TimeStamp beginTime = TimeStamp::now();
		ConstStrA method = req["method"]->getStringUtf8();
		JSON::PNode params = req["params"];
		JSON::PNode id = req["id"];
		JSON::PNode context = req->getPtr("context");
		IJsonRpc::CallResult callRes;
		HttpRequestWrapper httpwrp(&http,this);
		try
		{
			SyncReleased<FastLock> _(lock);
			callRes = handler.callMethod(&httpwrp,method,params,context,id);
			if (logobject)
				logobject->logMethod(http,method,params,context,callRes.logOutput);
		} catch (std::exception &e) {
			callRes.result= json.factory->newValue(null);
			callRes.error = json.factory->newValue(e.what());
			callRes.id = id;
			callRes.logOutput = callRes.error;
		}
		if (!id->isNull()) {
			JSON::Builder::Object response = json("result",callRes.result)
					("error",callRes.error)
					("id", callRes.id);
			if (callRes.newContext != nil) {
				response("context",callRes.newContext);
			}
			ConstStrA msg = json.factory->toString(*response);
			sendTextMessage(msg,true);
		}
		TimeStamp endTime = TimeStamp::now();
		natural ms = (endTime - beginTime).getMilis();
		lg.progress("%1 - POST %2 WSRPC/1.0 (%3 ms)") << http.getIfc<IHttpPeerInfo>().getPeerRealAddr()
						<< method << ms;
		http.recordRequestDuration(ms);
	}

}
JsonRpcWebsockets::JsonRpcWebsockets(IJsonRpc& handler,StringA openMethod):handler(handler),openMethod(openMethod.getMT()) {
}

WebSocketConnection* JsonRpcWebsockets::onNewConnection(
		IRuntimeAlloc& alloc, IHttpRequest& request,
		ConstStrA ) {

	IHttpRequest::HeaderValue origin =  request.getHeaderField(IHttpRequest::fldOrigin);
	bool allow = true;
	if (origin.defined) {
		Optional<bool> allowed = handler.isAllowedOrigin(origin);
		if (allowed == null) {
			ConstStrA corig = origin;

			if (corig.head(7) == ConstStrA("http://")) corig = corig.offset(7);
			else if (corig.head(8) == ConstStrA("https://")) corig = corig.offset(8);

			ConstStrA host = request.getHeaderField(IHttpRequest::fldHost);
			if (host.tail(corig.length()) != corig) allow = false;
		} else {
			allow = allowed;
		}
	}
	if (allow)
		return new(alloc) JsonRpcWebsocketsConnection(request,handler,openMethod);
	else
		throw HttpStatusException(THISLOCATION,request.getAbsoluteUrl(),403,"Forbidden");
}

void JsonRpcWebsocketsConnection::onCloseOutput(natural ) {
	Synchronized<FastLock> _(lock);
	context = nil;
}

JsonRpcWebsocketsConnection::~JsonRpcWebsocketsConnection() {
	Synchronized<FastLock> _(lock);
	context = nil;
}

void JsonRpcWebsocketsConnection::onConnect() {
	if (!openMethod.empty()) {
		IJsonRpc::CallResult callRes;
		{
			JSON::PNode nullNode = json.factory->newNullNode();
			JSON::PNode emptyArr = json.array();
			SyncReleased<FastLock> _(lock);
			callRes = handler.callMethod(&http,openMethod,emptyArr,nullNode,nullNode);
			if (logobject)
				logobject->logMethod(http,openMethod,emptyArr,nullNode,callRes.logOutput);
			if (callRes.error != nil && !callRes.error->isNull()) {
				this->closeConnection(closeAbnormal);
			}
		}

	}
}

void JsonRpcWebsocketsConnection::sendNotification(const PreparedNtf& ntf) {
	this->sendTextMessage(ntf,true);
}

JsonRpcWebsocketsConnection::PreparedNtf JsonRpcWebsocketsConnection::prepareNotification(
		LightSpeed::ConstStrA name, LightSpeed::JSON::PNode arguments) {
	Synchronized<FastLock> _(lock);
	JSON::PNode req = json("method",name)
			("params",arguments)
			("id",nil);
	return  IRpcNotify::prepare(json.factory->toString(*req));
}

IRpcNotify *IRpcNotify::fromRequest(RpcRequest *r) {
	JsonRpcWebsocketsConnection *conn = JsonRpcWebsocketsConnection::getConnection(*r->httpRequest);
	return conn;
}

} /* namespace jsonsrv */

<|endoftext|>