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

#include <ubus_utility.h>

bool parse_json_rpc(struct rpc_data *d, struct blob_attr *data) {
  const struct blobmsg_policy data_policy[] = {
      {.type = BLOBMSG_TYPE_STRING},
      {.type = BLOBMSG_TYPE_STRING},
      {.type = BLOBMSG_TYPE_STRING},
      {.type = BLOBMSG_TYPE_TABLE},
  };
  struct blob_attr *tb[__RPC_MAX];
  struct blob_attr *tb2[4];
  struct blob_attr *cur;

  blobmsg_parse(rpc_policy, __RPC_MAX, tb, blob_data(data), blob_len(data));

  cur = tb[RPC_JSONRPC];
  if (!cur || strcmp(blobmsg_data(cur), "2.0") != 0)
    return false;

  cur = tb[RPC_METHOD];
  if (!cur)
    return false;

  d->id = tb[RPC_ID];
  d->method = blobmsg_data(cur);

  cur = tb[RPC_PARAMS];
  if (!cur)
    return true;

  d->params = blob_memdup(cur);
  if (!d->params)
    return false;

  blobmsg_parse_array(data_policy, ARRAY_SIZE(data_policy), tb2,
                      blobmsg_data(d->params), blobmsg_data_len(d->params));

  if (tb2[0])
    d->sid = blobmsg_data(tb2[0]);

  if (!d->sid || !*d->sid)
    d->sid = UBUS_DEFAULT_SID;

  if (tb2[1])
    d->object = blobmsg_data(tb2[1]);

  if (tb2[2])
    d->function = blobmsg_data(tb2[2]);

  d->data = tb2[3];

  return true;
}

void ubus_init_response(struct blob_buf *buf, struct dispatch_ubus *du) {
  struct json_object *obj = du->jsobj_cur, *obj2 = NULL;

  blob_buf_init(buf, 0);
  blobmsg_add_string(buf, "jsonrpc", "2.0");

  if (obj)
    json_object_object_get_ex(obj, "id", &obj2);

  if (obj2)
    blobmsg_add_json_element(buf, "id", obj2);
  else
    blobmsg_add_field(buf, BLOBMSG_TYPE_UNSPEC, "id", NULL, 0);
}

void ubus_allowed_cb(struct ubus_request *req, int type,
                     struct blob_attr *msg) {
  struct blob_attr *tb[__SES_MAX];
  bool *allow = (bool *)req->priv;

  if (!msg)
    return;

  blobmsg_parse(ses_policy, __SES_MAX, tb, blob_data(msg), blob_len(msg));

  if (tb[SES_ACCESS])
    *allow = blobmsg_get_bool(tb[SES_ACCESS]);
}

void ubus_request_cb(struct ubus_request *req, int type,
                     struct blob_attr *msg) {
  ubus_ctx_t *ctx = (ubus_ctx_t *)req->priv;
  struct dispatch_ubus *du = ctx->ubus;

  blobmsg_add_field(du->buf, BLOBMSG_TYPE_TABLE, "", blob_data(msg),
                    blob_len(msg));
}

void ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj,
                  void *priv) {
  struct list_data *data = priv;
  struct blob_attr *sig, *attr;
  int rem, rem2;
  void *t, *o;

  if (!data->verbose) {
    blobmsg_add_string(data->buf, NULL, obj->path);
    return;
  }

  if (!obj->signature)
    return;

  o = blobmsg_open_table(data->buf, obj->path);
  blob_for_each_attr(sig, obj->signature, rem) {
    t = blobmsg_open_table(data->buf, blobmsg_name(sig));
    rem2 = blobmsg_data_len(sig);
    __blob_for_each_attr(attr, blobmsg_data(sig), rem2) {
      if (blob_id(attr) != BLOBMSG_TYPE_INT32)
        continue;

      switch (blobmsg_get_u32(attr)) {
      case BLOBMSG_TYPE_INT8:
        blobmsg_add_string(data->buf, blobmsg_name(attr), "boolean");
        break;
      case BLOBMSG_TYPE_INT32:
        blobmsg_add_string(data->buf, blobmsg_name(attr), "number");
        break;
      case BLOBMSG_TYPE_STRING:
        blobmsg_add_string(data->buf, blobmsg_name(attr), "string");
        break;
      case BLOBMSG_TYPE_ARRAY:
        blobmsg_add_string(data->buf, blobmsg_name(attr), "array");
        break;
      case BLOBMSG_TYPE_TABLE:
        blobmsg_add_string(data->buf, blobmsg_name(attr), "object");
        break;
      default:
        blobmsg_add_string(data->buf, blobmsg_name(attr), "unknown");
        break;
      }
    }
    blobmsg_close_table(data->buf, t);
  }
  blobmsg_close_table(data->buf, o);
}

void ubus_close_fds(struct ubus_context *ctx) {
  if (ctx->sock.fd < 0)
    return;

  close(ctx->sock.fd);
  ctx->sock.fd = -1;
}
