<commit_before>#include "xml.h"

#include <cstring>
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <vector>

#include "../leanify.h"
#include "../utils.h"
#include "base64.h"
#include "data_uri.h"

using std::string;

Xml::Xml(void* p, size_t s /*= 0*/) : Format(p, s) {
  pugi::xml_parse_result result = doc_.load_buffer(
      fp_, size_, pugi::parse_default | pugi::parse_declaration | pugi::parse_doctype | pugi::parse_ws_pcdata_single);
  is_valid_ = result;
  encoding_ = result.encoding;
}

namespace {
// https://github.com/svg/svgo/blob/master/plugins/_collections.js
const std::map<string, string> kDefaultAttributes = { { "x", "0" },
                                                      { "y", "0" },
                                                      { "width", "100%" },
                                                      { "height", "100%" },
                                                      { "clip", "auto" },
                                                      { "clip-path", "none" },
                                                      { "clip-rule", "nonzero" },
                                                      { "mask", "none" },
                                                      { "opacity", "1" },
                                                      { "solid-color", "#000" },
                                                      { "solid-opacity", "1" },
                                                      { "stop-color", "#000" },
                                                      { "stop-opacity", "1" },
                                                      { "fill-opacity", "1" },
                                                      { "fill-rule", "nonzero" },
                                                      { "fill", "#000" },
                                                      { "stroke", "none" },
                                                      { "stroke-width", "1" },
                                                      { "stroke-linecap", "butt" },
                                                      { "stroke-linejoin", "miter" },
                                                      { "stroke-miterlimit", "4" },
                                                      { "stroke-dasharray", "none" },
                                                      { "stroke-dashoffset", "0" },
                                                      { "stroke-opacity", "1" },
                                                      { "paint-order", "normal" },
                                                      { "vector-effect", "none" },
                                                      { "viewport-fill", "none" },
                                                      { "viewport-fill-opacity", "1" },
                                                      { "display", "inline" },
                                                      { "visibility", "visible" },
                                                      { "marker-start", "none" },
                                                      { "marker-mid", "none" },
                                                      { "marker-end", "none" },
                                                      { "color-interpolation", "sRGB" },
                                                      { "color-interpolation-filters", "linearRGB" },
                                                      { "color-rendering", "auto" },
                                                      { "shape-rendering", "auto" },
                                                      { "text-rendering", "auto" },
                                                      { "image-rendering", "auto" },
                                                      { "buffered-rendering", "auto" },
                                                      { "font-style", "normal" },
                                                      { "font-variant", "normal" },
                                                      { "font-weight", "normal" },
                                                      { "font-stretch", "normal" },
                                                      { "font-size", "medium" },
                                                      { "font-size-adjust", "none" },
                                                      { "kerning", "auto" },
                                                      { "letter-spacing", "normal" },
                                                      { "word-spacing", "normal" },
                                                      { "text-decoration", "none" },
                                                      { "text-anchor", "start" },
                                                      { "text-overflow", "clip" },
                                                      { "writing-mode", "lr-tb" },
                                                      { "glyph-orientation-vertical", "auto" },
                                                      { "glyph-orientation-horizontal", "0deg" },
                                                      { "direction", "ltr" },
                                                      { "unicode-bidi", "normal" },
                                                      { "dominant-baseline", "auto" },
                                                      { "alignment-baseline", "baseline" },
                                                      { "baseline-shift", "baseline" },
                                                      { "slope", "1" },
                                                      { "intercept", "0" },
                                                      { "amplitude", "1" },
                                                      { "exponent", "1" },
                                                      { "offset", "0" } };

// shrink spaces and newlines, also removes preceding and trailing spaces
string ShrinkSpace(const char* value) {
  string new_value;
  while (*value) {
    if (*value == ' ' || *value == '\n' || *value == '\t') {
      do {
        value++;
      } while (*value == ' ' || *value == '\n' || *value == '\t');

      if (*value == 0)
        break;
      new_value += ' ';
    }
    new_value += *value++;
  }
  if (!new_value.empty() && new_value[0] == ' ')
    new_value.erase(0, 1);

  return new_value;
}

void TraverseElements(pugi::xml_node node, std::function<void(pugi::xml_node)> callback) {
  // cannot use ranged loop here because we need to store the next_sibling before recursion so that if child was removed
  // the loop won't be terminated
  for (pugi::xml_node child = node.first_child(), next; child; child = next) {
    next = child.next_sibling();
    TraverseElements(child, callback);
  }

  callback(node);
}

// Remove single PCData only contains whitespace if xml:space="preserve" is not set.
void RemovePCDataSingle(pugi::xml_node node, bool xml_space_preserve) {
  xml_space_preserve |= strcmp(node.attribute("xml:space").value(), "preserve") == 0;
  if (xml_space_preserve)
    return;

  pugi::xml_node pcdata = node.first_child();
  if (pcdata.first_child() == nullptr && pcdata.type() == pugi::node_pcdata && pcdata.next_sibling() == nullptr) {
    if (ShrinkSpace(pcdata.value()).empty())
      node.remove_child(pcdata);
    return;
  }

  for (pugi::xml_node child : node.children())
    RemovePCDataSingle(child, xml_space_preserve);
}

struct xml_memory_writer : pugi::xml_writer {
  uint8_t* p_write;

  void write(const void* data, size_t size) override {
    memcpy(p_write, data, size);
    p_write += size;
  }
};
}  // namespace

size_t Xml::Leanify(size_t size_leanified /*= 0*/) {
  RemovePCDataSingle(doc_, false);

  // if the XML is fb2 file
  if (doc_.child("FictionBook")) {
    VerbosePrint("FB2 detected.");
    if (depth < max_depth) {
      depth++;

      pugi::xml_node root = doc_.child("FictionBook");

      // iterate through all binary element
      for (pugi::xml_node binary = root.child("binary"), next; binary; binary = next) {
        next = binary.next_sibling("binary");
        pugi::xml_attribute id = binary.attribute("id");
        if (id == nullptr || id.value() == nullptr || id.value()[0] == 0) {
          root.remove_child(binary);
          continue;
        }

        PrintFileName(id.value());

        const char* base64_data = binary.child_value();
        if (base64_data == nullptr || base64_data[0] == 0) {
          VerbosePrint("No data found.");
          continue;
        }
        size_t base64_len = strlen(base64_data);
        // copy to a new location because base64_data is const
        std::vector<char> new_base64_data(base64_data, base64_data + base64_len + 1);

        size_t new_base64_len = Base64(new_base64_data.data(), base64_len).Leanify();

        if (new_base64_len < base64_len) {
          new_base64_data[new_base64_len] = 0;
          binary.text() = new_base64_data.data();
        }
      }
      depth--;
    }
  } else if (doc_.child("svg")) {
    VerbosePrint("SVG detected.");

    // remove XML declaration and doctype
    for (pugi::xml_node child = doc_.first_child(), next; child; child = next) {
      next = child.next_sibling();

      if (child.type() == pugi::node_declaration || child.type() == pugi::node_doctype)
        doc_.remove_child(child);
    }

    TraverseElements(doc_.child("svg"), [](pugi::xml_node node) {
      for (pugi::xml_attribute attr = node.first_attribute(), next; attr; attr = next) {
        next = attr.next_attribute();

        string value = ShrinkSpace(attr.value());
        // Remove empty attribute
        if (value.empty()) {
          node.remove_attribute(attr);
          continue;
        }

        // Remove default attribute
        auto it = kDefaultAttributes.find(attr.name());
        if (it != kDefaultAttributes.end() && it->second == value) {
          // Only remove it if it's not an override of a parent non-default attribute
          bool is_override = false;
          for (pugi::xml_node n = node.parent(); n; n = n.parent()) {
            pugi::xml_attribute n_attr = n.attribute(attr.name());
            // Stop at the first parent that has the same attribute, we can safely remove the attribute in the current
            // node if this parent has the same value,
            // even if there's another parent higher up in the tree which has a different value for this attribute
            // (in which case we won't be able to remove the default attribute in this parent).
            if (!n_attr.empty()) {
              if (n_attr.value() != value)
                is_override = true;
              break;
            }
          }
          if (!is_override) {
            node.remove_attribute(attr);
            continue;
          }
        }

        const string kDataURIMagic = "data:";
        if (strcmp(attr.name(), "xlink:href") == 0 && value.size() > kDataURIMagic.size() &&
            memcmp(value.data(), kDataURIMagic.data(), kDataURIMagic.size()) == 0) {
          DataURI data_uri(&value[0], value.size());
          data_uri.SetSingleMode(true);
          size_t new_size = data_uri.Leanify();
          value.resize(new_size);
        }

        attr = value.c_str();
      }

      // remove empty text element and container element
      const char* name = node.name();
      if (node.first_child() == nullptr) {
        if (strcmp(name, "text") == 0 || strcmp(name, "tspan") == 0 || strcmp(name, "a") == 0 ||
            strcmp(name, "defs") == 0 || strcmp(name, "g") == 0 || strcmp(name, "marker") == 0 ||
            strcmp(name, "mask") == 0 || strcmp(name, "missing-glyph") == 0 || strcmp(name, "pattern") == 0 ||
            strcmp(name, "switch") == 0 || strcmp(name, "symbol") == 0) {
          node.parent().remove_child(node);
          return;
        }
      }

      if (strcmp(name, "tref") == 0 && node.attribute("xlink:href").empty()) {
        node.parent().remove_child(node);
        return;
      }

      if (strcmp(name, "metadata") == 0) {
        node.parent().remove_child(node);
        return;
      }
    });
  }

  // print leanified XML to memory
  xml_memory_writer writer;
  fp_ -= size_leanified;
  writer.p_write = fp_;
  doc_.save(writer, nullptr, pugi::format_raw | pugi::format_no_declaration, encoding_);
  size_ = writer.p_write - fp_;
  return size_;
}
<commit_msg>SVG: Improve default attribute removal<commit_after>#include "xml.h"

#include <cstring>
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <vector>

#include "../leanify.h"
#include "../utils.h"
#include "base64.h"
#include "data_uri.h"

using std::map;
using std::string;

Xml::Xml(void* p, size_t s /*= 0*/) : Format(p, s) {
  pugi::xml_parse_result result = doc_.load_buffer(
      fp_, size_, pugi::parse_default | pugi::parse_declaration | pugi::parse_doctype | pugi::parse_ws_pcdata_single);
  is_valid_ = result;
  encoding_ = result.encoding;
}

namespace {
// https://github.com/svg/svgo/blob/master/plugins/_collections.js
const map<string, string> kCommonDefaultAttributes = { { "x", "0" },
                                                       { "y", "0" },
                                                       { "clip", "auto" },
                                                       { "clip-path", "none" },
                                                       { "clip-rule", "nonzero" },
                                                       { "mask", "none" },
                                                       { "opacity", "1" },
                                                       { "solid-color", "#000" },
                                                       { "solid-opacity", "1" },
                                                       { "stop-color", "#000" },
                                                       { "stop-opacity", "1" },
                                                       { "fill-opacity", "1" },
                                                       { "fill-rule", "nonzero" },
                                                       { "fill", "#000" },
                                                       { "stroke", "none" },
                                                       { "stroke-width", "1" },
                                                       { "stroke-linecap", "butt" },
                                                       { "stroke-linejoin", "miter" },
                                                       { "stroke-miterlimit", "4" },
                                                       { "stroke-dasharray", "none" },
                                                       { "stroke-dashoffset", "0" },
                                                       { "stroke-opacity", "1" },
                                                       { "paint-order", "normal" },
                                                       { "vector-effect", "none" },
                                                       { "viewport-fill", "none" },
                                                       { "viewport-fill-opacity", "1" },
                                                       { "display", "inline" },
                                                       { "visibility", "visible" },
                                                       { "marker-start", "none" },
                                                       { "marker-mid", "none" },
                                                       { "marker-end", "none" },
                                                       { "color-interpolation", "sRGB" },
                                                       { "color-interpolation-filters", "linearRGB" },
                                                       { "color-rendering", "auto" },
                                                       { "shape-rendering", "auto" },
                                                       { "text-rendering", "auto" },
                                                       { "image-rendering", "auto" },
                                                       { "buffered-rendering", "auto" },
                                                       { "font-style", "normal" },
                                                       { "font-variant", "normal" },
                                                       { "font-weight", "normal" },
                                                       { "font-stretch", "normal" },
                                                       { "font-size", "medium" },
                                                       { "font-size-adjust", "none" },
                                                       { "kerning", "auto" },
                                                       { "letter-spacing", "normal" },
                                                       { "word-spacing", "normal" },
                                                       { "text-decoration", "none" },
                                                       { "text-anchor", "start" },
                                                       { "text-overflow", "clip" },
                                                       { "writing-mode", "lr-tb" },
                                                       { "glyph-orientation-vertical", "auto" },
                                                       { "glyph-orientation-horizontal", "0deg" },
                                                       { "direction", "ltr" },
                                                       { "unicode-bidi", "normal" },
                                                       { "dominant-baseline", "auto" },
                                                       { "alignment-baseline", "baseline" },
                                                       { "baseline-shift", "baseline" },
                                                       { "slope", "1" },
                                                       { "intercept", "0" },
                                                       { "amplitude", "1" },
                                                       { "exponent", "1" },
                                                       { "offset", "0" },
                                                       { "preserveAspectRatio", "xMidYMid" } };

const map<string, map<string, string>> kSingleDefaultAttributes = {
  { "a", { { "target", "_self" } } },
  { "svg",
    { { "width", "100%" },
      { "height", "100%" },
      { "zoomAndPan", "magnify" },
      { "baseProfile", "none" },
      { "contentScriptType", "application/ecmascript" },
      { "contentStyleType", "text/css" } } },
  { "filter",
    { { "primitiveUnits", "userSpaceOnUse" },
      { "x", "-10%" },
      { "y", "-10%" },
      { "width", "120%" },
      { "height", "120%" } } },
  { "mask",
    { { "maskUnits", "objectBoundingBox" },
      { "maskContentUnits", "userSpaceOnUse" },
      { "x", "-10%" },
      { "y", "-10%" },
      { "width", "120%" },
      { "height", "120%" } } }
};

// shrink spaces and newlines, also removes preceding and trailing spaces
string ShrinkSpace(const char* value) {
  string new_value;
  while (*value) {
    if (*value == ' ' || *value == '\n' || *value == '\t') {
      do {
        value++;
      } while (*value == ' ' || *value == '\n' || *value == '\t');

      if (*value == 0)
        break;
      new_value += ' ';
    }
    new_value += *value++;
  }
  if (!new_value.empty() && new_value[0] == ' ')
    new_value.erase(0, 1);

  return new_value;
}

void TraverseElements(pugi::xml_node node, std::function<void(pugi::xml_node)> callback) {
  // cannot use ranged loop here because we need to store the next_sibling before recursion so that if child was removed
  // the loop won't be terminated
  for (pugi::xml_node child = node.first_child(), next; child; child = next) {
    next = child.next_sibling();
    TraverseElements(child, callback);
  }

  callback(node);
}

// Remove single PCData only contains whitespace if xml:space="preserve" is not set.
void RemovePCDataSingle(pugi::xml_node node, bool xml_space_preserve) {
  xml_space_preserve |= strcmp(node.attribute("xml:space").value(), "preserve") == 0;
  if (xml_space_preserve)
    return;

  pugi::xml_node pcdata = node.first_child();
  if (pcdata.first_child() == nullptr && pcdata.type() == pugi::node_pcdata && pcdata.next_sibling() == nullptr) {
    if (ShrinkSpace(pcdata.value()).empty())
      node.remove_child(pcdata);
    return;
  }

  for (pugi::xml_node child : node.children())
    RemovePCDataSingle(child, xml_space_preserve);
}

// Check the parent and ancestor of the given node if the attribute is an override
bool IsOverride(pugi::xml_node node, const char* name, const string& value) {
  for (pugi::xml_node n = node.parent(); n; n = n.parent()) {
    pugi::xml_attribute n_attr = n.attribute(name);
    // Stop at the first parent that has the same attribute, we can safely remove the attribute in the current node if
    // this parent has the same value, even if there's another parent higher up in the tree which has a different value
    // for this attribute (in which case we won't be able to remove the default attribute in this parent).
    if (!n_attr.empty()) {
      if (n_attr.value() != value)
        return true;
      break;
    }
  }
  return false;
}

// Check if the given attribute is the default
bool IsDefaultAttribute(const map<string, string>* single_default_attrs, const string& name, const string& value) {
  if (single_default_attrs) {
    auto it = single_default_attrs->find(name);
    if (it != single_default_attrs->end())
      return it->second == value;
  }
  // Fallback to common
  auto it = kCommonDefaultAttributes.find(name);
  return it != kCommonDefaultAttributes.end() && it->second == value;
}

struct xml_memory_writer : pugi::xml_writer {
  uint8_t* p_write;

  void write(const void* data, size_t size) override {
    memcpy(p_write, data, size);
    p_write += size;
  }
};
}  // namespace

size_t Xml::Leanify(size_t size_leanified /*= 0*/) {
  RemovePCDataSingle(doc_, false);

  // if the XML is fb2 file
  if (doc_.child("FictionBook")) {
    VerbosePrint("FB2 detected.");
    if (depth < max_depth) {
      depth++;

      pugi::xml_node root = doc_.child("FictionBook");

      // iterate through all binary element
      for (pugi::xml_node binary = root.child("binary"), next; binary; binary = next) {
        next = binary.next_sibling("binary");
        pugi::xml_attribute id = binary.attribute("id");
        if (id == nullptr || id.value() == nullptr || id.value()[0] == 0) {
          root.remove_child(binary);
          continue;
        }

        PrintFileName(id.value());

        const char* base64_data = binary.child_value();
        if (base64_data == nullptr || base64_data[0] == 0) {
          VerbosePrint("No data found.");
          continue;
        }
        size_t base64_len = strlen(base64_data);
        // copy to a new location because base64_data is const
        std::vector<char> new_base64_data(base64_data, base64_data + base64_len + 1);

        size_t new_base64_len = Base64(new_base64_data.data(), base64_len).Leanify();

        if (new_base64_len < base64_len) {
          new_base64_data[new_base64_len] = 0;
          binary.text() = new_base64_data.data();
        }
      }
      depth--;
    }
  } else if (doc_.child("svg")) {
    VerbosePrint("SVG detected.");

    // remove XML declaration and doctype
    for (pugi::xml_node child = doc_.first_child(), next; child; child = next) {
      next = child.next_sibling();

      if (child.type() == pugi::node_declaration || child.type() == pugi::node_doctype)
        doc_.remove_child(child);
    }

    TraverseElements(doc_.child("svg"), [](pugi::xml_node node) {
      auto single_default_attrs_iter = kSingleDefaultAttributes.find(node.name());
      const map<string, string>* single_default_attrs = nullptr;
      if (single_default_attrs_iter != kSingleDefaultAttributes.end())
        single_default_attrs = &single_default_attrs_iter->second;

      for (pugi::xml_attribute attr = node.first_attribute(), next; attr; attr = next) {
        next = attr.next_attribute();

        string value = ShrinkSpace(attr.value());
        // Remove empty attribute
        if (value.empty()) {
          node.remove_attribute(attr);
          continue;
        }

        // the second parameter in preserveAspectRatio meet by default
        if (strcmp(attr.name(), "preserveAspectRatio") == 0 && value.substr(value.size() - 5) == " meet")
          value.resize(value.size() - 5);

        // Remove default attribute
        if (IsDefaultAttribute(single_default_attrs, attr.name(), value)) {
          // Only remove it if it's not an override of a parent non-default attribute
          if (!IsOverride(node, attr.name(), value)) {
            node.remove_attribute(attr);
            continue;
          }
        }

        const string kDataURIMagic = "data:";
        if (strcmp(attr.name(), "xlink:href") == 0 && value.size() > kDataURIMagic.size() &&
            memcmp(value.data(), kDataURIMagic.data(), kDataURIMagic.size()) == 0) {
          DataURI data_uri(&value[0], value.size());
          data_uri.SetSingleMode(true);
          size_t new_size = data_uri.Leanify();
          value.resize(new_size);
        }

        attr = value.c_str();
      }

      // remove empty text element and container element
      const char* name = node.name();
      if (node.first_child() == nullptr) {
        if (strcmp(name, "text") == 0 || strcmp(name, "tspan") == 0 || strcmp(name, "a") == 0 ||
            strcmp(name, "defs") == 0 || strcmp(name, "g") == 0 || strcmp(name, "marker") == 0 ||
            strcmp(name, "mask") == 0 || strcmp(name, "missing-glyph") == 0 || strcmp(name, "pattern") == 0 ||
            strcmp(name, "switch") == 0 || strcmp(name, "symbol") == 0) {
          node.parent().remove_child(node);
          return;
        }
      }

      if (strcmp(name, "tref") == 0 && node.attribute("xlink:href").empty()) {
        node.parent().remove_child(node);
        return;
      }

      if (strcmp(name, "metadata") == 0) {
        node.parent().remove_child(node);
        return;
      }
    });
  }

  // print leanified XML to memory
  xml_memory_writer writer;
  fp_ -= size_leanified;
  writer.p_write = fp_;
  doc_.save(writer, nullptr, pugi::format_raw | pugi::format_no_declaration, encoding_);
  size_ = writer.p_write - fp_;
  return size_;
}
<|endoftext|>


<commit_before>/*
 Basic Server code for CMPT 276, Spring 2016.
 */

#include <exception>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#include <cpprest/base_uri.h>
#include <cpprest/http_listener.h>
#include <cpprest/json.h>

#include <pplx/pplxtasks.h>

#include <was/common.h>
#include <was/storage_account.h>
#include <was/table.h>

#include "TableCache.h"
#include "config.h"
#include "make_unique.h"

#include "azure_keys.h"

using azure::storage::cloud_storage_account;
using azure::storage::storage_credentials;
using azure::storage::storage_exception;
using azure::storage::cloud_table;
using azure::storage::cloud_table_client;
using azure::storage::edm_type;
using azure::storage::entity_property;
using azure::storage::table_entity;
using azure::storage::table_operation;
using azure::storage::table_query;
using azure::storage::table_query_iterator;
using azure::storage::table_result;

using pplx::extensibility::critical_section_t;
using pplx::extensibility::scoped_critical_section_t;

using std::cin;
using std::cout;
using std::endl;
using std::getline;
using std::make_pair;
using std::pair;
using std::string;
using std::unordered_map;
using std::vector;

using web::http::http_headers;
using web::http::http_request;
using web::http::methods;
using web::http::status_codes;
using web::http::uri;

using web::json::value;

using web::http::experimental::listener::http_listener;

using prop_vals_t = vector<pair<string,value>>;

constexpr const char* def_url = "http://localhost:34568";

const string create_table {"CreateTable"};
const string delete_table {"DeleteTable"};
const string update_entity {"UpdateEntity"};
const string delete_entity {"DeleteEntity"};

/*
  Cache of opened tables
 */
TableCache table_cache {storage_connection_string};

/*
  Convert properties represented in Azure Storage type
  to prop_vals_t type.
 */
prop_vals_t get_properties (const table_entity::properties_type& properties, prop_vals_t values = prop_vals_t {}) {
  for (const auto v : properties) {
    if (v.second.property_type() == edm_type::string) {
      values.push_back(make_pair(v.first, value::string(v.second.string_value())));
    }
    else if (v.second.property_type() == edm_type::datetime) {
      values.push_back(make_pair(v.first, value::string(v.second.str())));
    }
    else if(v.second.property_type() == edm_type::int32) {
      values.push_back(make_pair(v.first, value::number(v.second.int32_value())));
    }
    else if(v.second.property_type() == edm_type::int64) {
      values.push_back(make_pair(v.first, value::number(v.second.int64_value())));
    }
    else if(v.second.property_type() == edm_type::double_floating_point) {
      values.push_back(make_pair(v.first, value::number(v.second.double_value())));
    }
    else if(v.second.property_type() == edm_type::boolean) {
      values.push_back(make_pair(v.first, value::boolean(v.second.boolean_value())));
    }
    else {
      values.push_back(make_pair(v.first, value::string(v.second.str())));
    }
  }
  return values;
}

/*
  Given an HTTP message with a JSON body, return the JSON
  body as an unordered map of strings to strings.

  Note that all types of JSON values are returned as strings.
  Use C++ conversion utilities to convert to numbers or dates
  as necessary.
 */
unordered_map<string,string> get_json_body(http_request message) {
  unordered_map<string,string> results {};
  const http_headers& headers {message.headers()};
  auto content_type (headers.find("Content-Type"));
  if (content_type == headers.end() ||
      content_type->second != "application/json")
    return results;

  value json{};
  message.extract_json(true)
    .then([&json](value v) -> bool
	  {
            json = v;
	    return true;
	  })
    .wait();

  if (json.is_object()) {
    for (const auto& v : json.as_object()) {
      if (v.second.is_string()) {
	results[v.first] = v.second.as_string();
      }
      else {
	results[v.first] = v.second.serialize();
      }
    }
  }
  return results;
}

/*
  Top-level routine for processing all HTTP GET requests.

  GET is the only request that has no command. All
  operands specify the value(s) to be retrieved.
 */
void handle_get(http_request message) {
  string path {uri::decode(message.relative_uri().path())};
  cout << endl << "**** GET " << path << endl;
  auto paths = uri::split_path(path);
  // Need at least a table name
  if (paths.size() < 1) {
    message.reply(status_codes::BadRequest);
    return;
  }

  cloud_table table {table_cache.lookup_table(paths[0])};
  if ( ! table.exists()) {
    message.reply(status_codes::NotFound);
    return;
  }

  // GET all entries in table
  if (paths.size() == 1) {
    table_query query {};
    table_query_iterator end;
    table_query_iterator it = table.execute_query(query);
    vector<value> key_vec;
    while (it != end) {
      cout << "Key: " << it->partition_key() << " / " << it->row_key() << endl;
      prop_vals_t keys {
	make_pair("Partition",value::string(it->partition_key())),
	make_pair("Row", value::string(it->row_key()))};
      keys = get_properties(it->properties(), keys);
      key_vec.push_back(value::object(keys));
      ++it;
    }
    message.reply(status_codes::OK, value::array(key_vec));
    return;
  }

  // GET all entities from a specific partition: Partition == paths[1], * == paths[2]
  // Checking for malformed request
  if (paths.size() == 2)
  {
    //Path includes table and partition but no row
    //Or table and row but no partition
    //Or partition and row but no table
    message.reply(status_codes::BadRequest);
    return;
  }
  // User has indicated they want all items in this partition by the `*`
  if (paths[2] == "*")
  {
    // Create Query
    table_query query {};
    table_query_iterator end;
    query.set_filter_string(azure::storage::table_query::generate_filter_condition("PartitionKey", azure::storage::query_comparison_operator::equal, paths[1]));
    // Execute Query
    table_query_iterator it = table.execute_query(query);
    // Parse into vector
    vector<value> key_vec;
    while (it != end) {
      cout << "Key: " << it->partition_key() << " / " << it->row_key() << endl;
      prop_vals_t keys {
  make_pair("Partition",value::string(it->partition_key())),
  make_pair("Row", value::string(it->row_key()))};
      keys = get_properties(it->properties(), keys);
      key_vec.push_back(value::object(keys));
      ++it;
    }
    // message reply
    message.reply(status_codes::OK, value::array(key_vec));
    return;
  }

  // GET specific entry: Partition == paths[1], Row == paths[2]
  table_operation retrieve_operation {table_operation::retrieve_entity(paths[1], paths[2])};
  table_result retrieve_result {table.execute(retrieve_operation)};
  cout << "HTTP code: " << retrieve_result.http_status_code() << endl;
  if (retrieve_result.http_status_code() == status_codes::NotFound) {
    message.reply(status_codes::NotFound);
    return;
  }

  table_entity entity {retrieve_result.entity()};
  table_entity::properties_type properties {entity.properties()};

  // If the entity has any properties, return them as JSON
  prop_vals_t values (get_properties(properties));
  if (values.size() > 0)
    message.reply(status_codes::OK, value::object(values));
  else
    message.reply(status_codes::OK);
}

/*
  Top-level routine for processing all HTTP POST requests.
 */
void handle_post(http_request message) {
  string path {uri::decode(message.relative_uri().path())};
  cout << endl << "**** POST " << path << endl;
  auto paths = uri::split_path(path);
  // Need at least an operation and a table name
  if (paths.size() < 2) {
    message.reply(status_codes::BadRequest);
    return;
  }

  string table_name {paths[1]};
  cloud_table table {table_cache.lookup_table(table_name)};

  // Create table (idempotent if table exists)
  if (paths[0] == create_table) {
    cout << "Create " << table_name << endl;
    bool created {table.create_if_not_exists()};
    cout << "Administrative table URI " << table.uri().primary_uri().to_string() << endl;
    if (created)
      message.reply(status_codes::Created);
    else
      message.reply(status_codes::Accepted);
  }
  else {
    message.reply(status_codes::BadRequest);
  }
}

/*
  Top-level routine for processing all HTTP PUT requests.
 */
void handle_put(http_request message) {
  string path {uri::decode(message.relative_uri().path())};
  cout << endl << "**** PUT " << path << endl;
  auto paths = uri::split_path(path);
  // Need at least an operation, table name, partition, and row
  if (paths.size() < 4) {
    message.reply(status_codes::BadRequest);
    return;
  }

  cloud_table table {table_cache.lookup_table(paths[1])};
  if ( ! table.exists()) {
    message.reply(status_codes::NotFound);
    return;
  }

  table_entity entity {paths[2], paths[3]};

  // Update entity
  if (paths[0] == update_entity) {
    cout << "Update " << entity.partition_key() << " / " << entity.row_key() << endl;
    table_entity::properties_type& properties = entity.properties();
    for (const auto v : get_json_body(message)) {
      properties[v.first] = entity_property {v.second};
    }

    table_operation operation {table_operation::insert_or_merge_entity(entity)};
    table_result op_result {table.execute(operation)};

    message.reply(status_codes::OK);
  }
  else {
    message.reply(status_codes::BadRequest);
  }
}

/*
  Top-level routine for processing all HTTP DELETE requests.
 */
void handle_delete(http_request message) {
  string path {uri::decode(message.relative_uri().path())};
  cout << endl << "**** DELETE " << path << endl;
  auto paths = uri::split_path(path);
  // Need at least an operation and table name
  if (paths.size() < 2) {
	message.reply(status_codes::BadRequest);
	return;
  }

  string table_name {paths[1]};
  cloud_table table {table_cache.lookup_table(table_name)};

  // Delete table
  if (paths[0] == delete_table) {
    cout << "Delete " << table_name << endl;
    if ( ! table.exists()) {
      message.reply(status_codes::NotFound);
    }
    table.delete_table();
    table_cache.delete_entry(table_name);
    message.reply(status_codes::OK);
  }
  // Delete entity
  else if (paths[0] == delete_entity) {
    // For delete entity, also need partition and row
    if (paths.size() < 4) {
	message.reply(status_codes::BadRequest);
	return;
    }
    table_entity entity {paths[2], paths[3]};
    cout << "Delete " << entity.partition_key() << " / " << entity.row_key()<< endl;

    table_operation operation {table_operation::delete_entity(entity)};
    table_result op_result {table.execute(operation)};

    int code {op_result.http_status_code()};
    if (code == status_codes::OK ||
	code == status_codes::NoContent)
      message.reply(status_codes::OK);
    else
      message.reply(code);
  }
  else {
    message.reply(status_codes::BadRequest);
  }
}

/*
  Main server routine

  Install handlers for the HTTP requests and open the listener,
  which processes each request asynchronously.

  Wait for a carriage return, then shut the server down.
 */
int main (int argc, char const * argv[]) {
  http_listener listener {def_url};
  listener.support(methods::GET, &handle_get);
  listener.support(methods::POST, &handle_post);
  listener.support(methods::PUT, &handle_put);
  listener.support(methods::DEL, &handle_delete);
  listener.open().wait(); // Wait for listener to complete starting

  cout << "Enter carriage return to stop server." << endl;
  string line;
  getline(std::cin, line);

  // Shut it down
  listener.close().wait();
  cout << "Closed" << endl;
}
<commit_msg>add check for GET partition size to be only three entries<commit_after>/*
 Basic Server code for CMPT 276, Spring 2016.
 */

#include <exception>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#include <cpprest/base_uri.h>
#include <cpprest/http_listener.h>
#include <cpprest/json.h>

#include <pplx/pplxtasks.h>

#include <was/common.h>
#include <was/storage_account.h>
#include <was/table.h>

#include "TableCache.h"
#include "config.h"
#include "make_unique.h"

#include "azure_keys.h"

using azure::storage::cloud_storage_account;
using azure::storage::storage_credentials;
using azure::storage::storage_exception;
using azure::storage::cloud_table;
using azure::storage::cloud_table_client;
using azure::storage::edm_type;
using azure::storage::entity_property;
using azure::storage::table_entity;
using azure::storage::table_operation;
using azure::storage::table_query;
using azure::storage::table_query_iterator;
using azure::storage::table_result;

using pplx::extensibility::critical_section_t;
using pplx::extensibility::scoped_critical_section_t;

using std::cin;
using std::cout;
using std::endl;
using std::getline;
using std::make_pair;
using std::pair;
using std::string;
using std::unordered_map;
using std::vector;

using web::http::http_headers;
using web::http::http_request;
using web::http::methods;
using web::http::status_codes;
using web::http::uri;

using web::json::value;

using web::http::experimental::listener::http_listener;

using prop_vals_t = vector<pair<string,value>>;

constexpr const char* def_url = "http://localhost:34568";

const string create_table {"CreateTable"};
const string delete_table {"DeleteTable"};
const string update_entity {"UpdateEntity"};
const string delete_entity {"DeleteEntity"};

/*
  Cache of opened tables
 */
TableCache table_cache {storage_connection_string};

/*
  Convert properties represented in Azure Storage type
  to prop_vals_t type.
 */
prop_vals_t get_properties (const table_entity::properties_type& properties, prop_vals_t values = prop_vals_t {}) {
  for (const auto v : properties) {
    if (v.second.property_type() == edm_type::string) {
      values.push_back(make_pair(v.first, value::string(v.second.string_value())));
    }
    else if (v.second.property_type() == edm_type::datetime) {
      values.push_back(make_pair(v.first, value::string(v.second.str())));
    }
    else if(v.second.property_type() == edm_type::int32) {
      values.push_back(make_pair(v.first, value::number(v.second.int32_value())));
    }
    else if(v.second.property_type() == edm_type::int64) {
      values.push_back(make_pair(v.first, value::number(v.second.int64_value())));
    }
    else if(v.second.property_type() == edm_type::double_floating_point) {
      values.push_back(make_pair(v.first, value::number(v.second.double_value())));
    }
    else if(v.second.property_type() == edm_type::boolean) {
      values.push_back(make_pair(v.first, value::boolean(v.second.boolean_value())));
    }
    else {
      values.push_back(make_pair(v.first, value::string(v.second.str())));
    }
  }
  return values;
}

/*
  Given an HTTP message with a JSON body, return the JSON
  body as an unordered map of strings to strings.

  Note that all types of JSON values are returned as strings.
  Use C++ conversion utilities to convert to numbers or dates
  as necessary.
 */
unordered_map<string,string> get_json_body(http_request message) {
  unordered_map<string,string> results {};
  const http_headers& headers {message.headers()};
  auto content_type (headers.find("Content-Type"));
  if (content_type == headers.end() ||
      content_type->second != "application/json")
    return results;

  value json{};
  message.extract_json(true)
    .then([&json](value v) -> bool
	  {
            json = v;
	    return true;
	  })
    .wait();

  if (json.is_object()) {
    for (const auto& v : json.as_object()) {
      if (v.second.is_string()) {
	results[v.first] = v.second.as_string();
      }
      else {
	results[v.first] = v.second.serialize();
      }
    }
  }
  return results;
}

/*
  Top-level routine for processing all HTTP GET requests.

  GET is the only request that has no command. All
  operands specify the value(s) to be retrieved.
 */
void handle_get(http_request message) {
  string path {uri::decode(message.relative_uri().path())};
  cout << endl << "**** GET " << path << endl;
  auto paths = uri::split_path(path);
  // Need at least a table name
  if (paths.size() < 1) {
    message.reply(status_codes::BadRequest);
    return;
  }

  cloud_table table {table_cache.lookup_table(paths[0])};
  if ( ! table.exists()) {
    message.reply(status_codes::NotFound);
    return;
  }

  // GET all entries in table
  if (paths.size() == 1) {
    table_query query {};
    table_query_iterator end;
    table_query_iterator it = table.execute_query(query);
    vector<value> key_vec;
    while (it != end) {
      cout << "Key: " << it->partition_key() << " / " << it->row_key() << endl;
      prop_vals_t keys {
	make_pair("Partition",value::string(it->partition_key())),
	make_pair("Row", value::string(it->row_key()))};
      keys = get_properties(it->properties(), keys);
      key_vec.push_back(value::object(keys));
      ++it;
    }
    message.reply(status_codes::OK, value::array(key_vec));
    return;
  }

  // GET all entities from a specific partition: Partition == paths[1], * == paths[2]
  // Checking for malformed request
  if (paths.size() == 2)
  {
    //Path includes table and partition but no row
    //Or table and row but no partition
    //Or partition and row but no table
    message.reply(status_codes::BadRequest);
    return;
  }
  // User has indicated they want all items in this partition by the `*`
  if (paths[2] == "*" && paths.size() == 3)
  {
    // Create Query
    table_query query {};
    table_query_iterator end;
    query.set_filter_string(azure::storage::table_query::generate_filter_condition("PartitionKey", azure::storage::query_comparison_operator::equal, paths[1]));
    // Execute Query
    table_query_iterator it = table.execute_query(query);
    // Parse into vector
    vector<value> key_vec;
    while (it != end) {
      cout << "Key: " << it->partition_key() << " / " << it->row_key() << endl;
      prop_vals_t keys {
  make_pair("Partition",value::string(it->partition_key())),
  make_pair("Row", value::string(it->row_key()))};
      keys = get_properties(it->properties(), keys);
      key_vec.push_back(value::object(keys));
      ++it;
    }
    // message reply
    message.reply(status_codes::OK, value::array(key_vec));
    return;
  }

  // GET specific entry: Partition == paths[1], Row == paths[2]
  table_operation retrieve_operation {table_operation::retrieve_entity(paths[1], paths[2])};
  table_result retrieve_result {table.execute(retrieve_operation)};
  cout << "HTTP code: " << retrieve_result.http_status_code() << endl;
  if (retrieve_result.http_status_code() == status_codes::NotFound) {
    message.reply(status_codes::NotFound);
    return;
  }

  table_entity entity {retrieve_result.entity()};
  table_entity::properties_type properties {entity.properties()};

  // If the entity has any properties, return them as JSON
  prop_vals_t values (get_properties(properties));
  if (values.size() > 0)
    message.reply(status_codes::OK, value::object(values));
  else
    message.reply(status_codes::OK);
}

/*
  Top-level routine for processing all HTTP POST requests.
 */
void handle_post(http_request message) {
  string path {uri::decode(message.relative_uri().path())};
  cout << endl << "**** POST " << path << endl;
  auto paths = uri::split_path(path);
  // Need at least an operation and a table name
  if (paths.size() < 2) {
    message.reply(status_codes::BadRequest);
    return;
  }

  string table_name {paths[1]};
  cloud_table table {table_cache.lookup_table(table_name)};

  // Create table (idempotent if table exists)
  if (paths[0] == create_table) {
    cout << "Create " << table_name << endl;
    bool created {table.create_if_not_exists()};
    cout << "Administrative table URI " << table.uri().primary_uri().to_string() << endl;
    if (created)
      message.reply(status_codes::Created);
    else
      message.reply(status_codes::Accepted);
  }
  else {
    message.reply(status_codes::BadRequest);
  }
}

/*
  Top-level routine for processing all HTTP PUT requests.
 */
void handle_put(http_request message) {
  string path {uri::decode(message.relative_uri().path())};
  cout << endl << "**** PUT " << path << endl;
  auto paths = uri::split_path(path);
  // Need at least an operation, table name, partition, and row
  if (paths.size() < 4) {
    message.reply(status_codes::BadRequest);
    return;
  }

  cloud_table table {table_cache.lookup_table(paths[1])};
  if ( ! table.exists()) {
    message.reply(status_codes::NotFound);
    return;
  }

  table_entity entity {paths[2], paths[3]};

  // Update entity
  if (paths[0] == update_entity) {
    cout << "Update " << entity.partition_key() << " / " << entity.row_key() << endl;
    table_entity::properties_type& properties = entity.properties();
    for (const auto v : get_json_body(message)) {
      properties[v.first] = entity_property {v.second};
    }

    table_operation operation {table_operation::insert_or_merge_entity(entity)};
    table_result op_result {table.execute(operation)};

    message.reply(status_codes::OK);
  }
  else {
    message.reply(status_codes::BadRequest);
  }
}

/*
  Top-level routine for processing all HTTP DELETE requests.
 */
void handle_delete(http_request message) {
  string path {uri::decode(message.relative_uri().path())};
  cout << endl << "**** DELETE " << path << endl;
  auto paths = uri::split_path(path);
  // Need at least an operation and table name
  if (paths.size() < 2) {
	message.reply(status_codes::BadRequest);
	return;
  }

  string table_name {paths[1]};
  cloud_table table {table_cache.lookup_table(table_name)};

  // Delete table
  if (paths[0] == delete_table) {
    cout << "Delete " << table_name << endl;
    if ( ! table.exists()) {
      message.reply(status_codes::NotFound);
    }
    table.delete_table();
    table_cache.delete_entry(table_name);
    message.reply(status_codes::OK);
  }
  // Delete entity
  else if (paths[0] == delete_entity) {
    // For delete entity, also need partition and row
    if (paths.size() < 4) {
	message.reply(status_codes::BadRequest);
	return;
    }
    table_entity entity {paths[2], paths[3]};
    cout << "Delete " << entity.partition_key() << " / " << entity.row_key()<< endl;

    table_operation operation {table_operation::delete_entity(entity)};
    table_result op_result {table.execute(operation)};

    int code {op_result.http_status_code()};
    if (code == status_codes::OK ||
	code == status_codes::NoContent)
      message.reply(status_codes::OK);
    else
      message.reply(code);
  }
  else {
    message.reply(status_codes::BadRequest);
  }
}

/*
  Main server routine

  Install handlers for the HTTP requests and open the listener,
  which processes each request asynchronously.

  Wait for a carriage return, then shut the server down.
 */
int main (int argc, char const * argv[]) {
  http_listener listener {def_url};
  listener.support(methods::GET, &handle_get);
  listener.support(methods::POST, &handle_post);
  listener.support(methods::PUT, &handle_put);
  listener.support(methods::DEL, &handle_delete);
  listener.open().wait(); // Wait for listener to complete starting

  cout << "Enter carriage return to stop server." << endl;
  string line;
  getline(std::cin, line);

  // Shut it down
  listener.close().wait();
  cout << "Closed" << endl;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#include "log.hh"
#include <cxxabi.h>
#include <system_error>
#include <boost/range/adaptor/map.hpp>
#include <map>

namespace logging {

registry& logger_registry();

const std::map<log_level, sstring> log_level_names = {
        { log_level::trace, "trace" },
        { log_level::debug, "debug" },
        { log_level::info, "info" },
        { log_level::warn, "warn" },
        { log_level::error, "error" },
};

std::ostream& operator<<(std::ostream& out, log_level level) {
    return out << log_level_names.at(level);
}

std::istream& operator>>(std::istream& in, log_level& level) {
    sstring s;
    in >> s;
    if (!in) {
        return in;
    }
    for (auto&& x : log_level_names) {
        if (s == x.second) {
            level = x.first;
            return in;
        }
    }
    in.setstate(std::ios::failbit);
    return in;
}

logger::logger(sstring name) : _name(std::move(name)) {
    logger_registry().register_logger(this);
}

logger::logger(logger&& x) : _name(std::move(x._name)), _level(x._level.load(std::memory_order_relaxed)) {
    logger_registry().moved(&x, this);
}

logger::~logger() {
    logger_registry().unregister_logger(this);
}

void
logger::really_do_log(log_level level, const char* fmt, stringer** s, size_t n) {
    const char* p = fmt;
    while (*p != '\0') {
        if (*p == '{' && *(p+1) == '}') {
            p += 2;
            if (n > 0) {
                (*s++)->append(std::cout);
                --n;
            } else {
                std::cout << "???";
            }
        } else {
            std::cout << *p++;
        }
    }
    std::cout << "\n";
}

void
registry::set_all_loggers_level(log_level level) {
    std::lock_guard<std::mutex> g(_mutex);
    for (auto&& l : _loggers | boost::adaptors::map_values) {
        l->set_level(level);
    }
}

log_level
registry::get_logger_level(sstring name) const {
    std::lock_guard<std::mutex> g(_mutex);
    return _loggers.at(name)->level();
}

void
registry::set_logger_level(sstring name, log_level level) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers.at(name)->set_level(level);
}

std::vector<sstring>
registry::get_all_logger_names() {
    std::lock_guard<std::mutex> g(_mutex);
    auto ret = _loggers | boost::adaptors::map_keys;
    return std::vector<sstring>(ret.begin(), ret.end());
}

void
registry::register_logger(logger* l) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers[l->name()] = l;
}

void
registry::unregister_logger(logger* l) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers.erase(l->name());
}

void
registry::moved(logger* from, logger* to) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers[from->name()] = to;
}

sstring pretty_type_name(const std::type_info& ti) {
    int status;
    std::unique_ptr<char[], void (*)(void*)> result(
            abi::__cxa_demangle(ti.name(), 0, 0, &status), std::free);
    return result.get() ? result.get() : ti.name();
}

registry& logger_registry() {
    static registry g_registry;
    return g_registry;
}

}

std::ostream& operator<<(std::ostream&out, std::exception_ptr eptr) {
    if (!eptr) {
        out << "<no exception>";
        return out;
    }
    try {
        std::rethrow_exception(eptr);
    } catch(...) {
        auto tp = abi::__cxa_current_exception_type();
        if (tp) {
            out << logging::pretty_type_name(*tp);
        } else {
            // This case shouldn't happen...
            out << "<unknown exception>";
        }
        // Print more information on some familiar exception types
        try {
            throw;
        } catch(const std::system_error &e) {
            out << " (error " << e.code() << ", " << e.code().message() << ")";
        } catch(const std::exception& e) {
            out << " (" << e.what() << ")";
        }
    }
    return out;
}
<commit_msg>fix iostream<commit_after>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#include "log.hh"
#include <cxxabi.h>
#include <system_error>
#include <boost/range/adaptor/map.hpp>
#include <map>

namespace logging {

registry& logger_registry();

const std::map<log_level, sstring> log_level_names = {
        { log_level::trace, "trace" },
        { log_level::debug, "debug" },
        { log_level::info, "info" },
        { log_level::warn, "warn" },
        { log_level::error, "error" },
};

}

using namespace logging;

std::ostream& operator<<(std::ostream& out, log_level level) {
    return out << log_level_names.at(level);
}

std::istream& operator>>(std::istream& in, log_level& level) {
    sstring s;
    in >> s;
    if (!in) {
        return in;
    }
    for (auto&& x : log_level_names) {
        if (s == x.second) {
            level = x.first;
            return in;
        }
    }
    in.setstate(std::ios::failbit);
    return in;
}

namespace logging {

logger::logger(sstring name) : _name(std::move(name)) {
    logger_registry().register_logger(this);
}

logger::logger(logger&& x) : _name(std::move(x._name)), _level(x._level.load(std::memory_order_relaxed)) {
    logger_registry().moved(&x, this);
}

logger::~logger() {
    logger_registry().unregister_logger(this);
}

void
logger::really_do_log(log_level level, const char* fmt, stringer** s, size_t n) {
    const char* p = fmt;
    while (*p != '\0') {
        if (*p == '{' && *(p+1) == '}') {
            p += 2;
            if (n > 0) {
                (*s++)->append(std::cout);
                --n;
            } else {
                std::cout << "???";
            }
        } else {
            std::cout << *p++;
        }
    }
    std::cout << "\n";
}

void
registry::set_all_loggers_level(log_level level) {
    std::lock_guard<std::mutex> g(_mutex);
    for (auto&& l : _loggers | boost::adaptors::map_values) {
        l->set_level(level);
    }
}

log_level
registry::get_logger_level(sstring name) const {
    std::lock_guard<std::mutex> g(_mutex);
    return _loggers.at(name)->level();
}

void
registry::set_logger_level(sstring name, log_level level) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers.at(name)->set_level(level);
}

std::vector<sstring>
registry::get_all_logger_names() {
    std::lock_guard<std::mutex> g(_mutex);
    auto ret = _loggers | boost::adaptors::map_keys;
    return std::vector<sstring>(ret.begin(), ret.end());
}

void
registry::register_logger(logger* l) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers[l->name()] = l;
}

void
registry::unregister_logger(logger* l) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers.erase(l->name());
}

void
registry::moved(logger* from, logger* to) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers[from->name()] = to;
}

sstring pretty_type_name(const std::type_info& ti) {
    int status;
    std::unique_ptr<char[], void (*)(void*)> result(
            abi::__cxa_demangle(ti.name(), 0, 0, &status), std::free);
    return result.get() ? result.get() : ti.name();
}

registry& logger_registry() {
    static registry g_registry;
    return g_registry;
}

}

std::ostream& operator<<(std::ostream&out, std::exception_ptr eptr) {
    if (!eptr) {
        out << "<no exception>";
        return out;
    }
    try {
        std::rethrow_exception(eptr);
    } catch(...) {
        auto tp = abi::__cxa_current_exception_type();
        if (tp) {
            out << logging::pretty_type_name(*tp);
        } else {
            // This case shouldn't happen...
            out << "<unknown exception>";
        }
        // Print more information on some familiar exception types
        try {
            throw;
        } catch(const std::system_error &e) {
            out << " (error " << e.code() << ", " << e.code().message() << ")";
        } catch(const std::exception& e) {
            out << " (" << e.what() << ")";
        }
    }
    return out;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#include "log.hh"
#include "core/array_map.hh"
#include <cxxabi.h>
#include <system_error>
#include <boost/range/adaptor/map.hpp>
#include <map>
#include <syslog.h>

namespace logging {

registry& logger_registry();

const std::map<log_level, sstring> log_level_names = {
        { log_level::trace, "trace" },
        { log_level::debug, "debug" },
        { log_level::info, "info" },
        { log_level::warn, "warn" },
        { log_level::error, "error" },
};

}

using namespace logging;

std::ostream& operator<<(std::ostream& out, log_level level) {
    return out << log_level_names.at(level);
}

std::istream& operator>>(std::istream& in, log_level& level) {
    sstring s;
    in >> s;
    if (!in) {
        return in;
    }
    for (auto&& x : log_level_names) {
        if (s == x.second) {
            level = x.first;
            return in;
        }
    }
    in.setstate(std::ios::failbit);
    return in;
}

namespace logging {

std::atomic<bool> logger::_stdout = { true };
std::atomic<bool> logger::_syslog = { false };

logger::logger(sstring name) : _name(std::move(name)) {
    logger_registry().register_logger(this);
}

logger::logger(logger&& x) : _name(std::move(x._name)), _level(x._level.load(std::memory_order_relaxed)) {
    logger_registry().moved(&x, this);
}

logger::~logger() {
    logger_registry().unregister_logger(this);
}

void
logger::really_do_log(log_level level, const char* fmt, stringer** s, size_t n) {
    std::ostringstream out;
    const char* p = fmt;
    while (*p != '\0') {
        if (*p == '{' && *(p+1) == '}') {
            p += 2;
            if (n > 0) {
                (*s++)->append(out);
                --n;
            } else {
                out << "???";
            }
        } else {
            out << *p++;
        }
    }
    out << "\n";
    auto msg = out.str();
    if (_stdout.load(std::memory_order_relaxed)) {
        std::cout << out;
    }
    if (_syslog.load(std::memory_order_relaxed)) {
        static array_map<int, 20> level_map = {
                { int(log_level::debug), LOG_DEBUG },
                { int(log_level::info), LOG_INFO },
                { int(log_level::trace), LOG_DEBUG },  // no LOG_TRACE
                { int(log_level::warn), LOG_WARNING },
                { int(log_level::error), LOG_ERR },
        };
        // NOTE: syslog() can block, which will stall the reactor thread.
        //       this should be rare (will have to fill the pipe buffer
        //       before syslogd can clear it) but can happen.  If it does,
        //       we'll have to implement some internal buffering (which
        //       still means the problem can happen, just less frequently).
        // syslog() interprets % characters, so send msg as a parameter
        syslog(level_map[int(level)], "%s", msg.c_str());
    }
}

void
logger::set_stdout_enabled(bool enabled) {
    _stdout.store(enabled, std::memory_order_relaxed);
}

void
logger::set_syslog_enabled(bool enabled) {
    _syslog.store(enabled, std::memory_order_relaxed);
}

void
registry::set_all_loggers_level(log_level level) {
    std::lock_guard<std::mutex> g(_mutex);
    for (auto&& l : _loggers | boost::adaptors::map_values) {
        l->set_level(level);
    }
}

log_level
registry::get_logger_level(sstring name) const {
    std::lock_guard<std::mutex> g(_mutex);
    return _loggers.at(name)->level();
}

void
registry::set_logger_level(sstring name, log_level level) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers.at(name)->set_level(level);
}

std::vector<sstring>
registry::get_all_logger_names() {
    std::lock_guard<std::mutex> g(_mutex);
    auto ret = _loggers | boost::adaptors::map_keys;
    return std::vector<sstring>(ret.begin(), ret.end());
}

void
registry::register_logger(logger* l) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers[l->name()] = l;
}

void
registry::unregister_logger(logger* l) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers.erase(l->name());
}

void
registry::moved(logger* from, logger* to) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers[from->name()] = to;
}

sstring pretty_type_name(const std::type_info& ti) {
    int status;
    std::unique_ptr<char[], void (*)(void*)> result(
            abi::__cxa_demangle(ti.name(), 0, 0, &status), std::free);
    return result.get() ? result.get() : ti.name();
}

registry& logger_registry() {
    static registry g_registry;
    return g_registry;
}

}

namespace boost {

template <>
logging::log_level lexical_cast(const std::string& source) {
    std::istringstream in(source);
    logging::log_level level;
    // Using the operator normall fails.
    if (!::operator>>(in, level)) {
        throw boost::bad_lexical_cast();
    }
    return level;
}

}

std::ostream& operator<<(std::ostream&out, std::exception_ptr eptr) {
    if (!eptr) {
        out << "<no exception>";
        return out;
    }
    try {
        std::rethrow_exception(eptr);
    } catch(...) {
        auto tp = abi::__cxa_current_exception_type();
        if (tp) {
            out << logging::pretty_type_name(*tp);
        } else {
            // This case shouldn't happen...
            out << "<unknown exception>";
        }
        // Print more information on some familiar exception types
        try {
            throw;
        } catch(const std::system_error &e) {
            out << " (error " << e.code() << ", " << e.code().message() << ")";
        } catch(const std::exception& e) {
            out << " (" << e.what() << ")";
        }
    }
    return out;
}
<commit_msg>logger: Fix compile error<commit_after>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#include "log.hh"
#include "core/array_map.hh"
#include <cxxabi.h>
#include <system_error>
#include <boost/range/adaptor/map.hpp>
#include <map>
#include <syslog.h>

namespace logging {

registry& logger_registry();

const std::map<log_level, sstring> log_level_names = {
        { log_level::trace, "trace" },
        { log_level::debug, "debug" },
        { log_level::info, "info" },
        { log_level::warn, "warn" },
        { log_level::error, "error" },
};

}

using namespace logging;

std::ostream& operator<<(std::ostream& out, log_level level) {
    return out << log_level_names.at(level);
}

std::istream& operator>>(std::istream& in, log_level& level) {
    sstring s;
    in >> s;
    if (!in) {
        return in;
    }
    for (auto&& x : log_level_names) {
        if (s == x.second) {
            level = x.first;
            return in;
        }
    }
    in.setstate(std::ios::failbit);
    return in;
}

namespace logging {

std::atomic<bool> logger::_stdout = { true };
std::atomic<bool> logger::_syslog = { false };

logger::logger(sstring name) : _name(std::move(name)) {
    logger_registry().register_logger(this);
}

logger::logger(logger&& x) : _name(std::move(x._name)), _level(x._level.load(std::memory_order_relaxed)) {
    logger_registry().moved(&x, this);
}

logger::~logger() {
    logger_registry().unregister_logger(this);
}

void
logger::really_do_log(log_level level, const char* fmt, stringer** s, size_t n) {
    std::ostringstream out;
    const char* p = fmt;
    while (*p != '\0') {
        if (*p == '{' && *(p+1) == '}') {
            p += 2;
            if (n > 0) {
                (*s++)->append(out);
                --n;
            } else {
                out << "???";
            }
        } else {
            out << *p++;
        }
    }
    out << "\n";
    auto msg = out.str();
    if (_stdout.load(std::memory_order_relaxed)) {
        std::cout << msg;
    }
    if (_syslog.load(std::memory_order_relaxed)) {
        static array_map<int, 20> level_map = {
                { int(log_level::debug), LOG_DEBUG },
                { int(log_level::info), LOG_INFO },
                { int(log_level::trace), LOG_DEBUG },  // no LOG_TRACE
                { int(log_level::warn), LOG_WARNING },
                { int(log_level::error), LOG_ERR },
        };
        // NOTE: syslog() can block, which will stall the reactor thread.
        //       this should be rare (will have to fill the pipe buffer
        //       before syslogd can clear it) but can happen.  If it does,
        //       we'll have to implement some internal buffering (which
        //       still means the problem can happen, just less frequently).
        // syslog() interprets % characters, so send msg as a parameter
        syslog(level_map[int(level)], "%s", msg.c_str());
    }
}

void
logger::set_stdout_enabled(bool enabled) {
    _stdout.store(enabled, std::memory_order_relaxed);
}

void
logger::set_syslog_enabled(bool enabled) {
    _syslog.store(enabled, std::memory_order_relaxed);
}

void
registry::set_all_loggers_level(log_level level) {
    std::lock_guard<std::mutex> g(_mutex);
    for (auto&& l : _loggers | boost::adaptors::map_values) {
        l->set_level(level);
    }
}

log_level
registry::get_logger_level(sstring name) const {
    std::lock_guard<std::mutex> g(_mutex);
    return _loggers.at(name)->level();
}

void
registry::set_logger_level(sstring name, log_level level) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers.at(name)->set_level(level);
}

std::vector<sstring>
registry::get_all_logger_names() {
    std::lock_guard<std::mutex> g(_mutex);
    auto ret = _loggers | boost::adaptors::map_keys;
    return std::vector<sstring>(ret.begin(), ret.end());
}

void
registry::register_logger(logger* l) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers[l->name()] = l;
}

void
registry::unregister_logger(logger* l) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers.erase(l->name());
}

void
registry::moved(logger* from, logger* to) {
    std::lock_guard<std::mutex> g(_mutex);
    _loggers[from->name()] = to;
}

sstring pretty_type_name(const std::type_info& ti) {
    int status;
    std::unique_ptr<char[], void (*)(void*)> result(
            abi::__cxa_demangle(ti.name(), 0, 0, &status), std::free);
    return result.get() ? result.get() : ti.name();
}

registry& logger_registry() {
    static registry g_registry;
    return g_registry;
}

}

namespace boost {

template <>
logging::log_level lexical_cast(const std::string& source) {
    std::istringstream in(source);
    logging::log_level level;
    // Using the operator normall fails.
    if (!::operator>>(in, level)) {
        throw boost::bad_lexical_cast();
    }
    return level;
}

}

std::ostream& operator<<(std::ostream&out, std::exception_ptr eptr) {
    if (!eptr) {
        out << "<no exception>";
        return out;
    }
    try {
        std::rethrow_exception(eptr);
    } catch(...) {
        auto tp = abi::__cxa_current_exception_type();
        if (tp) {
            out << logging::pretty_type_name(*tp);
        } else {
            // This case shouldn't happen...
            out << "<unknown exception>";
        }
        // Print more information on some familiar exception types
        try {
            throw;
        } catch(const std::system_error &e) {
            out << " (error " << e.code() << ", " << e.code().message() << ")";
        } catch(const std::exception& e) {
            out << " (" << e.what() << ")";
        }
    }
    return out;
}
<|endoftext|>


<commit_before>/*
 * esteid-browser-plugin - a browser plugin for Estonian EID card
 *
 * Copyright (C) 2010  Estonian Informatics Centre
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "CardService.h"
#include "converters.h"
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>

/* Singleton instance variable */
boost::weak_ptr<CardService> CardService::sCardService;

/**
 * Class constructor.
 * Right now we are making this a singleton.
 * It's generally a good idea to have only a single connection
 * to SmartCard manager service.
 * We might reconsider when times change :P
 */
CardService::CardService()
    : m_thread(boost::bind(&CardService::monitor, this))
{
}

CardService::~CardService()
{
    m_thread.interrupt();
    m_thread.join();
}

/**
 * Singleton "constructor". Will return an existing instance
 * or create a new one if none exists.
 */

boost::shared_ptr<CardService> CardService::getInstance()
{
    boost::shared_ptr<CardService> p = sCardService.lock();
    if (!p) {
        p.reset(new CardService());
        sCardService = p;
    }
    return p;
}

void CardService::findEstEid(vector<ReaderID>& readers)
{
    readers.clear();

    for (ReaderID i = 0; i < m_cache.size(); i++ ) {
        if (m_cache[i].cardPresent)
            readers.push_back(i);
    }
}

ReaderID CardService::findFirstEstEid()
{
    vector<ReaderID> readers;
    findEstEid(readers);

    // FIXME: Define a more sane exception to throw from here
    if (readers.size() <= 0)
        throw std::runtime_error("No cards found");
    else
        return readers[0];
}

/* Card monitor thread */
void CardService::monitor()
{
    while (!boost::this_thread::interruption_requested()) {
        boost::this_thread::sleep(boost::posix_time::milliseconds(500));
        try {
            poll();
        } catch(const std::runtime_error&) { }
    }
}

/*
 * SmartCardManager constructor throws if the pcsc daemon isn't running.
 * CardService class however needs to start polling even if the pcsc daemon
 * isn't currently running. As a workaround, all calls to SmartCardManager are
 * proxied through cardManager() which attempts to start SmartCardManager if
 * needed.
 */
ManagerInterface& CardService::cardManager()
{
    if (!m_manager)
        m_manager.reset(new SmartCardManager());

    return *m_manager;
}

void CardService::addObserver(MessageObserver *obs)
{
    boost::mutex::scoped_lock l(m_mutex); // TODO: Maybe use a different lock?

    m_observers.push_back(obs);
}

void CardService::removeObserver(MessageObserver *obs)
{
    boost::mutex::scoped_lock l(m_mutex);

    vector<MessageObserver *>::iterator i;
    for (i = m_observers.begin(); i != m_observers.end(); i++) {
        if (*i == obs) {
            m_observers.erase(i);
            break;
        }
    }
}

void CardService::postMessage(MsgType m, ReaderID r)
{
    vector<MessageObserver *>::iterator i;
    for (i = m_observers.begin(); i != m_observers.end(); i++)
        (*i)->onMessage(m, r);
}

void CardService::poll()
{
    size_t nReaders;

    {
        boost::mutex::scoped_lock l(m_mutex);
        nReaders = cardManager().getReaderCount();
    }

    /* See if the list of readers has been changed */
    if (m_cache.size() != nReaders) {
        /* We have no way of knowing which reader was
           removed, so it's safe to send card removed event to
           all readers and purge all cached data */
        for (unsigned int i = 0; i < m_cache.size(); i++ ) {
            if (m_cache[i].cardPresent) {
                m_cache[i].purge();
                postMessage(CARD_REMOVED, i);
            }
        }
        m_cache.resize(nReaders);
        postMessage(READERS_CHANGED, nReaders);
    }

    /* Check for card status changes */
    EstEidCard card(cardManager());
    for (unsigned int i = 0; i < m_cache.size(); i++ ) {
        bool inReader = readerHasCard(card, i);

        if (inReader && !m_cache[i].cardPresent) {
            m_cache[i].cardPresent = true;
            postMessage(CARD_INSERTED, i);
        } else if (!inReader && m_cache[i].cardPresent) {
            m_cache[i].purge();
            postMessage(CARD_REMOVED, i);
        }
    }
}

bool CardService::readerHasCard(EstEidCard& card, ReaderID i)
{
    boost::mutex::scoped_lock l(m_mutex);

    /* Ask manager if a token is inserted into that slot */
    std::string state = cardManager().getReaderState(i);
    if (state.find("PRESENT") == std::string::npos)
        return false;

    /* TODO: Investigate if this caching is actually needed */
    if (m_cache[i].cardPresent)
        return true;

    /* See if it's EstEID */
    return card.isInReader(i);
}

void CardService::readPersonalData(vector<std::string>& data)
{
    readPersonalData(data, findFirstEstEid());
}

void CardService::readPersonalData(vector<std::string>& data,
                                   ReaderID reader)
{
    /* Populate cache if needed */
    if (m_cache[reader].m_pData.size() <= 0) {
        boost::mutex::scoped_lock l(m_mutex);
        EstEidCard card(cardManager(), reader);
        card.readPersonalData(m_cache[reader].m_pData, PDATA_MIN, PDATA_MAX);
    }
    data = m_cache[reader].m_pData;
}

ByteVec CardService::getAuthCert()
{
    return getAuthCert(findFirstEstEid());
}

ByteVec CardService::getAuthCert(ReaderID reader)
{
    /* Populate cache if needed */
    if (m_cache[reader].m_authCert.size() <= 0) {
        boost::mutex::scoped_lock l(m_mutex);
        EstEidCard card(cardManager(), reader);
        m_cache[reader].m_authCert = card.getAuthCert();
    }
    return m_cache[reader].m_authCert;
}

ByteVec CardService::getSignCert()
{
    return getSignCert(findFirstEstEid());
}

ByteVec CardService::getSignCert(ReaderID reader)
{
    /* Populate cache if needed */
    if (m_cache[reader].m_signCert.size() <= 0) {
        boost::mutex::scoped_lock l(m_mutex);
        EstEidCard card(cardManager(), reader);
        m_cache[reader].m_signCert = card.getSignCert();
    }
    return m_cache[reader].m_signCert;
}

std::string CardService::signSHA1(const std::string& hash,
                                  EstEidCard::KeyType keyId,
                                  const std::string& pin)
{
    return signSHA1(hash, keyId, pin, findFirstEstEid());
}

std::string CardService::signSHA1(const std::string& hash,
                                  EstEidCard::KeyType keyId,
                                  const std::string& pin,
                                  ReaderID reader)
{
    ByteVec bhash = fromHex(hash);
    if (bhash.size() != 20) {
        throw std::runtime_error("Invalid SHA1 hash");
    }

    boost::mutex::scoped_lock l(m_mutex);
    EstEidCard card(cardManager(), reader);

    // FIXME: Ugly, ugly hack! This needs to be implemented correctly
    //        in order to protect PIN codes in program memory.
    return toHex(card.calcSignSHA1(bhash, keyId, PinString(pin.c_str())));
}

bool CardService::getRetryCounts(byte& puk, byte& pinAuth, byte& pinSign)
{
    return getRetryCounts(puk, pinAuth, pinSign, findFirstEstEid());
}

bool CardService::getRetryCounts(byte& puk, byte& pinAuth, byte& pinSign, ReaderID reader)
{
    boost::mutex::scoped_lock l(m_mutex);
    EstEidCard card(cardManager(), reader);
    return card.getRetryCounts(puk, pinAuth, pinSign);
}

bool CardService::hasSecurePinEntry()
{
    return hasSecurePinEntry(findFirstEstEid());
}

bool CardService::hasSecurePinEntry(ReaderID reader)
{
    boost::mutex::scoped_lock l(m_mutex);
    EstEidCard card(cardManager(), reader);
    return card.hasSecurePinEntry();
}
<commit_msg>esteid-browser-plugin: Protect card cache vector access with a mutex<commit_after>/*
 * esteid-browser-plugin - a browser plugin for Estonian EID card
 *
 * Copyright (C) 2010  Estonian Informatics Centre
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "CardService.h"
#include "converters.h"
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>

/* Singleton instance variable */
boost::weak_ptr<CardService> CardService::sCardService;

/**
 * Class constructor.
 * Right now we are making this a singleton.
 * It's generally a good idea to have only a single connection
 * to SmartCard manager service.
 * We might reconsider when times change :P
 */
CardService::CardService()
    : m_thread(boost::bind(&CardService::monitor, this))
{
}

CardService::~CardService()
{
    m_thread.interrupt();
    m_thread.join();
}

/**
 * Singleton "constructor". Will return an existing instance
 * or create a new one if none exists.
 */

boost::shared_ptr<CardService> CardService::getInstance()
{
    boost::shared_ptr<CardService> p = sCardService.lock();
    if (!p) {
        p.reset(new CardService());
        sCardService = p;
    }
    return p;
}

void CardService::findEstEid(vector<ReaderID>& readers)
{
    boost::mutex::scoped_lock l(m_mutex);

    readers.clear();
    for (ReaderID i = 0; i < m_cache.size(); i++ ) {
        if (m_cache[i].cardPresent)
            readers.push_back(i);
    }
}

ReaderID CardService::findFirstEstEid()
{
    vector<ReaderID> readers;
    findEstEid(readers);

    // FIXME: Define a more sane exception to throw from here
    if (readers.size() <= 0)
        throw std::runtime_error("No cards found");
    else
        return readers[0];
}

/* Card monitor thread */
void CardService::monitor()
{
    while (!boost::this_thread::interruption_requested()) {
        boost::this_thread::sleep(boost::posix_time::milliseconds(500));
        try {
            poll();
        } catch(const std::runtime_error&) { }
    }
}

/*
 * SmartCardManager constructor throws if the pcsc daemon isn't running.
 * CardService class however needs to start polling even if the pcsc daemon
 * isn't currently running. As a workaround, all calls to SmartCardManager are
 * proxied through cardManager() which attempts to start SmartCardManager if
 * needed.
 */
ManagerInterface& CardService::cardManager()
{
    if (!m_manager)
        m_manager.reset(new SmartCardManager());

    return *m_manager;
}

void CardService::addObserver(MessageObserver *obs)
{
    boost::mutex::scoped_lock l(m_mutex); // TODO: Maybe use a different lock?

    m_observers.push_back(obs);
}

void CardService::removeObserver(MessageObserver *obs)
{
    boost::mutex::scoped_lock l(m_mutex);

    vector<MessageObserver *>::iterator i;
    for (i = m_observers.begin(); i != m_observers.end(); i++) {
        if (*i == obs) {
            m_observers.erase(i);
            break;
        }
    }
}

void CardService::postMessage(MsgType m, ReaderID r)
{
    vector<MessageObserver *>::iterator i;
    for (i = m_observers.begin(); i != m_observers.end(); i++)
        (*i)->onMessage(m, r);
}

void CardService::poll()
{
    boost::mutex::scoped_lock l(m_mutex);

    /* See if the list of readers has been changed */
    size_t nReaders = cardManager().getReaderCount();
    if (m_cache.size() != nReaders) {
        /* We have no way of knowing which reader was
           removed, so it's safe to send card removed event to
           all readers and purge all cached data */
        for (unsigned int i = 0; i < m_cache.size(); i++ ) {
            if (m_cache[i].cardPresent) {
                m_cache[i].purge();
                postMessage(CARD_REMOVED, i);
            }
        }
        m_cache.resize(nReaders);
        postMessage(READERS_CHANGED, nReaders);
    }

    /* Check for card status changes */
    EstEidCard card(cardManager());
    for (unsigned int i = 0; i < m_cache.size(); i++ ) {
        bool inReader = readerHasCard(card, i);

        if (inReader && !m_cache[i].cardPresent) {
            m_cache[i].cardPresent = true;
            postMessage(CARD_INSERTED, i);
        } else if (!inReader && m_cache[i].cardPresent) {
            m_cache[i].purge();
            postMessage(CARD_REMOVED, i);
        }
    }
}

bool CardService::readerHasCard(EstEidCard& card, ReaderID i)
{
    /* Ask manager if a token is inserted into that slot */
    std::string state = cardManager().getReaderState(i);
    if (state.find("PRESENT") == std::string::npos)
        return false;

    /* TODO: Investigate if this caching is actually needed */
    if (m_cache[i].cardPresent)
        return true;

    /* See if it's EstEID */
    return card.isInReader(i);
}

void CardService::readPersonalData(vector<std::string>& data)
{
    readPersonalData(data, findFirstEstEid());
}

void CardService::readPersonalData(vector<std::string>& data,
                                   ReaderID reader)
{
    boost::mutex::scoped_lock l(m_mutex);

    /* Populate cache if needed */
    if (m_cache[reader].m_pData.size() <= 0) {
        EstEidCard card(cardManager(), reader);
        card.readPersonalData(m_cache[reader].m_pData, PDATA_MIN, PDATA_MAX);
    }
    data = m_cache[reader].m_pData;
}

ByteVec CardService::getAuthCert()
{
    return getAuthCert(findFirstEstEid());
}

ByteVec CardService::getAuthCert(ReaderID reader)
{
    boost::mutex::scoped_lock l(m_mutex);

    /* Populate cache if needed */
    if (m_cache[reader].m_authCert.size() <= 0) {
        EstEidCard card(cardManager(), reader);
        m_cache[reader].m_authCert = card.getAuthCert();
    }
    return m_cache[reader].m_authCert;
}

ByteVec CardService::getSignCert()
{
    return getSignCert(findFirstEstEid());
}

ByteVec CardService::getSignCert(ReaderID reader)
{
    boost::mutex::scoped_lock l(m_mutex);

    /* Populate cache if needed */
    if (m_cache[reader].m_signCert.size() <= 0) {
        EstEidCard card(cardManager(), reader);
        m_cache[reader].m_signCert = card.getSignCert();
    }
    return m_cache[reader].m_signCert;
}

std::string CardService::signSHA1(const std::string& hash,
                                  EstEidCard::KeyType keyId,
                                  const std::string& pin)
{
    return signSHA1(hash, keyId, pin, findFirstEstEid());
}

std::string CardService::signSHA1(const std::string& hash,
                                  EstEidCard::KeyType keyId,
                                  const std::string& pin,
                                  ReaderID reader)
{
    ByteVec bhash = fromHex(hash);
    if (bhash.size() != 20) {
        throw std::runtime_error("Invalid SHA1 hash");
    }

    boost::mutex::scoped_lock l(m_mutex);
    EstEidCard card(cardManager(), reader);

    // FIXME: Ugly, ugly hack! This needs to be implemented correctly
    //        in order to protect PIN codes in program memory.
    return toHex(card.calcSignSHA1(bhash, keyId, PinString(pin.c_str())));
}

bool CardService::getRetryCounts(byte& puk, byte& pinAuth, byte& pinSign)
{
    return getRetryCounts(puk, pinAuth, pinSign, findFirstEstEid());
}

bool CardService::getRetryCounts(byte& puk, byte& pinAuth, byte& pinSign, ReaderID reader)
{
    boost::mutex::scoped_lock l(m_mutex);
    EstEidCard card(cardManager(), reader);
    return card.getRetryCounts(puk, pinAuth, pinSign);
}

bool CardService::hasSecurePinEntry()
{
    return hasSecurePinEntry(findFirstEstEid());
}

bool CardService::hasSecurePinEntry(ReaderID reader)
{
    boost::mutex::scoped_lock l(m_mutex);
    EstEidCard card(cardManager(), reader);
    return card.hasSecurePinEntry();
}
<|endoftext|>


<commit_before>// ping_pong.cpp

#define N 100

#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cmath>
#include <mpi.h>
using namespace std;

	double cal_pi(int);

int main(int argc, char *argv[])
{

	MPI_Init(&argc, &argv);

	// get world size
	int world_size;
	MPI_Comm_size(MPI_COMM_WORLD, &world_size);
	// get my rank
	int world_rank;
	MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);

	int t1 = (double)MPI_Wtime();
	int i, j, t;

	double global_pi;
	double my_pi;

	// root-only array
	double *data = NULL;
	if (world_rank == 0) {
		data = new double[world_size];
	}

	/************************************************************/
	/*					 run core function                      */
	/************************************************************/

	my_pi = cal_pi(N);

	// collect results
	MPI_Gather(&my_pi, 1, MPI_DOUBLE,
		data, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

	int t2 = (double)MPI_Wtime();
	if (world_rank == 0) {
		// calculate mean
		for (int i = 0; i < world_size; i++) {
			global_pi += data[i];
		}
		global_pi = 4.0 * global_pi / world_size;
		// output result
		cout << "pi = " << global_pi << endl;
		cout << "Time elasped: " << t2 - t1 << endl;
	} // end of if

	MPI_Finalize();
	return 0;
}

double cal_pi(int N) {
	int count = 0;
	double x, y, r, result;
	for (int i = 0; i < N; i++) {
		x = 1.0 * rand(time(NULL)) / INT_MAX;
		y = 1.0 * rand(time(NULL)) / INT_MAX;
		r = sqrt( pow(x,2) + pow(y,2) );
		if (r < 1) {
			count++;
		}
	}
	result = 1.0 * count / N;
	return result;
}
<commit_msg>use cstdint<commit_after>// ping_pong.cpp

#define N 100

#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cmath>
#include <cstdint>
#include <mpi.h>
using namespace std;

	double cal_pi(int);

int main(int argc, char *argv[])
{

	MPI_Init(&argc, &argv);

	// get world size
	int world_size;
	MPI_Comm_size(MPI_COMM_WORLD, &world_size);
	// get my rank
	int world_rank;
	MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);

	int t1 = (double)MPI_Wtime();
	int i, j, t;

	double global_pi;
	double my_pi;

	// root-only array
	double *data = NULL;
	if (world_rank == 0) {
		data = new double[world_size];
	}

	/************************************************************/
	/*					 run core function                      */
	/************************************************************/

	my_pi = cal_pi(N);

	// collect results
	MPI_Gather(&my_pi, 1, MPI_DOUBLE,
		data, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

	int t2 = (double)MPI_Wtime();
	if (world_rank == 0) {
		// calculate mean
		for (int i = 0; i < world_size; i++) {
			global_pi += data[i];
		}
		global_pi = 4.0 * global_pi / world_size;
		// output result
		cout << "pi = " << global_pi << endl;
		cout << "Time elasped: " << t2 - t1 << endl;
	} // end of if

	MPI_Finalize();
	return 0;
}

double cal_pi(int M) {
	int count = 0;
	double x, y, r, result;
	for (int i = 0; i < M; i++) {
		x = 1.0 * rand(time(NULL)) / INT_MAX;
		y = 1.0 * rand(time(NULL)) / INT_MAX;
		r = sqrt( pow(x,2) + pow(y,2) );
		if (r < 1) {
			count++;
		}
	}
	result = 1.0 * count / M;
	return result;
}
<|endoftext|>


<commit_before>#pragma once
#include <iostream>
#include <cstdlib>
#include <cassert>

// A C++ circular buffer implementation designed for efficiency.
// No unneeded copies or initializations.
// Note: Nothing about this circular buffer is thread-safe
template <typename Element>
class Circular
{
  private:
    size_t   _capacity;
    size_t   _front;
    size_t   _back;
    size_t   _size;
    Element** _data;

  public:
    Circular(size_t capacity)
    {
        assert(capacity > 0);

        this->_capacity = capacity;
        this->_data = new Element*[this->_capacity];
        assert(this->_data);

        clear();
    }

    ~Circular()
    {
        delete[] this->_data;
    }

    size_t size() const
    {
        return this->_size;
    }

    bool isEmpty() const
    {
        return this->_size == 0;
    }

    bool isFull() const
    {
        return this->_size == this->_capacity;
    }

    void clear()
    {
        this->_size = 0;
        this->_front = 0;
        this->_back = 0;
    }

    bool pushBack(Element* elt)
    {
        if (isFull())
            return false;

        this->_data[this->_back] = elt;

        this->_size++;
        this->_back += 1;

        // Handle wrap around
        if (this->_back == this->_capacity) this->_back = 0;
        return true;
    }

    bool pushFront(Element* elt)
    {
        if (isFull())
            return false;

        this->_size++;
        if (this->_front != 0) this->_front -= 1;
        else this->_front = this->_capacity - 1;

        this->_data[this->_front] = elt;

        return true;
    }

    Element* front()
    {
        if (isEmpty())
            return nullptr;

        return this->_data[this->_front];
    }

    Element* back()
    {
        if (isEmpty())
            return nullptr;

        size_t temp;
        if (this->_back == 0) temp = this->_capacity - 1;
        else temp = this->_back - 1;

        return this->_data[temp];
    }

    void popFront()
    {
        assert(!isEmpty());

        this->_size--;
        this->_front += 1;

        // Handle wrap around
        if (this->_front == this->_capacity) this->_front = 0;
    }

    void popBack()
    {
        assert(!isEmpty());

        this->_size--;

        if (this->_back == 0) this->_back = this->_capacity - 1;
        else this->_back--;
    }

    void removeFront()
    {
        assert(!isEmpty());

        delete this->_data[this->_front];

        popFront();
    }

    void removeBack()
    {
        assert(!isEmpty());

        popBack();

        delete this->_data[this->_back];
    }

    Element* operator[](size_t i)
    {
        if (i >= size())
            return nullptr;

        int idx = i + this->_front;
        if (idx >= this->_capacity)
            idx -= this->_capacity;

        return this->_data[idx];
    }
};
<commit_msg>Signed integer fix<commit_after>#pragma once
#include <iostream>
#include <cstdlib>
#include <cassert>

// A C++ circular buffer implementation designed for efficiency.
// No unneeded copies or initializations.
// Note: Nothing about this circular buffer is thread-safe
template <typename Element>
class Circular
{
  private:
    size_t   _capacity;
    size_t   _front;
    size_t   _back;
    size_t   _size;
    Element** _data;

  public:
    Circular(size_t capacity)
    {
        assert(capacity > 0);

        this->_capacity = capacity;
        this->_data = new Element*[this->_capacity];
        assert(this->_data);

        clear();
    }

    ~Circular()
    {
        delete[] this->_data;
    }

    size_t size() const
    {
        return this->_size;
    }

    bool isEmpty() const
    {
        return this->_size == 0;
    }

    bool isFull() const
    {
        return this->_size == this->_capacity;
    }

    void clear()
    {
        this->_size = 0;
        this->_front = 0;
        this->_back = 0;
    }

    bool pushBack(Element* elt)
    {
        if (isFull())
            return false;

        this->_data[this->_back] = elt;

        this->_size++;
        this->_back += 1;

        // Handle wrap around
        if (this->_back == this->_capacity) this->_back = 0;
        return true;
    }

    bool pushFront(Element* elt)
    {
        if (isFull())
            return false;

        this->_size++;
        if (this->_front != 0) this->_front -= 1;
        else this->_front = this->_capacity - 1;

        this->_data[this->_front] = elt;

        return true;
    }

    Element* front()
    {
        if (isEmpty())
            return nullptr;

        return this->_data[this->_front];
    }

    Element* back()
    {
        if (isEmpty())
            return nullptr;

        size_t temp;
        if (this->_back == 0) temp = this->_capacity - 1;
        else temp = this->_back - 1;

        return this->_data[temp];
    }

    void popFront()
    {
        assert(!isEmpty());

        this->_size--;
        this->_front += 1;

        // Handle wrap around
        if (this->_front == this->_capacity) this->_front = 0;
    }

    void popBack()
    {
        assert(!isEmpty());

        this->_size--;

        if (this->_back == 0) this->_back = this->_capacity - 1;
        else this->_back--;
    }

    void removeFront()
    {
        assert(!isEmpty());

        delete this->_data[this->_front];

        popFront();
    }

    void removeBack()
    {
        assert(!isEmpty());

        popBack();

        delete this->_data[this->_back];
    }

    Element* operator[](size_t i)
    {
        if (i >= size())
            return nullptr;

        size_t idx = i + this->_front;
        if (idx >= this->_capacity)
            idx -= this->_capacity;

        return this->_data[idx];
    }
};
<|endoftext|>


<commit_before>#include "rooibus/codegen.hh"

using std::forward;
using std::make_shared;
using std::move;
using std::unique_ptr;

using llvm::Module;

namespace
{
  template<class T, class... Args>
  unique_ptr<T> make_unique(Args&&... args)
  {
    return unique_ptr<T>(new T(forward<Args>(args)...));
  }

}

namespace rooibus
{
  unique_ptr<ProgramAST>
  codegen(Module & module)
  {
    auto program = make_unique<ProgramAST>();
    auto iifeFunc = make_shared<FunctionExpressionAST>();
    iifeFunc->params.push_back(make_shared<IdentifierAST>("globals"));
    auto iifeCall = make_shared<CallExpressionAST>(iifeFunc);
    iifeCall->arguments.push_back(make_shared<IdentifierAST>("this"));
    auto iifeCallStmt = make_shared<ExpressionStatementAST>(iifeCall);
    program->body.push_back(iifeCallStmt);

    auto asmFunc = make_shared<FunctionExpressionAST>();
    asmFunc->params.push_back(make_shared<IdentifierAST>("stdlib"));
    asmFunc->params.push_back(make_shared<IdentifierAST>("ffi"));
    asmFunc->params.push_back(make_shared<IdentifierAST>("heap"));
    asmFunc->body.push_back(make_shared<ExpressionStatementAST>(
          make_shared<LiteralAST>("use asm")));
    asmFunc->body.push_back(make_shared<ReturnStatementAST>(
          make_shared<ObjectExpressionAST>()));
    iifeFunc->body.push_back(make_shared<VariableDeclarationAST>(
          make_shared<IdentifierAST>("ASM"), asmFunc));

    return program;
  }
}
<commit_msg>refactor codegen'd identifers<commit_after>#include "rooibus/codegen.hh"

using std::forward;
using std::make_shared;
using std::move;
using std::shared_ptr;
using std::unique_ptr;

using llvm::Module;

using rooibus::IdentifierAST;

namespace
{
  template<class T, class... Args>
  unique_ptr<T> make_unique(Args&&... args)
  {
    return unique_ptr<T>(new T(forward<Args>(args)...));
  }

  struct Identifiers
  {
    const shared_ptr<IdentifierAST>
      ASM = make_shared<IdentifierAST>("ASM"),
      asm_ = make_shared<IdentifierAST>("asm"),
      ffi = make_shared<IdentifierAST>("ffi"),
      globals = make_shared<IdentifierAST>("globals"),
      heap = make_shared<IdentifierAST>("heap"),
      stdlib = make_shared<IdentifierAST>("stdlib"),
      this_ = make_shared<IdentifierAST>("this");
  };

}

namespace rooibus
{
  unique_ptr<ProgramAST>
  codegen(Module & module)
  {
    Identifiers idents;

    auto program = make_unique<ProgramAST>();
    auto iifeFunc = make_shared<FunctionExpressionAST>();
    iifeFunc->params.push_back(idents.globals);
    auto iifeCall = make_shared<CallExpressionAST>(iifeFunc);
    iifeCall->arguments.push_back(idents.this_);
    auto iifeCallStmt = make_shared<ExpressionStatementAST>(iifeCall);
    program->body.push_back(iifeCallStmt);

    auto asmFunc = make_shared<FunctionExpressionAST>();
    asmFunc->params.push_back(idents.stdlib);
    asmFunc->params.push_back(idents.ffi);
    asmFunc->params.push_back(idents.heap);
    asmFunc->body.push_back(make_shared<ExpressionStatementAST>(
          make_shared<LiteralAST>("use asm")));
    asmFunc->body.push_back(make_shared<ReturnStatementAST>(
          make_shared<ObjectExpressionAST>()));
    iifeFunc->body.push_back(make_shared<VariableDeclarationAST>(
         idents.ASM, asmFunc));

    auto asmCall = make_shared<CallExpressionAST>(idents.ASM);
    iifeFunc->body.push_back(make_shared<ExpressionStatementAST>(asmCall));

    return program;
  }
}
<|endoftext|>