]> git.seodisparate.com - c_simple_http/commitdiff
Unescape percent-encoded uri when handling request
authorStephen Seo <seo.disparate@gmail.com>
Tue, 24 Sep 2024 04:16:34 +0000 (13:16 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Tue, 24 Sep 2024 04:16:34 +0000 (13:16 +0900)
Resolves https://git.seodisparate.com/stephenseo/c_simple_http/issues/6

src/helpers.c
src/helpers.h
src/http.c
src/test.c

index b2f2448502dffa83600fc278444b816fc33fb796..bf3a6fddf648237ce5c79d7a64fcc497715cfd2c 100644 (file)
@@ -144,4 +144,76 @@ char *c_simple_http_helper_to_lowercase(const char *buf, size_t size) {
   return ret_buf;
 }
 
+char c_simple_http_helper_hex_to_value(const char upper, const char lower) {
+  char result = 0;
+
+  if (upper >= '0' && upper <= '9') {
+    result |= (char)(upper - '0') << 4;
+  } else if (upper >= 'a' && upper <= 'f') {
+    result |= (char)(upper - 'a' + 10) << 4;
+  } else if (upper >= 'A' && upper <= 'F') {
+    result |= (char)(upper - 'A' + 10) << 4;
+  } else {
+    return 0;
+  }
+
+  if (lower >= '0' && lower <= '9') {
+    result |= lower - '0';
+  } else if (lower >= 'a' && lower <= 'f') {
+    result |= lower - 'a' + 10;
+  } else if (lower >= 'A' && lower <= 'F') {
+    result |= lower - 'A' + 10;
+  } else {
+    return 0;
+  }
+
+  return result;
+}
+
+char *c_simple_http_helper_unescape_uri(const char *uri) {
+  __attribute__((cleanup(simple_archiver_list_free)))
+  SDArchiverLinkedList *parts = simple_archiver_list_init();
+
+  const size_t size = strlen(uri);
+  size_t idx = 0;
+  size_t prev_idx = 0;
+  size_t buf_size;
+  char *buf;
+
+  for (; idx <= size; ++idx) {
+    if (uri[idx] == '%' && idx + 2 < size) {
+      if (idx > prev_idx) {
+        buf_size = idx - prev_idx + 1;
+        buf = malloc(buf_size);
+        memcpy(buf, uri + prev_idx, buf_size - 1);
+        buf[buf_size - 1] = 0;
+        c_simple_http_add_string_part(parts, buf, 0);
+        free(buf);
+      }
+      buf = malloc(2);
+      buf[0] = c_simple_http_helper_hex_to_value(uri[idx + 1], uri[idx + 2]);
+      buf[1] = 0;
+      if (buf[0] == 0) {
+        free(buf);
+        return NULL;
+      }
+      c_simple_http_add_string_part(parts, buf, 0);
+      free(buf);
+      prev_idx = idx + 3;
+      idx += 2;
+    }
+  }
+
+  if (idx > prev_idx) {
+    buf_size = idx - prev_idx + 1;
+    buf = malloc(buf_size);
+    memcpy(buf, uri + 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);
+}
+
 // vim: et ts=2 sts=2 sw=2
index 6c4554c2157d3a2e61b1d0cfdfcf129d663f2b7d..4a638341915a0be2e7b8ad6555fa87a62d9178c2 100644 (file)
@@ -50,6 +50,13 @@ void c_simple_http_helper_to_lowercase_in_place(char *buf, size_t size);
 /// uppercase to lowercase alpha chars.
 char *c_simple_http_helper_to_lowercase(const char *buf, size_t size);
 
+/// Converts two hexadecimal digits into its corresponding value.
+char c_simple_http_helper_hex_to_value(const char upper, const char lower);
+
+/// Unescapes percent-encoded parts in the given uri string. If this returns
+/// non-NULL, it must be free'd.
+char *c_simple_http_helper_unescape_uri(const char *uri);
+
 #endif
 
 // vim: et ts=2 sts=2 sw=2
index 3d77df82090cd476fd3f88d91438c7c52e5ce0b8..0db0099a63fd4c2a37c4058c5f5a369fe9f26031 100644 (file)
@@ -26,8 +26,6 @@
 #include <SimpleArchiver/src/data_structures/linked_list.h>
 
 // Local includes
-#include "SimpleArchiver/src/data_structures/priority_heap.h"
-#include "constants.h"
 #include "http_template.h"
 #include "helpers.h"
 
@@ -118,6 +116,13 @@ char *c_simple_http_request_response(
   }
 #ifndef NDEBUG
   fprintf(stderr, "Parsing request: got path \"%s\"\n", request_path);
+#endif
+  __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+  char *request_path_unescaped =
+    c_simple_http_helper_unescape_uri(request_path);
+#ifndef NDEBUG
+  fprintf(
+    stderr, "Parsing request: unescaped path \"%s\"\n", request_path_unescaped);
 #endif
   // skip whitespace until next part.
   for (; idx < size
@@ -165,9 +170,9 @@ char *c_simple_http_request_response(
   size_t generated_size = 0;
   __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
   char *stripped_path = c_simple_http_strip_path(
-    request_path, request_path_idx);
+    request_path_unescaped, strlen(request_path_unescaped));
   char *generated_buf = c_simple_http_path_to_generated(
-    stripped_path ? stripped_path : request_path,
+    stripped_path ? stripped_path : request_path_unescaped,
     templates,
     &generated_size,
     NULL); // TODO Use the output parameter "filenames list" here.
@@ -179,8 +184,10 @@ char *c_simple_http_request_response(
       if (
           simple_archiver_hash_map_get(
             templates->hash_map,
-            stripped_path ? stripped_path : request_path,
-            stripped_path ? strlen(stripped_path) + 1 : request_path_idx + 1)
+            stripped_path ? stripped_path : request_path_unescaped,
+            stripped_path
+              ? strlen(stripped_path) + 1
+              : strlen(request_path_unescaped) + 1)
           == NULL) {
         *out_response_code = C_SIMPLE_HTTP_Response_404_Not_Found;
       } else {
index 8f049f44d5a66a10bc2d0cdb5f4dfb7f886472d1..7c807a7ebf06e850eb8f8bd8a6f55d9e76f346ba 100644 (file)
@@ -568,6 +568,31 @@ int main(void) {
     char *buf = c_simple_http_combine_string_parts(list);
     ASSERT_TRUE(buf);
     ASSERT_TRUE(strcmp(buf, "one\ntwo\nthree\n") == 0);
+    free(buf);
+    buf = NULL;
+
+    char hex_result = c_simple_http_helper_hex_to_value('2', 'f');
+    CHECK_TRUE(hex_result == '/');
+    hex_result = c_simple_http_helper_hex_to_value('2', 'F');
+    CHECK_TRUE(hex_result == '/');
+
+    hex_result = c_simple_http_helper_hex_to_value('7', 'a');
+    CHECK_TRUE(hex_result == 'z');
+    hex_result = c_simple_http_helper_hex_to_value('7', 'A');
+    CHECK_TRUE(hex_result == 'z');
+
+    hex_result = c_simple_http_helper_hex_to_value('4', '1');
+    CHECK_TRUE(hex_result == 'A');
+
+    buf = c_simple_http_helper_unescape_uri("%2fderp%2Fdoop%21");
+    CHECK_TRUE(strcmp(buf, "/derp/doop!") == 0);
+    free(buf);
+    buf = NULL;
+
+    buf = c_simple_http_helper_unescape_uri("%41%42%43%25%5A%5a");
+    CHECK_TRUE(strcmp(buf, "ABC%ZZ") == 0);
+    free(buf);
+    buf = NULL;
   }
 
   // Test html_cache.