diff --git a/rhizome.h b/rhizome.h index 54949e94..72b8ec5e 100644 --- a/rhizome.h +++ b/rhizome.h @@ -45,6 +45,7 @@ typedef struct rhizome_http_request { // #define RHIZOME_HTTP_REQUEST_BUNDLEMANIFEST 32 // for anything too big, we can just use a blob #define RHIZOME_HTTP_REQUEST_BLOB 64 +#define RHIZOME_HTTP_REQUEST_FAVICON 128 /* Local buffer of data to be sent. If a RHIZOME_HTTP_REQUEST_FROMBUFFER, then the buffer is sent, and when empty @@ -60,6 +61,8 @@ typedef struct rhizome_http_request { request types */ unsigned char source[1024]; long long source_index; + long long source_count; + int source_record_size; char *blob_table; char *blob_column; @@ -183,3 +186,4 @@ int rhizome_server_simple_http_response(rhizome_http_request *r,int result, char long long sqlite_exec_int64(char *sqlformat,...); int rhizome_server_http_response_header(rhizome_http_request *r,int result, char *mime_type,unsigned long long bytes); +int rhizome_server_sql_query_fill_buffer(int rn,rhizome_http_request *r); diff --git a/rhizome_http.c b/rhizome_http.c index 48449176..82498d98 100644 --- a/rhizome_http.c +++ b/rhizome_http.c @@ -36,6 +36,31 @@ int sigIoFlag=0; rhizome_http_request *rhizome_live_http_requests[RHIZOME_SERVER_MAX_LIVE_REQUESTS]; int rhizome_server_live_request_count=0; +// Format icon data using: +// od -vt u1 ~/Downloads/favicon.ico | cut -c9- | sed 's/ */,/g' +unsigned char favicon_bytes[]={ +0,0,1,0,1,0,16,16,16,0,0,0,0,0,40,1 +,0,0,22,0,0,0,40,0,0,0,16,0,0,0,32,0 +,0,0,1,0,4,0,0,0,0,0,128,0,0,0,0,0 +,0,0,0,0,0,0,16,0,0,0,0,0,0,0,104,158 +,168,0,163,233,247,0,104,161,118,0,0,0,0,0,0,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17 +,17,17,17,18,34,17,17,18,34,17,17,18,34,17,17,2 +,34,17,17,18,34,17,16,18,34,1,17,17,1,17,1,17 +,1,16,1,16,17,17,17,17,1,17,16,16,17,17,17,17 +,1,17,18,34,17,17,17,16,17,17,2,34,17,17,17,16 +,17,16,18,34,17,17,17,16,17,1,17,1,17,17,17,18 +,34,17,17,16,17,17,17,18,34,17,17,18,34,17,17,18 +,34,17,17,18,34,17,17,16,17,17,17,18,34,17,17,16 +,17,17,17,17,17,0,17,1,17,17,17,17,17,17,0,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +int favicon_len=318; + void sigPipeHandler(int signal) { sigPipeFlag++; @@ -248,31 +273,200 @@ int rhizome_server_get_fds(struct pollfd *fds,int *fdcount,int fdmax) return 0; } +int hexFilter(char *s) +{ + int l=strlen(s); + int i; + int o=0; + int e=0; + for(i=0;i='0'&&s[i]<='9') + ||(s[i]>='a'&&s[i]<='f') + ||(s[i]>='A'&&s[i]<='F')) + s[o++]=s[i]; + else e++; + } + s[o]=0; + return -e; +} + +int rhizome_server_sql_query_http_response(int rn,rhizome_http_request *r, + char *column,char *query_body, + int bytes_per_row) +{ + /* 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 , 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. + */ + + char query[1024]; + + if (r->buffer) { free(r->buffer); r->buffer=NULL; } + r->buffer_size=16384; + r->buffer=malloc(r->buffer_size); + if (!r->buffer) return WHY("malloc() failed to allocate response buffer"); + r->buffer_length=0; + r->buffer_offset=0; + + snprintf(query,1024,"SELECT COUNT(*) %s",query_body); + query[1023]=0; + + r->source_record_size=bytes_per_row; + r->source_count=sqlite_exec_int64(query); + + if (r->source_count<1) r->source_count=0; + + /* 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); + printf("headers consumed %d bytes.\n",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 */ + + printf("Found %lld records.\n",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+ */ + WHY("no function yet exists to obtain our public key?"); + + /* build templated query */ + snprintf(query,1024,"SELECT %s %s",column,query_body); + query[1023]=0; + bcopy(query,r->source,1024); + r->source_index=0; + + printf("buffer_length=%d\n",r->buffer_length); + + /* Populate spare space in buffer with rows of data */ + return rhizome_server_sql_query_fill_buffer(rn,r); +} + +int rhizome_server_sql_query_fill_buffer(int rn,rhizome_http_request *r) +{ + printf("populating with sql rows at offset %d\n",r->buffer_length); + if (r->source_index>=r->source_count) + { + /* All done */ + return 0; + } + + int record_count=(r->buffer_size-r->buffer_length)/r->source_record_size; + if (record_count<1) { + printf("r->buffer_size=%d, r->buffer_length=%d, r->source_record_size=%d\n", + r->buffer_size, r->buffer_length, r->source_record_size); + return WHY("Not enough space to fit any records"); + } + + char query[1024]; + snprintf(query,1024,"%s LIMIT %lld,%d",r->source,r->source_index,record_count); + + sqlite3_stmt *statement; + WHY(query); + switch (sqlite3_prepare_v2(rhizome_db,query,-1,&statement,NULL)) + { + case SQLITE_OK: case SQLITE_DONE: case SQLITE_ROW: + break; + default: + sqlite3_finalize(statement); + sqlite3_close(rhizome_db); + rhizome_db=NULL; + WHY(query); + WHY(sqlite3_errmsg(rhizome_db)); + return WHY("Could not prepare sql statement."); + } + while(((r->buffer_length+r->source_record_size)buffer_size) + &&(sqlite3_step(statement)==SQLITE_ROW)) + { + r->source_index++; + + if (sqlite3_column_count(statement)!=1) { + sqlite3_finalize(statement); + return WHY("sqlite3 returned multiple columns for a single column query"); + } + const unsigned char *value; + if (sqlite3_column_type(statement, 0)==SQLITE_TEXT) + value=sqlite3_column_text(statement, 0); + else + /* improper column type, so don't include in report */ + continue; + + bcopy(value,&r->buffer[r->buffer_length],r->source_record_size); + r->buffer_length+=r->source_record_size; + + printf("wrote row %lld, buffer_length=%d\n", + r->source_index,r->buffer_length); + } + sqlite3_finalize(statement); + + return 0; +} + + int rhizome_server_parse_http_request(int rn,rhizome_http_request *r) { - WHY("not implemented. just returning an HTTP error for now."); char id[1024]; + + /* Clear request type flags */ + r->request_type=0; if (strlen(r->request)<1024) { - if (!strncasecmp("GET /rhizome/groups HTTP/1.",r->request, + if (!strncasecmp(r->request,"GET /favicon.ico HTTP/1.", + strlen("GET /favicon.ico HTTP/1."))) + { + r->request_type=RHIZOME_HTTP_REQUEST_FAVICON; + rhizome_server_http_response_header(r,200,"image/vnd.microsoft.icon", + favicon_len); + } + else if (!strncasecmp(r->request,"GET /rhizome/groups HTTP/1.", strlen("GET /rhizome/groups HTTP/1."))) { /* Return the list of known groups */ printf("get /rhizome/groups (list of groups)\n"); - rhizome_server_simple_http_response(r,200,"

List of groups

\r\n"); + rhizome_server_sql_query_http_response(rn,r,"id","from groups",64); } - else if (sscanf("GET /rhizome/file/%[0-9a-f] HTTP/1.",r->request, + else if (!strncasecmp(r->request,"GET /rhizome/files HTTP/1.", + strlen("GET /rhizome/files HTTP/1."))) + { + /* Return the list of known files */ + printf("get /rhizome/files (list of files)\n"); + rhizome_server_sql_query_http_response(rn,r,"id","from files",64); + } + else if (!strncasecmp(r->request,"GET /rhizome/manifests HTTP/1.", + strlen("GET /rhizome/manifests HTTP/1."))) + { + /* Return the list of known files */ + printf("get /rhizome/manifests (list of manifests)\n"); + rhizome_server_sql_query_http_response(rn,r,"id","from manifests",64); + } + else if (sscanf(r->request,"GET /rhizome/file/%s HTTP/1.", id)==1) { /* Stream the specified file */ int dud=0; int i; + hexFilter(id); printf("get /rhizome/file/ [%s]\n",id); WHY("Check for range: header, and return 206 if returning partial content"); for(i=0;i'f')||(id[i]=='\'')) dud++; if (dud) rhizome_server_simple_http_response(r,400,"

That doesn't look like hex to me.

\r\n"); else { - unsigned long long rowid = sqlite_exec_int64("select rowid from files where id='%s';",id); + long long rowid = sqlite_exec_int64("select rowid from files where id='%s';",id); sqlite3_blob *blob; if (rowid>=0) if (sqlite3_blob_open(rhizome_db,"main","files","id",rowid,0,&blob) @@ -296,10 +490,11 @@ int rhizome_server_parse_http_request(int rn,rhizome_http_request *r) } } } - else if (sscanf("GET /rhizome/manifest/%[0-9a-f] HTTP/1.",r->request, + else if (sscanf(r->request,"GET /rhizome/manifest/%s HTTP/1.", id)==1) { /* Stream the specified manifest */ + hexFilter(id); printf("get /rhizome/manifest/ [%s]\n",id); rhizome_server_simple_http_response(r,400,"

A specific manifest

\r\n"); } else @@ -332,6 +527,7 @@ char *httpResultString(int id) { int rhizome_server_simple_http_response(rhizome_http_request *r,int result, char *response) { + if (r->buffer) free(r->buffer); r->buffer_size=strlen(response)+strlen("HTTP/1.0 000 \r\n\r\n")+strlen(httpResultString(A_VALUE_GREATER_THAN_FOUR))+100; r->buffer=(unsigned char *)malloc(r->buffer_size); @@ -365,13 +561,20 @@ int rhizome_server_http_send_bytes(int rn,rhizome_http_request *r) bytes=r->buffer_length-r->buffer_offset; bytes=write(r->socket,&r->buffer[r->buffer_offset],bytes); if (bytes>0) { + printf("wrote %d bytes\n",bytes); + dump("bytes written",&r->buffer[r->buffer_offset],bytes); r->buffer_offset+=bytes; if (r->buffer_offset>=r->buffer_length) { /* Our work is done. close socket and go home */ r->request_type&=~RHIZOME_HTTP_REQUEST_FROMBUFFER; + r->buffer_offset=0; r->buffer_length=0; if (!r->request_type) { WHY("Finished sending data"); return rhizome_server_close_http_request(rn); + } else { + if (debug>1) { fprintf(stderr,"request type = 0x%x after sending buffer.\n", + r->request_type); + } } } else { /* Still more stuff in the buffer, so return now */ @@ -382,6 +585,24 @@ int rhizome_server_http_send_bytes(int rn,rhizome_http_request *r) switch(r->request_type) { + case RHIZOME_HTTP_REQUEST_FAVICON: + if (r->buffer_sizebuffer); + r->buffer_size=0; + r->buffer=malloc(favicon_len); + if (!r->buffer) r->request_type=0; + } + if (r->buffer) + { + int i; + for(i=0;ibuffer[i]=favicon_bytes[i]; + r->buffer_length=i; + printf("buffer_length for favicon is %d\n",r->buffer_length); + r->request_type=RHIZOME_HTTP_REQUEST_FROMBUFFER; + } + + break; case RHIZOME_HTTP_REQUEST_FROMBUFFER: /* This really shouldn't happen! */ @@ -399,12 +620,13 @@ int rhizome_server_http_send_bytes(int rn,rhizome_http_request *r) int rhizome_server_http_response_header(rhizome_http_request *r,int result, char *mime_type,unsigned long long bytes) { - r->buffer_size=bytes+strlen("HTTP/1.0 000 \r\n\r\n")+strlen(httpResultString(A_VALUE_GREATER_THAN_FOUR))+100; - r->buffer=(unsigned char *)malloc(r->buffer_size); + if (!r->buffer) { + r->buffer_size=bytes+strlen("HTTP/1.0 000 \r\n\r\n")+strlen(httpResultString(A_VALUE_GREATER_THAN_FOUR))+100; + r->buffer=(unsigned char *)malloc(r->buffer_size); + } snprintf((char *)r->buffer,r->buffer_size,"HTTP/1.0 %03d \r\nContent-type: text/html\r\nContent-length: %lld\r\n\r\n",result,bytes); - r->buffer_size=strlen((char *)r->buffer)+1; - r->buffer_length=r->buffer_size-1; + r->buffer_length=strlen((char *)r->buffer); r->buffer_offset=0; r->request_type|=RHIZOME_HTTP_REQUEST_FROMBUFFER;