Example #1
0
static void
req_process_set(struct context *ctx, struct conn *conn, struct msg *msg)
{
    uint8_t *key, nkey, cid;
    struct item *it;

    key = msg->key_start;
    nkey = (uint8_t)(msg->key_end - msg->key_start);

    cid = item_slabcid(nkey, msg->vlen);
    if (cid == SLABCLASS_INVALID_ID) {
        rsp_send_error(ctx, conn, msg, MSG_RSP_CLIENT_ERROR, EINVAL);
        return;
    }

    itemx_removex(msg->hash, msg->md);

    it = item_get(key, nkey, cid, msg->vlen, time_reltime(msg->expiry),
                  msg->flags, msg->md, msg->hash);
    if (it == NULL) {
        rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, ENOMEM);
        return;
    }

    mbuf_copy_to(&msg->mhdr, msg->value, item_data(it), msg->vlen);

    rsp_send_status(ctx, conn, msg, MSG_RSP_STORED);
}
Example #2
0
static int
get_query_params(struct evhttp_request *req, struct evkeyvalq *query, struct query_params *qp)
{
  const char *param;
  int ret;

  qp->offset = 0;
  param = evhttp_find_header(query, "offset");
  if (param)
    {
      ret = safe_atoi32(param, &qp->offset);
      if (ret < 0)
	{
	  rsp_send_error(req, "Invalid offset");
	  return -1;
	}
    }

  qp->limit = 0;
  param = evhttp_find_header(query, "limit");
  if (param)
    {
      ret = safe_atoi32(param, &qp->limit);
      if (ret < 0)
	{
	  rsp_send_error(req, "Invalid limit");
	  return -1;
	}
    }

  if (qp->offset || qp->limit)
    qp->idx_type = I_SUB;
  else
    qp->idx_type = I_NONE;

  qp->sort = S_NONE;

  param = evhttp_find_header(query, "query");
  if (param)
    {
      DPRINTF(E_DBG, L_RSP, "RSP browse query filter: %s\n", param);

      qp->filter = rsp_query_parse_sql(param);
      if (!qp->filter)
	DPRINTF(E_LOG, L_RSP, "Ignoring improper RSP query\n");
    }

  return 0;
}
Example #3
0
static void
rsp_send_reply(struct evhttp_request *req, mxml_node_t *reply)
{
  struct evbuffer *evbuf;

  evbuf = mxml_to_evbuf(reply);
  mxmlDelete(reply);

  if (!evbuf)
    {
      rsp_send_error(req, "Could not finalize reply");

      return;
    }

  evhttp_add_header(req->output_headers, "Content-Type", "text/xml; charset=utf-8");
  evhttp_add_header(req->output_headers, "Connection", "close");
  httpd_send_reply(req, HTTP_OK, "OK", evbuf);

  evbuffer_free(evbuf);
}
Example #4
0
static void
req_process_get(struct context *ctx, struct conn *conn, struct msg *msg)
{
    struct itemx *itx;
    struct item *it;

    itx = itemx_getx(msg->hash, msg->md);
    if (itx == NULL) {
        msg_type_t type;

        /*
         * On a miss, we send a "END\r\n" response, unless the request
         * is an intermediate fragment in a fragmented request.
         */
        if (msg->frag_id == 0 || msg->last_fragment) {
            type = MSG_RSP_END;
        } else {
            type = MSG_EMPTY;
        }

        rsp_send_status(ctx, conn, msg, type);
        return;
    }

    /*
     * On a hit, we read the item with address [sid, offset] and respond
     * with item value if the item hasn't expired yet.
     */
    it = slab_read_item(itx->sid, itx->offset);
    if (it == NULL) {
        rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, errno);
        return;
    }
    if (item_expired(it)) {
        rsp_send_status(ctx, conn, msg, MSG_RSP_NOT_FOUND);
        return;
    }

    rsp_send_value(ctx, conn, msg, it, itx->cas);
}
Example #5
0
static void
req_process_num(struct context *ctx, struct conn *conn, struct msg *msg)
{
    rstatus_t status;
    uint8_t *key, nkey, cid;
    struct item *it;
    struct itemx *itx;
    uint64_t cnum, nnum;
    char numstr[FC_UINT64_MAXLEN];
    int n;

    key = msg->key_start;
    nkey = (uint8_t)(msg->key_end - msg->key_start);

    /* 1). look up existing itemx */
    itx = itemx_getx(msg->hash, msg->md);
    if (itx == NULL) {
        /* 2a). miss -> return NOT_FOUND */
        rsp_send_status(ctx, conn, msg, MSG_RSP_NOT_FOUND);
        return;
    }

    /* 2b). hit -> read existing item into it */
    it = slab_read_item(itx->sid, itx->offset);
    if (it == NULL) {
        rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, errno);
        return;
    }
    if (item_expired(it)) {
        rsp_send_status(ctx, conn, msg, MSG_RSP_NOT_FOUND);
        return;
    }

    /* 3). sanity check item data to be a number */
    status = fc_atou64(item_data(it), it->ndata, &cnum);
    if (status != FC_OK) {
        rsp_send_error(ctx, conn, msg, MSG_RSP_CLIENT_ERROR, EINVAL);
        return;
    }

    /* 4). remove existing itemx of it */
    itemx_removex(msg->hash, msg->md);

    /* 5). compute the new incr/decr number nnum and numstr */
    if (msg->type == MSG_REQ_INCR) {
        nnum = cnum + msg->num;
    } else {
        if (cnum < msg->num) {
            nnum = 0;
        } else {
            nnum = cnum - msg->num;
        }
    }
    n = fc_scnprintf(numstr, sizeof(numstr), "%"PRIu64"", nnum);

    /* 6). alloc new item that can hold n worth of bytes */
    cid = item_slabcid(nkey, n);
    ASSERT(cid != SLABCLASS_INVALID_ID);

    it = item_get(key, nkey, cid, n, time_reltime(msg->expiry), msg->flags,
                   msg->md, msg->hash);
    if (it == NULL) {
        rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, ENOMEM);
        return;
    }

    /* 7). copy numstr to it */
    fc_memcpy(item_data(it), numstr, n);

    rsp_send_num(ctx, conn, msg, it);
}
Example #6
0
static void
req_process_concat(struct context *ctx, struct conn *conn, struct msg *msg)
{
    uint8_t *key, nkey, cid;
    struct item *oit, *it;
    uint32_t ndata;
    struct itemx *itx;

    key = msg->key_start;
    nkey = (uint8_t)(msg->key_end - msg->key_start);

    /* 1). look up existing itemx */
    itx = itemx_getx(msg->hash, msg->md);
    if (itx == NULL) {
        /* 2a). miss -> return NOT_STORED */
        rsp_send_status(ctx, conn, msg, MSG_RSP_NOT_STORED);
        return;
    }

    /* 2b). hit -> read existing item into oit */
    oit = slab_read_item(itx->sid, itx->offset);
    if (oit == NULL) {
        rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, errno);
        return;
    }
    if (item_expired(oit)) {
        rsp_send_status(ctx, conn, msg, MSG_RSP_NOT_STORED);
        return;
    }

    ndata = msg->vlen + oit->ndata;
    cid = item_slabcid(nkey, ndata);
    if (cid == SLABCLASS_INVALID_ID) {
        rsp_send_error(ctx, conn, msg, MSG_RSP_CLIENT_ERROR, EINVAL);
        return;
    }

    /* 3). remove existing itemx of oit */
    itemx_removex(msg->hash, msg->md);

    /* 4). alloc new item that can hold ndata worth of bytes */
    it = item_get(key, nkey, cid, ndata, time_reltime(msg->expiry),
                  msg->flags, msg->md, msg->hash);
    if (it == NULL) {
        rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, ENOMEM);
        return;
    }

    /* 5). copy data from msg to head or tail of new item it */
    switch (msg->type) {

    case MSG_REQ_PREPEND:
        mbuf_copy_to(&msg->mhdr, msg->value, item_data(it), msg->vlen);
        fc_memcpy(item_data(it) + msg->vlen, item_data(oit), oit->ndata);
        break;

    case MSG_REQ_APPEND:
        fc_memcpy(item_data(it), item_data(oit), oit->ndata);
        mbuf_copy_to(&msg->mhdr, msg->value, item_data(it) + oit->ndata, msg->vlen);
        break;

    default:
        NOT_REACHED();
    }

    rsp_send_status(ctx, conn, msg, MSG_RSP_STORED);
}
Example #7
0
void
rsp_request(struct evhttp_request *req)
{
  char *full_uri;
  char *uri;
  char *ptr;
  char *uri_parts[5];
  struct evkeyvalq query;
  cfg_t *lib;
  char *libname;
  char *passwd;
  int handler;
  int i;
  int ret;

  memset(&query, 0, sizeof(struct evkeyvalq));

  full_uri = httpd_fixup_uri(req);
  if (!full_uri)
    {
      rsp_send_error(req, "Server error");
      return;
    }

  ptr = strchr(full_uri, '?');
  if (ptr)
    *ptr = '\0';

  uri = strdup(full_uri);
  if (!uri)
    {
      rsp_send_error(req, "Server error");

      free(full_uri);
      return;
    }

  if (ptr)
    *ptr = '?';

  ptr = uri;
  uri = evhttp_decode_uri(uri);
  free(ptr);

  DPRINTF(E_DBG, L_RSP, "RSP request: %s\n", full_uri);

  handler = -1;
  for (i = 0; rsp_handlers[i].handler; i++)
    {
      ret = regexec(&rsp_handlers[i].preg, uri, 0, NULL, 0);
      if (ret == 0)
	{
	  handler = i;
	  break;
	}
    }

  if (handler < 0)
    {
      DPRINTF(E_LOG, L_RSP, "Unrecognized RSP request\n");

      rsp_send_error(req, "Bad path");

      free(uri);
      free(full_uri);
      return;
    }

  /* Check authentication */
  lib = cfg_getsec(cfg, "library");
  passwd = cfg_getstr(lib, "password");
  if (passwd)
    {
      libname = cfg_getstr(lib, "name");

      DPRINTF(E_DBG, L_HTTPD, "Checking authentication for library '%s'\n", libname);

      /* We don't care about the username */
      ret = httpd_basic_auth(req, NULL, passwd, libname);
      if (ret != 0)
	{
	  free(uri);
	  free(full_uri);
	  return;
	}

      DPRINTF(E_DBG, L_HTTPD, "Library authentication successful\n");
    }

  memset(uri_parts, 0, sizeof(uri_parts));

  uri_parts[0] = strtok_r(uri, "/", &ptr);
  for (i = 1; (i < sizeof(uri_parts) / sizeof(uri_parts[0])) && uri_parts[i - 1]; i++)
    {
      uri_parts[i] = strtok_r(NULL, "/", &ptr);
    }

  if (!uri_parts[0] || uri_parts[i - 1] || (i < 2))
    {
      DPRINTF(E_LOG, L_RSP, "RSP URI has too many/few components (%d)\n", (uri_parts[0]) ? i : 0);

      rsp_send_error(req, "Bad path");

      free(uri);
      free(full_uri);
      return;
    }

  evhttp_parse_query(full_uri, &query);

  rsp_handlers[handler].handler(req, uri_parts, &query);

  evhttp_clear_headers(&query);
  free(uri);
  free(full_uri);
}
Example #8
0
static void
rsp_reply_browse(struct evhttp_request *req, char **uri, struct evkeyvalq *query)
{
  struct query_params qp;
  char *browse_item;
  mxml_node_t *reply;
  mxml_node_t *status;
  mxml_node_t *items;
  mxml_node_t *node;
  int records;
  int ret;

  memset(&qp, 0, sizeof(struct query_params));

  if (strcmp(uri[3], "artist") == 0)
    qp.type = Q_BROWSE_ARTISTS;
  else if (strcmp(uri[3], "genre") == 0)
    qp.type = Q_BROWSE_GENRES;
  else if (strcmp(uri[3], "album") == 0)
    qp.type = Q_BROWSE_ALBUMS;
  else if (strcmp(uri[3], "composer") == 0)
    qp.type = Q_BROWSE_COMPOSERS;
  else
    {
      DPRINTF(E_LOG, L_RSP, "Unsupported browse type '%s'\n", uri[3]);

      rsp_send_error(req, "Unsupported browse type");
      return;
    }

  ret = safe_atoi32(uri[2], &qp.id);
  if (ret < 0)
    {
      rsp_send_error(req, "Invalid playlist ID");
      return;
    }

  ret = get_query_params(req, query, &qp);
  if (ret < 0)
    return;

  ret = db_query_start(&qp);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_RSP, "Could not start query\n");

      rsp_send_error(req, "Could not start query");

      if (qp.filter)
	free(qp.filter);
      return;
    }

  if (qp.offset > qp.results)
    records = 0;
  else if (qp.limit > (qp.results - qp.offset))
    records = qp.results - qp.offset;
  else
    records = qp.limit;

  /* We'd use mxmlNewXML(), but then we can't put any attributes
   * on the root node and we need some.
   */
  reply = mxmlNewElement(MXML_NO_PARENT, RSP_XML_ROOT);

  node = mxmlNewElement(reply, "response");
  status = mxmlNewElement(node, "status");
  items = mxmlNewElement(node, "items");

  /* Status block */
  node = mxmlNewElement(status, "errorcode");
  mxmlNewText(node, 0, "0");

  node = mxmlNewElement(status, "errorstring");
  mxmlNewText(node, 0, "");

  node = mxmlNewElement(status, "records");
  mxmlNewTextf(node, 0, "%d", records);

  node = mxmlNewElement(status, "totalrecords");
  mxmlNewTextf(node, 0, "%d", qp.results);

  /* Items block (all items) */
  while (((ret = db_query_fetch_string(&qp, &browse_item)) == 0) && (browse_item))
    {
      node = mxmlNewElement(items, "item");
      mxmlNewText(node, 0, browse_item);
    }

  if (qp.filter)
    free(qp.filter);

  if (ret < 0)
    {
      DPRINTF(E_LOG, L_RSP, "Error fetching results\n");

      mxmlDelete(reply);
      db_query_end(&qp);
      rsp_send_error(req, "Error fetching query results");
      return;
    }

  /* HACK
   * Add a dummy empty string to the items element if there is no data
   * to return - this prevents mxml from sending out an empty <items/>
   * tag that the SoundBridge does not handle. It's hackish, but it works.
   */
  if (qp.results == 0)
    mxmlNewText(items, 0, "");

  db_query_end(&qp);

  rsp_send_reply(req, reply);
}
Example #9
0
static void
rsp_reply_playlist(struct evhttp_request *req, char **uri, struct evkeyvalq *query)
{
  struct query_params qp;
  struct db_media_file_info dbmfi;
  const char *param;
  char **strval;
  mxml_node_t *reply;
  mxml_node_t *status;
  mxml_node_t *items;
  mxml_node_t *item;
  mxml_node_t *node;
  int mode;
  int records;
  int transcode;
  int32_t bitrate;
  int i;
  int ret;

  memset(&qp, 0, sizeof(struct query_params));

  ret = safe_atoi32(uri[2], &qp.id);
  if (ret < 0)
    {
      rsp_send_error(req, "Invalid playlist ID");
      return;
    }

  if (qp.id == 0)
    qp.type = Q_ITEMS;
  else
    qp.type = Q_PLITEMS;

  mode = F_FULL;
  param = evhttp_find_header(query, "type");
  if (param)
    {
      if (strcasecmp(param, "full") == 0)
	mode = F_FULL;
      else if (strcasecmp(param, "browse") == 0)
	mode = F_BROWSE;
      else if (strcasecmp(param, "id") == 0)
	mode = F_ID;
      else if (strcasecmp(param, "detailed") == 0)
	mode = F_DETAILED;
      else
	DPRINTF(E_LOG, L_RSP, "Unknown browse mode %s\n", param);
    }

  ret = get_query_params(req, query, &qp);
  if (ret < 0)
    return;

  ret = db_query_start(&qp);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_RSP, "Could not start query\n");

      rsp_send_error(req, "Could not start query");

      if (qp.filter)
	free(qp.filter);
      return;
    }

  if (qp.offset > qp.results)
    records = 0;
  else if (qp.limit > (qp.results - qp.offset))
    records = qp.results - qp.offset;
  else
    records = qp.limit;

  /* We'd use mxmlNewXML(), but then we can't put any attributes
   * on the root node and we need some.
   */
  reply = mxmlNewElement(MXML_NO_PARENT, RSP_XML_ROOT);

  node = mxmlNewElement(reply, "response");
  status = mxmlNewElement(node, "status");
  items = mxmlNewElement(node, "items");

  /* Status block */
  node = mxmlNewElement(status, "errorcode");
  mxmlNewText(node, 0, "0");

  node = mxmlNewElement(status, "errorstring");
  mxmlNewText(node, 0, "");

  node = mxmlNewElement(status, "records");
  mxmlNewTextf(node, 0, "%d", records);

  node = mxmlNewElement(status, "totalrecords");
  mxmlNewTextf(node, 0, "%d", qp.results);

  /* Items block (all items) */
  while (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id))
    {
      transcode = transcode_needed(req->input_headers, dbmfi.codectype);

      /* Item block (one item) */
      item = mxmlNewElement(items, "item");

      for (i = 0; rsp_fields[i].field; i++)
	{
	  if (!(rsp_fields[i].flags & mode))
	    continue;

	  strval = (char **) ((char *)&dbmfi + rsp_fields[i].offset);

	  if (!(*strval) || (strlen(*strval) == 0))
	    continue;

	  node = mxmlNewElement(item, rsp_fields[i].field);

	  if (!transcode)
	    mxmlNewText(node, 0, *strval);
	  else
	    {
	      switch (rsp_fields[i].offset)
		{
		  case dbmfi_offsetof(type):
		    mxmlNewText(node, 0, "wav");
		    break;

		  case dbmfi_offsetof(bitrate):
		    bitrate = 0;
		    ret = safe_atoi32(dbmfi.samplerate, &bitrate);
		    if ((ret < 0) || (bitrate == 0))
		      bitrate = 1411;
		    else
		      bitrate = (bitrate * 8) / 250;

		    mxmlNewTextf(node, 0, "%d", bitrate);
		    break;

		  case dbmfi_offsetof(description):
		    mxmlNewText(node, 0, "wav audio file");
		    break;

		  case dbmfi_offsetof(codectype):
		    mxmlNewText(node, 0, "wav");

		    node = mxmlNewElement(item, "original_codec");
		    mxmlNewText(node, 0, *strval);
		    break;

		  default:
		    mxmlNewText(node, 0, *strval);
		    break;
		}
	    }
	}
    }

  if (qp.filter)
    free(qp.filter);

  if (ret < 0)
    {
      DPRINTF(E_LOG, L_RSP, "Error fetching results\n");

      mxmlDelete(reply);
      db_query_end(&qp);
      rsp_send_error(req, "Error fetching query results");
      return;
    }

  /* HACK
   * Add a dummy empty string to the items element if there is no data
   * to return - this prevents mxml from sending out an empty <items/>
   * tag that the SoundBridge does not handle. It's hackish, but it works.
   */
  if (qp.results == 0)
    mxmlNewText(items, 0, "");

  db_query_end(&qp);

  rsp_send_reply(req, reply);
}
Example #10
0
static void
rsp_reply_db(struct evhttp_request *req, char **uri, struct evkeyvalq *query)
{
  struct query_params qp;
  struct db_playlist_info dbpli;
  char **strval;
  mxml_node_t *reply;
  mxml_node_t *status;
  mxml_node_t *pls;
  mxml_node_t *pl;
  mxml_node_t *node;
  int i;
  int ret;

  memset(&qp, 0, sizeof(struct db_playlist_info));

  qp.type = Q_PL;
  qp.idx_type = I_NONE;

  ret = db_query_start(&qp);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_RSP, "Could not start query\n");

      rsp_send_error(req, "Could not start query");
      return;
    }

  /* We'd use mxmlNewXML(), but then we can't put any attributes
   * on the root node and we need some.
   */
  reply = mxmlNewElement(MXML_NO_PARENT, RSP_XML_ROOT);

  node = mxmlNewElement(reply, "response");
  status = mxmlNewElement(node, "status");
  pls = mxmlNewElement(node, "playlists");

  /* Status block */
  node = mxmlNewElement(status, "errorcode");
  mxmlNewText(node, 0, "0");

  node = mxmlNewElement(status, "errorstring");
  mxmlNewText(node, 0, "");

  node = mxmlNewElement(status, "records");
  mxmlNewTextf(node, 0, "%d", qp.results);

  node = mxmlNewElement(status, "totalrecords");
  mxmlNewTextf(node, 0, "%d", qp.results);

  /* Playlists block (all playlists) */
  while (((ret = db_query_fetch_pl(&qp, &dbpli)) == 0) && (dbpli.id))
    {
      /* Playlist block (one playlist) */
      pl = mxmlNewElement(pls, "playlist");

      for (i = 0; pl_fields[i].field; i++)
	{
	  if (pl_fields[i].flags & F_FULL)
	    {
	      strval = (char **) ((char *)&dbpli + pl_fields[i].offset);

	      node = mxmlNewElement(pl, pl_fields[i].field);
	      mxmlNewText(node, 0, *strval);
            }
        }
    }

  if (ret < 0)
    {
      DPRINTF(E_LOG, L_RSP, "Error fetching results\n");

      mxmlDelete(reply);
      db_query_end(&qp);
      rsp_send_error(req, "Error fetching query results");
      return;
    }

  /* HACK
   * Add a dummy empty string to the playlists element if there is no data
   * to return - this prevents mxml from sending out an empty <playlists/>
   * tag that the SoundBridge does not handle. It's hackish, but it works.
   */
  if (qp.results == 0)
    mxmlNewText(pls, 0, "");

  db_query_end(&qp);

  rsp_send_reply(req, reply);
}