<commit_before>/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS 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 COPYRIGHT
 * OWNER OR 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.
 *
 */

#include <node.h>
#include <nan.h>
#include <v8.h>
#include "grpc/grpc.h"

#include "call.h"
#include "channel.h"
#include "server.h"
#include "completion_queue_async_worker.h"
#include "credentials.h"
#include "server_credentials.h"

using v8::Local;
using v8::Value;
using v8::Object;
using v8::Uint32;
using v8::String;

void InitStatusConstants(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> status = Nan::New<Object>();
  Nan::Set(exports, Nan::New("status").ToLocalChecked(), status);
  Local<Value> OK(Nan::New<Uint32, uint32_t>(GRPC_STATUS_OK));
  Nan::Set(status, Nan::New("OK").ToLocalChecked(), OK);
  Local<Value> CANCELLED(Nan::New<Uint32, uint32_t>(GRPC_STATUS_CANCELLED));
  Nan::Set(status, Nan::New("CANCELLED").ToLocalChecked(), CANCELLED);
  Local<Value> UNKNOWN(Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNKNOWN));
  Nan::Set(status, Nan::New("UNKNOWN").ToLocalChecked(), UNKNOWN);
  Local<Value> INVALID_ARGUMENT(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_INVALID_ARGUMENT));
  Nan::Set(status, Nan::New("INVALID_ARGUMENT").ToLocalChecked(),
           INVALID_ARGUMENT);
  Local<Value> DEADLINE_EXCEEDED(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_DEADLINE_EXCEEDED));
  Nan::Set(status, Nan::New("DEADLINE_EXCEEDED").ToLocalChecked(),
           DEADLINE_EXCEEDED);
  Local<Value> NOT_FOUND(Nan::New<Uint32, uint32_t>(GRPC_STATUS_NOT_FOUND));
  Nan::Set(status, Nan::New("NOT_FOUND").ToLocalChecked(), NOT_FOUND);
  Local<Value> ALREADY_EXISTS(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_ALREADY_EXISTS));
  Nan::Set(status, Nan::New("ALREADY_EXISTS").ToLocalChecked(), ALREADY_EXISTS);
  Local<Value> PERMISSION_DENIED(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_PERMISSION_DENIED));
  Nan::Set(status, Nan::New("PERMISSION_DENIED").ToLocalChecked(),
           PERMISSION_DENIED);
  Local<Value> UNAUTHENTICATED(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNAUTHENTICATED));
  Nan::Set(status, Nan::New("UNAUTHENTICATED").ToLocalChecked(),
           UNAUTHENTICATED);
  Local<Value> RESOURCE_EXHAUSTED(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_RESOURCE_EXHAUSTED));
  Nan::Set(status, Nan::New("RESOURCE_EXHAUSTED").ToLocalChecked(),
           RESOURCE_EXHAUSTED);
  Local<Value> FAILED_PRECONDITION(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_FAILED_PRECONDITION));
  Nan::Set(status, Nan::New("FAILED_PRECONDITION").ToLocalChecked(),
           FAILED_PRECONDITION);
  Local<Value> ABORTED(Nan::New<Uint32, uint32_t>(GRPC_STATUS_ABORTED));
  Nan::Set(status, Nan::New("ABORTED").ToLocalChecked(), ABORTED);
  Local<Value> OUT_OF_RANGE(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_OUT_OF_RANGE));
  Nan::Set(status, Nan::New("OUT_OF_RANGE").ToLocalChecked(), OUT_OF_RANGE);
  Local<Value> UNIMPLEMENTED(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNIMPLEMENTED));
  Nan::Set(status, Nan::New("UNIMPLEMENTED").ToLocalChecked(), UNIMPLEMENTED);
  Local<Value> INTERNAL(Nan::New<Uint32, uint32_t>(GRPC_STATUS_INTERNAL));
  Nan::Set(status, Nan::New("INTERNAL").ToLocalChecked(), INTERNAL);
  Local<Value> UNAVAILABLE(Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNAVAILABLE));
  Nan::Set(status, Nan::New("UNAVAILABLE").ToLocalChecked(), UNAVAILABLE);
  Local<Value> DATA_LOSS(Nan::New<Uint32, uint32_t>(GRPC_STATUS_DATA_LOSS));
  Nan::Set(status, Nan::New("DATA_LOSS").ToLocalChecked(), DATA_LOSS);
}

void InitCallErrorConstants(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> call_error = Nan::New<Object>();
  Nan::Set(exports, Nan::New("callError").ToLocalChecked(), call_error);
  Local<Value> OK(Nan::New<Uint32, uint32_t>(GRPC_CALL_OK));
  Nan::Set(call_error, Nan::New("OK").ToLocalChecked(), OK);
  Local<Value> ERROR(Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR));
  Nan::Set(call_error, Nan::New("ERROR").ToLocalChecked(), ERROR);
  Local<Value> NOT_ON_SERVER(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_SERVER));
  Nan::Set(call_error, Nan::New("NOT_ON_SERVER").ToLocalChecked(),
           NOT_ON_SERVER);
  Local<Value> NOT_ON_CLIENT(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_CLIENT));
  Nan::Set(call_error, Nan::New("NOT_ON_CLIENT").ToLocalChecked(),
           NOT_ON_CLIENT);
  Local<Value> ALREADY_INVOKED(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_INVOKED));
  Nan::Set(call_error, Nan::New("ALREADY_INVOKED").ToLocalChecked(),
           ALREADY_INVOKED);
  Local<Value> NOT_INVOKED(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_INVOKED));
  Nan::Set(call_error, Nan::New("NOT_INVOKED").ToLocalChecked(), NOT_INVOKED);
  Local<Value> ALREADY_FINISHED(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_FINISHED));
  Nan::Set(call_error, Nan::New("ALREADY_FINISHED").ToLocalChecked(),
           ALREADY_FINISHED);
  Local<Value> TOO_MANY_OPERATIONS(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS));
  Nan::Set(call_error, Nan::New("TOO_MANY_OPERATIONS").ToLocalChecked(),
           TOO_MANY_OPERATIONS);
  Local<Value> INVALID_FLAGS(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_INVALID_FLAGS));
  Nan::Set(call_error, Nan::New("INVALID_FLAGS").ToLocalChecked(),
           INVALID_FLAGS);
}

void InitOpTypeConstants(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> op_type = Nan::New<Object>();
  Nan::Set(exports, Nan::New("opType").ToLocalChecked(), op_type);
  Local<Value> SEND_INITIAL_METADATA(
      Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_INITIAL_METADATA));
  Nan::Set(op_type, Nan::New("SEND_INITIAL_METADATA").ToLocalChecked(),
           SEND_INITIAL_METADATA);
  Local<Value> SEND_MESSAGE(
      Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_MESSAGE));
  Nan::Set(op_type, Nan::New("SEND_MESSAGE").ToLocalChecked(), SEND_MESSAGE);
  Local<Value> SEND_CLOSE_FROM_CLIENT(
      Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_CLOSE_FROM_CLIENT));
  Nan::Set(op_type, Nan::New("SEND_CLOSE_FROM_CLIENT").ToLocalChecked(),
           SEND_CLOSE_FROM_CLIENT);
  Local<Value> SEND_STATUS_FROM_SERVER(
      Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_STATUS_FROM_SERVER));
  Nan::Set(op_type, Nan::New("SEND_STATUS_FROM_SERVER").ToLocalChecked(),
           SEND_STATUS_FROM_SERVER);
  Local<Value> RECV_INITIAL_METADATA(
      Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_INITIAL_METADATA));
  Nan::Set(op_type, Nan::New("RECV_INITIAL_METADATA").ToLocalChecked(),
           RECV_INITIAL_METADATA);
  Local<Value> RECV_MESSAGE(
      Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_MESSAGE));
  Nan::Set(op_type, Nan::New("RECV_MESSAGE").ToLocalChecked(), RECV_MESSAGE);
  Local<Value> RECV_STATUS_ON_CLIENT(
      Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_STATUS_ON_CLIENT));
  Nan::Set(op_type, Nan::New("RECV_STATUS_ON_CLIENT").ToLocalChecked(),
           RECV_STATUS_ON_CLIENT);
  Local<Value> RECV_CLOSE_ON_SERVER(
      Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_CLOSE_ON_SERVER));
  Nan::Set(op_type, Nan::New("RECV_CLOSE_ON_SERVER").ToLocalChecked(),
           RECV_CLOSE_ON_SERVER);
}

void InitPropagateConstants(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> propagate = Nan::New<Object>();
  Nan::Set(exports, Nan::New("propagate").ToLocalChecked(), propagate);
  Local<Value> DEADLINE(Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_DEADLINE));
  Nan::Set(propagate, Nan::New("DEADLINE").ToLocalChecked(), DEADLINE);
  Local<Value> CENSUS_STATS_CONTEXT(
      Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CENSUS_STATS_CONTEXT));
  Nan::Set(propagate, Nan::New("CENSUS_STATS_CONTEXT").ToLocalChecked(),
           CENSUS_STATS_CONTEXT);
  Local<Value> CENSUS_TRACING_CONTEXT(
      Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT));
  Nan::Set(propagate, Nan::New("CENSUS_TRACING_CONTEXT").ToLocalChecked(),
           CENSUS_TRACING_CONTEXT);
  Local<Value> CANCELLATION(
      Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CANCELLATION));
  Nan::Set(propagate, Nan::New("CANCELLATION").ToLocalChecked(), CANCELLATION);
  Local<Value> DEFAULTS(Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_DEFAULTS));
  Nan::Set(propagate, Nan::New("DEFAULTS").ToLocalChecked(), DEFAULTS);
}

void InitConnectivityStateConstants(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> channel_state = Nan::New<Object>();
  Nan::Set(exports, Nan::New("connectivityState").ToLocalChecked(),
           channel_state);
  Local<Value> IDLE(Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_IDLE));
  Nan::Set(channel_state, Nan::New("IDLE").ToLocalChecked(), IDLE);
  Local<Value> CONNECTING(Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_CONNECTING));
  Nan::Set(channel_state, Nan::New("CONNECTING").ToLocalChecked(), CONNECTING);
  Local<Value> READY(Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_READY));
  Nan::Set(channel_state, Nan::New("READY").ToLocalChecked(), READY);
  Local<Value> TRANSIENT_FAILURE(
      Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_TRANSIENT_FAILURE));
  Nan::Set(channel_state, Nan::New("TRANSIENT_FAILURE").ToLocalChecked(),
           TRANSIENT_FAILURE);
  Local<Value> FATAL_FAILURE(
      Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_FATAL_FAILURE));
  Nan::Set(channel_state, Nan::New("FATAL_FAILURE").ToLocalChecked(),
           FATAL_FAILURE);
}

void InitWriteFlags(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> write_flags = Nan::New<Object>();
  Nan::Set(exports, Nan::New("writeFlags").ToLocalChecked(), write_flags);
  Local<Value> BUFFER_HINT(Nan::New<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT));
  Nan::Set(write_flags, Nan::New("BUFFER_HINT").ToLocalChecked(), BUFFER_HINT);
  Local<Value> NO_COMPRESS(Nan::New<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS));
  Nan::Set(write_flags, Nan::New("NO_COMPRESS").ToLocalChecked(), NO_COMPRESS);
}

void init(Local<Object> exports) {
  Nan::HandleScope scope;
  grpc_init();
  InitStatusConstants(exports);
  InitCallErrorConstants(exports);
  InitOpTypeConstants(exports);
  InitPropagateConstants(exports);
  InitConnectivityStateConstants(exports);
  InitWriteFlags(exports);

  grpc::node::Call::Init(exports);
  grpc::node::Channel::Init(exports);
  grpc::node::Server::Init(exports);
  grpc::node::CompletionQueueAsyncWorker::Init(exports);
  grpc::node::Credentials::Init(exports);
  grpc::node::ServerCredentials::Init(exports);
}

NODE_MODULE(grpc, init)
<commit_msg>Fixed node extension module name<commit_after>/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS 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 COPYRIGHT
 * OWNER OR 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.
 *
 */

#include <node.h>
#include <nan.h>
#include <v8.h>
#include "grpc/grpc.h"

#include "call.h"
#include "channel.h"
#include "server.h"
#include "completion_queue_async_worker.h"
#include "credentials.h"
#include "server_credentials.h"

using v8::Local;
using v8::Value;
using v8::Object;
using v8::Uint32;
using v8::String;

void InitStatusConstants(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> status = Nan::New<Object>();
  Nan::Set(exports, Nan::New("status").ToLocalChecked(), status);
  Local<Value> OK(Nan::New<Uint32, uint32_t>(GRPC_STATUS_OK));
  Nan::Set(status, Nan::New("OK").ToLocalChecked(), OK);
  Local<Value> CANCELLED(Nan::New<Uint32, uint32_t>(GRPC_STATUS_CANCELLED));
  Nan::Set(status, Nan::New("CANCELLED").ToLocalChecked(), CANCELLED);
  Local<Value> UNKNOWN(Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNKNOWN));
  Nan::Set(status, Nan::New("UNKNOWN").ToLocalChecked(), UNKNOWN);
  Local<Value> INVALID_ARGUMENT(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_INVALID_ARGUMENT));
  Nan::Set(status, Nan::New("INVALID_ARGUMENT").ToLocalChecked(),
           INVALID_ARGUMENT);
  Local<Value> DEADLINE_EXCEEDED(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_DEADLINE_EXCEEDED));
  Nan::Set(status, Nan::New("DEADLINE_EXCEEDED").ToLocalChecked(),
           DEADLINE_EXCEEDED);
  Local<Value> NOT_FOUND(Nan::New<Uint32, uint32_t>(GRPC_STATUS_NOT_FOUND));
  Nan::Set(status, Nan::New("NOT_FOUND").ToLocalChecked(), NOT_FOUND);
  Local<Value> ALREADY_EXISTS(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_ALREADY_EXISTS));
  Nan::Set(status, Nan::New("ALREADY_EXISTS").ToLocalChecked(), ALREADY_EXISTS);
  Local<Value> PERMISSION_DENIED(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_PERMISSION_DENIED));
  Nan::Set(status, Nan::New("PERMISSION_DENIED").ToLocalChecked(),
           PERMISSION_DENIED);
  Local<Value> UNAUTHENTICATED(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNAUTHENTICATED));
  Nan::Set(status, Nan::New("UNAUTHENTICATED").ToLocalChecked(),
           UNAUTHENTICATED);
  Local<Value> RESOURCE_EXHAUSTED(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_RESOURCE_EXHAUSTED));
  Nan::Set(status, Nan::New("RESOURCE_EXHAUSTED").ToLocalChecked(),
           RESOURCE_EXHAUSTED);
  Local<Value> FAILED_PRECONDITION(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_FAILED_PRECONDITION));
  Nan::Set(status, Nan::New("FAILED_PRECONDITION").ToLocalChecked(),
           FAILED_PRECONDITION);
  Local<Value> ABORTED(Nan::New<Uint32, uint32_t>(GRPC_STATUS_ABORTED));
  Nan::Set(status, Nan::New("ABORTED").ToLocalChecked(), ABORTED);
  Local<Value> OUT_OF_RANGE(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_OUT_OF_RANGE));
  Nan::Set(status, Nan::New("OUT_OF_RANGE").ToLocalChecked(), OUT_OF_RANGE);
  Local<Value> UNIMPLEMENTED(
      Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNIMPLEMENTED));
  Nan::Set(status, Nan::New("UNIMPLEMENTED").ToLocalChecked(), UNIMPLEMENTED);
  Local<Value> INTERNAL(Nan::New<Uint32, uint32_t>(GRPC_STATUS_INTERNAL));
  Nan::Set(status, Nan::New("INTERNAL").ToLocalChecked(), INTERNAL);
  Local<Value> UNAVAILABLE(Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNAVAILABLE));
  Nan::Set(status, Nan::New("UNAVAILABLE").ToLocalChecked(), UNAVAILABLE);
  Local<Value> DATA_LOSS(Nan::New<Uint32, uint32_t>(GRPC_STATUS_DATA_LOSS));
  Nan::Set(status, Nan::New("DATA_LOSS").ToLocalChecked(), DATA_LOSS);
}

void InitCallErrorConstants(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> call_error = Nan::New<Object>();
  Nan::Set(exports, Nan::New("callError").ToLocalChecked(), call_error);
  Local<Value> OK(Nan::New<Uint32, uint32_t>(GRPC_CALL_OK));
  Nan::Set(call_error, Nan::New("OK").ToLocalChecked(), OK);
  Local<Value> ERROR(Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR));
  Nan::Set(call_error, Nan::New("ERROR").ToLocalChecked(), ERROR);
  Local<Value> NOT_ON_SERVER(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_SERVER));
  Nan::Set(call_error, Nan::New("NOT_ON_SERVER").ToLocalChecked(),
           NOT_ON_SERVER);
  Local<Value> NOT_ON_CLIENT(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_CLIENT));
  Nan::Set(call_error, Nan::New("NOT_ON_CLIENT").ToLocalChecked(),
           NOT_ON_CLIENT);
  Local<Value> ALREADY_INVOKED(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_INVOKED));
  Nan::Set(call_error, Nan::New("ALREADY_INVOKED").ToLocalChecked(),
           ALREADY_INVOKED);
  Local<Value> NOT_INVOKED(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_INVOKED));
  Nan::Set(call_error, Nan::New("NOT_INVOKED").ToLocalChecked(), NOT_INVOKED);
  Local<Value> ALREADY_FINISHED(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_FINISHED));
  Nan::Set(call_error, Nan::New("ALREADY_FINISHED").ToLocalChecked(),
           ALREADY_FINISHED);
  Local<Value> TOO_MANY_OPERATIONS(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS));
  Nan::Set(call_error, Nan::New("TOO_MANY_OPERATIONS").ToLocalChecked(),
           TOO_MANY_OPERATIONS);
  Local<Value> INVALID_FLAGS(
      Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_INVALID_FLAGS));
  Nan::Set(call_error, Nan::New("INVALID_FLAGS").ToLocalChecked(),
           INVALID_FLAGS);
}

void InitOpTypeConstants(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> op_type = Nan::New<Object>();
  Nan::Set(exports, Nan::New("opType").ToLocalChecked(), op_type);
  Local<Value> SEND_INITIAL_METADATA(
      Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_INITIAL_METADATA));
  Nan::Set(op_type, Nan::New("SEND_INITIAL_METADATA").ToLocalChecked(),
           SEND_INITIAL_METADATA);
  Local<Value> SEND_MESSAGE(
      Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_MESSAGE));
  Nan::Set(op_type, Nan::New("SEND_MESSAGE").ToLocalChecked(), SEND_MESSAGE);
  Local<Value> SEND_CLOSE_FROM_CLIENT(
      Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_CLOSE_FROM_CLIENT));
  Nan::Set(op_type, Nan::New("SEND_CLOSE_FROM_CLIENT").ToLocalChecked(),
           SEND_CLOSE_FROM_CLIENT);
  Local<Value> SEND_STATUS_FROM_SERVER(
      Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_STATUS_FROM_SERVER));
  Nan::Set(op_type, Nan::New("SEND_STATUS_FROM_SERVER").ToLocalChecked(),
           SEND_STATUS_FROM_SERVER);
  Local<Value> RECV_INITIAL_METADATA(
      Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_INITIAL_METADATA));
  Nan::Set(op_type, Nan::New("RECV_INITIAL_METADATA").ToLocalChecked(),
           RECV_INITIAL_METADATA);
  Local<Value> RECV_MESSAGE(
      Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_MESSAGE));
  Nan::Set(op_type, Nan::New("RECV_MESSAGE").ToLocalChecked(), RECV_MESSAGE);
  Local<Value> RECV_STATUS_ON_CLIENT(
      Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_STATUS_ON_CLIENT));
  Nan::Set(op_type, Nan::New("RECV_STATUS_ON_CLIENT").ToLocalChecked(),
           RECV_STATUS_ON_CLIENT);
  Local<Value> RECV_CLOSE_ON_SERVER(
      Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_CLOSE_ON_SERVER));
  Nan::Set(op_type, Nan::New("RECV_CLOSE_ON_SERVER").ToLocalChecked(),
           RECV_CLOSE_ON_SERVER);
}

void InitPropagateConstants(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> propagate = Nan::New<Object>();
  Nan::Set(exports, Nan::New("propagate").ToLocalChecked(), propagate);
  Local<Value> DEADLINE(Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_DEADLINE));
  Nan::Set(propagate, Nan::New("DEADLINE").ToLocalChecked(), DEADLINE);
  Local<Value> CENSUS_STATS_CONTEXT(
      Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CENSUS_STATS_CONTEXT));
  Nan::Set(propagate, Nan::New("CENSUS_STATS_CONTEXT").ToLocalChecked(),
           CENSUS_STATS_CONTEXT);
  Local<Value> CENSUS_TRACING_CONTEXT(
      Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT));
  Nan::Set(propagate, Nan::New("CENSUS_TRACING_CONTEXT").ToLocalChecked(),
           CENSUS_TRACING_CONTEXT);
  Local<Value> CANCELLATION(
      Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CANCELLATION));
  Nan::Set(propagate, Nan::New("CANCELLATION").ToLocalChecked(), CANCELLATION);
  Local<Value> DEFAULTS(Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_DEFAULTS));
  Nan::Set(propagate, Nan::New("DEFAULTS").ToLocalChecked(), DEFAULTS);
}

void InitConnectivityStateConstants(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> channel_state = Nan::New<Object>();
  Nan::Set(exports, Nan::New("connectivityState").ToLocalChecked(),
           channel_state);
  Local<Value> IDLE(Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_IDLE));
  Nan::Set(channel_state, Nan::New("IDLE").ToLocalChecked(), IDLE);
  Local<Value> CONNECTING(Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_CONNECTING));
  Nan::Set(channel_state, Nan::New("CONNECTING").ToLocalChecked(), CONNECTING);
  Local<Value> READY(Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_READY));
  Nan::Set(channel_state, Nan::New("READY").ToLocalChecked(), READY);
  Local<Value> TRANSIENT_FAILURE(
      Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_TRANSIENT_FAILURE));
  Nan::Set(channel_state, Nan::New("TRANSIENT_FAILURE").ToLocalChecked(),
           TRANSIENT_FAILURE);
  Local<Value> FATAL_FAILURE(
      Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_FATAL_FAILURE));
  Nan::Set(channel_state, Nan::New("FATAL_FAILURE").ToLocalChecked(),
           FATAL_FAILURE);
}

void InitWriteFlags(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<Object> write_flags = Nan::New<Object>();
  Nan::Set(exports, Nan::New("writeFlags").ToLocalChecked(), write_flags);
  Local<Value> BUFFER_HINT(Nan::New<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT));
  Nan::Set(write_flags, Nan::New("BUFFER_HINT").ToLocalChecked(), BUFFER_HINT);
  Local<Value> NO_COMPRESS(Nan::New<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS));
  Nan::Set(write_flags, Nan::New("NO_COMPRESS").ToLocalChecked(), NO_COMPRESS);
}

void init(Local<Object> exports) {
  Nan::HandleScope scope;
  grpc_init();
  InitStatusConstants(exports);
  InitCallErrorConstants(exports);
  InitOpTypeConstants(exports);
  InitPropagateConstants(exports);
  InitConnectivityStateConstants(exports);
  InitWriteFlags(exports);

  grpc::node::Call::Init(exports);
  grpc::node::Channel::Init(exports);
  grpc::node::Server::Init(exports);
  grpc::node::CompletionQueueAsyncWorker::Init(exports);
  grpc::node::Credentials::Init(exports);
  grpc::node::ServerCredentials::Init(exports);
}

NODE_MODULE(grpc_node, init)
<|endoftext|>


<commit_before>// vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:
/*
Copyright (c) 2008 Aristid Breitkreuz, Ruediger Sonderfeld

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "flusspferd/xml/xml.hpp"
#include "flusspferd/xml/context.hpp"
#include "flusspferd/xml/parse.hpp"
#include "flusspferd/xml/push_parser.hpp"
#include "flusspferd/xml/node.hpp"
#include "flusspferd/xml/document.hpp"
#include "flusspferd/xml/text.hpp"
#include "flusspferd/xml/namespace.hpp"
#include "flusspferd/xml/reference.hpp"
#include "flusspferd/xml/attribute.hpp"
#include "flusspferd/xml/processing_instruction.hpp"
#include "flusspferd/function_adapter.hpp"
#include "flusspferd/local_root_scope.hpp"
#include "flusspferd/create.hpp"
#include "flusspferd/string.hpp"
#include "flusspferd/security.hpp"
#include <boost/thread/once.hpp>
#include <libxml/xmlIO.h>
#include <libxml/xpath.h>

using namespace flusspferd;
using namespace flusspferd::xml;

extern "C" value flusspferd_load(object container)
{
  return load_xml(container);
}

static void safety_io_callbacks();

static void once() {
  LIBXML_TEST_VERSION
  xmlXPathInit();
  safety_io_callbacks();
}

static boost::once_flag once_flag;

object flusspferd::xml::load_xml(object container) {
  local_root_scope scope;

  value previous = container.get_property("XML");

  if (previous.is_object())
    return previous.to_object();

  boost::call_once(once_flag, &once);

  object XML = flusspferd::create_object();

  load_class<node>(XML);
  load_class<document>(XML);
  load_class<text>(XML);
  load_class<comment>(XML);
  load_class<cdata_section>(XML);
  load_class<reference_>(XML);
  load_class<processing_instruction>(XML);
  load_class<attribute_>(XML);
  load_class<namespace_>(XML);

  load_class<push_parser>(XML);

  create_native_function(XML, "parseBlob", &parse_blob);
  create_native_function(XML, "parseFile", &parse_file);

  load_class<xml::context>(XML);

  container.define_property(
    "XML",
    XML,
    object::read_only_property | object::dont_enumerate);

  return XML;
}

template<int (*OldMatch)(char const *), unsigned mode>
static int safety_match(char const *name) {
  if (!OldMatch(name))
    return 0;

  std::string path(name);

  std::size_t nonletter = path.find_first_not_of(
    "abcdefghijklmnopqrstuvwxyz"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ");

  if (nonletter != 0 && nonletter != std::string::npos &&
      path[nonletter] == ':')
    return flusspferd::security::get().check_url(path, mode);
  else
    return flusspferd::security::get().check_path(path, mode);
}

static void safety_io_callbacks() {
  xmlCleanupInputCallbacks();
  xmlCleanupOutputCallbacks();

#define REG_INPUT(name) \
  xmlRegisterInputCallbacks( \
    safety_match<name ## Match, security::READ>, \
    name ## Open, \
    name ## Read, \
    name ## Close) \
  /**/

  REG_INPUT(xmlFile);
  REG_INPUT(xmlIOHTTP);

  // disable any output
  xmlRegisterOutputCallbacks(0, 0, 0, 0);
}
<commit_msg>XML: forgot BOOST_ONCE_INIT<commit_after>// vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:
/*
Copyright (c) 2008 Aristid Breitkreuz, Ruediger Sonderfeld

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "flusspferd/xml/xml.hpp"
#include "flusspferd/xml/context.hpp"
#include "flusspferd/xml/parse.hpp"
#include "flusspferd/xml/push_parser.hpp"
#include "flusspferd/xml/node.hpp"
#include "flusspferd/xml/document.hpp"
#include "flusspferd/xml/text.hpp"
#include "flusspferd/xml/namespace.hpp"
#include "flusspferd/xml/reference.hpp"
#include "flusspferd/xml/attribute.hpp"
#include "flusspferd/xml/processing_instruction.hpp"
#include "flusspferd/function_adapter.hpp"
#include "flusspferd/local_root_scope.hpp"
#include "flusspferd/create.hpp"
#include "flusspferd/string.hpp"
#include "flusspferd/security.hpp"
#include <boost/thread/once.hpp>
#include <libxml/xmlIO.h>
#include <libxml/xpath.h>

using namespace flusspferd;
using namespace flusspferd::xml;

extern "C" value flusspferd_load(object container)
{
  return load_xml(container);
}

static void safety_io_callbacks();

static void once() {
  LIBXML_TEST_VERSION
  xmlXPathInit();
  safety_io_callbacks();
}

static boost::once_flag once_flag = BOOST_ONCE_INIT;

object flusspferd::xml::load_xml(object container) {
  local_root_scope scope;

  value previous = container.get_property("XML");

  if (previous.is_object())
    return previous.to_object();

  boost::call_once(once_flag, &once);

  object XML = flusspferd::create_object();

  load_class<node>(XML);
  load_class<document>(XML);
  load_class<text>(XML);
  load_class<comment>(XML);
  load_class<cdata_section>(XML);
  load_class<reference_>(XML);
  load_class<processing_instruction>(XML);
  load_class<attribute_>(XML);
  load_class<namespace_>(XML);

  load_class<push_parser>(XML);

  create_native_function(XML, "parseBlob", &parse_blob);
  create_native_function(XML, "parseFile", &parse_file);

  load_class<xml::context>(XML);

  container.define_property(
    "XML",
    XML,
    object::read_only_property | object::dont_enumerate);

  return XML;
}

template<int (*OldMatch)(char const *), unsigned mode>
static int safety_match(char const *name) {
  if (!OldMatch(name))
    return 0;

  std::string path(name);

  std::size_t nonletter = path.find_first_not_of(
    "abcdefghijklmnopqrstuvwxyz"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ");

  if (nonletter != 0 && nonletter != std::string::npos &&
      path[nonletter] == ':')
    return flusspferd::security::get().check_url(path, mode);
  else
    return flusspferd::security::get().check_path(path, mode);
}

static void safety_io_callbacks() {
  xmlCleanupInputCallbacks();
  xmlCleanupOutputCallbacks();

#define REG_INPUT(name) \
  xmlRegisterInputCallbacks( \
    safety_match<name ## Match, security::READ>, \
    name ## Open, \
    name ## Read, \
    name ## Close) \
  /**/

  REG_INPUT(xmlFile);
  REG_INPUT(xmlIOHTTP);

  // disable any output
  xmlRegisterOutputCallbacks(0, 0, 0, 0);
}
<|endoftext|>


<commit_before>/*
*
* Copyright 2013 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker 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 Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* iot_support at tid dot es
*
* Author: Fermín Galán
*/
#include <stdint.h>
#include <utility>
#include <map>
#include <string>
#include <vector>

#include "logMsg/logMsg.h"
#include "logMsg/traceLevels.h"

#include "common/string.h"
#include "common/globals.h"
#include "common/statistics.h"
#include "common/sem.h"
#include "common/RenderFormat.h"
#include "common/defaultValues.h"
#include "alarmMgr/alarmMgr.h"

#include "mongoBackend/MongoGlobal.h"
#include "mongoBackend/TriggeredSubscription.h"
#include "mongoBackend/connectionOperations.h"
#include "mongoBackend/mongoConnectionPool.h"
#include "mongoBackend/safeMongo.h"
#include "mongoBackend/dbConstants.h"
#include "mongoBackend/MongoCommonRegister.h"

/* ****************************************************************************
*
* USING
*/
using mongo::BSONArrayBuilder;
using mongo::BSONObjBuilder;
using mongo::BSONObj;
using mongo::BSONElement;
using mongo::DBClientBase;
using mongo::DBClientCursor;
using mongo::OID;

/* ****************************************************************************
*
* processRegisterContext -
*
* This function has a slightly different behaviour depending on whether the id
* parameter is null (new registration case) or not null (update case), in
* particular:
*
* - In the new registration case, the _id is generated and insert() is used to
*   put the document in the DB.
* - In the update case, the _id is set according to the argument 'id' and update() is
*   used to put the document in the DB.
*/
HttpStatusCode processRegisterContext
(
  RegisterContextRequest*   requestP,
  RegisterContextResponse*  responseP,
  OID*                      id,
  const std::string&        tenant,
  const std::string&        servicePath,
  const std::string&        format,
  const std::string&        fiwareCorrelator
)
{
  std::string err;

  /* If expiration is not present, then use a default one */
  if (requestP->duration.isEmpty())
  {
    requestP->duration.set(DEFAULT_DURATION);
  }

  /* Calculate expiration (using the current time and the duration field in the request) */
  long long expiration = getCurrentTime() + requestP->duration.parse();

  LM_T(LmtMongo, ("Registration expiration: %lu", expiration));

  /* Create the mongoDB registration document */
  BSONObjBuilder reg;
  OID oid;

  if (id == NULL)
  {
    oid.init();
  }
  else
  {
    oid = *id;
  }

  reg.append("_id", oid);
  reg.append(REG_EXPIRATION, expiration);

  // FIXME P4: See issue #3078
  reg.append(REG_SERVICE_PATH, servicePath.empty() ? SERVICE_PATH_ROOT : servicePath);
  reg.append(REG_FORMAT, format);

  BSONArrayBuilder  contextRegistration;

  for (unsigned int ix = 0; ix < requestP->contextRegistrationVector.size(); ++ix)
  {
    ContextRegistration*  cr = requestP->contextRegistrationVector[ix];
    BSONArrayBuilder      entities;

    for (unsigned int jx = 0; jx < cr->entityIdVector.size(); ++jx)
    {
      EntityId* en = cr->entityIdVector[jx];

      if (en->type.empty())
      {
        entities.append(BSON(REG_ENTITY_ID << en->id));
        LM_T(LmtMongo, ("Entity registration: {id: %s}", en->id.c_str()));
      }
      else
      {
        entities.append(BSON(REG_ENTITY_ID << en->id << REG_ENTITY_TYPE << en->type));
        LM_T(LmtMongo, ("Entity registration: {id: %s, type: %s}", en->id.c_str(), en->type.c_str()));
      }
    }

    BSONArrayBuilder attrs;

    for (unsigned int jx = 0; jx < cr->contextRegistrationAttributeVector.size(); ++jx)
    {
      ContextRegistrationAttribute* cra = cr->contextRegistrationAttributeVector[jx];

      attrs.append(BSON(REG_ATTRS_NAME << cra->name << REG_ATTRS_TYPE << cra->type));
      LM_T(LmtMongo, ("Attribute registration: {name: %s, type: %s}",
                      cra->name.c_str(),
                      cra->type.c_str()));
    }

    contextRegistration.append(
      BSON(
        REG_ENTITIES << entities.arr() <<
        REG_ATTRS << attrs.arr() <<
        REG_PROVIDING_APPLICATION << requestP->contextRegistrationVector[ix]->providingApplication.get()));

    LM_T(LmtMongo, ("providingApplication registration: %s",
                    requestP->contextRegistrationVector[ix]->providingApplication.c_str()));
  }
  reg.append(REG_CONTEXT_REGISTRATION, contextRegistration.arr());

  /* Note that we are using upsert = "true". This means that if the document doesn't previously
   * exist in the collection, it is created. Thus, this way both uses of registerContext are OK
   * (either new registration or updating an existing one)
   */
  if (!collectionUpdate(getRegistrationsCollectionName(tenant), BSON("_id" << oid), reg.obj(), true, &err))
  {
    responseP->errorCode.fill(SccReceiverInternalError, err);
    return SccOk;
  }

  // Fill the response element
  responseP->duration = requestP->duration;
  responseP->registrationId.set(oid.toString());
  responseP->errorCode.fill(SccOk);

  return SccOk;
}
<commit_msg>FIX wrongly solved merge conflict<commit_after>/*
*
* Copyright 2013 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker 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 Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* iot_support at tid dot es
*
* Author: Fermín Galán
*/
#include <stdint.h>
#include <utility>
#include <map>
#include <string>
#include <vector>

#include "logMsg/logMsg.h"
#include "logMsg/traceLevels.h"

#include "common/string.h"
#include "common/globals.h"
#include "common/statistics.h"
#include "common/sem.h"
#include "common/RenderFormat.h"
#include "common/defaultValues.h"
#include "alarmMgr/alarmMgr.h"

#include "mongoBackend/MongoGlobal.h"
#include "mongoBackend/TriggeredSubscription.h"
#include "mongoBackend/connectionOperations.h"
#include "mongoBackend/mongoConnectionPool.h"
#include "mongoBackend/safeMongo.h"
#include "mongoBackend/dbConstants.h"
#include "mongoBackend/MongoCommonRegister.h"

/* ****************************************************************************
*
* USING
*/
using mongo::BSONArrayBuilder;
using mongo::BSONObjBuilder;
using mongo::BSONObj;
using mongo::BSONElement;
using mongo::DBClientBase;
using mongo::DBClientCursor;
using mongo::OID;

/* ****************************************************************************
*
* processRegisterContext -
*
* This function has a slightly different behaviour depending on whether the id
* parameter is null (new registration case) or not null (update case), in
* particular:
*
* - In the new registration case, the _id is generated and insert() is used to
*   put the document in the DB.
* - In the update case, the _id is set according to the argument 'id' and update() is
*   used to put the document in the DB.
*/
HttpStatusCode processRegisterContext
(
  RegisterContextRequest*   requestP,
  RegisterContextResponse*  responseP,
  OID*                      id,
  const std::string&        tenant,
  const std::string&        servicePath,
  const std::string&        format,
  const std::string&        fiwareCorrelator
)
{
  std::string err;

  /* If expiration is not present, then use a default one */
  if (requestP->duration.isEmpty())
  {
    requestP->duration.set(DEFAULT_DURATION);
  }

  /* Calculate expiration (using the current time and the duration field in the request) */
  long long expiration = getCurrentTime() + requestP->duration.parse();

  LM_T(LmtMongo, ("Registration expiration: %lu", expiration));

  /* Create the mongoDB registration document */
  BSONObjBuilder reg;
  OID oid;

  if (id == NULL)
  {
    oid.init();
  }
  else
  {
    oid = *id;
  }

  reg.append("_id", oid);
  reg.append(REG_EXPIRATION, expiration);

  // FIXME P4: See issue #3078
  reg.append(REG_SERVICE_PATH, servicePath.empty() ? SERVICE_PATH_ROOT : servicePath);
  reg.append(REG_FORMAT, format);

  // In NGISv1 forwarding mode is always "all"
   reg.append(REG_FORWARDING_MODE, "all");

  BSONArrayBuilder  contextRegistration;

  for (unsigned int ix = 0; ix < requestP->contextRegistrationVector.size(); ++ix)
  {
    ContextRegistration*  cr = requestP->contextRegistrationVector[ix];
    BSONArrayBuilder      entities;

    for (unsigned int jx = 0; jx < cr->entityIdVector.size(); ++jx)
    {
      EntityId* en = cr->entityIdVector[jx];

      if (en->type.empty())
      {
        entities.append(BSON(REG_ENTITY_ID << en->id));
        LM_T(LmtMongo, ("Entity registration: {id: %s}", en->id.c_str()));
      }
      else
      {
        entities.append(BSON(REG_ENTITY_ID << en->id << REG_ENTITY_TYPE << en->type));
        LM_T(LmtMongo, ("Entity registration: {id: %s, type: %s}", en->id.c_str(), en->type.c_str()));
      }
    }

    BSONArrayBuilder attrs;

    for (unsigned int jx = 0; jx < cr->contextRegistrationAttributeVector.size(); ++jx)
    {
      ContextRegistrationAttribute* cra = cr->contextRegistrationAttributeVector[jx];

      attrs.append(BSON(REG_ATTRS_NAME << cra->name << REG_ATTRS_TYPE << cra->type));
      LM_T(LmtMongo, ("Attribute registration: {name: %s, type: %s}",
                      cra->name.c_str(),
                      cra->type.c_str()));
    }

    contextRegistration.append(
      BSON(
        REG_ENTITIES << entities.arr() <<
        REG_ATTRS << attrs.arr() <<
        REG_PROVIDING_APPLICATION << requestP->contextRegistrationVector[ix]->providingApplication.get()));

    LM_T(LmtMongo, ("providingApplication registration: %s",
                    requestP->contextRegistrationVector[ix]->providingApplication.c_str()));
  }
  reg.append(REG_CONTEXT_REGISTRATION, contextRegistration.arr());

  /* Note that we are using upsert = "true". This means that if the document doesn't previously
   * exist in the collection, it is created. Thus, this way both uses of registerContext are OK
   * (either new registration or updating an existing one)
   */
  if (!collectionUpdate(getRegistrationsCollectionName(tenant), BSON("_id" << oid), reg.obj(), true, &err))
  {
    responseP->errorCode.fill(SccReceiverInternalError, err);
    return SccOk;
  }

  // Fill the response element
  responseP->duration = requestP->duration;
  responseP->registrationId.set(oid.toString());
  responseP->errorCode.fill(SccOk);

  return SccOk;
}
<|endoftext|>


<commit_before>#include "amgdata.h"
using namespace std;

AMGData::AMGData(){
}

AMGData::~AMGData(){
}

bool AMGData::CreateDBs(){
	try{
		if(CreateBaseballDB()){
			if(CreateStockDB()){
			}
			else{
				return false;
			}
		}
		else{
			return false;
		}
	}
	catch(exception &e){
		return false;
	}
	return true;
}

bool AMGData::CreateBaseballDB(){
	try{
		string longSQL = "create table if not exists baseball_stats(player_name text primary";
		longSQL += "key,games_played integer default '0',average real default '0.00',salary real";
		longSQL += "default '0.00')";

		AMGSQLite dbSession = AMGSQLite(basePath + "/db/baseball.db");
		if(dbSession.RunQuery(longSQL)){
		}
		else{
			return false;
		}
		dbSession.RunQuery("delete from baseball_stats");
	}
	catch(exception &e){
	}
	return true;
}

bool AMGData::CreateStockDB(){
    try{
        string longSQL = "create table if not exists stock_stats(company_name text,ticker text";
        longSQL += "primary key,country text,price real default '0.00',exchange_rate real default";
        longSQL += "shares_outstanding real default '0.00',net_income real default";
        longSQL += "'0.00',market_value real default '0.00',pe_ratio real default '0.00')";

        AMGSQLite dbSession = AMGSQLite(basePath + "/db/stocks.db");
        if(dbSession.RunQuery(longSQL)){
        }
        else{
            return false;
        }
		dbSession.RunQuery("delete from stock_stats");
    }
    catch(exception &e){
    }
	return true;
}

bool AMGData::InsertBaseball(BaseballStatRecord a){
	try{
		AMGSQLite dbSession = AMGSQLite(basePath + "/db/baseball.db");
		string longQuery = "insert into baseball_stats (player_name,games_played, average, salary) values ";
		longQuery += ("('" + a.name + "','" + to_string(a.games) + "','" + to_string(a.average) + "','" + to_string(a.salary) + "')");
		dbSession.RunQuery(longQuery);

	}
	catch(exception &e){
		return false;
	}
	return true;
}

bool AMGData::InsertStock(StockStatRecord a){
	try{
		AMGSQLite dbSession = AMGSQLite(basePath + "/db/stocks.db");
		string longQuery = "insert into stock_stats (company_name,ticker, country, price, exchange_rate";
		longQuery += ",net_income, market_value, pe_ratio) values ('";
		longQuery += (a.companyName + "','" + a.name + "','" + a.exchangeCountry + "','" + to_string(a.price) + "','" + to_string(a.exchangeRate) + "','");
		longQuery += (to_string(a.netIncome) + "','" + to_string(a.marketValueUsd) + "','" + to_string(a.peRatio) + "')");
		dbSession.RunQuery(longQuery);
	}
	catch(exception &e){
		return false;
	}
	return true;
}

<commit_msg>Fixed shares_outstanding column in stocks database.<commit_after>#include "amgdata.h"
using namespace std;

AMGData::AMGData(){
}

AMGData::~AMGData(){
}

bool AMGData::CreateDBs(){
	try{
		if(CreateBaseballDB()){
			if(CreateStockDB()){
			}
			else{
				return false;
			}
		}
		else{
			return false;
		}
	}
	catch(exception &e){
		return false;
	}
	return true;
}

bool AMGData::CreateBaseballDB(){
	try{
		string longSQL = "create table if not exists baseball_stats(player_name text primary";
		longSQL += "key,games_played integer default '0',average real default '0.00',salary real";
		longSQL += "default '0.00')";

		AMGSQLite dbSession = AMGSQLite(basePath + "/db/baseball.db");
		if(dbSession.RunQuery(longSQL)){
		}
		else{
			return false;
		}
		dbSession.RunQuery("delete from baseball_stats");
	}
	catch(exception &e){
	}
	return true;
}

bool AMGData::CreateStockDB(){
    try{
        string longSQL = "create table if not exists stock_stats(company_name text,ticker text";
        longSQL += "primary key,country text,price real default '0.00',exchange_rate real default '0.00',";
        longSQL += "shares_outstanding real default '0.00',net_income real default";
        longSQL += "'0.00',market_value real default '0.00',pe_ratio real default '0.00')";

        AMGSQLite dbSession = AMGSQLite(basePath + "/db/stocks.db");
        if(dbSession.RunQuery(longSQL)){
        }
        else{
            return false;
        }
		dbSession.RunQuery("delete from stock_stats");
    }
    catch(exception &e){
    }
	return true;
}

bool AMGData::InsertBaseball(BaseballStatRecord a){
	try{
		AMGSQLite dbSession = AMGSQLite(basePath + "/db/baseball.db");
		string longQuery = "insert into baseball_stats (player_name,games_played, average, salary) values ";
		longQuery += ("('" + a.name + "','" + to_string(a.games) + "','" + to_string(a.average) + "','" + to_string(a.salary) + "')");
		dbSession.RunQuery(longQuery);

	}
	catch(exception &e){
		return false;
	}
	return true;
}

bool AMGData::InsertStock(StockStatRecord a){
	try{
		AMGSQLite dbSession = AMGSQLite(basePath + "/db/stocks.db");
		string longQuery = "insert into stock_stats (company_name,ticker, country, price, exchange_rate";
		longQuery += ",net_income, market_value, pe_ratio,shares_outstanding) values ('";
		longQuery += (a.companyName + "','" + a.name + "','" + a.exchangeCountry + "','" + to_string(a.price) + "','" + to_string(a.exchangeRate) + "','");
		longQuery += (to_string(a.netIncome) + "','" + to_string(a.marketValueUsd) + "','" + to_string(a.peRatio) + "' + to_string(a.sharesOutstanding))");
		dbSession.RunQuery(longQuery);
	}
	catch(exception &e){
		return false;
	}
	return true;
}

<|endoftext|>


<commit_before>/*
 *
 *    Copyright (c) 2020 Project CHIP Authors
 *
 *    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.
 */

#include "Server.h"

#include <errno.h>
#include <utility>

#include <lib/dnssd/minimal_mdns/core/DnsHeader.h>

namespace mdns {
namespace Minimal {
namespace {

class ShutdownOnError
{
public:
    ShutdownOnError(ServerBase * s) : mServer(s) {}
    ~ShutdownOnError()
    {
        if (mServer != nullptr)
        {
            mServer->Shutdown();
        }
    }

    CHIP_ERROR ReturnSuccess()
    {
        mServer = nullptr;
        return CHIP_NO_ERROR;
    }

private:
    ServerBase * mServer;
};

/**
 * Extracts the Listening UDP Endpoint from an underlying ServerBase::EndpointInfo
 */
class ListenSocketPickerDelegate : public ServerBase::BroadcastSendDelegate
{
public:
    chip::Inet::UDPEndPoint * Accept(ServerBase::EndpointInfo * info) override { return info->mListenUdp; }
};

#if CHIP_MINMDNS_USE_EPHEMERAL_UNICAST_PORT

/**
 * Extracts the Querying UDP Endpoint from an underlying ServerBase::EndpointInfo
 */
class QuerySocketPickerDelegate : public ServerBase::BroadcastSendDelegate
{
public:
    chip::Inet::UDPEndPoint * Accept(ServerBase::EndpointInfo * info) override { return info->mUnicastQueryUdp; }
};

#else

using QuerySocketPickerDelegate = ListenSocketPickerDelegate;

#endif

/**
 * Validates that an endpoint belongs to a specific interface/ip address type before forwarding the
 * endpoint accept logic to another BroadcastSendDelegate.
 *
 * Usage like:
 *
 * SomeDelegate *child = ....;
 * InterfaceTypeFilterDelegate filter(interfaceId, IPAddressType::IPv6, child);
 *
 * UDPEndPoint *udp = filter.Accept(endpointInfo);
 */
class InterfaceTypeFilterDelegate : public ServerBase::BroadcastSendDelegate
{
public:
    InterfaceTypeFilterDelegate(chip::Inet::InterfaceId interface, chip::Inet::IPAddressType type,
                                ServerBase::BroadcastSendDelegate * child) :
        mInterface(interface),
        mAddressType(type), mChild(child)
    {}

    chip::Inet::UDPEndPoint * Accept(ServerBase::EndpointInfo * info) override
    {
        if ((info->mInterfaceId != mInterface) && (info->mInterfaceId != chip::Inet::InterfaceId::Null()))
        {
            return nullptr;
        }

        if ((mAddressType != chip::Inet::IPAddressType::kAny) && (info->mAddressType != mAddressType))
        {
            return nullptr;
        }

        return mChild->Accept(info);
    }

private:
    chip::Inet::InterfaceId mInterface;
    chip::Inet::IPAddressType mAddressType;
    ServerBase::BroadcastSendDelegate * mChild = nullptr;
};

} // namespace

namespace BroadcastIpAddresses {

// Get standard mDNS Broadcast addresses

void GetIpv6Into(chip::Inet::IPAddress & dest)
{
    if (!chip::Inet::IPAddress::FromString("FF02::FB", dest))
    {
        ChipLogError(Discovery, "Failed to parse standard IPv6 broadcast address");
    }
}

void GetIpv4Into(chip::Inet::IPAddress & dest)
{
    if (!chip::Inet::IPAddress::FromString("224.0.0.251", dest))
    {
        ChipLogError(Discovery, "Failed to parse standard IPv4 broadcast address");
    }
}

} // namespace BroadcastIpAddresses

namespace {

CHIP_ERROR JoinMulticastGroup(chip::Inet::InterfaceId interfaceId, chip::Inet::UDPEndPoint * endpoint,
                              chip::Inet::IPAddressType addressType)
{

    chip::Inet::IPAddress address;

    if (addressType == chip::Inet::IPAddressType::kIPv6)
    {
        BroadcastIpAddresses::GetIpv6Into(address);
#if INET_CONFIG_ENABLE_IPV4
    }
    else if (addressType == chip::Inet::IPAddressType::kIPv4)
    {
        BroadcastIpAddresses::GetIpv4Into(address);
#endif // INET_CONFIG_ENABLE_IPV4
    }
    else
    {
        return CHIP_ERROR_INVALID_ARGUMENT;
    }

    return endpoint->JoinMulticastGroup(interfaceId, address);
}

const char * AddressTypeStr(chip::Inet::IPAddressType addressType)
{
    switch (addressType)
    {
    case chip::Inet::IPAddressType::kIPv6:
        return "IPv6";
#if INET_CONFIG_ENABLE_IPV4
    case chip::Inet::IPAddressType::kIPv4:
        return "IPv4";
#endif // INET_CONFIG_ENABLE_IPV4
    default:
        return "UNKNOWN";
    }
}

} // namespace

ServerBase::~ServerBase()
{
    Shutdown();
}

void ServerBase::Shutdown()
{
    mEndpoints.ForEachActiveObject([&](auto * endpoint) {
        ShutdownEndpoint(*endpoint);
        return chip::Loop::Continue;
    });
}

void ServerBase::ShutdownEndpoint(EndpointInfo & aEndpoint)
{
    mEndpoints.ReleaseObject(&aEndpoint);
}

bool ServerBase::IsListening() const
{
    bool listening = false;
    mEndpoints.ForEachActiveObject([&](auto * endpoint) {
        if (endpoint->mListenUdp != nullptr)
        {
            listening = true;
            return chip::Loop::Break;
        }
        return chip::Loop::Continue;
    });
    return listening;
}

CHIP_ERROR ServerBase::Listen(chip::Inet::EndPointManager<chip::Inet::UDPEndPoint> * udpEndPointManager, ListenIterator * it,
                              uint16_t port)
{
    Shutdown(); // ensure everything starts fresh

    chip::Inet::InterfaceId interfaceId = chip::Inet::InterfaceId::Null();
    chip::Inet::IPAddressType addressType;

    ShutdownOnError autoShutdown(this);

    while (it->Next(&interfaceId, &addressType))
    {
        chip::Inet::UDPEndPoint * listenUdp;
        ReturnErrorOnFailure(udpEndPointManager->NewEndPoint(&listenUdp));
        std::unique_ptr<chip::Inet::UDPEndPoint, EndpointInfo::EndPointDeletor> endPointHolder(listenUdp, {});

        ReturnErrorOnFailure(listenUdp->Bind(addressType, chip::Inet::IPAddress::Any, port, interfaceId));

        ReturnErrorOnFailure(listenUdp->Listen(OnUdpPacketReceived, nullptr /*OnReceiveError*/, this));

        CHIP_ERROR err = JoinMulticastGroup(interfaceId, listenUdp, addressType);
        if (err != CHIP_NO_ERROR)
        {
            char interfaceName[chip::Inet::InterfaceId::kMaxIfNameLength];
            interfaceId.GetInterfaceName(interfaceName, sizeof(interfaceName));

            // Log only as non-fatal error. Failure to join will mean we reply to unicast queries only.
            ChipLogError(DeviceLayer, "MDNS failed to join multicast group on %s for address type %s: %s", interfaceName,
                         AddressTypeStr(addressType), chip::ErrorStr(err));
        }

#if CHIP_MINMDNS_USE_EPHEMERAL_UNICAST_PORT
        // Separate UDP endpoint for unicast queries, bound to 0 (i.e. pick random ephemeral port)
        //   - helps in not having conflicts on port 5353, will receive unicast replies directly
        //   - has a *DRAWBACK* of unicast queries being considered LEGACY by mdns since they do
        //     not originate from 5353 and the answers will include a query section.
        chip::Inet::UDPEndPoint * unicastQueryUdp;
        ReturnErrorOnFailure(udpEndPointManager->NewEndPoint(&unicastQueryUdp));
        std::unique_ptr<chip::Inet::UDPEndPoint, EndpointInfo::EndPointDeletor> endPointHolderUnicast(unicastQueryUdp, {});
        ReturnErrorOnFailure(unicastQueryUdp->Bind(addressType, chip::Inet::IPAddress::Any, 0, interfaceId));
        ReturnErrorOnFailure(unicastQueryUdp->Listen(OnUdpPacketReceived, nullptr /*OnReceiveError*/, this));
#endif

#if CHIP_MINMDNS_USE_EPHEMERAL_UNICAST_PORT
        if (endPointHolder || endPointHolderUnicast)
        {
            // If allocation fails, the rref will not be consumed, so that the endpoint will also be freed correctly
            mEndpoints.CreateObject(interfaceId, addressType, std::move(endPointHolder), std::move(endPointHolderUnicast));
        }
#else
        if (endPointHolder)
        {
            // If allocation fails, the rref will not be consumed, so that the endpoint will also be freed correctly
            mEndpoints.CreateObject(interfaceId, addressType, std::move(endPointHolder));
        }
#endif
    }

    return autoShutdown.ReturnSuccess();
}

CHIP_ERROR ServerBase::DirectSend(chip::System::PacketBufferHandle && data, const chip::Inet::IPAddress & addr, uint16_t port,
                                  chip::Inet::InterfaceId interface)
{
    CHIP_ERROR err = CHIP_ERROR_NOT_CONNECTED;
    mEndpoints.ForEachActiveObject([&](auto * info) {
        if (info->mListenUdp == nullptr)
        {
            return chip::Loop::Continue;
        }

        if (info->mAddressType != addr.Type())
        {
            return chip::Loop::Continue;
        }

        chip::Inet::InterfaceId boundIf = info->mListenUdp->GetBoundInterface();

        if ((boundIf.IsPresent()) && (boundIf != interface))
        {
            return chip::Loop::Continue;
        }

        err = info->mListenUdp->SendTo(addr, port, std::move(data));
        return chip::Loop::Break;
    });

    return err;
}

CHIP_ERROR ServerBase::BroadcastUnicastQuery(chip::System::PacketBufferHandle && data, uint16_t port)
{
    QuerySocketPickerDelegate socketPicker;
    return BroadcastImpl(std::move(data), port, &socketPicker);
}

CHIP_ERROR ServerBase::BroadcastUnicastQuery(chip::System::PacketBufferHandle && data, uint16_t port,
                                             chip::Inet::InterfaceId interface, chip::Inet::IPAddressType addressType)
{
    QuerySocketPickerDelegate socketPicker;
    InterfaceTypeFilterDelegate filter(interface, addressType, &socketPicker);

    return BroadcastImpl(std::move(data), port, &filter);
}

CHIP_ERROR ServerBase::BroadcastSend(chip::System::PacketBufferHandle && data, uint16_t port, chip::Inet::InterfaceId interface,
                                     chip::Inet::IPAddressType addressType)
{
    ListenSocketPickerDelegate socketPicker;
    InterfaceTypeFilterDelegate filter(interface, addressType, &socketPicker);

    return BroadcastImpl(std::move(data), port, &filter);
}

CHIP_ERROR ServerBase::BroadcastSend(chip::System::PacketBufferHandle && data, uint16_t port)
{
    ListenSocketPickerDelegate socketPicker;
    return BroadcastImpl(std::move(data), port, &socketPicker);
}

CHIP_ERROR ServerBase::BroadcastImpl(chip::System::PacketBufferHandle && data, uint16_t port, BroadcastSendDelegate * delegate)
{
    // Broadcast requires sending data multiple times, each of which may error
    // out, yet broadcast only has a single error code.
    //
    // The general logic of error handling is:
    //   - if no send done at all, return error
    //   - if at least one broadcast succeeds, assume success overall
    //   + some internal consistency validations for state error.

    bool hadSuccesfulSend = false;
    CHIP_ERROR lastError  = CHIP_ERROR_NO_ENDPOINT;

    if (chip::Loop::Break == mEndpoints.ForEachActiveObject([&](auto * info) {
            chip::Inet::UDPEndPoint * udp = delegate->Accept(info);

            if (udp == nullptr)
            {
                return chip::Loop::Continue;
            }

            CHIP_ERROR err;

            /// The same packet needs to be sent over potentially multiple interfaces.
            /// LWIP does not like having a pbuf sent over serparate interfaces, hence we create a copy
            /// for sending via `CloneData`
            ///
            /// TODO: this wastes one copy of the data and that could be optimized away
            if (info->mAddressType == chip::Inet::IPAddressType::kIPv6)
            {
                err = udp->SendTo(mIpv6BroadcastAddress, port, data.CloneData(), udp->GetBoundInterface());
            }
#if INET_CONFIG_ENABLE_IPV4
            else if (info->mAddressType == chip::Inet::IPAddressType::kIPv4)
            {
                err = udp->SendTo(mIpv4BroadcastAddress, port, data.CloneData(), udp->GetBoundInterface());
            }
#endif
            else
            {
                // This is a general error of internal consistency: every address has a known type. Fail completely otherwise.
                lastError = CHIP_ERROR_INCORRECT_STATE;
                return chip::Loop::Break;
            }

            if (err == CHIP_NO_ERROR)
            {
                hadSuccesfulSend = true;
            }
            else
            {
                ChipLogError(Discovery, "Attempt to mDNS broadcast failed:  %s", chip::ErrorStr(err));
                lastError = err;
            }
            return chip::Loop::Continue;
        }))
    {
        return lastError;
    }

    if (!hadSuccesfulSend)
    {
        return lastError;
    }

    return CHIP_NO_ERROR;
}

void ServerBase::OnUdpPacketReceived(chip::Inet::UDPEndPoint * endPoint, chip::System::PacketBufferHandle && buffer,
                                     const chip::Inet::IPPacketInfo * info)
{
    ServerBase * srv = static_cast<ServerBase *>(endPoint->mAppState);
    if (!srv->mDelegate)
    {
        return;
    }

    mdns::Minimal::BytesRange data(buffer->Start(), buffer->Start() + buffer->DataLength());
    if (data.Size() < HeaderRef::kSizeBytes)
    {
        ChipLogError(Discovery, "Packet to small for mDNS data: %d bytes", static_cast<int>(data.Size()));
        return;
    }

    if (HeaderRef(const_cast<uint8_t *>(data.Start())).GetFlags().IsQuery())
    {
        srv->mDelegate->OnQuery(data, info);
    }
    else
    {
        srv->mDelegate->OnResponse(data, info);
    }
}

} // namespace Minimal
} // namespace mdns
<commit_msg>Fix mDNS UDP leak when JoinMulticastGroup fails (#13184)<commit_after>/*
 *
 *    Copyright (c) 2020 Project CHIP Authors
 *
 *    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.
 */

#include "Server.h"

#include <errno.h>
#include <utility>

#include <lib/dnssd/minimal_mdns/core/DnsHeader.h>

namespace mdns {
namespace Minimal {
namespace {

class ShutdownOnError
{
public:
    ShutdownOnError(ServerBase * s) : mServer(s) {}
    ~ShutdownOnError()
    {
        if (mServer != nullptr)
        {
            mServer->Shutdown();
        }
    }

    CHIP_ERROR ReturnSuccess()
    {
        mServer = nullptr;
        return CHIP_NO_ERROR;
    }

private:
    ServerBase * mServer;
};

/**
 * Extracts the Listening UDP Endpoint from an underlying ServerBase::EndpointInfo
 */
class ListenSocketPickerDelegate : public ServerBase::BroadcastSendDelegate
{
public:
    chip::Inet::UDPEndPoint * Accept(ServerBase::EndpointInfo * info) override { return info->mListenUdp; }
};

#if CHIP_MINMDNS_USE_EPHEMERAL_UNICAST_PORT

/**
 * Extracts the Querying UDP Endpoint from an underlying ServerBase::EndpointInfo
 */
class QuerySocketPickerDelegate : public ServerBase::BroadcastSendDelegate
{
public:
    chip::Inet::UDPEndPoint * Accept(ServerBase::EndpointInfo * info) override { return info->mUnicastQueryUdp; }
};

#else

using QuerySocketPickerDelegate = ListenSocketPickerDelegate;

#endif

/**
 * Validates that an endpoint belongs to a specific interface/ip address type before forwarding the
 * endpoint accept logic to another BroadcastSendDelegate.
 *
 * Usage like:
 *
 * SomeDelegate *child = ....;
 * InterfaceTypeFilterDelegate filter(interfaceId, IPAddressType::IPv6, child);
 *
 * UDPEndPoint *udp = filter.Accept(endpointInfo);
 */
class InterfaceTypeFilterDelegate : public ServerBase::BroadcastSendDelegate
{
public:
    InterfaceTypeFilterDelegate(chip::Inet::InterfaceId interface, chip::Inet::IPAddressType type,
                                ServerBase::BroadcastSendDelegate * child) :
        mInterface(interface),
        mAddressType(type), mChild(child)
    {}

    chip::Inet::UDPEndPoint * Accept(ServerBase::EndpointInfo * info) override
    {
        if ((info->mInterfaceId != mInterface) && (info->mInterfaceId != chip::Inet::InterfaceId::Null()))
        {
            return nullptr;
        }

        if ((mAddressType != chip::Inet::IPAddressType::kAny) && (info->mAddressType != mAddressType))
        {
            return nullptr;
        }

        return mChild->Accept(info);
    }

private:
    chip::Inet::InterfaceId mInterface;
    chip::Inet::IPAddressType mAddressType;
    ServerBase::BroadcastSendDelegate * mChild = nullptr;
};

} // namespace

namespace BroadcastIpAddresses {

// Get standard mDNS Broadcast addresses

void GetIpv6Into(chip::Inet::IPAddress & dest)
{
    if (!chip::Inet::IPAddress::FromString("FF02::FB", dest))
    {
        ChipLogError(Discovery, "Failed to parse standard IPv6 broadcast address");
    }
}

void GetIpv4Into(chip::Inet::IPAddress & dest)
{
    if (!chip::Inet::IPAddress::FromString("224.0.0.251", dest))
    {
        ChipLogError(Discovery, "Failed to parse standard IPv4 broadcast address");
    }
}

} // namespace BroadcastIpAddresses

namespace {

CHIP_ERROR JoinMulticastGroup(chip::Inet::InterfaceId interfaceId, chip::Inet::UDPEndPoint * endpoint,
                              chip::Inet::IPAddressType addressType)
{

    chip::Inet::IPAddress address;

    if (addressType == chip::Inet::IPAddressType::kIPv6)
    {
        BroadcastIpAddresses::GetIpv6Into(address);
#if INET_CONFIG_ENABLE_IPV4
    }
    else if (addressType == chip::Inet::IPAddressType::kIPv4)
    {
        BroadcastIpAddresses::GetIpv4Into(address);
#endif // INET_CONFIG_ENABLE_IPV4
    }
    else
    {
        return CHIP_ERROR_INVALID_ARGUMENT;
    }

    return endpoint->JoinMulticastGroup(interfaceId, address);
}

const char * AddressTypeStr(chip::Inet::IPAddressType addressType)
{
    switch (addressType)
    {
    case chip::Inet::IPAddressType::kIPv6:
        return "IPv6";
#if INET_CONFIG_ENABLE_IPV4
    case chip::Inet::IPAddressType::kIPv4:
        return "IPv4";
#endif // INET_CONFIG_ENABLE_IPV4
    default:
        return "UNKNOWN";
    }
}

} // namespace

ServerBase::~ServerBase()
{
    Shutdown();
}

void ServerBase::Shutdown()
{
    mEndpoints.ForEachActiveObject([&](auto * endpoint) {
        ShutdownEndpoint(*endpoint);
        return chip::Loop::Continue;
    });
}

void ServerBase::ShutdownEndpoint(EndpointInfo & aEndpoint)
{
    mEndpoints.ReleaseObject(&aEndpoint);
}

bool ServerBase::IsListening() const
{
    bool listening = false;
    mEndpoints.ForEachActiveObject([&](auto * endpoint) {
        if (endpoint->mListenUdp != nullptr)
        {
            listening = true;
            return chip::Loop::Break;
        }
        return chip::Loop::Continue;
    });
    return listening;
}

CHIP_ERROR ServerBase::Listen(chip::Inet::EndPointManager<chip::Inet::UDPEndPoint> * udpEndPointManager, ListenIterator * it,
                              uint16_t port)
{
    Shutdown(); // ensure everything starts fresh

    chip::Inet::InterfaceId interfaceId = chip::Inet::InterfaceId::Null();
    chip::Inet::IPAddressType addressType;

    ShutdownOnError autoShutdown(this);

    while (it->Next(&interfaceId, &addressType))
    {
        chip::Inet::UDPEndPoint * listenUdp;
        ReturnErrorOnFailure(udpEndPointManager->NewEndPoint(&listenUdp));
        std::unique_ptr<chip::Inet::UDPEndPoint, EndpointInfo::EndPointDeletor> endPointHolder(listenUdp, {});

        ReturnErrorOnFailure(listenUdp->Bind(addressType, chip::Inet::IPAddress::Any, port, interfaceId));

        ReturnErrorOnFailure(listenUdp->Listen(OnUdpPacketReceived, nullptr /*OnReceiveError*/, this));

        CHIP_ERROR err = JoinMulticastGroup(interfaceId, listenUdp, addressType);
        if (err != CHIP_NO_ERROR)
        {
            char interfaceName[chip::Inet::InterfaceId::kMaxIfNameLength];
            interfaceId.GetInterfaceName(interfaceName, sizeof(interfaceName));

            // Log only as non-fatal error. Failure to join will mean we reply to unicast queries only.
            ChipLogError(DeviceLayer, "MDNS failed to join multicast group on %s for address type %s: %s", interfaceName,
                         AddressTypeStr(addressType), chip::ErrorStr(err));

            endPointHolder.reset();
        }

#if CHIP_MINMDNS_USE_EPHEMERAL_UNICAST_PORT
        // Separate UDP endpoint for unicast queries, bound to 0 (i.e. pick random ephemeral port)
        //   - helps in not having conflicts on port 5353, will receive unicast replies directly
        //   - has a *DRAWBACK* of unicast queries being considered LEGACY by mdns since they do
        //     not originate from 5353 and the answers will include a query section.
        chip::Inet::UDPEndPoint * unicastQueryUdp;
        ReturnErrorOnFailure(udpEndPointManager->NewEndPoint(&unicastQueryUdp));
        std::unique_ptr<chip::Inet::UDPEndPoint, EndpointInfo::EndPointDeletor> endPointHolderUnicast(unicastQueryUdp, {});
        ReturnErrorOnFailure(unicastQueryUdp->Bind(addressType, chip::Inet::IPAddress::Any, 0, interfaceId));
        ReturnErrorOnFailure(unicastQueryUdp->Listen(OnUdpPacketReceived, nullptr /*OnReceiveError*/, this));
#endif

#if CHIP_MINMDNS_USE_EPHEMERAL_UNICAST_PORT
        if (endPointHolder || endPointHolderUnicast)
        {
            // If allocation fails, the rref will not be consumed, so that the endpoint will also be freed correctly
            mEndpoints.CreateObject(interfaceId, addressType, std::move(endPointHolder), std::move(endPointHolderUnicast));
        }
#else
        if (endPointHolder)
        {
            // If allocation fails, the rref will not be consumed, so that the endpoint will also be freed correctly
            mEndpoints.CreateObject(interfaceId, addressType, std::move(endPointHolder));
        }
#endif
    }

    return autoShutdown.ReturnSuccess();
}

CHIP_ERROR ServerBase::DirectSend(chip::System::PacketBufferHandle && data, const chip::Inet::IPAddress & addr, uint16_t port,
                                  chip::Inet::InterfaceId interface)
{
    CHIP_ERROR err = CHIP_ERROR_NOT_CONNECTED;
    mEndpoints.ForEachActiveObject([&](auto * info) {
        if (info->mListenUdp == nullptr)
        {
            return chip::Loop::Continue;
        }

        if (info->mAddressType != addr.Type())
        {
            return chip::Loop::Continue;
        }

        chip::Inet::InterfaceId boundIf = info->mListenUdp->GetBoundInterface();

        if ((boundIf.IsPresent()) && (boundIf != interface))
        {
            return chip::Loop::Continue;
        }

        err = info->mListenUdp->SendTo(addr, port, std::move(data));
        return chip::Loop::Break;
    });

    return err;
}

CHIP_ERROR ServerBase::BroadcastUnicastQuery(chip::System::PacketBufferHandle && data, uint16_t port)
{
    QuerySocketPickerDelegate socketPicker;
    return BroadcastImpl(std::move(data), port, &socketPicker);
}

CHIP_ERROR ServerBase::BroadcastUnicastQuery(chip::System::PacketBufferHandle && data, uint16_t port,
                                             chip::Inet::InterfaceId interface, chip::Inet::IPAddressType addressType)
{
    QuerySocketPickerDelegate socketPicker;
    InterfaceTypeFilterDelegate filter(interface, addressType, &socketPicker);

    return BroadcastImpl(std::move(data), port, &filter);
}

CHIP_ERROR ServerBase::BroadcastSend(chip::System::PacketBufferHandle && data, uint16_t port, chip::Inet::InterfaceId interface,
                                     chip::Inet::IPAddressType addressType)
{
    ListenSocketPickerDelegate socketPicker;
    InterfaceTypeFilterDelegate filter(interface, addressType, &socketPicker);

    return BroadcastImpl(std::move(data), port, &filter);
}

CHIP_ERROR ServerBase::BroadcastSend(chip::System::PacketBufferHandle && data, uint16_t port)
{
    ListenSocketPickerDelegate socketPicker;
    return BroadcastImpl(std::move(data), port, &socketPicker);
}

CHIP_ERROR ServerBase::BroadcastImpl(chip::System::PacketBufferHandle && data, uint16_t port, BroadcastSendDelegate * delegate)
{
    // Broadcast requires sending data multiple times, each of which may error
    // out, yet broadcast only has a single error code.
    //
    // The general logic of error handling is:
    //   - if no send done at all, return error
    //   - if at least one broadcast succeeds, assume success overall
    //   + some internal consistency validations for state error.

    bool hadSuccesfulSend = false;
    CHIP_ERROR lastError  = CHIP_ERROR_NO_ENDPOINT;

    if (chip::Loop::Break == mEndpoints.ForEachActiveObject([&](auto * info) {
            chip::Inet::UDPEndPoint * udp = delegate->Accept(info);

            if (udp == nullptr)
            {
                return chip::Loop::Continue;
            }

            CHIP_ERROR err;

            /// The same packet needs to be sent over potentially multiple interfaces.
            /// LWIP does not like having a pbuf sent over serparate interfaces, hence we create a copy
            /// for sending via `CloneData`
            ///
            /// TODO: this wastes one copy of the data and that could be optimized away
            if (info->mAddressType == chip::Inet::IPAddressType::kIPv6)
            {
                err = udp->SendTo(mIpv6BroadcastAddress, port, data.CloneData(), udp->GetBoundInterface());
            }
#if INET_CONFIG_ENABLE_IPV4
            else if (info->mAddressType == chip::Inet::IPAddressType::kIPv4)
            {
                err = udp->SendTo(mIpv4BroadcastAddress, port, data.CloneData(), udp->GetBoundInterface());
            }
#endif
            else
            {
                // This is a general error of internal consistency: every address has a known type. Fail completely otherwise.
                lastError = CHIP_ERROR_INCORRECT_STATE;
                return chip::Loop::Break;
            }

            if (err == CHIP_NO_ERROR)
            {
                hadSuccesfulSend = true;
            }
            else
            {
                ChipLogError(Discovery, "Attempt to mDNS broadcast failed:  %s", chip::ErrorStr(err));
                lastError = err;
            }
            return chip::Loop::Continue;
        }))
    {
        return lastError;
    }

    if (!hadSuccesfulSend)
    {
        return lastError;
    }

    return CHIP_NO_ERROR;
}

void ServerBase::OnUdpPacketReceived(chip::Inet::UDPEndPoint * endPoint, chip::System::PacketBufferHandle && buffer,
                                     const chip::Inet::IPPacketInfo * info)
{
    ServerBase * srv = static_cast<ServerBase *>(endPoint->mAppState);
    if (!srv->mDelegate)
    {
        return;
    }

    mdns::Minimal::BytesRange data(buffer->Start(), buffer->Start() + buffer->DataLength());
    if (data.Size() < HeaderRef::kSizeBytes)
    {
        ChipLogError(Discovery, "Packet to small for mDNS data: %d bytes", static_cast<int>(data.Size()));
        return;
    }

    if (HeaderRef(const_cast<uint8_t *>(data.Start())).GetFlags().IsQuery())
    {
        srv->mDelegate->OnQuery(data, info);
    }
    else
    {
        srv->mDelegate->OnResponse(data, info);
    }
}

} // namespace Minimal
} // namespace mdns
<|endoftext|>