diff --git a/src/html_cache.c b/src/html_cache.c index d5dbc3c..0f114b5 100644 --- a/src/html_cache.c +++ b/src/html_cache.c @@ -104,6 +104,83 @@ char *c_simple_http_path_to_cache_filename(const char *path) { } } +char *c_simple_http_cache_filename_to_path(const char *cache_filename) { + uint_fast8_t is_percent_encoded = 0; + if (!cache_filename) { + return NULL; + } else if (strcmp(cache_filename, "ROOT") == 0) { + char *buf = malloc(2); + buf[0] = '/'; + buf[1] = 0; + return buf; + } else if (cache_filename[0] == '%') { + is_percent_encoded = 1; + } + + __attribute__((cleanup(simple_archiver_list_free))) + SDArchiverLinkedList *parts = simple_archiver_list_init(); + + size_t idx = 0; + size_t prev_idx = 0; + const size_t size = strlen(cache_filename); + char *buf; + size_t buf_size; + + for(; idx < size && cache_filename[idx] != 0; ++idx) { + if (is_percent_encoded && strncmp(cache_filename + idx, "%2F", 3) == 0) { + if (prev_idx < idx) { + buf_size = idx - prev_idx + 2; + buf = malloc(buf_size); + memcpy(buf, cache_filename + prev_idx, buf_size - 2); + buf[buf_size - 2] = '/'; + buf[buf_size - 1] = 0; + c_simple_http_add_string_part(parts, buf, 0); + free(buf); + } else { + buf_size = 2; + buf = malloc(buf_size); + buf[0] = '/'; + buf[1] = 0; + c_simple_http_add_string_part(parts, buf, 0); + free(buf); + } + idx += 2; + prev_idx = idx + 1; + } else if (!is_percent_encoded + && strncmp(cache_filename + idx, "0x2F", 4) == 0) { + if (prev_idx < idx) { + buf_size = idx - prev_idx + 2; + buf = malloc(buf_size); + memcpy(buf, cache_filename + prev_idx, buf_size - 2); + buf[buf_size - 2] = '/'; + buf[buf_size - 1] = 0; + c_simple_http_add_string_part(parts, buf, 0); + free(buf); + } else { + buf_size = 2; + buf = malloc(buf_size); + buf[0] = '/'; + buf[1] = 0; + c_simple_http_add_string_part(parts, buf, 0); + free(buf); + } + idx += 3; + prev_idx = idx + 1; + } + } + + if (prev_idx < idx) { + buf_size = idx - prev_idx + 1; + buf = malloc(buf_size); + memcpy(buf, cache_filename + prev_idx, buf_size - 1); + buf[buf_size - 1] = 0; + c_simple_http_add_string_part(parts, buf, 0); + free(buf); + } + + return c_simple_http_combine_string_parts(parts); +} + int c_simple_http_cache_path( const char *path, const char *config_filename, diff --git a/src/test.c b/src/test.c index 7c807a7..e9a5da0 100644 --- a/src/test.c +++ b/src/test.c @@ -598,49 +598,129 @@ int main(void) { // Test html_cache. { char *ret = c_simple_http_path_to_cache_filename("/"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "ROOT") == 0); free(ret); ret = c_simple_http_path_to_cache_filename("////"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "ROOT") == 0); free(ret); ret = c_simple_http_path_to_cache_filename("/inner"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "0x2Finner") == 0); free(ret); ret = c_simple_http_path_to_cache_filename("/inner////"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "0x2Finner") == 0); free(ret); ret = c_simple_http_path_to_cache_filename("/outer/inner"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "0x2Fouter0x2Finner") == 0); free(ret); ret = c_simple_http_path_to_cache_filename("/outer/inner////"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "0x2Fouter0x2Finner") == 0); free(ret); ret = c_simple_http_path_to_cache_filename("/outer///inner"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "0x2Fouter0x2Finner") == 0); free(ret); ret = c_simple_http_path_to_cache_filename("/outer/with_hex_0x2F_inner"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "%2Fouter%2Fwith_hex_0x2F_inner") == 0); free(ret); ret = c_simple_http_path_to_cache_filename("/outer/0x2F_hex_inner"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "%2Fouter%2F0x2F_hex_inner") == 0); free(ret); ret = c_simple_http_path_to_cache_filename("/outer0x2F/inner_hex_0x2F"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "%2Fouter0x2F%2Finner_hex_0x2F") == 0); free(ret); ret = c_simple_http_path_to_cache_filename( "/0x2Fouter0x2F/0x2Finner_0x2F_hex_0x2F"); + ASSERT_TRUE(ret); CHECK_TRUE(strcmp(ret, "%2F0x2Fouter0x2F%2F0x2Finner_0x2F_hex_0x2F") == 0); free(ret); + + ret = c_simple_http_cache_filename_to_path("0x2Fouter0x2Finner"); + ASSERT_TRUE(ret); + printf("%s\n", ret); + CHECK_TRUE(strcmp(ret, "/outer/inner") == 0); + free(ret); + + ret = c_simple_http_cache_filename_to_path("0x2Fouter0x2Finner0x2F%2F0x2Fmore_inner"); + ASSERT_TRUE(ret); + CHECK_TRUE(strcmp(ret, "/outer/inner/%2F/more_inner") == 0); + free(ret); + + ret = c_simple_http_cache_filename_to_path("%2Fouter%2Finner"); + ASSERT_TRUE(ret); + CHECK_TRUE(strcmp(ret, "/outer/inner") == 0); + free(ret); + + ret = c_simple_http_cache_filename_to_path("%2Fouter%2Finner%2F0x2F%2Fmore_inner"); + ASSERT_TRUE(ret); + CHECK_TRUE(strcmp(ret, "/outer/inner/0x2F/more_inner") == 0); + free(ret); + + const char *uri0 = "/a/simple/url/with/inner/paths"; + ret = + c_simple_http_path_to_cache_filename(uri0); + ASSERT_TRUE(ret); + CHECK_TRUE( + strcmp(ret, "0x2Fa0x2Fsimple0x2Furl0x2Fwith0x2Finner0x2Fpaths") + == 0); + char *ret2 = c_simple_http_cache_filename_to_path(ret); + free(ret); + ASSERT_TRUE(ret2); + CHECK_TRUE(strcmp(ret2, uri0) == 0); + free(ret2); + + const char *uri1 = "/a/url/with/0x2F/in/it"; + ret = + c_simple_http_path_to_cache_filename(uri1); + ASSERT_TRUE(ret); + CHECK_TRUE( + strcmp(ret, "%2Fa%2Furl%2Fwith%2F0x2F%2Fin%2Fit") + == 0); + ret2 = c_simple_http_cache_filename_to_path(ret); + free(ret); + ASSERT_TRUE(ret2); + CHECK_TRUE(strcmp(ret2, uri1) == 0); + free(ret2); + + const char *uri2 = "/"; + ret = + c_simple_http_path_to_cache_filename(uri2); + ASSERT_TRUE(ret); + CHECK_TRUE(strcmp(ret, "ROOT") == 0); + ret2 = c_simple_http_cache_filename_to_path(ret); + free(ret); + ASSERT_TRUE(ret2); + CHECK_TRUE(strcmp(ret2, uri2) == 0); + free(ret2); + + const char *uri3 = "/a"; + ret = + c_simple_http_path_to_cache_filename(uri3); + ASSERT_TRUE(ret); + CHECK_TRUE(strcmp(ret, "0x2Fa") == 0); + ret2 = c_simple_http_cache_filename_to_path(ret); + free(ret); + ASSERT_TRUE(ret2); + CHECK_TRUE(strcmp(ret2, uri3) == 0); + free(ret2); } RETURN()