diff --git a/build/test/unit_test.c b/build/test/unit_test.c index c7cb6e76535c80b7af39b8002d7c2c3ab5c7d9e4..e6c35aed70870653c05255e1d265279a73b28367 100644 --- a/build/test/unit_test.c +++ b/build/test/unit_test.c @@ -453,6 +453,41 @@ static const char *test_server(void) { return NULL; } +#define DISP "Content-Disposition: form/data; " +#define CRLF "\r\n" +#define BOUNDARY "--xyz" +static const char *test_parse_multipart(void) { + char a[100], b[100]; + const char *p; + static const char f1[] = BOUNDARY CRLF DISP "name=f1" CRLF CRLF + "some_content" CRLF BOUNDARY CRLF + BOUNDARY CRLF DISP "name=f2; filename=\"foo bar.txt\"" CRLF CRLF + "another_content" CRLF BOUNDARY CRLF + "--" CRLF; + int n, n2, len, f1_len = sizeof(f1) - 1; + + ASSERT(mg_parse_multipart("", 0, a, sizeof(a), b, sizeof(b), &p, &len) == 0); + ASSERT(mg_parse_multipart("a", 1, a, sizeof(a), b, sizeof(b), &p, &len) == 0); + ASSERT((n = mg_parse_multipart(f1, f1_len, a, sizeof(a), + b, sizeof(b), &p, &len)) > 0); + ASSERT(len == 12); + ASSERT(memcmp(p, "some_content", len) == 0); + ASSERT(strcmp(a, "f1") == 0); + ASSERT(b[0] == '\0'); + + ASSERT((n2 = mg_parse_multipart(f1 + n, f1_len - n, a, sizeof(a), + b, sizeof(b), &p, &len)) > 0); + ASSERT(len == 15); + ASSERT(memcmp(p, "another_content", len) == 0); + ASSERT(strcmp(a, "f2") == 0); + ASSERT(strcmp(b, "foo bar.txt") == 0); + + ASSERT((n2 = mg_parse_multipart(f1 + n + n2, f1_len - (n + n2), a, sizeof(a), + b, sizeof(b), &p, &len)) == 0); + + return NULL; +} + static const char *run_all_tests(void) { RUN_TEST(test_should_keep_alive); RUN_TEST(test_match_prefix); @@ -466,6 +501,7 @@ static const char *run_all_tests(void) { RUN_TEST(test_get_var); RUN_TEST(test_next_option); RUN_TEST(test_server); + RUN_TEST(test_parse_multipart); return NULL; } diff --git a/mongoose.c b/mongoose.c index f85681bdb20e9042381b58639ae81d0bb3171809..4be85dc4ce7e6123f9db10f8edcfff27f0f5e1bf 100644 --- a/mongoose.c +++ b/mongoose.c @@ -2979,24 +2979,24 @@ static int is_dav_mutation(const struct connection *conn) { } #endif // NO_AUTH -int mg_parse_header(const char *str, const char *var_name, char *buf, - size_t buf_size) { +int parse_header(const char *str, int str_len, const char *var_name, char *buf, + size_t buf_size) { int ch = ' ', len = 0, n = strlen(var_name); - const char *p, *s = NULL; + const char *p, *end = str + str_len, *s = NULL; if (buf != NULL) buf[0] = '\0'; // Find where variable starts - while (str != NULL && (s = strstr(str, var_name)) != NULL && - ((s > str && s[-1] != ' ') || s[n] != '=')) { - str = s + n; + for (s = str; s != NULL && &s[n] < end; s++) { + if ((s == str || s[-1] == ' ') && s[n] == '=' && + !memcmp(s, var_name, n)) break; } - if (s != NULL && s[n + 1] != '\0') { + if (s != NULL && &s[n + 1] < end) { s += n + 1; if (*s == '"' || *s == '\'') ch = *s++; p = s; - while (p[0] != '\0' && p[0] != ch && len < (int) buf_size) { + while (p < end && p[0] != ch && len < (int) buf_size) { if (p[0] == '\\' && p[1] == ch) p++; buf[len++] = *p++; } @@ -3004,6 +3004,7 @@ int mg_parse_header(const char *str, const char *var_name, char *buf, len = 0; } else { if (len > 0 && s[len - 1] == ',') len--; + if (len > 0 && s[len - 1] == ';') len--; buf[len] = '\0'; } } @@ -3011,6 +3012,11 @@ int mg_parse_header(const char *str, const char *var_name, char *buf, return len; } +int mg_parse_header(const char *str, const char *var_name, char *buf, + size_t buf_size) { + return parse_header(str, strlen(str), var_name, buf, buf_size); +} + #ifdef USE_LUA #include "lua_5.2.1.h" @@ -3769,6 +3775,51 @@ int mg_get_var(const struct mg_connection *conn, const char *name, return len; } +static int get_line_len(const char *buf, int buf_len) { + int len = 0; + while (len < buf_len && buf[len] != '\n') len++; + return buf[len] == '\n' ? len + 1: -1; +} + +int mg_parse_multipart(const char *buf, int buf_len, + char *var_name, int var_name_len, + char *file_name, int file_name_len, + const char **data, int *data_len) { + static const char cd[] = "Content-Disposition: "; + //struct mg_connection c; + int hl, bl, n, ll, pos, cdl = sizeof(cd) - 1; + //char *p; + + if (buf == NULL || buf_len <= 0) return 0; + if ((hl = get_request_len(buf, buf_len)) <= 0) return 0; + if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0; + + // Get boundary length + bl = get_line_len(buf, buf_len); + + // Loop through headers, fetch variable name and file name + var_name[0] = file_name[0] = '\0'; + for (n = bl; (ll = get_line_len(buf + n, hl - n)) > 0; n += ll) { + if (mg_strncasecmp(cd, buf + n, cdl) == 0) { + parse_header(buf + n + cdl, ll - (cdl + 2), "name", + var_name, var_name_len); + parse_header(buf + n + cdl, ll - (cdl + 2), "filename", + file_name, file_name_len); + } + } + + // Scan body, search for terminating boundary + for (pos = hl; pos + (bl - 2) < buf_len; pos++) { + if (buf[pos] == '-' && !memcmp(buf, &buf[pos], bl - 2)) { + if (data_len != NULL) *data_len = (pos - 2) - hl; + if (data != NULL) *data = buf + hl; + return pos; + } + } + + return 0; +} + const char **mg_get_valid_option_names(void) { return static_config_options; } diff --git a/mongoose.h b/mongoose.h index 0b644c9c6b1350b08d4ed3f25d5a2399a769391c..720dc36367c45ea6a3f5783aeca5c09acf929cbc 100644 --- a/mongoose.h +++ b/mongoose.h @@ -92,6 +92,10 @@ const char *mg_get_mime_type(const char *file_name); int mg_get_var(const struct mg_connection *conn, const char *var_name, char *buf, size_t buf_len); int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t); +int mg_parse_multipart(const char *buf, int buf_len, + char *var_name, int var_name_len, + char *file_name, int file_name_len, + const char **data, int *data_len); // Utility functions void *mg_start_thread(void *(*func)(void *), void *param);