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); }
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; }