<commit_before>/******************************************************************************

  This source file is part of the TEM tomography project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

******************************************************************************/
#include "DataSource.h"

#include "OperatorPython.h"
#include "Utilities.h"
#include "vtkDataObject.h"
#include "vtkNew.h"
#include "vtkSmartPointer.h"
#include "vtkSMCoreUtilities.h"
#include "vtkSMParaViewPipelineController.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMSessionProxyManager.h"
#include "vtkSMSourceProxy.h"
#include "vtkSMTransferFunctionManager.h"
#include "vtkTrivialProducer.h"

#include <vtk_pugixml.h>

namespace TEM
{

class DataSource::DSInternals
{
public:
  vtkSmartPointer<vtkSMSourceProxy> OriginalDataSource;
  vtkWeakPointer<vtkSMSourceProxy> Producer;
  QList<QSharedPointer<Operator> > Operators;
  vtkSmartPointer<vtkSMProxy> ColorMap;
};

//-----------------------------------------------------------------------------
DataSource::DataSource(vtkSMSourceProxy* dataSource, QObject* parentObject)
  : Superclass(parentObject),
  Internals(new DataSource::DSInternals())
{
  Q_ASSERT(dataSource);
  this->Internals->OriginalDataSource = dataSource;

  vtkNew<vtkSMParaViewPipelineController> controller;
  vtkSMSessionProxyManager* pxm = dataSource->GetSessionProxyManager();
  Q_ASSERT(pxm);

  vtkSmartPointer<vtkSMProxy> source;
  source.TakeReference(pxm->NewProxy("sources", "TrivialProducer"));
  Q_ASSERT(source != NULL);
  Q_ASSERT(vtkSMSourceProxy::SafeDownCast(source));

  // We add an annotation to the proxy so that it'll be easier for code to
  // locate registered pipeline proxies that are being treated as data sources.
  TEM::annotateDataProducer(source,
    vtkSMPropertyHelper(dataSource,
      vtkSMCoreUtilities::GetFileNameProperty(dataSource)).GetAsString());
  controller->RegisterPipelineProxy(source);
  this->Internals->Producer = vtkSMSourceProxy::SafeDownCast(source);

  // Setup color map for this data-source.
  static unsigned int colorMapCounter=0;
  colorMapCounter++;

  vtkNew<vtkSMTransferFunctionManager> tfmgr;
  this->Internals->ColorMap = tfmgr->GetColorTransferFunction(
    QString("DataSourceColorMap%1").arg(colorMapCounter).toLatin1().data(), pxm);

  // every time the data changes, we should update the color map.
  this->connect(this, SIGNAL(dataChanged()), SLOT(updateColorMap()));

  this->resetData();
}

//-----------------------------------------------------------------------------
DataSource::~DataSource()
{
  if (this->Internals->Producer)
    {
    vtkNew<vtkSMParaViewPipelineController> controller;
    controller->UnRegisterProxy(this->Internals->Producer);
    }
}

//-----------------------------------------------------------------------------
QString DataSource::filename() const
{
  vtkSMProxy* dataSource = this->originalDataSource();
  return vtkSMPropertyHelper(dataSource,
    vtkSMCoreUtilities::GetFileNameProperty(dataSource)).GetAsString();
}

//-----------------------------------------------------------------------------
bool DataSource::serialize(pugi::xml_node& ns) const
{
  pugi::xml_node node = ns.append_child("ColorMap");
  TEM::serialize(this->colorMap(), node);

  node = ns.append_child("OpacityMap");
  TEM::serialize(this->opacityMap(), node);

  ns.append_attribute("number_of_operators").set_value(
    static_cast<int>(this->Internals->Operators.size()));

  foreach (QSharedPointer<Operator> op, this->Internals->Operators)
    {
    pugi::xml_node node = ns.append_child("Operator");
    if (!op->serialize(node))
      {
      qWarning("failed to serialize Operator. Skipping it.");
      ns.remove_child(node);
      }
    }
  return true;
}

//-----------------------------------------------------------------------------
bool DataSource::deserialize(const pugi::xml_node& ns)
{
  TEM::deserialize(this->colorMap(), ns.child("ColorMap"));
  TEM::deserialize(this->opacityMap(), ns.child("OpacityMap"));
  vtkSMPropertyHelper(this->colorMap(),
                      "ScalarOpacityFunction").Set(this->opacityMap());
  this->colorMap()->UpdateVTKObjects();

  int num_operators = ns.attribute("number_of_operators").as_int(-1);
  if (num_operators < 0)
    {
    return false;
    }

  this->Internals->Operators.clear();
  this->resetData();

  for (pugi::xml_node node=ns.child("Operator"); node; node = node.next_sibling("Operator"))
    {
    QSharedPointer<OperatorPython> op (new OperatorPython());
    if (op->deserialize(node))
      {
      this->addOperator(op);
      }
    }
  return true;
}

//-----------------------------------------------------------------------------
DataSource* DataSource::clone(bool clone_operators) const
{
  DataSource* newClone = new DataSource(this->Internals->OriginalDataSource);
  if (clone_operators)
    {
    // now, clone the operators.
    foreach (QSharedPointer<Operator> op, this->Internals->Operators)
      {
      QSharedPointer<Operator> opClone(op->clone());
      newClone->addOperator(opClone);
      }
    }
  return newClone;
}

//-----------------------------------------------------------------------------
vtkSMSourceProxy* DataSource::originalDataSource() const
{
  return this->Internals->OriginalDataSource;
}

//-----------------------------------------------------------------------------
vtkSMSourceProxy* DataSource::producer() const
{
  return this->Internals->Producer;
}

//-----------------------------------------------------------------------------
int DataSource::addOperator(const QSharedPointer<Operator>& op)
{
  int index = this->Internals->Operators.count();
  this->Internals->Operators.push_back(op);
  this->connect(op.data(), SIGNAL(transformModified()),
    SLOT(operatorTransformModified()));
  emit this->operatorAdded(op.data());
  this->operate(op.data());
  return index;
}

//-----------------------------------------------------------------------------
void DataSource::operate(Operator* op)
{
  Q_ASSERT(op);

  vtkTrivialProducer* tp = vtkTrivialProducer::SafeDownCast(
    this->Internals->Producer->GetClientSideObject());
  Q_ASSERT(tp);
  if (op->transform(tp->GetOutputDataObject(0)))
    {
    tp->Modified();
    tp->GetOutputDataObject(0)->Modified();
    this->Internals->Producer->MarkModified(NULL);
    this->Internals->Producer->UpdatePipeline();
    }

  emit this->dataChanged();
}

//-----------------------------------------------------------------------------
const QList<QSharedPointer<Operator> >& DataSource::operators() const
{
  return this->Internals->Operators;
}

//-----------------------------------------------------------------------------
void DataSource::resetData()
{
  vtkSMSourceProxy* dataSource = this->Internals->OriginalDataSource;
  Q_ASSERT(dataSource);

  dataSource->UpdatePipeline();
  vtkAlgorithm* vtkalgorithm = vtkAlgorithm::SafeDownCast(
    dataSource->GetClientSideObject());
  Q_ASSERT(vtkalgorithm);

  vtkSMSourceProxy* source = this->Internals->Producer;
  Q_ASSERT(source != NULL);

  // Create a clone and release the reader data.
  vtkDataObject* data = vtkalgorithm->GetOutputDataObject(0);
  vtkDataObject* clone = data->NewInstance();
  clone->DeepCopy(data);
  //data->ReleaseData();  FIXME: how it this supposed to work? I get errors on
  //attempting to re-execute the reader pipeline in clone().

  vtkTrivialProducer* tp = vtkTrivialProducer::SafeDownCast(
    source->GetClientSideObject());
  Q_ASSERT(tp);
  tp->SetOutput(clone);
  clone->FastDelete();
  emit this->dataChanged();
}

//-----------------------------------------------------------------------------
void DataSource::operatorTransformModified()
{
  bool prev = this->blockSignals(true);

  this->resetData();
  foreach (QSharedPointer<Operator> op, this->Internals->Operators)
    {
    this->operate(op.data());
    }
  this->blockSignals(prev);
  emit this->dataChanged();
}

//-----------------------------------------------------------------------------
vtkSMProxy* DataSource::colorMap() const
{
  return this->Internals->ColorMap;
}

//-----------------------------------------------------------------------------
vtkSMProxy* DataSource::opacityMap() const
{
  return this->Internals->ColorMap?
  vtkSMPropertyHelper(this->Internals->ColorMap, "ScalarOpacityFunction").GetAsProxy() : NULL;
}

//-----------------------------------------------------------------------------
void DataSource::updateColorMap()
{
  // rescale the color/opacity maps for the data source.
  TEM::rescaleColorMap(this->colorMap(), this);
}

}
<commit_msg>Hack to ensure the extents are properly updated<commit_after>/******************************************************************************

  This source file is part of the TEM tomography project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

******************************************************************************/
#include "DataSource.h"

#include "OperatorPython.h"
#include "Utilities.h"
#include "vtkDataObject.h"
#include "vtkNew.h"
#include "vtkSmartPointer.h"
#include "vtkSMCoreUtilities.h"
#include "vtkSMParaViewPipelineController.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMSessionProxyManager.h"
#include "vtkSMSourceProxy.h"
#include "vtkSMTransferFunctionManager.h"
#include "vtkTrivialProducer.h"

#include <vtk_pugixml.h>

namespace TEM
{

class DataSource::DSInternals
{
public:
  vtkSmartPointer<vtkSMSourceProxy> OriginalDataSource;
  vtkWeakPointer<vtkSMSourceProxy> Producer;
  QList<QSharedPointer<Operator> > Operators;
  vtkSmartPointer<vtkSMProxy> ColorMap;
};

//-----------------------------------------------------------------------------
DataSource::DataSource(vtkSMSourceProxy* dataSource, QObject* parentObject)
  : Superclass(parentObject),
  Internals(new DataSource::DSInternals())
{
  Q_ASSERT(dataSource);
  this->Internals->OriginalDataSource = dataSource;

  vtkNew<vtkSMParaViewPipelineController> controller;
  vtkSMSessionProxyManager* pxm = dataSource->GetSessionProxyManager();
  Q_ASSERT(pxm);

  vtkSmartPointer<vtkSMProxy> source;
  source.TakeReference(pxm->NewProxy("sources", "TrivialProducer"));
  Q_ASSERT(source != NULL);
  Q_ASSERT(vtkSMSourceProxy::SafeDownCast(source));

  // We add an annotation to the proxy so that it'll be easier for code to
  // locate registered pipeline proxies that are being treated as data sources.
  TEM::annotateDataProducer(source,
    vtkSMPropertyHelper(dataSource,
      vtkSMCoreUtilities::GetFileNameProperty(dataSource)).GetAsString());
  controller->RegisterPipelineProxy(source);
  this->Internals->Producer = vtkSMSourceProxy::SafeDownCast(source);

  // Setup color map for this data-source.
  static unsigned int colorMapCounter=0;
  colorMapCounter++;

  vtkNew<vtkSMTransferFunctionManager> tfmgr;
  this->Internals->ColorMap = tfmgr->GetColorTransferFunction(
    QString("DataSourceColorMap%1").arg(colorMapCounter).toLatin1().data(), pxm);

  // every time the data changes, we should update the color map.
  this->connect(this, SIGNAL(dataChanged()), SLOT(updateColorMap()));

  this->resetData();
}

//-----------------------------------------------------------------------------
DataSource::~DataSource()
{
  if (this->Internals->Producer)
    {
    vtkNew<vtkSMParaViewPipelineController> controller;
    controller->UnRegisterProxy(this->Internals->Producer);
    }
}

//-----------------------------------------------------------------------------
QString DataSource::filename() const
{
  vtkSMProxy* dataSource = this->originalDataSource();
  return vtkSMPropertyHelper(dataSource,
    vtkSMCoreUtilities::GetFileNameProperty(dataSource)).GetAsString();
}

//-----------------------------------------------------------------------------
bool DataSource::serialize(pugi::xml_node& ns) const
{
  pugi::xml_node node = ns.append_child("ColorMap");
  TEM::serialize(this->colorMap(), node);

  node = ns.append_child("OpacityMap");
  TEM::serialize(this->opacityMap(), node);

  ns.append_attribute("number_of_operators").set_value(
    static_cast<int>(this->Internals->Operators.size()));

  foreach (QSharedPointer<Operator> op, this->Internals->Operators)
    {
    pugi::xml_node node = ns.append_child("Operator");
    if (!op->serialize(node))
      {
      qWarning("failed to serialize Operator. Skipping it.");
      ns.remove_child(node);
      }
    }
  return true;
}

//-----------------------------------------------------------------------------
bool DataSource::deserialize(const pugi::xml_node& ns)
{
  TEM::deserialize(this->colorMap(), ns.child("ColorMap"));
  TEM::deserialize(this->opacityMap(), ns.child("OpacityMap"));
  vtkSMPropertyHelper(this->colorMap(),
                      "ScalarOpacityFunction").Set(this->opacityMap());
  this->colorMap()->UpdateVTKObjects();

  int num_operators = ns.attribute("number_of_operators").as_int(-1);
  if (num_operators < 0)
    {
    return false;
    }

  this->Internals->Operators.clear();
  this->resetData();

  for (pugi::xml_node node=ns.child("Operator"); node; node = node.next_sibling("Operator"))
    {
    QSharedPointer<OperatorPython> op (new OperatorPython());
    if (op->deserialize(node))
      {
      this->addOperator(op);
      }
    }
  return true;
}

//-----------------------------------------------------------------------------
DataSource* DataSource::clone(bool clone_operators) const
{
  DataSource* newClone = new DataSource(this->Internals->OriginalDataSource);
  if (clone_operators)
    {
    // now, clone the operators.
    foreach (QSharedPointer<Operator> op, this->Internals->Operators)
      {
      QSharedPointer<Operator> opClone(op->clone());
      newClone->addOperator(opClone);
      }
    }
  return newClone;
}

//-----------------------------------------------------------------------------
vtkSMSourceProxy* DataSource::originalDataSource() const
{
  return this->Internals->OriginalDataSource;
}

//-----------------------------------------------------------------------------
vtkSMSourceProxy* DataSource::producer() const
{
  return this->Internals->Producer;
}

//-----------------------------------------------------------------------------
int DataSource::addOperator(const QSharedPointer<Operator>& op)
{
  int index = this->Internals->Operators.count();
  this->Internals->Operators.push_back(op);
  this->connect(op.data(), SIGNAL(transformModified()),
    SLOT(operatorTransformModified()));
  emit this->operatorAdded(op.data());
  this->operate(op.data());
  return index;
}

//-----------------------------------------------------------------------------
void DataSource::operate(Operator* op)
{
  Q_ASSERT(op);

  vtkTrivialProducer* tp = vtkTrivialProducer::SafeDownCast(
    this->Internals->Producer->GetClientSideObject());
  Q_ASSERT(tp);
  if (op->transform(tp->GetOutputDataObject(0)))
    {
    tp->Modified();
    tp->GetOutputDataObject(0)->Modified();
    this->Internals->Producer->MarkModified(NULL);

    // This indirection is necessary to overcome a bug in VTK/ParaView when
    // explicitly calling UpdatePipeline(). The extents don't reset to the whole
    // extent. Until a  proper fix makes it into VTK, this is needed.
    vtkSMSessionProxyManager* pxm =
        this->Internals->Producer->GetSessionProxyManager();
    vtkSMSourceProxy* filter =
      vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("filters", "PassThrough"));
    Q_ASSERT(filter);
    vtkSMPropertyHelper(filter, "Input").Set(this->Internals->Producer, 0);
    filter->UpdateVTKObjects();
    filter->UpdatePipeline();
    filter->Delete();
    //this->Internals->Producer->UpdatePipeline();
    }

  emit this->dataChanged();
}

//-----------------------------------------------------------------------------
const QList<QSharedPointer<Operator> >& DataSource::operators() const
{
  return this->Internals->Operators;
}

//-----------------------------------------------------------------------------
void DataSource::resetData()
{
  vtkSMSourceProxy* dataSource = this->Internals->OriginalDataSource;
  Q_ASSERT(dataSource);

  dataSource->UpdatePipeline();
  vtkAlgorithm* vtkalgorithm = vtkAlgorithm::SafeDownCast(
    dataSource->GetClientSideObject());
  Q_ASSERT(vtkalgorithm);

  vtkSMSourceProxy* source = this->Internals->Producer;
  Q_ASSERT(source != NULL);

  // Create a clone and release the reader data.
  vtkDataObject* data = vtkalgorithm->GetOutputDataObject(0);
  vtkDataObject* clone = data->NewInstance();
  clone->DeepCopy(data);
  //data->ReleaseData();  FIXME: how it this supposed to work? I get errors on
  //attempting to re-execute the reader pipeline in clone().

  vtkTrivialProducer* tp = vtkTrivialProducer::SafeDownCast(
    source->GetClientSideObject());
  Q_ASSERT(tp);
  tp->SetOutput(clone);
  clone->FastDelete();
  emit this->dataChanged();
}

//-----------------------------------------------------------------------------
void DataSource::operatorTransformModified()
{
  bool prev = this->blockSignals(true);

  this->resetData();
  foreach (QSharedPointer<Operator> op, this->Internals->Operators)
    {
    this->operate(op.data());
    }
  this->blockSignals(prev);
  emit this->dataChanged();
}

//-----------------------------------------------------------------------------
vtkSMProxy* DataSource::colorMap() const
{
  return this->Internals->ColorMap;
}

//-----------------------------------------------------------------------------
vtkSMProxy* DataSource::opacityMap() const
{
  return this->Internals->ColorMap?
  vtkSMPropertyHelper(this->Internals->ColorMap, "ScalarOpacityFunction").GetAsProxy() : NULL;
}

//-----------------------------------------------------------------------------
void DataSource::updateColorMap()
{
  // rescale the color/opacity maps for the data source.
  TEM::rescaleColorMap(this->colorMap(), this);
}

}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief batch request handler
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "RestBatchHandler.h"

#include "Basics/StringUtils.h"
#include "Logger/Logger.h"
#include "HttpServer/HttpServer.h"
#include "Rest/HttpRequest.h"

using namespace std;
using namespace triagens::basics;
using namespace triagens::rest;
using namespace triagens::arango;

// -----------------------------------------------------------------------------
// --SECTION--                                      constructors and destructors
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoDB
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////

RestBatchHandler::RestBatchHandler (HttpRequest* request, TRI_vocbase_t* vocbase)
  : RestVocbaseBaseHandler(request, vocbase),
    _partContentType(HttpRequest::getPartContentType()) {
}

////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////

RestBatchHandler::~RestBatchHandler () {
}

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                   Handler methods
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoDB
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////

bool RestBatchHandler::isDirect () {
  return false;
}

////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////

string const& RestBatchHandler::queue () {
  static string const client = "STANDARD";

  return client;
}

////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////

Handler::status_e RestBatchHandler::execute() {
  // extract the request type
  const HttpRequest::HttpRequestType type = _request->requestType();

  if (type != HttpRequest::HTTP_REQUEST_POST && type != HttpRequest::HTTP_REQUEST_PUT) {
    generateError(HttpResponse::METHOD_NOT_ALLOWED, TRI_ERROR_HTTP_METHOD_NOT_ALLOWED);

    return Handler::HANDLER_DONE;
  }

  string boundary;
  if (! getBoundary(&boundary)) {
    // invalid content-type or boundary sent
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "invalid content-type or boundary received");

    return Handler::HANDLER_FAILED;
  }

  size_t errors = 0;

  // get authorization header. we will inject this into the subparts
  string authorization = _request->header("authorization");

  // create the response
  _response = createResponse(HttpResponse::OK);
  _response->setContentType(_request->header("content-type"));

  // setup some auxiliary structures to parse the multipart message
  MultipartMessage message(boundary.c_str(), boundary.size(), _request->body(), _request->body() + _request->bodySize());

  SearchHelper helper;
  helper.message = &message;
  helper.searchStart = (char*) message.messageStart;

  // iterate over all parts of the multipart message
  while (true) {
    // get the next part from the multipart message
    if (! extractPart(&helper)) {
      // error
      generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "invalid multipart message received");
      LOGGER_WARNING << "received a corrupted multipart message";

      return Handler::HANDLER_FAILED;
    }

    // split part into header & body
    const char* partStart = helper.foundStart;
    const char* partEnd = partStart + helper.foundLength;
    const size_t partLength = helper.foundLength;

    const char* headerStart = partStart;
    char* bodyStart = NULL;
    size_t headerLength = 0;
    size_t bodyLength = 0;

    // assume Windows linebreak \r\n\r\n as delimiter
    char* p = strstr((char*) headerStart, "\r\n\r\n");
    if (p != NULL) {
      headerLength = p - partStart;
      bodyStart = p + 4;
      bodyLength = partEnd - bodyStart;
    }
    else {
      // test Unix linebreak
      p = strstr((char*) headerStart, "\n\n");
      if (p != NULL) {
        headerLength = p - partStart;
        bodyStart = p + 2;
        bodyLength = partEnd - bodyStart;
      }
      else {
        // no delimiter found, assume we have only a header
        headerLength = partLength;
      }
    }

    // set up request object for the part
    LOGGER_TRACE << "part header is " << string(headerStart, headerLength);
    HttpRequest* request = new HttpRequest(headerStart, headerLength);
    if (bodyLength > 0) {
      LOGGER_TRACE << "part body is " << string(bodyStart, bodyLength);
      request->setBody(bodyStart, bodyLength);
    }

    if (authorization.size()) {
      // inject Authorization header of multipart message into part message
      request->setHeader("authorization", 13, authorization.c_str());
    }

    HttpHandler* handler = _server->createHandler(request);
    if (! handler) {
      delete request;

      generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "could not create handler for batch part processing");

      return Handler::HANDLER_FAILED;
    }

    Handler::status_e status = Handler::HANDLER_FAILED;
    do {
      try {
        status = handler->execute();
      }
      catch (triagens::basics::TriagensError const& ex) {
        handler->handleError(ex);
      }
      catch (std::exception const& ex) {
        triagens::basics::InternalError err(ex, __FILE__, __LINE__);

        handler->handleError(err);
      }
      catch (...) {
        triagens::basics::InternalError err("executeDirectHandler", __FILE__, __LINE__);
        handler->handleError(err);
      }
    }
    while (status == Handler::HANDLER_REQUEUE);

    if (status == Handler::HANDLER_FAILED) {
      // one of the handlers failed, we must exit now
      delete handler;
      generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "executing a handler for batch part failed");

      return Handler::HANDLER_FAILED;
    }

    HttpResponse* partResponse = handler->getResponse();
    if (partResponse == 0) {
      delete handler;
      generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "could not create a response for batch part request");

      return Handler::HANDLER_FAILED;
    }

    const HttpResponse::HttpResponseCode code = partResponse->responseCode();
    if (code >= 400) {
      // error
      ++errors;
    }

    // append the boundary for this subpart
    _response->body().appendText(boundary + "\r\nContent-Type: ");
    _response->body().appendText(_partContentType);

    if (helper.contentId != 0) {
      // append content-id
      _response->body().appendText("\r\nContent-Id: " + string(helper.contentId, helper.contentIdLength));
    }

    _response->body().appendText("\r\n\r\n", 4);

    // append the response header
    partResponse->writeHeader(&_response->body());
    // append the response body
    _response->body().appendText(partResponse->body());
    _response->body().appendText("\r\n", 2);

    delete handler;

    if (! helper.containsMore) {
      // we've read the last part
      break;
    }
  } // next part

  // append final boundary + "--"
  _response->body().appendText(boundary + "--");

  if (errors > 0) {
    _response->setHeader(HttpResponse::getBatchErrorHeader(), StringUtils::itoa(errors));
  }

  // success
  return Handler::HANDLER_DONE;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief extract the boundary of a multipart message
////////////////////////////////////////////////////////////////////////////////

bool RestBatchHandler::getBoundary (string* result) {
  assert(_request);

  // extract content type
  string contentType = StringUtils::trim(_request->header("content-type"));

  // content type is expect to contain a boundary like this:
  // "Content-Type: multipart/form-data; boundary=<boundary goes here>"
  vector<string> parts = StringUtils::split(contentType, ';');
  if (parts.size() != 2 || parts[0] != HttpRequest::getMultipartContentType().c_str()) {
    // content-type is not formatted as expected
    return false;
  }

  static const size_t boundaryLength = 9; // strlen("boundary=");

  // trim 2nd part and lowercase it
  StringUtils::trimInPlace(parts[1]);
  string p = parts[1].substr(0, boundaryLength);
  StringUtils::tolowerInPlace(&p);

  if (p != "boundary=") {
    return false;
  }

  string boundary = "--" + parts[1].substr(boundaryLength);
  if (boundary.size() < 5) {
    // 3 bytes is min length for boundary (without "--")
    return false;
  }

  LOGGER_TRACE << "boundary of multipart-message is " << boundary;

  *result = boundary;
  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief extract the next part from a multipart message
////////////////////////////////////////////////////////////////////////////////

bool RestBatchHandler::extractPart (SearchHelper* helper) {
  assert(helper->searchStart != NULL);

  // init the response
  helper->foundStart = NULL;
  helper->foundLength = 0;
  helper->containsMore = false;
  helper->contentId = 0;
  helper->contentIdLength = 0;

  const char* searchEnd = helper->message->messageEnd;

  if (helper->searchStart >= searchEnd) {
    // we're at the end already
    return false;
  }

  // search for boundary
  char* found = strstr(helper->searchStart, helper->message->boundary);
  if (found == NULL) {
    // not contained. this is an error
    return false;
  }

  if (found != helper->searchStart) {
    // boundary not located at beginning. this is an error
    return false;
  }

  found += helper->message->boundaryLength;
  if (found + 1 >= searchEnd) {
    // we're outside the buffer. this is an error
    return false;
  }

  while (found < searchEnd && *found == ' ') {
    ++found;
  }

  if (found + 2 >= searchEnd) {
    // we're outside the buffer. this is an error
    return false;
  }

  if (*found == '\r') {
    ++found;
  }
  if (*found != '\n') {
    // no linebreak found
    return false;
  }
  ++found;

  bool hasTypeHeader = false;

  while (found < searchEnd) {
    char* eol = strstr(found, "\r\n");
    if (0 == eol || eol == found) {
      break;
    }

    // split key/value of header
    char* colon = (char*) memchr(found, (int) ':', eol - found);

    if (0 == colon) {
      // invalid header, not containing ':'
      return false;
    }

    // set up key/value pair
    string key(found, colon - found);
    StringUtils::trimInPlace(key);
    StringUtils::tolowerInPlace(&key);

    // skip the colon itself
    ++colon;
    // skip any whitespace
    while (*colon == ' ') {
      ++colon;
    }

    string value(colon, eol - colon);
    StringUtils::trimInPlace(value);

    if ("content-type" == key) {
      if (_partContentType == value) {
        hasTypeHeader = true;
      }
    }
    else if ("content-id" == key) {
      helper->contentId = colon;
      helper->contentIdLength = eol - colon;
    }
    else {
      // ignore other headers
    }

    found = eol + 2;
  }

  found += 2; // for 2nd \r\n

  if (!hasTypeHeader) {
    // no Content-Type header. this is an error
    return false;
  }

  // we're at the start of the body part. set the return value
  helper->foundStart = found;

  // search for the end of the boundary
  found = strstr(helper->foundStart, helper->message->boundary);
  if (found == NULL || found >= searchEnd) {
    // did not find the end. this is an error
    return false;
  }

  helper->foundLength = found - helper->foundStart;

  char* p = found + helper->message->boundaryLength;
  if (p + 2 >= searchEnd) {
    // end of boundary is outside the buffer
    return false;
  }

  if (*p != '-' || *(p + 1) != '-') {
    // we've not reached the end yet
    helper->containsMore = true;
    helper->searchStart = found;
  }

  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End:
<commit_msg>remove unnecessary response headers in sub responses<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief batch request handler
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "RestBatchHandler.h"

#include "Basics/StringUtils.h"
#include "Logger/Logger.h"
#include "HttpServer/HttpServer.h"
#include "Rest/HttpRequest.h"

using namespace std;
using namespace triagens::basics;
using namespace triagens::rest;
using namespace triagens::arango;

// -----------------------------------------------------------------------------
// --SECTION--                                      constructors and destructors
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoDB
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////

RestBatchHandler::RestBatchHandler (HttpRequest* request, TRI_vocbase_t* vocbase)
  : RestVocbaseBaseHandler(request, vocbase),
    _partContentType(HttpRequest::getPartContentType()) {
}

////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////

RestBatchHandler::~RestBatchHandler () {
}

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                   Handler methods
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoDB
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////

bool RestBatchHandler::isDirect () {
  return false;
}

////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////

string const& RestBatchHandler::queue () {
  static string const client = "STANDARD";

  return client;
}

////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////

Handler::status_e RestBatchHandler::execute() {
  // extract the request type
  const HttpRequest::HttpRequestType type = _request->requestType();

  if (type != HttpRequest::HTTP_REQUEST_POST && type != HttpRequest::HTTP_REQUEST_PUT) {
    generateError(HttpResponse::METHOD_NOT_ALLOWED, TRI_ERROR_HTTP_METHOD_NOT_ALLOWED);

    return Handler::HANDLER_DONE;
  }

  string boundary;
  if (! getBoundary(&boundary)) {
    // invalid content-type or boundary sent
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "invalid content-type or boundary received");

    return Handler::HANDLER_FAILED;
  }

  size_t errors = 0;

  // get authorization header. we will inject this into the subparts
  string authorization = _request->header("authorization");

  // create the response
  _response = createResponse(HttpResponse::OK);
  _response->setContentType(_request->header("content-type"));

  // setup some auxiliary structures to parse the multipart message
  MultipartMessage message(boundary.c_str(), boundary.size(), _request->body(), _request->body() + _request->bodySize());

  SearchHelper helper;
  helper.message = &message;
  helper.searchStart = (char*) message.messageStart;

  // iterate over all parts of the multipart message
  while (true) {
    // get the next part from the multipart message
    if (! extractPart(&helper)) {
      // error
      generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "invalid multipart message received");
      LOGGER_WARNING << "received a corrupted multipart message";

      return Handler::HANDLER_FAILED;
    }

    // split part into header & body
    const char* partStart = helper.foundStart;
    const char* partEnd = partStart + helper.foundLength;
    const size_t partLength = helper.foundLength;

    const char* headerStart = partStart;
    char* bodyStart = NULL;
    size_t headerLength = 0;
    size_t bodyLength = 0;

    // assume Windows linebreak \r\n\r\n as delimiter
    char* p = strstr((char*) headerStart, "\r\n\r\n");
    if (p != NULL) {
      headerLength = p - partStart;
      bodyStart = p + 4;
      bodyLength = partEnd - bodyStart;
    }
    else {
      // test Unix linebreak
      p = strstr((char*) headerStart, "\n\n");
      if (p != NULL) {
        headerLength = p - partStart;
        bodyStart = p + 2;
        bodyLength = partEnd - bodyStart;
      }
      else {
        // no delimiter found, assume we have only a header
        headerLength = partLength;
      }
    }

    // set up request object for the part
    LOGGER_TRACE << "part header is " << string(headerStart, headerLength);
    HttpRequest* request = new HttpRequest(headerStart, headerLength);
    if (bodyLength > 0) {
      LOGGER_TRACE << "part body is " << string(bodyStart, bodyLength);
      request->setBody(bodyStart, bodyLength);
    }

    if (authorization.size()) {
      // inject Authorization header of multipart message into part message
      request->setHeader("authorization", 13, authorization.c_str());
    }

    HttpHandler* handler = _server->createHandler(request);
    if (! handler) {
      delete request;

      generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "could not create handler for batch part processing");

      return Handler::HANDLER_FAILED;
    }

    Handler::status_e status = Handler::HANDLER_FAILED;
    do {
      try {
        status = handler->execute();
      }
      catch (triagens::basics::TriagensError const& ex) {
        handler->handleError(ex);
      }
      catch (std::exception const& ex) {
        triagens::basics::InternalError err(ex, __FILE__, __LINE__);

        handler->handleError(err);
      }
      catch (...) {
        triagens::basics::InternalError err("executeDirectHandler", __FILE__, __LINE__);
        handler->handleError(err);
      }
    }
    while (status == Handler::HANDLER_REQUEUE);

    if (status == Handler::HANDLER_FAILED) {
      // one of the handlers failed, we must exit now
      delete handler;
      generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "executing a handler for batch part failed");

      return Handler::HANDLER_FAILED;
    }

    HttpResponse* partResponse = handler->getResponse();
    if (partResponse == 0) {
      delete handler;
      generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "could not create a response for batch part request");

      return Handler::HANDLER_FAILED;
    }

    const HttpResponse::HttpResponseCode code = partResponse->responseCode();
    if (code >= 400) {
      // error
      ++errors;
    }

    // append the boundary for this subpart
    _response->body().appendText(boundary + "\r\nContent-Type: ");
    _response->body().appendText(_partContentType);

    if (helper.contentId != 0) {
      // append content-id
      _response->body().appendText("\r\nContent-Id: " + string(helper.contentId, helper.contentIdLength));
    }

    _response->body().appendText("\r\n\r\n", 4);

    // remove some headers we don't need
    partResponse->setHeader("connection", 10, "");
    partResponse->setHeader("server", 6, "");

    // append the part response header
    partResponse->writeHeader(&_response->body());
    // append the part response body
    _response->body().appendText(partResponse->body());
    _response->body().appendText("\r\n", 2);

    delete handler;

    if (! helper.containsMore) {
      // we've read the last part
      break;
    }
  } // next part

  // append final boundary + "--"
  _response->body().appendText(boundary + "--");

  if (errors > 0) {
    _response->setHeader(HttpResponse::getBatchErrorHeader(), StringUtils::itoa(errors));
  }

  // success
  return Handler::HANDLER_DONE;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief extract the boundary of a multipart message
////////////////////////////////////////////////////////////////////////////////

bool RestBatchHandler::getBoundary (string* result) {
  assert(_request);

  // extract content type
  string contentType = StringUtils::trim(_request->header("content-type"));

  // content type is expect to contain a boundary like this:
  // "Content-Type: multipart/form-data; boundary=<boundary goes here>"
  vector<string> parts = StringUtils::split(contentType, ';');
  if (parts.size() != 2 || parts[0] != HttpRequest::getMultipartContentType().c_str()) {
    // content-type is not formatted as expected
    return false;
  }

  static const size_t boundaryLength = 9; // strlen("boundary=");

  // trim 2nd part and lowercase it
  StringUtils::trimInPlace(parts[1]);
  string p = parts[1].substr(0, boundaryLength);
  StringUtils::tolowerInPlace(&p);

  if (p != "boundary=") {
    return false;
  }

  string boundary = "--" + parts[1].substr(boundaryLength);
  if (boundary.size() < 5) {
    // 3 bytes is min length for boundary (without "--")
    return false;
  }

  LOGGER_TRACE << "boundary of multipart-message is " << boundary;

  *result = boundary;
  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief extract the next part from a multipart message
////////////////////////////////////////////////////////////////////////////////

bool RestBatchHandler::extractPart (SearchHelper* helper) {
  assert(helper->searchStart != NULL);

  // init the response
  helper->foundStart = NULL;
  helper->foundLength = 0;
  helper->containsMore = false;
  helper->contentId = 0;
  helper->contentIdLength = 0;

  const char* searchEnd = helper->message->messageEnd;

  if (helper->searchStart >= searchEnd) {
    // we're at the end already
    return false;
  }

  // search for boundary
  char* found = strstr(helper->searchStart, helper->message->boundary);
  if (found == NULL) {
    // not contained. this is an error
    return false;
  }

  if (found != helper->searchStart) {
    // boundary not located at beginning. this is an error
    return false;
  }

  found += helper->message->boundaryLength;
  if (found + 1 >= searchEnd) {
    // we're outside the buffer. this is an error
    return false;
  }

  while (found < searchEnd && *found == ' ') {
    ++found;
  }

  if (found + 2 >= searchEnd) {
    // we're outside the buffer. this is an error
    return false;
  }

  if (*found == '\r') {
    ++found;
  }
  if (*found != '\n') {
    // no linebreak found
    return false;
  }
  ++found;

  bool hasTypeHeader = false;

  while (found < searchEnd) {
    char* eol = strstr(found, "\r\n");
    if (0 == eol || eol == found) {
      break;
    }

    // split key/value of header
    char* colon = (char*) memchr(found, (int) ':', eol - found);

    if (0 == colon) {
      // invalid header, not containing ':'
      return false;
    }

    // set up key/value pair
    string key(found, colon - found);
    StringUtils::trimInPlace(key);
    StringUtils::tolowerInPlace(&key);

    // skip the colon itself
    ++colon;
    // skip any whitespace
    while (*colon == ' ') {
      ++colon;
    }

    string value(colon, eol - colon);
    StringUtils::trimInPlace(value);

    if ("content-type" == key) {
      if (_partContentType == value) {
        hasTypeHeader = true;
      }
    }
    else if ("content-id" == key) {
      helper->contentId = colon;
      helper->contentIdLength = eol - colon;
    }
    else {
      // ignore other headers
    }

    found = eol + 2;
  }

  found += 2; // for 2nd \r\n

  if (!hasTypeHeader) {
    // no Content-Type header. this is an error
    return false;
  }

  // we're at the start of the body part. set the return value
  helper->foundStart = found;

  // search for the end of the boundary
  found = strstr(helper->foundStart, helper->message->boundary);
  if (found == NULL || found >= searchEnd) {
    // did not find the end. this is an error
    return false;
  }

  helper->foundLength = found - helper->foundStart;

  char* p = found + helper->message->boundaryLength;
  if (p + 2 >= searchEnd) {
    // end of boundary is outside the buffer
    return false;
  }

  if (*p != '-' || *(p + 1) != '-') {
    // we've not reached the end yet
    helper->containsMore = true;
    helper->searchStart = found;
  }

  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End:
<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

#if !defined(XALANVERSION_HEADER_GUARD_1357924680)
#define XALANVERSION_HEADER_GUARD_1357924680

// ---------------------------------------------------------------------------
// X A L A N   V E R S I O N   H E A D E R   D O C U M E N T A T I O N

/**
 * User Documentation for Xalan Version Values:
 *
 *
 *
 * Xalan  Notes:
 *
 * Xalan Committers Documentation:
 *
 *    Xalan committers normally only need to modify one or two of the
 *    following macros:
 *
 *      XALAN_VERSION_MAJOR
 *      XALAN_VERSION_MINOR
 *      XALAN_VERSION_REVISION
 *
 *    The integer values of these macros define the Xalan version number. All
 *    other constants and preprocessor macros are automatically generated from
 *    these three definitions.
 *
 * Xalan User Documentation:
 *
 *  The following sections in the user documentation have examples based upon
 *  the following three version input values:
 *
 *    #define XALAN_VERSION_MAJOR 19
 *    #define XALAN_VERSION_MINOR 3
 *    #define XALAN_VERSION_REVISION 74
 *
 *  The minor and revision (patch level) numbers have two digits of resolution
 *  which means that '3' becomes '03' in this example. This policy guarantees
 *  that when using preprocessor macros, version 19.3.74 will be greater than
 *  version 1.94.74 since the first will expand to 190374 and the second to
 *  19474.
 *
 *  Preprocessor Macros:
 *
 *    _XALAN_VERSION defines the primary preprocessor macro that users will
 *    introduce into their code to perform conditional compilation where the
 *    version of Xalan is detected in order to enable or disable version
 *    specific capabilities. The value of _XALAN_VERSION for the above example
 *    will be 190374. To use it a user would perform an operation such as the
 *    following:
 *
 *      #if _XALAN_VERSION >= 190374
 *        // code specific to new version of Xalan...
 *      #else
 *        // old code here...
 *      #endif
 *
 *    XALAN_FULLVERSIONSTR is a preprocessor macro that expands to a string
 *    constant whose value, for the above example, will be "19_3_74".
 *
 *    XALAN_FULLVERSIONDOT is a preprocessor macro that expands to a string
 *    constant whose value, for the above example, will be "19.3.74".
 *
 *    XALAN_VERSIONSTR is a preprocessor macro that expands to a string
 *    constant whose value, for the above example, will be "19374". This
 *    particular macro is very dangerous if it were to be used for comparing
 *    version numbers since ordering will not be guaranteed.
 *
 *    Xalan_DLLVersionStr is a preprocessor macro that expands to a string
 *    constant whose value, for the above example, will be "19_3_74". This
 *    macro is provided for backwards compatibility to pre-1.7 versions of
 *    Xalan.
 *
 *  String Constants:
 *
 *    gXalanVersionStr is a global string constant whose value corresponds to
 *    the value "19_3" for the above example.
 *
 *    gXalanFullVersionStr is a global string constant whose value corresponds
 *    to the value "19_3_74" for the above example.
 *
 *  Numeric Constants:
 *
 *    gXalanMajVersion is a global integer constant whose value corresponds to
 *    the major version number. For the above example its value will be 19.
 *
 *    gXalanMinVersion is a global integer constant whose value corresponds to
 *    the minor version number. For the above example its value will be 3.
 *
 *    gXalanRevision is a global integer constant whose value corresponds to
 *    the revision (patch) version number. For the above example its value will
 *    be 74.
 *
 */

// ---------------------------------------------------------------------------
// X A L A N   V E R S I O N   S P E C I F I C A T I O N

/**
 * MODIFY THESE NUMERIC VALUES TO COINCIDE WITH XALAN VERSION
 * AND DO NOT MODIFY ANYTHING ELSE IN THIS VERSION HEADER FILE
 */

#define XALAN_VERSION_MAJOR 1
#define XALAN_VERSION_MINOR 4
#define XALAN_VERSION_REVISION 0

/** DO NOT MODIFY BELOW THIS LINE */

/**
 * MAGIC THAT AUTOMATICALLY GENERATES THE FOLLOWING:
 *
 *	Xalan_DLLVersionStr, gXalanVersionStr, gXalanFullVersionStr,
 *	gXalanMajVersion, gXalanMinVersion, gXalanRevision
 */

// ---------------------------------------------------------------------------
// T W O   A R G U M E N T   C O N C A T E N A T I O N   M A C R O S

// two argument concatenation routines
#define CAT2_SEP_UNDERSCORE(a, b) #a "_" #b
#define CAT2_SEP_PERIOD(a, b) #a "." #b
#define CAT2_SEP_NIL(a, b) #a #b
#define CAT2_RAW_NUMERIC(a, b) a ## b

// two argument macro invokers
#define INVK_CAT2_SEP_UNDERSCORE(a,b) CAT2_SEP_UNDERSCORE(a,b)
#define INVK_CAT2_SEP_PERIOD(a,b)     CAT2_SEP_PERIOD(a,b)
#define INVK_CAT2_STR_SEP_NIL(a,b)    CAT2_SEP_NIL(a,b)
#define INVK_CAT2_RAW_NUMERIC(a,b)    CAT2_RAW_NUMERIC(a,b)

// ---------------------------------------------------------------------------
// T H R E E   A R G U M E N T   C O N C A T E N A T I O N   M A C R O S

// three argument concatenation routines
#define CAT3_SEP_UNDERSCORE(a, b, c) #a "_" #b "_" #c
#define CAT3_SEP_PERIOD(a, b, c) #a "." #b "." #c
#define CAT3_SEP_NIL(a, b, c) #a #b #c
#define CAT3_RAW_NUMERIC(a, b, c) a ## b ## c

// three argument macro invokers
#define INVK_CAT3_SEP_UNDERSCORE(a,b,c) CAT3_SEP_UNDERSCORE(a,b,c)
#define INVK_CAT3_SEP_PERIOD(a,b,c)     CAT3_SEP_PERIOD(a,b,c)
#define INVK_CAT3_SEP_NIL(a,b,c)        CAT3_SEP_NIL(a,b,c)
#define INVK_CAT3_RAW_NUMERIC(a,b,c)    CAT3_RAW_NUMERIC(a,b,c)

// ---------------------------------------------------------------------------
// C A L C U L A T E   V E R S I O N   -   E X P A N D E D   F O R M

#define MULTIPLY(factor,value) factor * value
#define CALC_EXPANDED_FORM(a,b,c) ( MULTIPLY(10000,a) + MULTIPLY(100,b) + MULTIPLY(1,c) )

// ---------------------------------------------------------------------------
// X A L A N   V E R S I O N   I N F O R M A T I O N

// Xalan version strings; these particular macros cannot be used for
// conditional compilation as they are not numeric constants

#define XALAN_FULLVERSIONSTR INVK_CAT3_SEP_UNDERSCORE(XALAN_VERSION_MAJOR,XALAN_VERSION_MINOR,XALAN_VERSION_REVISION)
#define XALAN_FULLVERSIONDOT INVK_CAT3_SEP_PERIOD(XALAN_VERSION_MAJOR,XALAN_VERSION_MINOR,XALAN_VERSION_REVISION)
#define XALAN_FULLVERSIONNUM INVK_CAT3_SEP_NIL(XALAN_VERSION_MAJOR,XALAN_VERSION_MINOR,XALAN_VERSION_REVISION)
#define XALAN_VERSIONSTR     INVK_CAT2_SEP_UNDERSCORE(XALAN_VERSION_MAJOR,XALAN_VERSION_MINOR)

// original from Xalan header
#define Xalan_DLLVersionStr XALAN_FULLVERSIONSTR

const char* const    gXalanVersionStr = XALAN_VERSIONSTR;
const char* const    gXalanFullVersionStr = XALAN_FULLVERSIONSTR;
const unsigned int   gXalanMajVersion = XALAN_VERSION_MAJOR;
const unsigned int   gXalanMinVersion = XALAN_VERSION_MINOR;
const unsigned int   gXalanRevision   = XALAN_VERSION_REVISION;

// Xalan version numeric constants that can be used for conditional
// compilation purposes.

#define _XALAN_VERSION CALC_EXPANDED_FORM (XALAN_VERSION_MAJOR,XALAN_VERSION_MINOR,XALAN_VERSION_REVISION)

#endif // XALANVERSION_HEADER_GUARD_1357924680
<commit_msg>Bumped minor version number.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

#if !defined(XALANVERSION_HEADER_GUARD_1357924680)
#define XALANVERSION_HEADER_GUARD_1357924680

// ---------------------------------------------------------------------------
// X A L A N   V E R S I O N   H E A D E R   D O C U M E N T A T I O N

/**
 * User Documentation for Xalan Version Values:
 *
 *
 *
 * Xalan  Notes:
 *
 * Xalan Committers Documentation:
 *
 *    Xalan committers normally only need to modify one or two of the
 *    following macros:
 *
 *      XALAN_VERSION_MAJOR
 *      XALAN_VERSION_MINOR
 *      XALAN_VERSION_REVISION
 *
 *    The integer values of these macros define the Xalan version number. All
 *    other constants and preprocessor macros are automatically generated from
 *    these three definitions.
 *
 * Xalan User Documentation:
 *
 *  The following sections in the user documentation have examples based upon
 *  the following three version input values:
 *
 *    #define XALAN_VERSION_MAJOR 19
 *    #define XALAN_VERSION_MINOR 3
 *    #define XALAN_VERSION_REVISION 74
 *
 *  The minor and revision (patch level) numbers have two digits of resolution
 *  which means that '3' becomes '03' in this example. This policy guarantees
 *  that when using preprocessor macros, version 19.3.74 will be greater than
 *  version 1.94.74 since the first will expand to 190374 and the second to
 *  19474.
 *
 *  Preprocessor Macros:
 *
 *    _XALAN_VERSION defines the primary preprocessor macro that users will
 *    introduce into their code to perform conditional compilation where the
 *    version of Xalan is detected in order to enable or disable version
 *    specific capabilities. The value of _XALAN_VERSION for the above example
 *    will be 190374. To use it a user would perform an operation such as the
 *    following:
 *
 *      #if _XALAN_VERSION >= 190374
 *        // code specific to new version of Xalan...
 *      #else
 *        // old code here...
 *      #endif
 *
 *    XALAN_FULLVERSIONSTR is a preprocessor macro that expands to a string
 *    constant whose value, for the above example, will be "19_3_74".
 *
 *    XALAN_FULLVERSIONDOT is a preprocessor macro that expands to a string
 *    constant whose value, for the above example, will be "19.3.74".
 *
 *    XALAN_VERSIONSTR is a preprocessor macro that expands to a string
 *    constant whose value, for the above example, will be "19374". This
 *    particular macro is very dangerous if it were to be used for comparing
 *    version numbers since ordering will not be guaranteed.
 *
 *    Xalan_DLLVersionStr is a preprocessor macro that expands to a string
 *    constant whose value, for the above example, will be "19_3_74". This
 *    macro is provided for backwards compatibility to pre-1.7 versions of
 *    Xalan.
 *
 *  String Constants:
 *
 *    gXalanVersionStr is a global string constant whose value corresponds to
 *    the value "19_3" for the above example.
 *
 *    gXalanFullVersionStr is a global string constant whose value corresponds
 *    to the value "19_3_74" for the above example.
 *
 *  Numeric Constants:
 *
 *    gXalanMajVersion is a global integer constant whose value corresponds to
 *    the major version number. For the above example its value will be 19.
 *
 *    gXalanMinVersion is a global integer constant whose value corresponds to
 *    the minor version number. For the above example its value will be 3.
 *
 *    gXalanRevision is a global integer constant whose value corresponds to
 *    the revision (patch) version number. For the above example its value will
 *    be 74.
 *
 */

// ---------------------------------------------------------------------------
// X A L A N   V E R S I O N   S P E C I F I C A T I O N

/**
 * MODIFY THESE NUMERIC VALUES TO COINCIDE WITH XALAN VERSION
 * AND DO NOT MODIFY ANYTHING ELSE IN THIS VERSION HEADER FILE
 */

#define XALAN_VERSION_MAJOR 1
#define XALAN_VERSION_MINOR 5
#define XALAN_VERSION_REVISION 0

/** DO NOT MODIFY BELOW THIS LINE */

/**
 * MAGIC THAT AUTOMATICALLY GENERATES THE FOLLOWING:
 *
 *	Xalan_DLLVersionStr, gXalanVersionStr, gXalanFullVersionStr,
 *	gXalanMajVersion, gXalanMinVersion, gXalanRevision
 */

// ---------------------------------------------------------------------------
// T W O   A R G U M E N T   C O N C A T E N A T I O N   M A C R O S

// two argument concatenation routines
#define CAT2_SEP_UNDERSCORE(a, b) #a "_" #b
#define CAT2_SEP_PERIOD(a, b) #a "." #b
#define CAT2_SEP_NIL(a, b) #a #b
#define CAT2_RAW_NUMERIC(a, b) a ## b

// two argument macro invokers
#define INVK_CAT2_SEP_UNDERSCORE(a,b) CAT2_SEP_UNDERSCORE(a,b)
#define INVK_CAT2_SEP_PERIOD(a,b)     CAT2_SEP_PERIOD(a,b)
#define INVK_CAT2_STR_SEP_NIL(a,b)    CAT2_SEP_NIL(a,b)
#define INVK_CAT2_RAW_NUMERIC(a,b)    CAT2_RAW_NUMERIC(a,b)

// ---------------------------------------------------------------------------
// T H R E E   A R G U M E N T   C O N C A T E N A T I O N   M A C R O S

// three argument concatenation routines
#define CAT3_SEP_UNDERSCORE(a, b, c) #a "_" #b "_" #c
#define CAT3_SEP_PERIOD(a, b, c) #a "." #b "." #c
#define CAT3_SEP_NIL(a, b, c) #a #b #c
#define CAT3_RAW_NUMERIC(a, b, c) a ## b ## c

// three argument macro invokers
#define INVK_CAT3_SEP_UNDERSCORE(a,b,c) CAT3_SEP_UNDERSCORE(a,b,c)
#define INVK_CAT3_SEP_PERIOD(a,b,c)     CAT3_SEP_PERIOD(a,b,c)
#define INVK_CAT3_SEP_NIL(a,b,c)        CAT3_SEP_NIL(a,b,c)
#define INVK_CAT3_RAW_NUMERIC(a,b,c)    CAT3_RAW_NUMERIC(a,b,c)

// ---------------------------------------------------------------------------
// C A L C U L A T E   V E R S I O N   -   E X P A N D E D   F O R M

#define MULTIPLY(factor,value) factor * value
#define CALC_EXPANDED_FORM(a,b,c) ( MULTIPLY(10000,a) + MULTIPLY(100,b) + MULTIPLY(1,c) )

// ---------------------------------------------------------------------------
// X A L A N   V E R S I O N   I N F O R M A T I O N

// Xalan version strings; these particular macros cannot be used for
// conditional compilation as they are not numeric constants

#define XALAN_FULLVERSIONSTR INVK_CAT3_SEP_UNDERSCORE(XALAN_VERSION_MAJOR,XALAN_VERSION_MINOR,XALAN_VERSION_REVISION)
#define XALAN_FULLVERSIONDOT INVK_CAT3_SEP_PERIOD(XALAN_VERSION_MAJOR,XALAN_VERSION_MINOR,XALAN_VERSION_REVISION)
#define XALAN_FULLVERSIONNUM INVK_CAT3_SEP_NIL(XALAN_VERSION_MAJOR,XALAN_VERSION_MINOR,XALAN_VERSION_REVISION)
#define XALAN_VERSIONSTR     INVK_CAT2_SEP_UNDERSCORE(XALAN_VERSION_MAJOR,XALAN_VERSION_MINOR)

// original from Xalan header
#define Xalan_DLLVersionStr XALAN_FULLVERSIONSTR

const char* const    gXalanVersionStr = XALAN_VERSIONSTR;
const char* const    gXalanFullVersionStr = XALAN_FULLVERSIONSTR;
const unsigned int   gXalanMajVersion = XALAN_VERSION_MAJOR;
const unsigned int   gXalanMinVersion = XALAN_VERSION_MINOR;
const unsigned int   gXalanRevision   = XALAN_VERSION_REVISION;

// Xalan version numeric constants that can be used for conditional
// compilation purposes.

#define _XALAN_VERSION CALC_EXPANDED_FORM (XALAN_VERSION_MAJOR,XALAN_VERSION_MINOR,XALAN_VERSION_REVISION)

#endif // XALANVERSION_HEADER_GUARD_1357924680
<|endoftext|>


<commit_before>#include <node.h>
#include <nan.h>
#include <memory>

#define TYPECHK(expr, msg) if (! expr) { \
  Nan::ThrowTypeError(msg); \
  return; \
}

extern "C" {
  #include "sss/sss.h"
}

class CreateSharesWorker : public Nan::AsyncWorker {
 public:
  CreateSharesWorker(char* data, uint8_t n, uint8_t k, Nan::Callback *callback)
      : AsyncWorker(callback), n(n), k(k) {
    Nan::HandleScope scope;

    memcpy(this->data, data, sss_MLEN);
  }

  void Execute() {
    this->output = std::unique_ptr<sss_Share[]>{ new sss_Share[n]() };
    sss_create_shares(output.get(), (uint8_t*) data, n, k);
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;
    v8::Isolate* isolate = v8::Isolate::GetCurrent();

    // Copy the output to a list of node.js buffers
    v8::Local<v8::Array> array = v8::Array::New(isolate, n);
    for (size_t idx = 0; idx < n; ++idx) {
      array->Set(idx, Nan::CopyBuffer((char*) output[idx],
        sss_SHARE_LEN).ToLocalChecked());
    }
    v8::Local<v8::Value> argv[] = { array };

    // Call the provided callback
    Nan::Call(**callback, Nan::GetCurrentContext()->Global(), 1, argv);
  }

 private:
  uint8_t n, k;
  char data[sss_MLEN];
  std::unique_ptr<sss_Share[]> output;
};

class CombineSharesWorker : public Nan::AsyncWorker {
 public:
  CombineSharesWorker(std::unique_ptr<v8::Local<v8::Object>[]> &shares,
                      uint8_t k, Nan::Callback *callback)
      : AsyncWorker(callback), k(k) {
    Nan::HandleScope scope;

    this->input = std::unique_ptr<sss_Share[]>{ new sss_Share[k] };
    for (auto idx = 0; idx < k; ++idx) {
      memcpy(&this->input[idx], node::Buffer::Data(shares[idx]), sss_SHARE_LEN);
    }
  }

  void Execute() {
    status = sss_combine_shares((uint8_t*) data, input.get(), k);
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[1];
    if (status == 0) {
      // All went well, call the callback the restored buffer
      argv[0] = Nan::CopyBuffer(data, sss_MLEN).ToLocalChecked();
    } else {
      // Some kind of error occurred, return null
      argv[0] = Nan::Null();
    }
    Nan::Call(**callback, Nan::GetCurrentContext()->Global(), 1, argv);
  }

 private:
  uint8_t k;
  std::unique_ptr<sss_Share[]> input;
  int status;
  char data[sss_MLEN];
};

NAN_METHOD(CreateShares) {
  Nan::HandleScope scope;

  v8::Local<v8::Value> data_val = info[0];
  v8::Local<v8::Value> n_val = info[1];
  v8::Local<v8::Value> k_val = info[2];
  v8::Local<v8::Value> callback_val = info[3];

  // Type check the arguments
  TYPECHK(!data_val->IsUndefined(), "`data` is not defined");
  TYPECHK(!n_val->IsUndefined(), "`n` is not defined");
  TYPECHK(!k_val->IsUndefined(), "`k` is not defined");
  TYPECHK(!callback_val->IsUndefined(), "`callback` is not defined");

  TYPECHK(data_val->IsObject(), "`data` is not an Object");
  v8::Local<v8::Object> data = data_val->ToObject();
  TYPECHK(node::Buffer::HasInstance(data), "`data` must be a Buffer")
  TYPECHK(n_val->IsUint32(), "`n` is not a valid integer");
  uint32_t n = n_val->Uint32Value();
  TYPECHK(k_val->IsUint32(), "`k` is not a valid integer");
  uint32_t k = k_val->Uint32Value();
  TYPECHK(callback_val->IsFunction(), "`callback` must be a function");
  Nan::Callback *callback = new Nan::Callback(callback_val.As<v8::Function>());

  // Check if the buffers have the correct lengths
  if (node::Buffer::Length(data) != sss_MLEN) {
    Nan::ThrowRangeError("`data` buffer size must be exactly 64 bytes");
    return;
  };

  // Check if n and k are correct
  if (n < 1 || n > 255) {
    Nan::ThrowRangeError("`n` must be between 1 and 255");
    return;
  }
  if (k < 1 || k > n) {
    Nan::ThrowRangeError("`k` must be between 1 and n");
    return;
  }

  // Create worker
  CreateSharesWorker* worker = new CreateSharesWorker(
    node::Buffer::Data(data), n, k, callback);
  AsyncQueueWorker(worker);
}

NAN_METHOD(CombineShares) {
  Nan::HandleScope scope;

  v8::Local<v8::Value> shares_val = info[0];
  v8::Local<v8::Value> callback_val = info[1];

  // Type check the argument `shares` and `callback`
  TYPECHK(!shares_val->IsUndefined(), "`shares` is not defined");
  TYPECHK(!callback_val->IsUndefined(), "`callback` is not defined");

  TYPECHK(shares_val->IsObject(), "`shares` is not an initialized Object")
  v8::Local<v8::Object> shares_obj = shares_val->ToObject();
  TYPECHK(shares_val->IsArray(), "`shares` must be an array of buffers");
  v8::Local<v8::Array> shares_arr = shares_obj.As<v8::Array>();
  TYPECHK(callback_val->IsFunction(), "`callback` must be a function");
  Nan::Callback *callback = new Nan::Callback(callback_val.As<v8::Function>());

  // Extract all the share buffers
  auto k = shares_arr->Length();
  std::unique_ptr<v8::Local<v8::Object>[]> shares(new v8::Local<v8::Object>[k]);
  for (auto idx = 0; idx < k; ++idx) {
    shares[idx] = shares_arr->Get(idx)->ToObject();
  }

  // Check if all the elements in the array are buffers
  for (auto idx = 0; idx < k; ++idx) {
    if (!node::Buffer::HasInstance(shares[idx])) {
      Nan::ThrowTypeError("array element is not a buffer");
      return;
    }
  }

  // Check if all the elements in the array are of the correct length
  for (auto idx = 0; idx < k; ++idx) {
    if (node::Buffer::Length(shares[idx]) != sss_SHARE_LEN) {
      Nan::ThrowTypeError("array buffer element is not of the correct length");
      return;
    }
  }

  // Create worker
  CombineSharesWorker* worker = new CombineSharesWorker(shares, k, callback);
  AsyncQueueWorker(worker);
}

void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {
  Nan::HandleScope scope;
  Nan::SetMethod(exports, "createShares", CreateShares);
  Nan::SetMethod(exports, "combineShares", CombineShares);
}

NODE_MODULE(shamirsecretsharing, Initialize)
<commit_msg>Implement hazmat functions in C++<commit_after>#include <node.h>
#include <nan.h>
#include <memory>

#define TYPECHK(expr, msg) if (! expr) { \
  Nan::ThrowTypeError(msg); \
  return; \
}

extern "C" {
  #include "sss/sss.h"
}

class CreateSharesWorker : public Nan::AsyncWorker {
 public:
  CreateSharesWorker(char* data, uint8_t n, uint8_t k, Nan::Callback *callback)
      : AsyncWorker(callback), n(n), k(k) {
    Nan::HandleScope scope;

    memcpy(this->data, data, sss_MLEN);
  }

  void Execute() {
    this->output = std::unique_ptr<sss_Share[]>{ new sss_Share[n]() };
    sss_create_shares(output.get(), (uint8_t*) data, n, k);
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;
    v8::Isolate* isolate = v8::Isolate::GetCurrent();

    // Copy the output to a list of node.js buffers
    v8::Local<v8::Array> array = v8::Array::New(isolate, n);
    for (size_t idx = 0; idx < n; ++idx) {
      array->Set(idx, Nan::CopyBuffer((char*) output[idx],
        sss_SHARE_LEN).ToLocalChecked());
    }
    v8::Local<v8::Value> argv[] = { array };

    // Call the provided callback
    Nan::Call(**callback, Nan::GetCurrentContext()->Global(), 1, argv);
  }

 private:
  uint8_t n, k;
  char data[sss_MLEN];
  std::unique_ptr<sss_Share[]> output;
};

class CombineSharesWorker : public Nan::AsyncWorker {
 public:
  CombineSharesWorker(std::unique_ptr<v8::Local<v8::Object>[]> &shares,
                      uint8_t k, Nan::Callback *callback)
      : AsyncWorker(callback), k(k) {
    Nan::HandleScope scope;

    this->input = std::unique_ptr<sss_Share[]>{ new sss_Share[k] };
    for (auto idx = 0; idx < k; ++idx) {
      memcpy(&this->input[idx], node::Buffer::Data(shares[idx]), sss_SHARE_LEN);
    }
  }

  void Execute() {
    status = sss_combine_shares((uint8_t*) data, input.get(), k);
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[1];
    if (status == 0) {
      // All went well, call the callback the restored buffer
      argv[0] = Nan::CopyBuffer(data, sss_MLEN).ToLocalChecked();
    } else {
      // Some kind of error occurred, return null
      argv[0] = Nan::Null();
    }
    Nan::Call(**callback, Nan::GetCurrentContext()->Global(), 1, argv);
  }

 private:
  uint8_t k;
  std::unique_ptr<sss_Share[]> input;
  int status;
  char data[sss_MLEN];
};

class CreateKeysharesWorker : public Nan::AsyncWorker {
 public:
  CreateKeysharesWorker(char* key, uint8_t n, uint8_t k, Nan::Callback *callback)
      : AsyncWorker(callback), n(n), k(k) {
    Nan::HandleScope scope;

    memcpy(this->key, key, 32);
  }

  void Execute() {
    this->output = std::unique_ptr<sss_Keyshare[]>{ new sss_Keyshare[n]() };
    sss_create_keyshares(output.get(), (uint8_t*) key, n, k);
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;
    v8::Isolate* isolate = v8::Isolate::GetCurrent();

    // Copy the output keyshares to a list of node.js buffers
    v8::Local<v8::Array> array = v8::Array::New(isolate, n);
    for (size_t idx = 0; idx < n; ++idx) {
      array->Set(idx, Nan::CopyBuffer((char*) output[idx],
                 sss_KEYSHARE_LEN).ToLocalChecked());
    }
    v8::Local<v8::Value> argv[] = { array };

    // Call the provided callback
    Nan::Call(**callback, Nan::GetCurrentContext()->Global(), 1, argv);
  }

 private:
  uint8_t n, k;
  char key[32];
  std::unique_ptr<sss_Keyshare[]> output;
};

class CombineKeysharesWorker : public Nan::AsyncWorker {
 public:
  CombineKeysharesWorker(std::unique_ptr<v8::Local<v8::Object>[]> &keyshares,
                         uint8_t k, Nan::Callback *callback)
      : AsyncWorker(callback), k(k) {
    Nan::HandleScope scope;

    this->input = std::unique_ptr<sss_Keyshare[]>{ new sss_Keyshare[k] };
    for (auto idx = 0; idx < k; ++idx) {
      memcpy(&this->input[idx], node::Buffer::Data(keyshares[idx]),
             sss_KEYSHARE_LEN);
    }
  }

  void Execute() {
    sss_combine_keyshares((uint8_t*) key, input.get(), k);
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = { Nan::CopyBuffer(key, 32).ToLocalChecked() };
    Nan::Call(**callback, Nan::GetCurrentContext()->Global(), 1, argv);
  }

 private:
  uint8_t k;
  std::unique_ptr<sss_Keyshare[]> input;
  char key[32];
};

NAN_METHOD(CreateShares) {
  Nan::HandleScope scope;

  v8::Local<v8::Value> data_val = info[0];
  v8::Local<v8::Value> n_val = info[1];
  v8::Local<v8::Value> k_val = info[2];
  v8::Local<v8::Value> callback_val = info[3];

  // Type check the arguments
  TYPECHK(!data_val->IsUndefined(), "`data` is not defined");
  TYPECHK(!n_val->IsUndefined(), "`n` is not defined");
  TYPECHK(!k_val->IsUndefined(), "`k` is not defined");
  TYPECHK(!callback_val->IsUndefined(), "`callback` is not defined");

  TYPECHK(data_val->IsObject(), "`data` is not an Object");
  v8::Local<v8::Object> data = data_val->ToObject();
  TYPECHK(node::Buffer::HasInstance(data), "`data` must be a Buffer")
  TYPECHK(n_val->IsUint32(), "`n` is not a valid integer");
  uint32_t n = n_val->Uint32Value();
  TYPECHK(k_val->IsUint32(), "`k` is not a valid integer");
  uint32_t k = k_val->Uint32Value();
  TYPECHK(callback_val->IsFunction(), "`callback` must be a function");
  Nan::Callback *callback = new Nan::Callback(callback_val.As<v8::Function>());

  // Check if the buffers have the correct lengths
  if (node::Buffer::Length(data) != sss_MLEN) {
    Nan::ThrowRangeError("`data` buffer size must be exactly 64 bytes");
    return;
  };

  // Check if n and k are correct
  if (n < 1 || n > 255) {
    Nan::ThrowRangeError("`n` must be between 1 and 255");
    return;
  }
  if (k < 1 || k > n) {
    Nan::ThrowRangeError("`k` must be between 1 and n");
    return;
  }

  // Create worker
  CreateSharesWorker* worker = new CreateSharesWorker(
    node::Buffer::Data(data), n, k, callback);
  AsyncQueueWorker(worker);
}

NAN_METHOD(CombineShares) {
  Nan::HandleScope scope;

  v8::Local<v8::Value> shares_val = info[0];
  v8::Local<v8::Value> callback_val = info[1];

  // Type check the argument `shares` and `callback`
  TYPECHK(!shares_val->IsUndefined(), "`shares` is not defined");
  TYPECHK(!callback_val->IsUndefined(), "`callback` is not defined");

  TYPECHK(shares_val->IsObject(), "`shares` is not an initialized Object")
  v8::Local<v8::Object> shares_obj = shares_val->ToObject();
  TYPECHK(shares_val->IsArray(), "`shares` must be an array of buffers");
  v8::Local<v8::Array> shares_arr = shares_obj.As<v8::Array>();
  TYPECHK(callback_val->IsFunction(), "`callback` must be a function");
  Nan::Callback *callback = new Nan::Callback(callback_val.As<v8::Function>());

  // Extract all the share buffers
  auto k = shares_arr->Length();
  std::unique_ptr<v8::Local<v8::Object>[]> shares(new v8::Local<v8::Object>[k]);
  for (auto idx = 0; idx < k; ++idx) {
    shares[idx] = shares_arr->Get(idx)->ToObject();
  }

  // Check if all the elements in the array are buffers
  for (auto idx = 0; idx < k; ++idx) {
    if (!node::Buffer::HasInstance(shares[idx])) {
      Nan::ThrowTypeError("array element is not a buffer");
      return;
    }
  }

  // Check if all the elements in the array are of the correct length
  for (auto idx = 0; idx < k; ++idx) {
    if (node::Buffer::Length(shares[idx]) != sss_SHARE_LEN) {
      Nan::ThrowTypeError("array buffer element is not of the correct length");
      return;
    }
  }

  // Create worker
  CombineSharesWorker* worker = new CombineSharesWorker(shares, k, callback);
  AsyncQueueWorker(worker);
}

NAN_METHOD(CreateKeyshares) {
  Nan::HandleScope scope;

  v8::Local<v8::Value> key_val = info[0];
  v8::Local<v8::Value> n_val = info[1];
  v8::Local<v8::Value> k_val = info[2];
  v8::Local<v8::Value> callback_val = info[3];

  // Type check the arguments
  TYPECHK(!key_val->IsUndefined(), "`key` is not defined");
  TYPECHK(!n_val->IsUndefined(), "`n` is not defined");
  TYPECHK(!k_val->IsUndefined(), "`k` is not defined");
  TYPECHK(!callback_val->IsUndefined(), "`callback` is not defined");

  TYPECHK(key_val->IsObject(), "`key` is not an Object");
  v8::Local<v8::Object> key = key_val->ToObject();
  TYPECHK(node::Buffer::HasInstance(key), "`key` must be a Buffer")
  TYPECHK(n_val->IsUint32(), "`n` is not a valid integer");
  uint32_t n = n_val->Uint32Value();
  TYPECHK(k_val->IsUint32(), "`k` is not a valid integer");
  uint32_t k = k_val->Uint32Value();
  TYPECHK(callback_val->IsFunction(), "`callback` must be a function");
  Nan::Callback *callback = new Nan::Callback(callback_val.As<v8::Function>());

  // Check if the buffers have the correct lengths
  if (node::Buffer::Length(key) != 32) {
    Nan::ThrowRangeError("`key` buffer size must be exactly 32 bytes");
    return;
  };

  // Check if n and k are correct
  if (n < 1 || n > 255) {
    Nan::ThrowRangeError("`n` must be between 1 and 255");
    return;
  }
  if (k < 1 || k > n) {
    Nan::ThrowRangeError("`k` must be between 1 and n");
    return;
  }

  // Create worker
  CreateKeysharesWorker* worker = new CreateKeysharesWorker(
    node::Buffer::Data(key), n, k, callback);
  AsyncQueueWorker(worker);
}

NAN_METHOD(CombineKeyshares) {
  Nan::HandleScope scope;

  v8::Local<v8::Value> keyshares_val = info[0];
  v8::Local<v8::Value> callback_val = info[1];

  // Type check the argument `keyshares` and `callback`
  TYPECHK(!keyshares_val->IsUndefined(), "`keyshares` is not defined");
  TYPECHK(!callback_val->IsUndefined(), "`callback` is not defined");

  TYPECHK(keyshares_val->IsObject(), "`keyshares` is not an initialized Object")
  v8::Local<v8::Object> keyshares_obj = keyshares_val->ToObject();
  TYPECHK(keyshares_val->IsArray(), "`keyshares` must be an array of buffers");
  v8::Local<v8::Array> keyshares_arr = keyshares_obj.As<v8::Array>();
  TYPECHK(callback_val->IsFunction(), "`callback` must be a function");
  Nan::Callback *callback = new Nan::Callback(callback_val.As<v8::Function>());

  // Extract all the share buffers
  auto k = keyshares_arr->Length();
  std::unique_ptr<v8::Local<v8::Object>[]> keyshares(new v8::Local<v8::Object>[k]);
  for (auto idx = 0; idx < k; ++idx) {
    keyshares[idx] = keyshares_arr->Get(idx)->ToObject();
  }

  // Check if all the elements in the array are buffers
  for (auto idx = 0; idx < k; ++idx) {
    if (!node::Buffer::HasInstance(keyshares[idx])) {
      Nan::ThrowTypeError("array element is not a buffer");
      return;
    }
  }

  // Check if all the elements in the array are of the correct length
  for (auto idx = 0; idx < k; ++idx) {
    if (node::Buffer::Length(keyshares[idx]) != sss_KEYSHARE_LEN) {
      Nan::ThrowTypeError("array buffer element is not of the correct length");
      return;
    }
  }

  // Create worker
  CombineKeysharesWorker* worker = new CombineKeysharesWorker(keyshares, k, callback);
  AsyncQueueWorker(worker);
}

void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {
  Nan::HandleScope scope;
  Nan::SetMethod(exports, "createShares", CreateShares);
  Nan::SetMethod(exports, "combineShares", CombineShares);
  Nan::SetMethod(exports, "createKeyshares", CreateKeyshares);
  Nan::SetMethod(exports, "combineKeyshares", CombineKeyshares);
}

NODE_MODULE(shamirsecretsharing, Initialize)
<|endoftext|>