/* Jon Arney, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ #include #include /* 0.4.28.c17 */ #include "sh_sys_types.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cache.h" #include "conf.h" #include "connection.h" #include "hash.h" #include "host.h" #include "http.h" #include "lib.h" #include "net.h" #include "qry.h" #include "transfer.h" #include "share.h" #include "threads.h" #include "ui.h" #define GNUT_HTTP_CACHE_EXPIRE 20 uint32 urate_history[10]; uint32 best_urate; int urate_clipped; int urate_measured; int gh_did_upload; #ifdef GNUT_HTTP_SEARCH int cgi_parse( char * CGI, char * field, char * value ); static void gnut_guid_to_string(char *string, char *guid); /* static void gnut_string_to_guid(char *guid, char *string); */ Gnut_Hash *query_cache; char *html_template_head; char *html_template_foot; void fre_ghq(gnut_http_query_t **x, int bugnum) { yfre((void **) x, bugnum); } /* Argument parsing routines * Return: 1 if invalid CGI string * 0 if valid CGI string */ int cgi_parse(char * CGI, char * field, char * value) { char * start; char * end; char * eq; char tmp[8192]; char tmp2[8192]; int flag=1; if (! strstr(CGI, "=")) { /* If no equal sign then this is an invalid CGI string */ GD_S(1, "This is _not_ a CGI string\n"); return 1; } start = CGI; end = start; while(*start && *end && (flag == 1)) { /* Find the end of the CGI string */ end = strstr( start, "&" ); if (!end) { end = start + strlen(start); } /* Is this the correct one? */ strncpy(tmp, start, end - start); tmp[end - start + 1] = '\0'; cgi_to_normal(tmp2, tmp); if (! strcmp(tmp2, field) || ! strcmp(tmp, field)) { eq = strstr(start, "="); if (eq) { strncpy(tmp, eq + 1, end - eq + 1); tmp[ end - eq + 1 ] = '\0'; cgi_to_normal(value, tmp); flag = 0; } } /* Move on to next one */ start = end + 1; } return flag; } void cgi_to_normal(char * news, char * olds) { int done = 0; int c; char pbuf[3]; while(!done) { if (G_ISALNUM(* olds) || * olds == '_' || * olds == '.' ) { *news = *olds; } else if (*olds == '+') { *news = ' '; } else if (*olds == '%') { olds++; pbuf[0] = *olds; pbuf[1] = *(olds + 1); pbuf[2] = '\0'; sscanf(pbuf, "%02x", &c); * news = c; olds++; } else { * news = '\0'; done = 1; } olds++; news++; } return; } static uchar gnut_http_query_cache_hash(void *p_data) { gnut_http_query_t *data; uchar hashval; int i; GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_cache_hash IN\n"); data = (gnut_http_query_t *)p_data; hashval = 0; for (i = 1; i < 16; i++) { if (i != 6) { hashval += data->guid[i]; } } GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_cache_hash OUT\n"); return hashval; } static int gnut_http_query_cache_compare(void *p_a, void *p_b) { gnut_http_query_t *a; gnut_http_query_t *b; char stringa[65]; char stringb[65]; a = (gnut_http_query_t *)p_a; b = (gnut_http_query_t *)p_b; GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_cache_compare IN\n"); /* * They match if the guid's match. */ gnut_guid_to_string(stringa, a->guid); gnut_guid_to_string(stringb, b->guid); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "Hasha = "); GD_S(GNUT_HTTP_INTERNALS_DEBUG, stringa); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "\n"); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "Hashb = "); GD_S(GNUT_HTTP_INTERNALS_DEBUG, stringb); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "\n"); a->guid[6] = '\0'; b->guid[6] = '\0'; if (memcmp(a->guid, b->guid, sizeof(a->guid)) == 0) { GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_cache_compare OUT - Found an equal hash by GUID\n"); return 0; } GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_cache_compare OUT - Hash not equal\n"); return -1; } static void gnut_http_query_cleanup(void) { gnut_http_query_t *req; Gnut_List *list; int i; time_t last_time_to_keep; GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_cleanup IN\n"); if (!query_cache) { GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_cleanup OUT - nothing to clean\n"); return; } /* Do garbage collection on the hash. * based on 'oldest'. Anything older than * EXPIRE minutes gets deleted. */ last_time_to_keep = time(0); last_time_to_keep -= (GNUT_HTTP_CACHE_EXPIRE*60); /* For each element of the hash, * remove the oldest. */ for (i = 0; i < 256; i++) { list = query_cache->list[i]; /* Walk the list looking for * old guys, and delete them. */ GD_S(GNUT_HTTP_INTERNALS_DEBUG, "Starting garbage collection with "); GD_I(GNUT_HTTP_INTERNALS_DEBUG, gnut_list_size(query_cache->list[i])); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "\n"); while (1) { if (!list) { break; } req = list->data; if (!req) { break; } /* This is a hack because I'm trying to remove an entry from the same * list I'm walking. */ if (req->last_used < last_time_to_keep) { GD_S(GNUT_HTTP_INTERNALS_DEBUG, "Removing an old req from the list\n"); list = gnut_list_remove(list, req, 4); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "Fr""eeing an old one\n"); fre_ghq(&req, 383); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "Re-assigning the list\n"); query_cache->list[i] = list; } else { list = gnut_list_next(list); } } GD_S(GNUT_HTTP_INTERNALS_DEBUG, "Finished garbage collection, now have "); GD_I(GNUT_HTTP_INTERNALS_DEBUG, gnut_list_size(query_cache->list[i])); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "\n"); } GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_cleanup OUT\n"); } Gnut_List *gnut_http_query_get(gnut_transfer *gt, char *guid) { gnut_http_query_t *req, req_findme; GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_get IN\n"); memcpy(req_findme.guid, guid, 16); if (!query_cache) { GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_get OUT - no cache yet\n"); return 0; } dqi(0x0157); req = gnut_hash_find(query_cache, &req_findme); if (!req) { GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_get OUT - no entry found in cache\n"); return 0; } req->last_used = time(0); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_get OUT\n"); return req->query_list; } void gnut_http_query_new(gnut_transfer *gt, char *request, char *guid) { gnut_http_query_t *req, req_findme; gnutella_packet *gpa; int i; GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_new IN\n"); if (request == 0) { /* The request was a frivolous one, ignore it. */ GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_new OUT - the query was frivolous\n"); return; } gnut_http_query_cleanup(); /* Actually submit the request with the new MAC * and stuff. */ gpa=gp_request_make(conf_get_str("mac"),request,conf_get_int("ttl")); for (i = 7; i < 16; i++) { gpa->gh[i] = guid[i]; } send_to_all(gpa); if (!query_cache) { query_cache = gnut_hash_new(gnut_http_query_cache_hash, gnut_http_query_cache_compare); } /* See if we're already on the list.... */ /* memcpy(req_findme.ip, gt->ip, sizeof(gt->ip)); */ /* req_findme.port = gt->gt_port; */ memcpy(req_findme.guid, gpa->gh, 16); dqi(0x0158); req = gnut_hash_find(query_cache, &req_findme); /* We're not on the list, so add us to the list. */ if (!req) { GD_S(GNUT_HTTP_INTERNALS_DEBUG, "Appending an http to the hash list\n"); req = (gnut_http_query_t *)ymaloc(sizeof(gnut_http_query_t), 326); memcpy(req->guid, gpa->gh, 16); memcpy(req->ip, gt->ip, 4); req->port = gt->gt_port; req->last_used = time(0); req->query_list = 0; gnut_hash_insert(query_cache, req, 500); } else { memcpy(req->guid, gpa->gh, 16); req->last_used = time(0); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "Updating the GUID to the hash list\n"); } fre_v(&(gpa->data), 183); fre_gpa(&gpa, 184); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_query_new OUT\n"); } /* Check a query reply (0x81) packet to see if it belongs to a pending * query issued through the HTTP interface, and if it is, append its * data to the list for that query. Returns 1 if this was done, 0 if not. */ int gnut_http_result_append(uint8 ip[4], uint8 port_le[2], uint32 ref, uint32 speed, uint32 size, char *name, uint8 guid[16], int16 htype, int16 rating) { gnut_http_query_t *req, req_findme; query_resp *a_qrp; char string[64]; GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_result_append IN\n"); if (!query_cache) { GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_result_append OUT - no query to append result to\n"); return 0; } memcpy(req_findme.guid, guid, sizeof(req_findme.guid)); gnut_guid_to_string(string, guid); dqi(0x0159); req = gnut_hash_find(query_cache, &req_findme); if (!req) { /* It doesn't appear to be our packet. */ GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_result_append OUT - this query did not belong to us : "); GD_S(GNUT_HTTP_INTERNALS_DEBUG, string); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "\n"); return 0; } a_qrp = query_create(327, /* qr-new */ ip, port_le, ref, speed, size, guid, htype, rating, 0, ' ', 0, 0, 0, name); req->last_used = time(0); req->query_list = gnut_list_prepend(req->query_list , a_qrp); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "gnut_http_result_append OUT - this reply belonged to us : "); GD_S(GNUT_HTTP_INTERNALS_DEBUG, string); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "\n"); return 1; } #define BUFSZ1 512 /* tagok */ int http_write_str(http_request_t *req, char *a) { int ret; GD_S(HTTP_IO_DEBUG, "http_write_str IN "); GD_S(HTTP_IO_DEBUG, a); GD_S(HTTP_IO_DEBUG, "\n"); if (!req) { return -1; } if (req->sock < 0) { return -1; } ret = write(req->sock, a, strlen(a)); GD_S(HTTP_IO_DEBUG, "http_write_str OUT "); GD_I(HTTP_IO_DEBUG, ret); GD_S(HTTP_IO_DEBUG, "\n"); return ret; } int http_write_va(http_request_t *req, int maxlen, char *format, ...) { va_list argp; int ret; char *value; GD_S(HTTP_IO_DEBUG, "http_write_va IN\n"); value = ymaloc(sizeof(char)*maxlen, 306); va_start(argp,format); vsprintf(value,format,argp); va_end(argp); ret = http_write_str(req, value); fre_str(&value, 379); GD_S(HTTP_IO_DEBUG, "http_write_va OUT "); GD_I(HTTP_IO_DEBUG, ret); GD_S(HTTP_IO_DEBUG, "\n"); return ret; } int gnut_http_file_search(gnut_transfer *gt, http_request_t *req) { Gnut_List *list; query_resp *a_qrp; char *search_string = 0; char *query_string; int ret; char c_port[32]; char c_address[BUFSZ1]; unsigned int i_port; char value[BUFSZ1]; #ifdef GNUT_HTTP_SEARCH char c_gnut_min_returns[32]; char c_gnut_time_to_wait[32]; int gnut_time_to_wait; int gnut_min_returns; int n; int count; #endif int i, i1; char guid[16]; http_cookie_t *cookp; gnut_time_to_wait = 60; gnut_min_returns = 20; GD_S(GNUT_HTTP_DEBUG, "gnut_http_file_search IN\n"); /* Check to see if we got a GUID in a cookie already... */ cookp = http_cookie_new(); /* Create a new guid and set it as a cookie to the browser. */ GD_S(1,"creating new cookie\n"); cookp->value = ymaloc(sizeof(char)*33, 328); memcpy(guid, conf_get_str("mac"), 6); for (i=6; i<16; i++) { guid[i] = rand(); } gnut_guid_to_string(cookp->value, guid); http_response_set_cookie(req, cookp); GD_S(GNUT_HTTP_DEBUG, "Setting the guid in a cookie "); GD_S(GNUT_HTTP_DEBUG, cookp->value); GD_S(GNUT_HTTP_DEBUG, "\n"); http_cookie_delete(cookp); /* Get the CGI string out of the query. */ query_string = strstr(req->url_file, "/search/?"); if (query_string) { query_string += 9; ret = cgi_parse(query_string, "query", value); GD_S(GNUT_HTTP_DEBUG, "Found "); GD_I(GNUT_HTTP_DEBUG, ret); GD_S(GNUT_HTTP_DEBUG, ":"); GD_S(GNUT_HTTP_DEBUG, value); GD_S(GNUT_HTTP_DEBUG, "\n"); if (!ret) { search_string = value; } #ifdef GNUT_HTTP_SEARCH ret = cgi_parse(query_string, "returns", c_gnut_min_returns); if (ret) { gnut_min_returns = 20; } else { gnut_min_returns = atoi(c_gnut_min_returns); } if (gnut_min_returns > 1200) { gnut_min_returns = 1200; } else if (gnut_min_returns < 1) { gnut_min_returns = 1; } GD_S(GNUT_HTTP_DEBUG, "Min Returns: "); GD_I(GNUT_HTTP_DEBUG, gnut_min_returns); GD_S(GNUT_HTTP_DEBUG, "\n"); ret = cgi_parse(query_string, "wait", c_gnut_time_to_wait); if (ret) { gnut_time_to_wait = 3; } else { gnut_time_to_wait = atoi(c_gnut_time_to_wait); } if (gnut_time_to_wait >600) { gnut_time_to_wait = 600; } else if (gnut_time_to_wait < 3) { gnut_time_to_wait = 3; } GD_S(GNUT_HTTP_DEBUG, "Max time to wait for returns "); GD_I(GNUT_HTTP_DEBUG, gnut_time_to_wait); GD_S(GNUT_HTTP_DEBUG, "\n"); #endif } else { search_string = 0; } GD_S(GNUT_HTTP_DEBUG, "Writing headers\n"); http_response_header_add(req, "Content-Type", "text/html"); http_response_header_add_va(req, "Server", "Gnut/%s", VERSION); http_response_header_add(req, "Connection", "close"); http_response_header_write(req); GD_S(GNUT_HTTP_DEBUG, "Writing search form\n"); gnut_http_write_top(req); gnut_http_write_form(req, gnut_time_to_wait, gnut_min_returns); /* End this now if there's no meaningful search to perform. */ if ((!search_string) || (strlen(search_string) < 4)) { http_write_str(req, "

"); if (search_string) { http_write_str(req, UI_HT_4CHARS_ERROR); } http_write_str(req, "\r\n"); gnut_http_write_bottom(req); return 0; } GD_S(GNUT_HTTP_DEBUG, "Starting new Gnutella-net query for "); GD_S(GNUT_HTTP_DEBUG, search_string); GD_S(GNUT_HTTP_DEBUG, "\n"); gnut_http_query_new(gt, search_string, guid); http_write_str(req, ""); http_write_va(req, BUFSZ1, UI_HT_SEARCH_RESULTS, search_string); http_write_str(req, ""); http_write_va(req, BUFSZ1, UI_HT_PLEASE_WAIT, gnut_time_to_wait); http_write_str(req, "
\r\n"); /* Wait for time_to_wait seconds, * or min_returns requests, whichever * is first. - By default, this is not a good * thing to do, as it's a little cumbersome. */ #ifdef GNUT_HTTP_SEARCH count = 0; while (count < gnut_time_to_wait) { list = gnut_http_query_get(gt, guid); if (list) { n = gnut_list_size(list); } else { /* No list means no results have come back yet */ n = 0; } if (n >= gnut_min_returns) { count = gnut_time_to_wait; } sleep(1); count++; /* This keeps some browsers from printing "stalled" in the status bar */ http_write_str(req, " "); } #endif http_write_str(req, ""); list = gnut_http_query_get(gt, guid); i1 = 0; while(1) { if (!list) { break; } a_qrp = list->data; if (!a_qrp) { break; } i1++; http_write_str(req, ""); http_write_va(req, BUFSZ1, "", i1); http_write_va(req, BUFSZ1, "", a_qrp->qr_size); http_write_va(req, BUFSZ1, "", a_qrp->qr_speed); /* Write the filename (which is also an anchor) */ http_write_str(req, ""); http_write_str(req, ""); list = gnut_list_next(list); if (!list) { break; } } http_write_str(req, "
"); http_write_str(req, UI_HT_FILE_NUM); http_write_str(req, ""); http_write_str(req, UI_HT_FILE_SIZE); http_write_str(req, ""); http_write_str(req, UI_HT_SPEED); http_write_str(req, ""); http_write_str(req, UI_HT_FILE_LOCATION); http_write_str(req, "
%i%u%u"); i_port = GET_LEU16(a_qrp->qr_port_le, 22); sprintf(c_port, "%i", i_port); sprintf(c_address, "%i.%i.%i.%i", a_qrp->qr_ip[0], a_qrp->qr_ip[1], a_qrp->qr_ip[2], a_qrp->qr_ip[3]); { /* 0.4.28.c15 */ char * name2; name2 = ystdup(a_qrp->qr_ndata, 168); make_htmlsafe(name2); http_write_va(req, BUFSZ1, "", c_address, c_port, a_qrp->qr_ref, name2); http_write_str(req, name2); fre_str(&name2, 170); } http_write_str(req, ""); http_write_str(req, "
"); gnut_http_write_bottom(req); return 0; } #endif #ifdef GNUT_HTTP_FILE_LIST int gnut_http_file_list(gnut_transfer *gt, http_request_t *req) { Gnut_List *list; share_item *si; int i1; GD_S(GNUT_HTTP_DEBUG, "gnut_http_file_list IN\n"); GD_S(GNUT_HTTP_DEBUG, "Writing headers\n"); http_response_header_add(req, "Content-Type", "text/html"); http_response_header_add_va(req, "Server", "Gnut/%s", VERSION); http_response_header_add(req, "Connection", "close"); http_response_header_write(req); GD_S(GNUT_HTTP_DEBUG, "Writing top\n"); gnut_http_write_top(req); http_write_str(req, ""); http_write_str(req, UI_HT_FILES_ON); http_write_str(req, ""); http_write_str(req, ""); GD_S(GNUT_HTTP_DEBUG, "Getting share root\n"); list = share_get_root(); GD_S(GNUT_HTTP_DEBUG, "Writing local share list\n"); i1 = 0; while(1) { if (!list) break; si = list->data; if (!si) break; i1++; http_write_str(req, ""); http_write_va(req, BUFSZ1, "", i1); http_write_va(req, BUFSZ1, "", si->size); http_write_str(req, ""); http_write_str(req, "\n"); list = gnut_list_next(list); } http_write_str(req, "
"); http_write_str(req, UI_HT_FILE_NUM); http_write_str(req, ""); http_write_str(req, UI_HT_FILE_SIZE); http_write_str(req, ""); http_write_str(req, UI_HT_FILE_LOCATION); http_write_str(req, ""); http_write_str(req, UI_HT_FILE_CACHE); http_write_str(req, "
%i%u"); { /* 0.4.28.c15 */ char * name2; name2 = ystdup(si->path, 238); make_htmlsafe(name2); http_write_va(req, BUFSZ1, "", si->ref, name2); http_write_str(req, name2); fre_str(&name2, 239); } http_write_str(req, ""); http_write_va(req, BUFSZ1, "%s", si->ref > share_cache_divider ? UI_HT_YES : UI_HT_NO); http_write_str(req, "
"); GD_S(GNUT_HTTP_DEBUG, "Writing bottom of HTML page.\n"); gnut_http_write_bottom(req); GD_S(GNUT_HTTP_DEBUG, "gnut_http_file_list OUT\n"); return 0; } #endif int parse_http_request(char *request, int32 *ref, char **name) { uint32 iref; char *ptr, *ptr2; GD_S(GNUT_HTTP_INTERNALS_DEBUG, "parse_http.request IN - "); GD_S(GNUT_HTTP_INTERNALS_DEBUG, request); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "\n"); ptr = strstr(request,"/get/"); if (ptr == 0) { GD_S(GNUT_HTTP_INTERNALS_DEBUG, "OUT - no /get/ in url\n"); return -2; } /* Danger will robinson. Unsafe pointer manipulations. */ ptr += 5; ptr2 = strchr(ptr,'/'); if (ptr2 == 0) { GD_S(GNUT_HTTP_INTERNALS_DEBUG, "OUT - no reference\n"); return -3; } GD_S(GNUT_HTTP_INTERNALS_DEBUG, "located end slash ptr = "); GD_S(GNUT_HTTP_INTERNALS_DEBUG, ptr); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "\n"); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "located end slash ptr2 = "); GD_S(GNUT_HTTP_INTERNALS_DEBUG, ptr2); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "\n"); /* Get file refnum */ iref = gl_stou32(ptr, 0); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "located ref num: "); GD_U(GNUT_HTTP_INTERNALS_DEBUG, iref); GD_S(GNUT_HTTP_INTERNALS_DEBUG, "\n"); /* By the Gnutellanet protocol standard, the name is irrelevant -- but * our caller might want to use it for logging purposes. */ *name = ystdup(ptr2, 464); *ref = iref; GD_S(GNUT_HTTP_INTERNALS_DEBUG, "parse_http.request OUT\n"); return 0; } static int parse_range(char *buf, uint32 size, uint32 *rangemin, uint32 *rangemax) { int i; uint32 num; uint32 num2; char *ptr; GD_S(GNUT_HTTP_INTERNALS_DEBUG, "parse.range IN\n"); for (i=6; i best) { best = urate_history[i]; } } if (best) { best_urate = best; urate_measured = 1; } /* Advertised measured speed cannot exceed either of the bandwidth limits */ uc = conf_get_int("default_upload_cap"); if ((uc > 0) && (uc < best_urate)) { best_urate = uc; urate_measured = 1; urate_clipped = 1; } uc = conf_get_int("global_bandwidth_cap"); if ((uc > 0) && (uc < best_urate)) { best_urate = uc; urate_measured = 1; urate_clipped = 1; } } int gnut_http_serve_file(gnut_transfer *gt, http_request_t *req) { uint32 ref; int ret; share_item *si = 0; char *buf = 0; uint32 partial = 0; uint32 rangemin = 0; uint32 rangemax = 0; char *range; GD_S(GNUT_HTTP_DEBUG, "gnut_http_serve_file IN\n"); range = http_request_find_header(req, "Range"); /* Parse to find the actual file we're looking for. */ ret = parse_http_request(req->url_file, &ref, >->fname); GD_S(GNUT_HTTP_DEBUG, "parse_http returned "); GD_I(GNUT_HTTP_DEBUG, ret); GD_S(GNUT_HTTP_DEBUG, " ref="); GD_U(GNUT_HTTP_DEBUG, ref); GD_S(GNUT_HTTP_DEBUG, " name="); GD_S(GNUT_HTTP_DEBUG, gt->fname); GD_S(GNUT_HTTP_DEBUG, "\n"); if (ref) { si = share_find(ref); } /* this next bit is not very intuitive, but I didn't want to have * to have 3 separate instances of cleanup code... */ if (ret<0 || si==0) { GD_S(GNUT_HTTP_DEBUG,"error with something!\n"); if (si) { fre_str(&(si->path), 171); fre_str(&(si->lc_path), 172); fre_str(&(si->fpath), 173); fre_si(&si, 174); } return -1; } buf = ymaloc(4096, 329); /* If this was a partial content request, * then parse the amount of the partial content. */ if (range) { partial = parse_range(range, si->size, &rangemin, &rangemax); GD_S(3, "range min: "); GD_U(3, rangemin); GD_S(3, " max: "); GD_U(3, rangemax); GD_S(3, "\n"); /* workaround Gnutella asking for a rangemin > file size */ if (partial && (rangemin > rangemax)) { partial = 0; } /* Workaround BearShare specifying a range even when they want the whole file */ if (rangemin == 0) { partial = 0; } } gt->fsock = open(si->fpath, O_RDONLY); if (gt->fsock<0) { GD_S(GNUT_HTTP_DEBUG,"couldn't open something!\n"); if (gt->fsock >= 0) { close_s(gt->fsock); } fre_str(&buf, 175); if (si) { fre_str(&(si->path), 176); fre_str(&(si->lc_path), 177); fre_str(&(si->fpath), 178); fre_si(&si, 179); } return -1; } GD_S(GNUT_HTTP_DEBUG,"writing out http reply\n"); if (partial == 0) { gt->gt_total = si->size; sprintf(buf, "Gnut/%s", VERSION); http_response_header_add(req, "Server", buf); http_response_header_add(req, "Content-Type", "application/octet-stream"); sprintf(buf, "%u", si->size); http_response_header_add(req, "Content-Length", buf); http_response_header_write(req); } else { gt->gt_bytes = rangemin; gt->gt_total = rangemax + 1; /* I used to have the correct header here HTTP 206 Partial Content tagok but the real gnutella thought this was an error so I changed it */ http_response_header_add(req, "Content-Type", "application/octet-stream"); sprintf(buf, "bytes %u-%u", rangemin, rangemax + 1); http_response_header_add(req, "Content-Range", buf); sprintf(buf, "%u", rangemax - rangemin + 1); http_response_header_add(req, "Content-Length", buf); http_response_header_write(req); lseek(gt->fsock, rangemin, SEEK_SET); } gt->gt_state = STATE_CONNECTED; gnut_xfer_loop(gt, 0, 0, 0); /* Log the completed upload */ if (gc_transferlog) { time_t tim; struct tm *ltim; tim = time(0); ltim = localtime(&tim); fprintf(gc_transferlog, "%d%02d%02d.%02d%02d%02d" "\ts\t%i.%i.%i.%i:%i\t%u\t%u\t%u\t%s\n", 1900+ltim->tm_year, 1+ltim->tm_mon, ltim->tm_mday, ltim->tm_hour, ltim->tm_min, ltim->tm_sec, gt->ip[0], gt->ip[1], gt->ip[2], gt->ip[3], gt->gt_port, gt->gt_bytes, gt->gt_total, (gt->rate_bytes) >> 4L, gt->fname); } /* Since we have accomplished an upload or partial upload, that proves we can upload (it doesn't matter if the upload was cancelled by the remote user or stopped by our user, we're just interested in whether uploads are possible. */ gh_did_upload = 1; /* Update our upload rate history */ if (gt->gt_bytes == gt->gt_total) { int i; /* Shift entries 0-8 to positions 1-9 */ for(i=8; i>=0; i--) { urate_history[i+1] = urate_history[i]; } urate_history[0] = (gt->rate_bytes) >> 4L; update_urate(); } GD_S(GNUT_HTTP_DEBUG,"closing up transfer\n"); fre_str(&buf, 180); GD_S(GNUT_HTTP_DEBUG, "gnut_http_serve_file OUT\n"); return 0; } #if GNUT_HTTP_FILE_LIST || GNUT_HTTP_SEARCH void gnut_http_write_top(http_request_t *req) { uint h_num; float64 bytes, files; if (html_template_head) { http_write_str(req, html_template_head); } else { http_write_str(req, "\r\n"); http_write_str(req, ""); http_write_str(req, UI_HT_MAIN_TITLE); http_write_str(req, "\r\n"); http_write_str(req, ""); http_write_str(req, "
"); http_write_str(req, UI_HT_MAIN_TITLE); http_write_str(req, "
"); http_write_str(req, UI_HT_CLIENT_WEB); http_write_str(req, " "); http_write_str(req, UI_HT_POWERED); http_write_str(req, VERSION); http_write_str(req, "\r\n
\r\n"); http_write_str(req, ""); http_write_str(req, UI_HT_GET_LOCAL_FILES); http_write_str(req, " | \r\n"); http_write_str(req, ""); http_write_str(req, UI_HT_SEARCH_THE); http_write_str(req, ""); http_write_str(req, "\r\n
\r\n"); http_write_str(req, UI_HT_RELATED); http_write_str(req, ""); http_write_str(req, UI_HT_GNUTELLA_URL); http_write_str(req, " "); http_write_str(req, ""); http_write_str(req, UI_HT_GNUT_URL); http_write_str(req, ""); http_write_str(req, "\r\n
\r\n"); http_write_str(req, UI_HT_NET_STATS); host_totals(&h_num, &files, &bytes); http_write_va(req, BUFSZ1, UI_HT_STAT_HOSTS, h_num); http_write_va(req, BUFSZ1, UI_HT_STAT_FILES, files); http_write_va(req, BUFSZ1, UI_HT_STAT_BYTES, bytes / 1000000.0); http_write_str(req, "\r\n
\r\n"); } } void gnut_http_write_bottom(http_request_t *req) { if (html_template_foot) { http_write_str(req,html_template_foot); } else { http_write_str(req, ""); http_write_str(req, "\r\n"); } } int gnut_http_write_form(http_request_t *req, int ttw, int rets) { int val, checked; GD_S(2,"gnut_http_write_form entering\n"); http_write_str(req, "
"); http_write_str(req, ""); http_write_str(req, UI_HT_SEARCH_THE); http_write_str(req, "
"); http_write_str(req, " "); http_write_str(req, "
"); #ifdef GNUT_HTTP_SEARCH http_write_str(req, UI_HT_WAIT_UP_TO); http_write_str(req, ""); http_write_str(req, UI_HT_OR_UNTIL); http_write_str(req, ""); http_write_str(req, UI_HT_REPLIES_ARRIVE); #endif http_write_str(req, "
"); return 0; } void gnut_http_template_scan() { FILE *fp; char *fname; long len; if (!conf_get_str("html_template")) { return; } if (strlen(conf_get_str("html_template"))<=1) { return; } fname = expand_path(conf_get_str("html_template")); GD_S(1, "path to html template: "); GD_S(1, fname); GD_S(1, "\n"); fp=fopen(fname,"r"); if (fp == 0) { GD_S(1, "Couldn't open html_template "); GD_S(1, fname); GD_S(1, "\n"); fre_str(&fname, 181); } else { fre_str(&fname, 182); /* find the length */ fseek(fp, 0, SEEK_END); len = ftell(fp); rewind(fp); GD_S(3, "html_template length: "); GD_I(3, len); GD_S(3, "\n"); /* real.loc space for the entire file */ if (html_template_head) { fre_str(&html_template_head, 490); } html_template_head = ymaloc(len+1, 491); if (!html_template_head) { GD_S(1, "Unable to real""loc space for HTML template.\n"); return; } /* slurp! */ fread(html_template_head, sizeof(char), len, fp); fclose(fp); /* search for comment */ html_template_foot=strstr(html_template_head,""); if (html_template_foot) { *html_template_foot = '\0'; html_template_foot+=13; } else { GD_S(1, "Comment not found.\n"); html_template_foot=&html_template_head[len]; } } } #endif static void gnut_guid_to_string(char *string, char *guid) { int i; for (i = 0; i < 16; i++) { sprintf(&string[i*2], "%02x", (unsigned char)guid[i]); } } void fre_hth(http_header_t **x, int bugnum) { yfre((void **) x, bugnum); } void fre_hrt(http_request_t **x, int bugnum) { yfre((void **) x, bugnum); } void fre_hct(http_cookie_t **x, int bugnum) { yfre((void **) x, bugnum); } http_request_t *http_request_new(int sock) { http_request_t *req; GD_S(HTTP_REQUEST_DEBUG, "http_request_new IN\n"); req = ymaloc(sizeof(http_request_t), 414); req->method = 0; req->version = 0; req->url_file = 0; req->rq_headers = 0; req->response_headers = 0; req->sock = sock; GD_S(HTTP_REQUEST_DEBUG, "http_request_new OUT\n"); return req; } void http_request_delete(http_request_t *req) { GD_S(HTTP_REQUEST_DEBUG, "http_request_delete IN\n"); if (req) { if (req->method) { fre_str(&(req->method), 270); } if (req->version) { fre_str(&(req->version), 271); } if (req->url_file) { fre_str(&(req->url_file), 272); } /* HACK - slightly leaky - we should * walk the list and fr.ee the contents first. */ if (req->rq_headers) { gnut_list_fre(req->rq_headers); } if (req->response_headers) { gnut_list_fre(req->response_headers); } fre_hrt(&req, 273); } GD_S(HTTP_REQUEST_DEBUG, "http_request_delete OUT\n"); } /* Break the HTTP request into its parts. */ int http_request_parse(http_request_t *req, char *request) { char *ptr_to_http; char *ptr_to_file; int len; GD_S(HTTP_REQUEST_DEBUG, "http_request_parse IN\n"); ptr_to_file = strstr(request, " "); if (!ptr_to_file) { GD_S(HTTP_REQUEST_DEBUG, "http_request_parse OUT - nothing to parse\n"); return -1; } ptr_to_file++; ptr_to_http=strstr(request,"HTTP/"); if (ptr_to_http == 0) { GD_S(HTTP_REQUEST_DEBUG, "http_request_parse OUT - not an HTTP/1.0 or higher request\n"); return -1; } /* * Save the "method" portion of the request. */ len = (ptr_to_file - request); req->method = ymaloc(sizeof(char)*(len+1), 415); strncpy(req->method, request, len); *(req->method+len) = '\0'; /* * Save the "url" portion of the request. */ len = (ptr_to_http-1-ptr_to_file); req->url_file = ymaloc(sizeof(char)*(len+1), 416); strncpy(req->url_file, ptr_to_file, len); *(req->url_file+len) = '\0'; /* * Save the HTTP version of the request - just in case we need it. */ len = strlen(ptr_to_http); req->version = ymaloc(sizeof(char)*(len+1), 417); strncpy(req->version, ptr_to_http, len); *(req->version + len) = '\0'; GD_S(HTTP_REQUEST_DEBUG, "http_request_parse OUT\n"); return 0; } /* Examine the HTTP headers and find one of the values. */ char *http_request_find_header(http_request_t *req, char *name) { Gnut_List *list; http_header_t *hdr; GD_S(HTTP_REQUEST_DEBUG, "http_request_find_header IN - "); GD_S(HTTP_REQUEST_DEBUG, name); GD_S(HTTP_REQUEST_DEBUG, "\n"); list = req->rq_headers; while (1) { if (!list) break; hdr = list->data; if (!hdr) break; if (!strcmp(hdr->name, name)) { GD_S(HTTP_REQUEST_DEBUG, "http_request_find_header OUT - found header "); GD_S(HTTP_REQUEST_DEBUG, hdr->value); GD_S(HTTP_REQUEST_DEBUG, "\n"); return hdr->value; } list = gnut_list_next(list); } GD_S(HTTP_REQUEST_DEBUG, "http_request_find_header OUT - no such header\n"); return 0; } /* Read HTTP headers into the request object. */ int http_request_read_header(http_request_t *req) { char buf[4096]; int ret; http_header_t *hdr; GD_S(HTTP_REQUEST_DEBUG, "http_request_read_header IN\n"); /* We must first read the remaining headers * and ignore them. */ ret = 1; while (1) { errno = 0; ret = read_line(req->sock, buf, 4096); if (gc_debug_opts & 4) { printf("hrh01 %s", buf); } if (errno == EINTR ) break; if (strlen(buf) < 3) break; hdr = http_header_new(); if (!hdr) break; ret = http_header_parse(hdr, buf); if (ret) { break; } GD_S(1, "Got header "); GD_S(1, hdr->name); GD_S(1, ":"); GD_S(1, hdr->value); GD_S(1, "\n"); req->rq_headers = gnut_list_prepend(req->rq_headers, hdr); } GD_S(HTTP_REQUEST_DEBUG, "http_request_read_header OUT\n"); return 0; } /* Set the contents of an HTTP header. */ int http_response_header_add(http_request_t *req, char *name, char *value) { http_header_t *hdr; GD_S(HTTP_HEADER_DEBUG, "http_response_header_add IN - "); GD_S(HTTP_HEADER_DEBUG, name); GD_S(HTTP_HEADER_DEBUG, " - "); GD_S(HTTP_HEADER_DEBUG, value); GD_S(HTTP_HEADER_DEBUG, "\n"); hdr = http_header_new(); http_header_set(hdr, name, value); req->response_headers = gnut_list_prepend(req->response_headers, hdr); GD_S(HTTP_HEADER_DEBUG, "http_response_header_add OUT\n"); return 0; } int http_response_header_add_va(http_request_t *req, char *name, char *format, ...) { va_list argp; char hdr_value[64]; int ret; GD_S(HTTP_HEADER_DEBUG, "http_response_header_add_va IN\n"); va_start(argp,format); vsprintf(hdr_value,format,argp); va_end(argp); ret = http_response_header_add(req, name, hdr_value); GD_S(HTTP_HEADER_DEBUG, "http_response_header_add_va OUT\n"); return ret; } http_cookie_t *http_cookie_new(void) { http_cookie_t *cookp; cookp = ymaloc(sizeof(http_cookie_t), 418); cookp->value = 0; cookp->path = 0; cookp->domain = 0; cookp->expiration = time(0); return cookp; } int http_header_get_cookie(http_request_t *req, http_cookie_t *cookp) { char *end_of_value; int len_of_value; char *header_value; header_value = http_request_find_header(req, "Cookie"); if (!header_value) return -1; end_of_value = strchr(header_value, ';'); if (!end_of_value) { len_of_value = strlen(header_value)+1; } else { len_of_value = end_of_value - header_value; } cookp->value = ymaloc(sizeof(char)*len_of_value, 419); if (!cookp->value) { return -1; } strncpy(cookp->value, header_value, len_of_value); cookp->value[len_of_value-1] = '\0'; return 0; } void http_cookie_delete(http_cookie_t *cookie) { if (cookie) { if (cookie->value) { fre_str(&(cookie->value), 274); } if (cookie->path) { fre_str(&(cookie->path), 275); } if (cookie->domain) { fre_str(&(cookie->domain), 276); } fre_hct(&cookie, 558); } } int http_response_set_cookie(http_request_t *req, http_cookie_t *cookie) { return http_response_header_add_va(req, "Set-Cookie", "%s; path=%s", cookie->value, "/"); } int http_response_header_write(http_request_t *req) { http_header_t *hdr; Gnut_List *list; GD_S(HTTP_REQUEST_DEBUG, "http_response_header_write IN\n"); /* If returning a partial file, we would want to write * "206 Partial Content", but alas, the original Gnutella * (Nullsoft Windows client) doesn't work if we do that */ http_write_str(req, "HTTP/1.0 200 OK\r\n"); /* tagok */ list = req->response_headers; while (1) { if (!list) break; hdr = list->data; if (!hdr) break; http_write_va(req, HTTP_MAX_HEADER_SIZE, "%s: %s\r\n", hdr->name, hdr->value); list = gnut_list_next(list); } http_write_str(req, "\r\n"); GD_S(HTTP_REQUEST_DEBUG, "http_response_header_write OUT\n"); return 0; } /* Create a new header object. */ http_header_t *http_header_new(void) { http_header_t *hdr; GD_S(HTTP_HEADER_DEBUG, "http_header_new IN\n"); hdr = (http_header_t *)ymaloc(sizeof(http_header_t), 307); if (hdr) { hdr->name = 0; hdr->value = 0; } GD_S(HTTP_HEADER_DEBUG, "http_header_new OUT\n"); return hdr; } /* Parse an HTTP header from a header line. */ int http_header_parse(http_header_t *hdr, char *hdr_line) { char *value; char *s; int len; GD_S(HTTP_HEADER_DEBUG, "http_header_parse IN\n"); value = strchr(hdr_line, ':'); if (!value) { GD_S(HTTP_HEADER_DEBUG, "This is not a proper header\n"); return -1; } len = value - hdr_line; hdr->name = ymaloc(sizeof(char)*(len+1), 308); if (hdr->name) { strncpy(hdr->name, hdr_line, len); *(hdr->name+len) = '\0'; } /* Skip past the ":" and the space. */ value++; if (*value) { value++; } if (!(*value)) { if (hdr->name) { fre_str(&(hdr->name), 380); } fre_hth(&hdr, 381); GD_S(HTTP_HEADER_DEBUG, "http_header_parse OUT - not a proper header\n"); return -1; } len = strlen(value)-2; hdr->value = ymaloc(sizeof(char)*(len+1), 309); if (hdr->value) { strncpy(hdr->value, value, len); *(hdr->value+len) = '\0'; s = hdr->value+(len-1); while (G_ISSPACE(*s)) { *s = '\0'; } } GD_S(HTTP_HEADER_DEBUG, "http_header_parse OUT\n"); return 0; } /* Set the contents of an HTTP header. */ int http_header_set(http_header_t *hdr, char *name, char *value) { GD_S(HTTP_HEADER_DEBUG, "http_header_set IN\n"); hdr->name = ystdup(name, 455); hdr->value = ystdup(value, 456); GD_S(HTTP_HEADER_DEBUG, "http_header_set OUT\n"); return 0; } void http_header_delete(http_header_t *hdr) { GD_S(HTTP_HEADER_DEBUG, "http_header_delete IN\n"); if (hdr) { if (hdr->name) { fre_str(&(hdr->name), 376); } if (hdr->value) { fre_str(&(hdr->value), 377); } fre_hth(&hdr, 378); } GD_S(HTTP_HEADER_DEBUG, "http_header_delete OUT\n"); } #if 0 these are sent by clients getting files from us User-Agent: Java1.1.8 Host: 24.128.226.169:5634 Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 */ Connection: keep-alive User-Agent: Gnutella User-Agent: Gnotella Range: bytes=0- User-Agent: Gnotella 0.9.9 Referer: Gnutella Connection: Keep-Alive Connection: Keep-Alive Range: bytes=0- Host: 24.128.226.169:5634 Accept: */* */ Range: bytes=1-1 User-Agent: RealDownload/4.0.0.41 User-Agent: LimeWire Range: bytes=0- User-Agent: LimeWire 1.4 Range: bytes=0- User-Agent: BearShare 1.3.1 Connection: Keep-Alive Range: bytes=0- User-Agent: BearShare 1.3.2 Connection: Keep-Alive Range: bytes=0- User-Agent: BearShare 2.0.0 Referrer: Gnutella file sharing network Connection: Keep-Alive Range: bytes=0- Server: BearShare 2.0.1 Content-type:application/binary Content-Length:3616520 Content-Range: bytes=0-3616519/3616520 User-Agent: BearShare 2.0.4 Referrer: Gnutella Connection: Keep-Alive Range: bytes 0- User-Agent: BearShare 2.0.9 Referrer: Gnutella Connection: Keep-Alive Range: bytes 0- User-Agent: BearShare 2.2.3 Referrer: Gnutella Connection: Keep-Alive Range: bytes=0- these are returned by servers when we do downloads HTTP 200 OK tagok Server: Gnutella Content-type:application/binary Content-length:3071634 HTTP 200 OK tagok Server: BearShare 2.0.0 Content-type:application/binary Content-Length:5016346 Content-Range: bytes=0-5016345/5016346 HTTP 503 Duplicate Request tagok Server: BearShare 2.0.0 HTTP 503 Server Busy tagok Server: BearShare 2.0.1 #endif