Exemple #1
0
static int rhizome_server_sql_query_http_response(rhizome_http_request *r,
					   char *column,char *table,char *query_body,
					   int bytes_per_row,int dehexP)
{
  /* Run the provided SQL query progressively and return the values of the first
     column it returns.  As the result list may be very long, we will add the
     LIMIT <skip>,<count> clause to do it piece by piece.

     Otherwise, the response is prefixed by a 256 byte header, including the public
     key of the sending node, and allowing space for information about encryption of
     the body, although encryption is not yet implemented here.
 */

  if (r->buffer == NULL || r->buffer_size < 16384) {
    if (r->buffer)
      free(r->buffer);
    r->buffer_size = 16384;
    r->buffer = malloc(r->buffer_size);
    if (r->buffer == NULL) {
      r->buffer_size = 0;
      WHY_perror("malloc");
      return WHY("Cannot send response, out of memory");
    }
  }
  r->buffer_length=0;
  r->buffer_offset=0;
  r->source_record_size=bytes_per_row;
  r->source_count = 0;
  sqlite_exec_int64(&r->source_count, "SELECT COUNT(*) %s", query_body);

  /* Work out total response length */
  long long response_bytes=256+r->source_count*r->source_record_size;
  rhizome_server_http_response_header(r, 200, "servalproject.org/rhizome-list", response_bytes);
  if (debug & DEBUG_RHIZOME_TX)
    DEBUGF("headers consumed %d bytes", r->buffer_length);

  /* Clear and prepare response header */
  bzero(&r->buffer[r->buffer_length],256);
  
  r->buffer[r->buffer_length]=0x01; /* type of response (list) */
  r->buffer[r->buffer_length+1]=0x01; /* version of response */

  if (debug & DEBUG_RHIZOME_TX)
    DEBUGF("Found %lld records",r->source_count);
  /* Number of records we intend to return */
  r->buffer[r->buffer_length+4]=(r->source_count>>0)&0xff;
  r->buffer[r->buffer_length+5]=(r->source_count>>8)&0xff;
  r->buffer[r->buffer_length+6]=(r->source_count>>16)&0xff;
  r->buffer[r->buffer_length+7]=(r->source_count>>24)&0xff;

  r->buffer_length+=256;

  /* copy our public key in to bytes 32+ */
  // TODO get out public key (SID) from keyring and copy into response packet

  /* build templated query */
  strbuf b = strbuf_local(r->source, sizeof r->source);
  strbuf_sprintf(b, "SELECT %s,rowid %s", column, query_body);
  if (strbuf_overrun(b))
    WHYF("SQL query overrun: %s", strbuf_str(b));
  r->source_index=0;
  r->source_flags=dehexP;

  DEBUGF("buffer_length=%d",r->buffer_length);

  /* Populate spare space in buffer with rows of data */
  return rhizome_server_sql_query_fill_buffer(r, table, column);
}
Exemple #2
0
static int rhizome_server_parse_http_request(rhizome_http_request *r)
{
  /* Switching to writing, so update the call-back */
  r->alarm.poll.events=POLLOUT;
  watch(&r->alarm);
  // Start building up a response.
  r->request_type = 0;
  // Parse the HTTP "GET" line.
  char *path = NULL;
  size_t pathlen = 0;
  if (str_startswith(r->request, "GET ", &path)) {
    char *p;
    // This loop is guaranteed to terminate before the end of the buffer, because we know that the
    // buffer contains at least "\n\n" and maybe "\r\n\r\n" at the end of the header block.
    for (p = path; !isspace(*p); ++p)
      ;
    pathlen = p - path;
    if ( str_startswith(p, " HTTP/1.", &p)
      && (str_startswith(p, "0", &p) || str_startswith(p, "1", &p))
      && (str_startswith(p, "\r\n", &p) || str_startswith(p, "\n", &p))
    )
      path[pathlen] = '\0';
    else
      path = NULL;
  }
  if (path) {
    char *id = NULL;
    INFOF("RHIZOME HTTP SERVER, GET %s", alloca_toprint(1024, path, pathlen));
    if (strcmp(path, "/favicon.ico") == 0) {
      r->request_type = RHIZOME_HTTP_REQUEST_FAVICON;
      rhizome_server_http_response_header(r, 200, "image/vnd.microsoft.icon", favicon_len);
    } else if (strcmp(path, "/rhizome/groups") == 0) {
      /* Return the list of known groups */
      rhizome_server_sql_query_http_response(r, "id", "groups", "from groups", 32, 1);
    } else if (strcmp(path, "/rhizome/files") == 0) {
      /* Return the list of known files */
      rhizome_server_sql_query_http_response(r, "id", "files", "from files", 32, 1);
    } else if (strcmp(path, "/rhizome/bars") == 0) {
      /* Return the list of known BARs */
      rhizome_server_sql_query_http_response(r, "bar", "manifests", "from manifests", 32, 0);
    } else if (str_startswith(path, "/rhizome/file/", &id)) {
      /* Stream the specified payload */
      if (!rhizome_str_is_file_hash(id)) {
	rhizome_server_simple_http_response(r, 400, "<html><h1>Invalid payload ID</h1></html>\r\n");
      } else {
	// TODO: Check for Range: header and return 206 if returning partial content
	str_toupper_inplace(id);
	long long rowid = -1;
	sqlite_exec_int64(&rowid, "select rowid from files where id='%s';", id);
	if (rowid >= 0 && sqlite3_blob_open(rhizome_db, "main", "files", "data", rowid, 0, &r->blob) != SQLITE_OK)
	  rowid = -1;
	if (rowid == -1) {
	  rhizome_server_simple_http_response(r, 404, "<html><h1>Payload not found</h1></html>\r\n");
	} else {
	  r->source_index = 0;
	  r->blob_end = sqlite3_blob_bytes(r->blob);
	  rhizome_server_http_response_header(r, 200, "application/binary", r->blob_end - r->source_index);
	  r->request_type |= RHIZOME_HTTP_REQUEST_BLOB;
	}
      }
    } else if (str_startswith(path, "/rhizome/manifest/", &id)) {
      // TODO: Stream the specified manifest
      rhizome_server_simple_http_response(r, 500, "<html><h1>Not implemented</h1></html>\r\n");
    } else {
      rhizome_server_simple_http_response(r, 404, "<html><h1>Not found</h1></html>\r\n");
    }
  } else {
    if (debug & DEBUG_RHIZOME_TX)
      DEBUGF("Received malformed HTTP request: %s", alloca_toprint(120, (const char *)r->request, r->request_length));
    rhizome_server_simple_http_response(r, 400, "<html><h1>Malformed request</h1></html>\r\n");
  }
  
  /* Try sending data immediately. */
  rhizome_server_http_send_bytes(r);

  return 0;
}
int rhizome_direct_parse_http_request(rhizome_http_request *r)
{
  const char *submitBareFileURI=confValueGet("rhizome.api.addfile.uri", NULL);
  DEBUGF("uri=%s", submitBareFileURI ? alloca_str_toprint(submitBareFileURI) : "NULL");
  
  /* Switching to writing, so update the call-back */
  r->alarm.poll.events=POLLOUT;
  watch(&r->alarm);
  // Parse the HTTP request into verb, path, protocol, headers and content.
  char *const request_end = r->request + r->request_length;
  char *verb = r->request;
  char *path = NULL;
  char *proto = NULL;
  size_t pathlen = 0;
  char *headers = NULL;
  int headerlen = 0;
  char *content = NULL;
  int contentlen = 0;
  char *p;
  if ((str_startswith(verb, "GET", &p) || str_startswith(verb, "POST", &p)) && isspace(*p)) {
    *p++ = '\0';
    path = p;
    while (p < request_end && !isspace(*p))
      ++p;
    if (p < request_end) {
      pathlen = p - path;
      *p++ = '\0';
      proto = p;
      if ( str_startswith(p, "HTTP/1.", &p)
	&& (str_startswith(p, "0", &p) || str_startswith(p, "1", &p))
	&& (str_startswith(p, "\r\n", &headers) || str_startswith(p, "\n", &headers))
      ) {
	*p = '\0';
	char *eoh = str_str(headers, "\r\n\r\n", request_end - p);
	if (eoh) {
	  content = eoh + 4;
	  headerlen = content - headers;
	  contentlen = request_end - content;
	}
      }
    }
  }
  if (content == NULL) {
    if (debug & DEBUG_RHIZOME_TX)
      DEBUGF("Received malformed HTTP request %s", alloca_toprint(160, (const char *)r->request, r->request_length));
    return rhizome_server_simple_http_response(r, 400, "<html><h1>Malformed request</h1></html>\r\n");
  }
  INFOF("RHIZOME HTTP SERVER, %s %s %s", verb, alloca_toprint(-1, path, pathlen), proto);
  if (debug & DEBUG_RHIZOME_TX)
    DEBUGF("headers %s", alloca_toprint(-1, headers, headerlen));
  if (strcmp(verb, "GET") == 0 && strcmp(path, "/favicon.ico") == 0) {
    r->request_type = RHIZOME_HTTP_REQUEST_FAVICON;
    rhizome_server_http_response_header(r, 200, "image/vnd.microsoft.icon", favicon_len);
  } else if (strcmp(verb, "POST") == 0
      && (   strcmp(path, "/rhizome/import") == 0 
	  || strcmp(path, "/rhizome/enquiry") == 0
	  || (submitBareFileURI && strcmp(path, submitBareFileURI) == 0)
	 )
  ) {
    const char *cl_str=str_str(headers,"Content-Length: ",headerlen);
    const char *ct_str=str_str(headers,"Content-Type: multipart/form-data; boundary=",headerlen);
    if (!cl_str)
      return rhizome_server_simple_http_response(r,400,"<html><h1>Missing Content-Length header</h1></html>\r\n");
    if (!ct_str)
      return rhizome_server_simple_http_response(r,400,"<html><h1>Missing or unsupported Content-Type header</h1></html>\r\n");
    /* ok, we have content-type and content-length, now make sure they are well formed. */
    long long content_length;
    if (sscanf(cl_str,"Content-Length: %lld",&content_length)!=1)
      return rhizome_server_simple_http_response(r,400,"<html><h1>Malformed Content-Length header</h1></html>\r\n");
    char boundary_string[1024];
    int i;
    ct_str+=strlen("Content-Type: multipart/form-data; boundary=");
    for(i=0;i<1023&&*ct_str&&*ct_str!='\n'&&*ct_str!='\r';i++,ct_str++)
      boundary_string[i]=*ct_str;
    boundary_string[i] = '\0';
    if (i<4||i>128)
      return rhizome_server_simple_http_response(r,400,"<html><h1>Malformed Content-Type header</h1></html>\r\n");

    DEBUGF("content_length=%lld, boundary_string=%s contentlen=%d", (long long) content_length, alloca_str_toprint(boundary_string), contentlen);

    /* Now start receiving and parsing multi-part data.  If we already received some of the
	post-header data, process that first.  Tell the HTTP request that it has moved to multipart
	form data parsing, and what the actual requested action is.
    */

    /* Remember boundary string and source path.
	Put the preceeding -- on the front to make our life easier when
	parsing the rest later. */
    strbuf bs = strbuf_local(r->boundary_string, sizeof r->boundary_string);
    strbuf_puts(bs, "--");
    strbuf_puts(bs, boundary_string);
    if (strbuf_overrun(bs))
      return rhizome_server_simple_http_response(r,500,"<html><h1>Internal server error: Multipart boundary string too long</h1></html>\r\n");
    strbuf ps = strbuf_local(r->path, sizeof r->path);
    strbuf_puts(ps, path);
    if (strbuf_overrun(ps))
      return rhizome_server_simple_http_response(r,500,"<html><h1>Internal server error: Path too long</h1></html>\r\n");
    r->boundary_string_length = strbuf_len(bs);
    r->source_index = 0;
    r->source_count = content_length;
    r->request_type = RHIZOME_HTTP_REQUEST_RECEIVING_MULTIPART;
    r->request_length = 0;
    r->source_flags = 0;

    /* Find the end of the headers and start of any body bytes that we have read
	so far. Copy the bytes to a separate buffer, because r->request and 
	r->request_length get used internally in the parser.
      */
    if (contentlen) {
      char buffer[contentlen];
      bcopy(content, buffer, contentlen);
      rhizome_direct_process_post_multipart_bytes(r, buffer, contentlen);
    }

    /* Handle the rest of the transfer asynchronously. */
    return 0;
  } else {
    rhizome_server_simple_http_response(r, 404, "<html><h1>Not found (OTHER)</h1></html>\r\n");
  }
  
  /* Try sending data immediately. */
  rhizome_server_http_send_bytes(r);

  return 0;
}