From: Petr Štetiar Date: Sun, 11 Oct 2020 10:43:40 +0000 (+0200) Subject: Refactor utility functions into static library X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=6b0615b728ede4290c15526b3ed0151d9d78da91;p=project%2Fcgi-io.git Refactor utility functions into static library For reusability during testing. Signed-off-by: Petr Štetiar --- diff --git a/CMakeLists.txt b/CMakeLists.txt index c7c9d40..693830a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,10 @@ IF(APPLE) LINK_DIRECTORIES(/opt/local/lib) ENDIF() -ADD_EXECUTABLE(cgi-io main.c multipart_parser.c) -TARGET_LINK_LIBRARIES(cgi-io ${ubox} ${ubus}) +ADD_LIBRARY(cgi-lib STATIC multipart_parser.c util.c) + +ADD_EXECUTABLE(cgi-io main.c) +TARGET_LINK_LIBRARIES(cgi-io cgi-lib ${ubox} ${ubus}) + INSTALL(TARGETS cgi-io RUNTIME DESTINATION sbin) diff --git a/main.c b/main.c index 53c6728..ff9bb63 100644 --- a/main.c +++ b/main.c @@ -35,6 +35,7 @@ #include #include +#include "util.h" #include "multipart_parser.h" #ifndef O_TMPFILE @@ -42,7 +43,6 @@ #endif #define READ_BLOCK 4096 -#define POST_LIMIT 131072 enum part { PART_UNKNOWN, @@ -173,201 +173,6 @@ checksum(const char *applet, size_t sumlen, const char *file) return chksum; } -static char * -datadup(const void *in, size_t len) -{ - char *out = malloc(len + 1); - - if (!out) - return NULL; - - memcpy(out, in, len); - - *(out + len) = 0; - - return out; -} - -static bool -urldecode(char *buf) -{ - char *c, *p; - - if (!buf || !*buf) - return true; - -#define hex(x) \ - (((x) <= '9') ? ((x) - '0') : \ - (((x) <= 'F') ? ((x) - 'A' + 10) : \ - ((x) - 'a' + 10))) - - for (c = p = buf; *p; c++) - { - if (*p == '%') - { - if (!isxdigit(*(p + 1)) || !isxdigit(*(p + 2))) - return false; - - *c = (char)(16 * hex(*(p + 1)) + hex(*(p + 2))); - - p += 3; - } - else if (*p == '+') - { - *c = ' '; - p++; - } - else - { - *c = *p++; - } - } - - *c = 0; - - return true; -} - -static char * -postdecode(char **fields, int n_fields) -{ - const char *var; - char *p, *postbuf; - int i, field, found = 0; - ssize_t len = 0, rlen = 0, content_length = 0; - - var = getenv("CONTENT_TYPE"); - - if (!var || strncmp(var, "application/x-www-form-urlencoded", 33)) - return NULL; - - var = getenv("CONTENT_LENGTH"); - - if (!var) - return NULL; - - content_length = strtol(var, &p, 10); - - if (p == var || content_length <= 0 || content_length >= POST_LIMIT) - return NULL; - - postbuf = calloc(1, content_length + 1); - - if (postbuf == NULL) - return NULL; - - for (len = 0; len < content_length; ) - { - rlen = read(0, postbuf + len, content_length - len); - - if (rlen <= 0) - break; - - len += rlen; - } - - if (len < content_length) - { - free(postbuf); - return NULL; - } - - for (p = postbuf, i = 0; i <= len; i++) - { - if (postbuf[i] == '=') - { - postbuf[i] = 0; - - for (field = 0; field < (n_fields * 2); field += 2) - { - if (!strcmp(p, fields[field])) - { - fields[field + 1] = postbuf + i + 1; - found++; - } - } - } - else if (postbuf[i] == '&' || postbuf[i] == '\0') - { - postbuf[i] = 0; - - if (found >= n_fields) - break; - - p = postbuf + i + 1; - } - } - - for (field = 0; field < (n_fields * 2); field += 2) - { - if (!urldecode(fields[field + 1])) - { - free(postbuf); - return NULL; - } - } - - return postbuf; -} - -static char * -canonicalize_path(const char *path, size_t len) -{ - char *canonpath, *cp; - const char *p, *e; - - if (path == NULL || *path == '\0') - return NULL; - - canonpath = datadup(path, len); - - if (canonpath == NULL) - return NULL; - - /* normalize */ - for (cp = canonpath, p = path, e = path + len; p < e; ) { - if (*p != '/') - goto next; - - /* skip repeating / */ - if ((p + 1 < e) && (p[1] == '/')) { - p++; - continue; - } - - /* /./ or /../ */ - if ((p + 1 < e) && (p[1] == '.')) { - /* skip /./ */ - if ((p + 2 >= e) || (p[2] == '/')) { - p += 2; - continue; - } - - /* collapse /x/../ */ - if ((p + 2 < e) && (p[2] == '.') && ((p + 3 >= e) || (p[3] == '/'))) { - while ((cp > canonpath) && (*--cp != '/')) - ; - - p += 3; - continue; - } - } - -next: - *cp++ = *p++; - } - - /* remove trailing slash if not root / */ - if ((cp > canonpath + 1) && (cp[-1] == '/')) - cp--; - else if (cp == canonpath) - *cp++ = '/'; - - *cp = '\0'; - - return canonpath; -} - static int response(bool success, const char *message) { @@ -916,79 +721,6 @@ lookup_executable(const char *cmd) return NULL; } -static char ** -parse_command(const char *cmdline) -{ - const char *p = cmdline, *s; - char **argv = NULL, *out; - size_t arglen = 0; - int argnum = 0; - bool esc; - - while (isspace(*cmdline)) - cmdline++; - - for (p = cmdline, s = p, esc = false; p; p++) { - if (esc) { - esc = false; - } - else if (*p == '\\' && p[1] != 0) { - esc = true; - } - else if (isspace(*p) || *p == 0) { - if (p > s) { - argnum += 1; - arglen += sizeof(char *) + (p - s) + 1; - } - - s = p + 1; - } - - if (*p == 0) - break; - } - - if (arglen == 0) - return NULL; - - argv = calloc(1, arglen + sizeof(char *)); - - if (!argv) - return NULL; - - out = (char *)argv + sizeof(char *) * (argnum + 1); - argv[0] = out; - - for (p = cmdline, s = p, esc = false, argnum = 0; p; p++) { - if (esc) { - esc = false; - *out++ = *p; - } - else if (*p == '\\' && p[1] != 0) { - esc = true; - } - else if (isspace(*p) || *p == 0) { - if (p > s) { - *out++ = ' '; - argv[++argnum] = out; - } - - s = p + 1; - } - else { - *out++ = *p; - } - - if (*p == 0) - break; - } - - argv[argnum] = NULL; - out[-1] = 0; - - return argv; -} - static int main_exec(int argc, char **argv) { diff --git a/util.c b/util.c new file mode 100644 index 0000000..9eb7b48 --- /dev/null +++ b/util.c @@ -0,0 +1,276 @@ +#include +#include +#include +#include +#include +#include + +#include "util.h" + +char ** +parse_command(const char *cmdline) +{ + const char *p = cmdline, *s; + char **argv = NULL, *out; + size_t arglen = 0; + int argnum = 0; + bool esc; + + while (isspace(*cmdline)) + cmdline++; + + for (p = cmdline, s = p, esc = false; p; p++) { + if (esc) { + esc = false; + } + else if (*p == '\\' && p[1] != 0) { + esc = true; + } + else if (isspace(*p) || *p == 0) { + if (p > s) { + argnum += 1; + arglen += sizeof(char *) + (p - s) + 1; + } + + s = p + 1; + } + + if (*p == 0) + break; + } + + if (arglen == 0) + return NULL; + + argv = calloc(1, arglen + sizeof(char *)); + + if (!argv) + return NULL; + + out = (char *)argv + sizeof(char *) * (argnum + 1); + argv[0] = out; + + for (p = cmdline, s = p, esc = false, argnum = 0; p; p++) { + if (esc) { + esc = false; + *out++ = *p; + } + else if (*p == '\\' && p[1] != 0) { + esc = true; + } + else if (isspace(*p) || *p == 0) { + if (p > s) { + *out++ = ' '; + argv[++argnum] = out; + } + + s = p + 1; + } + else { + *out++ = *p; + } + + if (*p == 0) + break; + } + + argv[argnum] = NULL; + out[-1] = 0; + + return argv; +} + +char * +postdecode(char **fields, int n_fields) +{ + const char *var; + char *p, *postbuf; + int i, field, found = 0; + ssize_t len = 0, rlen = 0, content_length = 0; + + var = getenv("CONTENT_TYPE"); + + if (!var || strncmp(var, "application/x-www-form-urlencoded", 33)) + return NULL; + + var = getenv("CONTENT_LENGTH"); + + if (!var) + return NULL; + + content_length = strtol(var, &p, 10); + + if (p == var || content_length <= 0 || content_length >= POST_LIMIT) + return NULL; + + postbuf = calloc(1, content_length + 1); + + if (postbuf == NULL) + return NULL; + + for (len = 0; len < content_length; ) + { + rlen = read(0, postbuf + len, content_length - len); + + if (rlen <= 0) + break; + + len += rlen; + } + + if (len < content_length) + { + free(postbuf); + return NULL; + } + + for (p = postbuf, i = 0; i <= len; i++) + { + if (postbuf[i] == '=') + { + postbuf[i] = 0; + + for (field = 0; field < (n_fields * 2); field += 2) + { + if (!strcmp(p, fields[field])) + { + fields[field + 1] = postbuf + i + 1; + found++; + } + } + } + else if (postbuf[i] == '&' || postbuf[i] == '\0') + { + postbuf[i] = 0; + + if (found >= n_fields) + break; + + p = postbuf + i + 1; + } + } + + for (field = 0; field < (n_fields * 2); field += 2) + { + if (!urldecode(fields[field + 1])) + { + free(postbuf); + return NULL; + } + } + + return postbuf; +} + +char * +datadup(const void *in, size_t len) +{ + char *out = malloc(len + 1); + + if (!out) + return NULL; + + memcpy(out, in, len); + + *(out + len) = 0; + + return out; +} + +char * +canonicalize_path(const char *path, size_t len) +{ + char *canonpath, *cp; + const char *p, *e; + + if (path == NULL || *path == '\0') + return NULL; + + canonpath = datadup(path, len); + + if (canonpath == NULL) + return NULL; + + /* normalize */ + for (cp = canonpath, p = path, e = path + len; p < e; ) { + if (*p != '/') + goto next; + + /* skip repeating / */ + if ((p + 1 < e) && (p[1] == '/')) { + p++; + continue; + } + + /* /./ or /../ */ + if ((p + 1 < e) && (p[1] == '.')) { + /* skip /./ */ + if ((p + 2 >= e) || (p[2] == '/')) { + p += 2; + continue; + } + + /* collapse /x/../ */ + if ((p + 2 < e) && (p[2] == '.') && ((p + 3 >= e) || (p[3] == '/'))) { + while ((cp > canonpath) && (*--cp != '/')) + ; + + p += 3; + continue; + } + } + +next: + *cp++ = *p++; + } + + /* remove trailing slash if not root / */ + if ((cp > canonpath + 1) && (cp[-1] == '/')) + cp--; + else if (cp == canonpath) + *cp++ = '/'; + + *cp = '\0'; + + return canonpath; +} + +bool +urldecode(char *buf) +{ + char *c, *p; + + if (!buf || !*buf) + return true; + +#define hex(x) \ + (((x) <= '9') ? ((x) - '0') : \ + (((x) <= 'F') ? ((x) - 'A' + 10) : \ + ((x) - 'a' + 10))) + + for (c = p = buf; *p; c++) + { + if (*p == '%') + { + if (!isxdigit(*(p + 1)) || !isxdigit(*(p + 2))) + return false; + + *c = (char)(16 * hex(*(p + 1)) + hex(*(p + 2))); + + p += 3; + } + else if (*p == '+') + { + *c = ' '; + p++; + } + else + { + *c = *p++; + } + } + + *c = 0; + + return true; +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..0001195 --- /dev/null +++ b/util.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#define POST_LIMIT 131072 + +char** parse_command(const char *cmdline); +char* postdecode(char **fields, int n_fields); +char* canonicalize_path(const char *path, size_t len); +bool urldecode(char *buf); +char* datadup(const void *in, size_t len);