--- trunk/jscoverage-server.c 2008/09/21 18:35:21 179 +++ trunk/jscoverage-server.c 2008/10/05 18:10:10 255 @@ -237,6 +237,44 @@ return result; } +static unsigned int hex_value(char c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } + else if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } + else if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } + else { + return 0; + } +} + +static char * decode_uri_component(const char * s) { + size_t length = strlen(s); + char * result = xmalloc(length + 1); + char * p = result; + while (*s != '\0') { + if (*s == '%') { + if (s[1] == '\0' || s[2] == '\0') { + *p = '\0'; + return result; + } + *p = hex_value(s[1]) * 16 + hex_value(s[2]); + s += 2; + } + else { + *p = *s; + } + p++; + s++; + } + *p = '\0'; + return result; +} + static const char * get_entity(char c) { switch(c) { case '<': @@ -528,7 +566,7 @@ static int write_json(Coverage * coverage, const char * path) { /* write the JSON */ - FILE * f = fopen(path, "w"); + FILE * f = fopen(path, "wb"); if (f == NULL) { return -1; } @@ -576,13 +614,23 @@ } mkdir_if_necessary(report_directory); - char * path = make_path(report_directory, "jscoverage.json"); + char * current_report_directory; + if (str_starts_with(abs_path, "/jscoverage-store/") && abs_path[18] != '\0') { + char * dir = decode_uri_component(abs_path + 18); + current_report_directory = make_path(report_directory, dir); + free(dir); + } + else { + current_report_directory = xstrdup(report_directory); + } + mkdir_if_necessary(current_report_directory); + char * path = make_path(current_report_directory, "jscoverage.json"); /* check if the JSON file exists */ struct stat buf; if (stat(path, &buf) == 0) { /* it exists: merge */ - FILE * f = fopen(path, "r"); + FILE * f = fopen(path, "rb"); if (f == NULL) { result = 1; } @@ -593,6 +641,7 @@ } } if (result != 0) { + free(current_report_directory); free(path); Coverage_delete(coverage); send_response(exchange, 500, "Could not merge with existing coverage data\n"); @@ -604,13 +653,15 @@ free(path); Coverage_delete(coverage); if (result != 0) { + free(current_report_directory); send_response(exchange, 500, "Could not write coverage data\n"); return; } /* copy other files */ - jscoverage_copy_resources(report_directory); - path = make_path(report_directory, "jscoverage.js"); + jscoverage_copy_resources(current_report_directory); + path = make_path(current_report_directory, "jscoverage.js"); + free(current_report_directory); FILE * f = fopen(path, "ab"); free(path); if (f == NULL) { @@ -668,12 +719,12 @@ } static void instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output_stream) { + const struct Resource * resource = get_resource("report.js"); + Stream_write(output_stream, resource->data, resource->length); + LOCK(&javascript_mutex); jscoverage_instrument_js(id, characters, num_characters, output_stream); UNLOCK(&javascript_mutex); - - const struct Resource * resource = get_resource("report.js"); - Stream_write(output_stream, resource->data, resource->length); } static bool is_hop_by_hop_header(const char * h) { @@ -801,12 +852,10 @@ free(encoding); Stream_delete(input_stream); if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) { - free(characters); send_response(client_exchange, 502, "Encoding not supported\n"); goto done; } else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) { - free(characters); send_response(client_exchange, 502, "Error decoding response\n"); goto done; } @@ -882,22 +931,25 @@ /* add the `Server' response-header (RFC 2616 14.38, 3.8) */ HTTPExchange_add_response_header(exchange, HTTP_SERVER, "jscoverage-server/" VERSION); + char * decoded_path = NULL; char * filesystem_path = NULL; const char * abs_path = HTTPExchange_get_abs_path(exchange); assert(*abs_path != '\0'); - if (str_starts_with(abs_path, "/jscoverage")) { + decoded_path = decode_uri_component(abs_path); + + if (str_starts_with(decoded_path, "/jscoverage")) { handle_jscoverage_request(exchange); goto done; } - if (strstr(abs_path, "..") != NULL) { + if (strstr(decoded_path, "..") != NULL) { send_response(exchange, 403, "Forbidden\n"); goto done; } - filesystem_path = make_path(document_root, abs_path + 1); + filesystem_path = make_path(document_root, decoded_path + 1); size_t filesystem_path_length = strlen(filesystem_path); if (filesystem_path_length > 0 && filesystem_path[filesystem_path_length - 1] == '/') { /* stat on Windows doesn't work with trailing slash */ @@ -1001,6 +1053,7 @@ done: free(filesystem_path); + free(decoded_path); } static void handler(HTTPExchange * exchange) { @@ -1041,7 +1094,7 @@ else if (strcmp(argv[i], "--report-dir") == 0) { i++; if (i == argc) { - fatal("--report-dir: option requires an argument"); + fatal_command_line("--report-dir: option requires an argument"); } report_directory = argv[i]; } @@ -1052,7 +1105,7 @@ else if (strcmp(argv[i], "--document-root") == 0) { i++; if (i == argc) { - fatal("--document-root: option requires an argument"); + fatal_command_line("--document-root: option requires an argument"); } document_root = argv[i]; } @@ -1063,7 +1116,7 @@ else if (strcmp(argv[i], "--encoding") == 0) { i++; if (i == argc) { - fatal("--encoding: option requires an argument"); + fatal_command_line("--encoding: option requires an argument"); } jscoverage_encoding = argv[i]; } @@ -1074,7 +1127,7 @@ else if (strcmp(argv[i], "--ip-address") == 0) { i++; if (i == argc) { - fatal("--ip-address: option requires an argument"); + fatal_command_line("--ip-address: option requires an argument"); } ip_address = argv[i]; } @@ -1089,7 +1142,7 @@ else if (strcmp(argv[i], "--no-instrument") == 0) { i++; if (i == argc) { - fatal("--no-instrument: option requires an argument"); + fatal_command_line("--no-instrument: option requires an argument"); } no_instrument[num_no_instrument] = argv[i]; num_no_instrument++; @@ -1102,7 +1155,7 @@ else if (strcmp(argv[i], "--port") == 0) { i++; if (i == argc) { - fatal("--port: option requires an argument"); + fatal_command_line("--port: option requires an argument"); } port = argv[i]; } @@ -1119,10 +1172,10 @@ } else if (strncmp(argv[i], "-", 1) == 0) { - fatal("unrecognized option `%s'", argv[i]); + fatal_command_line("unrecognized option `%s'", argv[i]); } else { - fatal("too many arguments"); + fatal_command_line("too many arguments"); } } @@ -1130,10 +1183,10 @@ char * end; unsigned long numeric_port = strtoul(port, &end, 10); if (*end != '\0') { - fatal("--port: option must be an integer"); + fatal_command_line("--port: option must be an integer"); } if (numeric_port > UINT16_MAX) { - fatal("--port: option must be 16 bits"); + fatal_command_line("--port: option must be 16 bits"); } /* is this a shutdown? */