
/*
 *	BSD 3-Clause License
 *
 *	Copyright (c) 2019, Christian Marangi
 * 	All rights reserved.
 */

#ifndef NGINX_NGX_HTTP_UBUS_UTILITY_HEADERS_H
#define NGINX_NGX_HTTP_UBUS_UTILITY_HEADERS_H

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/avl.h>
#include <libubox/avl-cmp.h>

#include <pthread.h>
#include <semaphore.h>

#include <libubus.h>
#include <json-c/json.h>

#define UBUS_MAX_POST_SIZE 65536
#define UBUS_DEFAULT_SID "00000000000000000000000000000000"

struct dispatch_ubus {
  struct ubus_request req;

  struct json_tokener *jstok;
  struct json_object *jsobj;
  struct json_object *jsobj_cur;

  uint32_t obj;
  const char *func;

  struct blob_buf *buf;
};

typedef struct {
  ngx_http_request_t *r;
  int res_len;
  ngx_chain_t *out_chain;
  ngx_chain_t *out_chain_start;
  struct ubus_context *ubus_ctx;
  char **array_res;
  sem_t *sem;
} request_ctx_t;

typedef struct {
  struct blob_buf *buf;
  struct dispatch_ubus *ubus;
  struct json_object *obj;
  bool array;
  int index;
  request_ctx_t *request;
} ubus_ctx_t;

enum {
  RPC_JSONRPC,
  RPC_METHOD,
  RPC_PARAMS,
  RPC_ID,
  __RPC_MAX,
};

static const struct blobmsg_policy rpc_policy[__RPC_MAX] = {
    [RPC_JSONRPC] = {.name = "jsonrpc", .type = BLOBMSG_TYPE_STRING},
    [RPC_METHOD] = {.name = "method", .type = BLOBMSG_TYPE_STRING},
    [RPC_PARAMS] = {.name = "params", .type = BLOBMSG_TYPE_ARRAY},
    [RPC_ID] = {.name = "id", .type = BLOBMSG_TYPE_UNSPEC},
};

enum {
  SES_ACCESS,
  __SES_MAX,
};

static const struct blobmsg_policy ses_policy[__SES_MAX] = {
    [SES_ACCESS] = {.name = "access", .type = BLOBMSG_TYPE_BOOL},
};

struct rpc_data {
  struct blob_attr *id;
  const char *sid;
  const char *method;
  const char *object;
  const char *function;
  struct blob_attr *data;
  struct blob_attr *params;
};

struct list_data {
  bool verbose;
  struct blob_buf *buf;
};

enum rpc_status {
  REQUEST_OK,
  ERROR_PARSE,
  ERROR_REQUEST,
  ERROR_METHOD,
  ERROR_PARAMS,
  ERROR_INTERNAL,
  ERROR_OBJECT,
  ERROR_SESSION,
  ERROR_ACCESS,
  ERROR_TIMEOUT,
  __ERROR_MAX
};

static const struct {
  int code;
  const char *msg;
} json_errors[__ERROR_MAX] = {
    [REQUEST_OK] = {0, "Request complete correctly"},
    [ERROR_PARSE] = {-32700, "Parse error"},
    [ERROR_REQUEST] = {-32600, "Invalid request"},
    [ERROR_METHOD] = {-32601, "Method not found"},
    [ERROR_PARAMS] = {-32602, "Invalid parameters"},
    [ERROR_INTERNAL] = {-32603, "Internal error"},
    [ERROR_OBJECT] = {-32000, "Object not found"},
    [ERROR_SESSION] = {-32001, "Session not found"},
    [ERROR_ACCESS] = {-32002, "Access denied"},
    [ERROR_TIMEOUT] = {-32003, "ubus request timed out"},
};

bool parse_json_rpc(struct rpc_data *d, struct blob_attr *data);
void ubus_init_response(struct blob_buf *buf, struct dispatch_ubus *du);
void ubus_allowed_cb(struct ubus_request *req, int type, struct blob_attr *msg);
void ubus_request_cb(struct ubus_request *req, int type, struct blob_attr *msg);
void ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj,
                  void *priv);
void ubus_close_fds(struct ubus_context *ctx);

#endif /* NGINX_NGX_HTTP_UBUS_UTILITY_HEADERS_H */
