From b11b1c52280f172ebfa42c3da906ea9aa3ea8799 Mon Sep 17 00:00:00 2001 From: Aleksey Veresov Date: Wed, 6 Nov 2019 20:18:29 +0300 Subject: [magi] FastCGI is comming. --- examples/append.c | 5 +- examples/cookie.c | 5 +- examples/echo.c | 7 +- examples/upload.c | 3 +- src/cgi.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++ src/cgi.h | 21 +++++ src/fastcgi.c | 0 src/fastcgi.h | 0 src/request.c | 248 +----------------------------------------------------- src/request.h | 18 +--- src/utils.h | 38 +++++++++ 11 files changed, 292 insertions(+), 271 deletions(-) create mode 100644 src/cgi.c create mode 100644 src/cgi.h create mode 100644 src/fastcgi.c create mode 100644 src/fastcgi.h create mode 100644 src/utils.h diff --git a/examples/append.c b/examples/append.c index 65caa33..b8aa1cf 100644 --- a/examples/append.c +++ b/examples/append.c @@ -1,12 +1,13 @@ +#include +#include #include #include -#include void handle_request() { struct magi_request request; - if (magi_request_build_cgi(&request, 0, 0)) { + if (magi_cgi(&request, 0, 0)) { struct magi_field *a = magi_field_list_get(request.fields, "addon"); if (a && a->data) { FILE *file = fopen("file_to_append", "a"); diff --git a/examples/cookie.c b/examples/cookie.c index 8f4018b..dd18cc6 100644 --- a/examples/cookie.c +++ b/examples/cookie.c @@ -1,7 +1,8 @@ +#include #include +#include #include #include -#include void print_preamble() @@ -26,7 +27,7 @@ void print_webpage_top() void read_and_print_cookies() { struct magi_request request; - if (magi_request_build_cgi(&request, 0, 0)) { + if (magi_cgi(&request, 0, 0)) { struct magi_cookie_list *cookie; for (cookie = request.cookies; cookie; cookie = cookie->next) { printf( diff --git a/examples/echo.c b/examples/echo.c index 33094a4..ff037a6 100644 --- a/examples/echo.c +++ b/examples/echo.c @@ -1,8 +1,9 @@ -#include -#include +#include #include #include #include +#include +#include void print_preamble() @@ -76,7 +77,7 @@ void proceed_params(struct magi_param_list *params) void handle_request() { struct magi_request request; - if (magi_request_build_cgi(&request, 0, 0)) { + if (magi_cgi(&request, 0, 0)) { puts("

Echo CGI Script

"); puts("I was called with method ["); puts(request.method); diff --git a/examples/upload.c b/examples/upload.c index 1dc9659..59dd020 100644 --- a/examples/upload.c +++ b/examples/upload.c @@ -3,6 +3,7 @@ #include #include #include +#include void tempfile_callback(struct magi_field *field, char *buffer, int len) { @@ -30,7 +31,7 @@ void tempfile_callback(struct magi_field *field, char *buffer, int len) void handle_request() { struct magi_request request; - if (magi_request_build_cgi(&request, tempfile_callback, 0)) { + if (magi_cgi(&request, tempfile_callback, 0)) { struct magi_field *name = magi_field_list_get(request.fields, "name"); struct magi_field *data = magi_field_list_get(request.fields, "data"); if (name && name->data && data) { diff --git a/src/cgi.c b/src/cgi.c new file mode 100644 index 0000000..1a318a1 --- /dev/null +++ b/src/cgi.c @@ -0,0 +1,218 @@ +#include "cgi.h" + +#include "cookie.h" +#include "field.h" +#include "log.h" +#include "multipart.h" +#include "param.h" +#include "request.h" +#include "urlencoded.h" +#include +#include +#include +#include + + +extern char **environ; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * CGI Request Handling + */ +/* Helpers for CGI Request Handling */ +static int plain_env(char **dest, char *env_name) +{ + int ok = 1; + const char *env = getenv(env_name); + if (env) { + *dest = str_alloc(strlen(env)); + if (*dest) { + strcpy(*dest, env); + } else { + ok = 0; + } + } else { + *dest = 0; + } + return ok; +} + +static int lower_env(char **dest, char *env_name) +{ + int ok = plain_env(dest, env_name); + lowercase(*dest); + return ok; +} + +static int cgi_http_env(struct magi_request *r) +{ + int ok = 1; + char **env = environ; + r->http_params = 0; + while (*env) { + if (!strncmp(*env, "HTTP_", 5) && strncmp(*env, "HTTP_COOKIE=", 12)) { + struct magi_param param; + /* At least one '=' must be in *env, according to format. */ + char *name_end = strchr(*env, '='); + param.name = str_alloc(name_end - *env - 5); + if (param.name) { + memcpy(param.name, *env + 5, name_end - *env - 5); + param.data = str_alloc(strlen(name_end + 1)); + if (param.data) { + strcpy(param.data, name_end + 1); + } else { + free(param.name); + } + } + if (param.name && param.data) { + ok = magi_param_list_add(&r->http_params, ¶m); + } else { + ok = 0; + } + } + ++env; + } + return ok; +} + +static int cgi_env(struct magi_request *r) +{ + int ok = cgi_http_env(r); + ok = ok && lower_env(&r->method, "REQUEST_METHOD"); + ok = ok && plain_env(&r->uri, "REQUEST_URI"); + ok = ok && plain_env(&r->document_root, "DOCUMENT_ROOT"); + ok = ok && plain_env(&r->document_uri, "DOCUMENT_URI"); + ok = ok && plain_env(&r->script_name, "SCRIPT_NAME"); + ok = ok && plain_env(&r->script_filename, "SCRIPT_FILENAME"); + ok = ok && plain_env(&r->remote_addr, "REMOTE_ADDR"); + ok = ok && plain_env(&r->remote_port, "REMOTE_PORT"); + ok = ok && plain_env(&r->server_addr, "SERVER_ADDR"); + ok = ok && lower_env(&r->server_name, "SERVER_NAME"); + ok = ok && plain_env(&r->server_port, "SERVER_PORT"); + ok = ok && lower_env(&r->server_protocol, "SERVER_PROTOCOL"); + ok = ok && plain_env(&r->server_software, "SERVER_SOFTWARE"); + ok = ok && plain_env(&r->path_info, "PATH_INFO"); + return ok; +} + +static int cgi_cookies(struct magi_cookie_list **list) +{ + int ok = 1; + const char *env = getenv("HTTP_COOKIE"); + *list = 0; + if (env && *env) { + ok = magi_parse_cookie(list, env); + } else { + *list = 0; + } + return ok; +} + +static int cgi_input_get(char **input) +{ + int ok = 1; + const char *env_input = getenv("QUERY_STRING"); + if (env_input) { + *input = str_alloc(strlen(env_input)); + if (*input) { + strcpy(*input, env_input); + } else { + ok = 0; + } + } + return ok; +} + +static int cgi_input_post(char **input, int max) +{ + int ok = 1; + int input_len = strtoul(getenv("CONTENT_LENGTH"), 0, 10); + if (input_len && (input_len < max || !max)) { + *input = str_alloc(input_len); + if (*input) { + if (fread(*input, 1, input_len, stdin) != input_len) { + ok = 0; + magi_log("[request:cgi] Content-length is not correct."); + } + } else { + ok = 0; + } + } + return ok; +} + +static char *bound(const char *type) +{ + char *res = 0; + type = strchr(type, '='); + if (type) { + type += strspn(type, " \t") + 1; + if (*type == '"') { + ++type; + res = create_str(type, strchr(type, '"')); + } else { + res = create_str(type, type + strcspn(type, " \t")); + } + } + return res; +} + +static int intput_getter(void *any) +{ + return getchar(); +} + + +/* Interfacial CGI Request Handling */ +int magi_cgi_request( + struct magi_request *request, + void (*callback)(struct magi_field *field, char *buffer, int len), + int max_post +) +{ + int ok = cgi_env(request) && cgi_cookies(&request->cookies); + request->fields = 0; + if (request->method) { + if (!strcmp(request->method, "post")) { + const char *t = getenv("CONTENT_TYPE"); + if (t) { + if (!strncmp(t, "multipart/form-data", 19)) { + char *boundary = bound(t); + if (boundary && *boundary) { + ok = magi_parse_multipart( + &request->fields, + intput_getter, + 0, + boundary, + callback + ); + } else { + ok = 0; + magi_log("[request:cgi] Multipart bound is not set."); + } + free(boundary); + } else if (!strcmp(t, "application/x-www-form-urlencoded")) { + char *in = 0; + ok = cgi_input_post(&in, max_post); + ok = ok && magi_parse_urlencoded(&request->fields, in); + free(in); + } else { + ok = 0; + magi_log("[request:cgi] Unknown content type."); + } + } else { + ok = 0; + magi_log("[request:cgi] Content-type is not set."); + } + } else if (!strcmp(request->method, "get")) { + char *in = 0; + ok = cgi_input_get(&in); + ok = ok && magi_parse_urlencoded(&request->fields, in); + free(in); + } + } + if (!ok) { + magi_request_destroy(request); + } + return ok; +} diff --git a/src/cgi.h b/src/cgi.h new file mode 100644 index 0000000..c7b173c --- /dev/null +++ b/src/cgi.h @@ -0,0 +1,21 @@ +#ifndef MAGI_INCLUDED_CGI +#define MAGI_INCLUDED_CGI + +#include "field.h" +#include "request.h" + + +/* + * Constructs request using environment variables and standart I/O; + * Returns null if succeed, otherwise error code. + */ +int magi_cgi_request( + struct magi_request *request, + /* Callback will be used only for fields loaded via multipart. */ + /* Null callback disables callback system. */ + void (*callback)(struct magi_field *field, char *buffer, int len), + int max_post +); + + +#endif diff --git a/src/fastcgi.c b/src/fastcgi.c new file mode 100644 index 0000000..e69de29 diff --git a/src/fastcgi.h b/src/fastcgi.h new file mode 100644 index 0000000..e69de29 diff --git a/src/request.c b/src/request.c index aa2cd9b..58eefc6 100644 --- a/src/request.c +++ b/src/request.c @@ -1,53 +1,9 @@ #include "request.h" +#include "field.h" #include "cookie.h" -#include "log.h" -#include "multipart.h" #include "param.h" -#include "urlencoded.h" -#include -#include #include -#include - - -extern char **environ; - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Local Shortcuts - */ -static void lowercase(char *str) -{ - if (str) { - while (*str) { - *str = tolower(*str); - ++str; - } - } -} - -static char *create_str(const char *begin, const char *end) -{ - char *res; - res = malloc(end - begin + 1); - if (res) { - memcpy(res, begin, end - begin); - res[end - begin] = 0; - } - return res; -} - -static char *str_alloc(int len) -{ - char *str = malloc(len + 1); - if (str) { - str[len] = 0; - } else { - magi_log("[request] Cannot allocate string."); - } - return str; -} /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -77,205 +33,3 @@ void magi_request_destroy(struct magi_request *request) free(request->http_params); } } - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * CGI Request Handling - */ -/* Helpers for CGI Request Handling */ -static int plain_env(char **dest, char *env_name) -{ - int ok = 1; - const char *env = getenv(env_name); - if (env) { - *dest = str_alloc(strlen(env)); - if (*dest) { - strcpy(*dest, env); - } else { - ok = 0; - } - } else { - *dest = 0; - } - return ok; -} - -static int lower_env(char **dest, char *env_name) -{ - int ok = plain_env(dest, env_name); - lowercase(*dest); - return ok; -} - -static int cgi_http_env(struct magi_request *r) -{ - int ok = 1; - char **env = environ; - r->http_params = 0; - while (*env) { - if (!strncmp(*env, "HTTP_", 5) && strncmp(*env, "HTTP_COOKIE=", 12)) { - struct magi_param param; - /* At least one '=' must be in *env, according to format. */ - char *name_end = strchr(*env, '='); - param.name = str_alloc(name_end - *env - 5); - if (param.name) { - memcpy(param.name, *env + 5, name_end - *env - 5); - param.data = str_alloc(strlen(name_end + 1)); - if (param.data) { - strcpy(param.data, name_end + 1); - } else { - free(param.name); - } - } - if (param.name && param.data) { - ok = magi_param_list_add(&r->http_params, ¶m); - } else { - ok = 0; - } - } - ++env; - } - return ok; -} - -static int cgi_env(struct magi_request *r) -{ - int ok = cgi_http_env(r); - ok = ok && lower_env(&r->method, "REQUEST_METHOD"); - ok = ok && plain_env(&r->uri, "REQUEST_URI"); - ok = ok && plain_env(&r->document_root, "DOCUMENT_ROOT"); - ok = ok && plain_env(&r->document_uri, "DOCUMENT_URI"); - ok = ok && plain_env(&r->script_name, "SCRIPT_NAME"); - ok = ok && plain_env(&r->script_filename, "SCRIPT_FILENAME"); - ok = ok && plain_env(&r->remote_addr, "REMOTE_ADDR"); - ok = ok && plain_env(&r->remote_port, "REMOTE_PORT"); - ok = ok && plain_env(&r->server_addr, "SERVER_ADDR"); - ok = ok && lower_env(&r->server_name, "SERVER_NAME"); - ok = ok && plain_env(&r->server_port, "SERVER_PORT"); - ok = ok && lower_env(&r->server_protocol, "SERVER_PROTOCOL"); - ok = ok && plain_env(&r->server_software, "SERVER_SOFTWARE"); - ok = ok && plain_env(&r->path_info, "PATH_INFO"); - return ok; -} - -static int cgi_cookies(struct magi_cookie_list **list) -{ - int ok = 1; - const char *env = getenv("HTTP_COOKIE"); - *list = 0; - if (env && *env) { - ok = magi_parse_cookie(list, env); - } else { - *list = 0; - } - return ok; -} - -static int cgi_input_get(char **input) -{ - int ok = 1; - const char *env_input = getenv("QUERY_STRING"); - if (env_input) { - *input = str_alloc(strlen(env_input)); - if (*input) { - strcpy(*input, env_input); - } else { - ok = 0; - } - } - return ok; -} - -static int cgi_input_post(char **input, int max) -{ - int ok = 1; - int input_len = strtoul(getenv("CONTENT_LENGTH"), 0, 10); - if (input_len && (input_len < max || !max)) { - *input = str_alloc(input_len); - if (*input) { - if (fread(*input, 1, input_len, stdin) != input_len) { - ok = 0; - magi_log("[request:cgi] Content-length is not correct."); - } - } else { - ok = 0; - } - } - return ok; -} - -static char *bound(const char *type) -{ - char *res = 0; - type = strchr(type, '='); - if (type) { - type += strspn(type, " \t") + 1; - if (*type == '"') { - ++type; - res = create_str(type, strchr(type, '"')); - } else { - res = create_str(type, type + strcspn(type, " \t")); - } - } - return res; -} - -static int intput_getter(void *any) -{ - return getchar(); -} - - -/* Interfacial CGI Request Handling */ -int magi_request_build_cgi( - struct magi_request *request, - void (*callback)(struct magi_field *field, char *buffer, int len), - int max_post -) -{ - int ok = cgi_env(request) && cgi_cookies(&request->cookies); - request->fields = 0; - if (request->method) { - if (!strcmp(request->method, "post")) { - const char *t = getenv("CONTENT_TYPE"); - if (t) { - if (!strncmp(t, "multipart/form-data", 19)) { - char *boundary = bound(t); - if (boundary && *boundary) { - ok = magi_parse_multipart( - &request->fields, - intput_getter, - 0, - boundary, - callback - ); - } else { - ok = 0; - magi_log("[request:cgi] Multipart bound is not set."); - } - free(boundary); - } else if (!strcmp(t, "application/x-www-form-urlencoded")) { - char *in = 0; - ok = cgi_input_post(&in, max_post); - ok = ok && magi_parse_urlencoded(&request->fields, in); - free(in); - } else { - ok = 0; - magi_log("[request:cgi] Unknown content type."); - } - } else { - ok = 0; - magi_log("[request:cgi] Content-type is not set."); - } - } else if (!strcmp(request->method, "get")) { - char *in = 0; - ok = cgi_input_get(&in); - ok = ok && magi_parse_urlencoded(&request->fields, in); - free(in); - } - } - if (!ok) { - magi_request_destroy(request); - } - return ok; -} diff --git a/src/request.h b/src/request.h index a43ba63..6b9df6e 100644 --- a/src/request.h +++ b/src/request.h @@ -1,7 +1,9 @@ #ifndef MAGI_INCLUDED_REQUEST #define MAGI_INCLUDED_REQUEST +#include "cookie.h" #include "field.h" +#include "param.h" /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -46,26 +48,10 @@ struct magi_request { }; -/* Common Request Handling */ /* * Destroys request; request is not valid after destruction. */ void magi_request_destroy(struct magi_request *request); -/* CGI Request Handling */ -/* - * Constructs request using environment variables and standart I/O; - * Returns null if succeed, otherwise error code. - */ -int magi_request_build_cgi( - struct magi_request *request, - /* Callback will be used only for fields loaded via multipart. */ - /* Null callback disables callback system. */ - void (*callback)(struct magi_field *field, char *buffer, int len), - int max_post -); - -/* TODO: FastCGI Request Handling */ - #endif diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..a6c0aff --- /dev/null +++ b/src/utils.h @@ -0,0 +1,38 @@ +#ifndef MAGI_INCLUDED_UTILS +#define MAGI_INCLUDED_UTILS + + +static void lowercase(char *str) +{ + if (str) { + while (*str) { + *str = tolower(*str); + ++str; + } + } +} + +static char *create_str(const char *begin, const char *end) +{ + char *res; + res = malloc(end - begin + 1); + if (res) { + memcpy(res, begin, end - begin); + res[end - begin] = 0; + } + return res; +} + +static char *str_alloc(int len) +{ + char *str = malloc(len + 1); + if (str) { + str[len] = 0; + } else { + magi_log("[request] Cannot allocate string."); + } + return str; +} + + +#endif -- cgit v1.2.3