From 83cd2267b309fa7411acaed356f88101196bb2ad Mon Sep 17 00:00:00 2001 From: Aleksey Veresov Date: Thu, 6 Feb 2020 19:43:57 +0300 Subject: [magi] Broken to become better. --- src/cgi.c | 12 +- src/cgi.h | 34 ---- src/cookie.h | 32 ---- src/cookies.c | 290 ++++++++++++++++++++++++++++++ src/cookies.h | 10 ++ src/error.h | 21 --- src/fastcgi.h | 10 -- src/file.h | 30 ---- src/inner_cookies.c | 290 ------------------------------ src/inner_cookies.h | 10 -- src/inner_multipart.c | 479 ------------------------------------------------- src/inner_multipart.h | 13 -- src/inner_tools.c | 51 ------ src/inner_tools.h | 19 -- src/inner_urlencoded.c | 144 --------------- src/inner_urlencoded.h | 13 -- src/magi.h | 16 -- src/multipart.c | 479 +++++++++++++++++++++++++++++++++++++++++++++++++ src/multipart.h | 13 ++ src/param.h | 28 --- src/request.h | 117 ------------ src/response.c | 24 +-- src/response.h | 43 ----- src/tools.c | 51 ++++++ src/tools.h | 19 ++ src/urlenc.h | 10 -- src/urlencoded.c | 144 +++++++++++++++ src/urlencoded.h | 13 ++ 28 files changed, 1023 insertions(+), 1392 deletions(-) delete mode 100644 src/cgi.h delete mode 100644 src/cookie.h create mode 100644 src/cookies.c create mode 100644 src/cookies.h delete mode 100644 src/error.h delete mode 100644 src/fastcgi.h delete mode 100644 src/file.h delete mode 100644 src/inner_cookies.c delete mode 100644 src/inner_cookies.h delete mode 100644 src/inner_multipart.c delete mode 100644 src/inner_multipart.h delete mode 100644 src/inner_tools.c delete mode 100644 src/inner_tools.h delete mode 100644 src/inner_urlencoded.c delete mode 100644 src/inner_urlencoded.h delete mode 100644 src/magi.h create mode 100644 src/multipart.c create mode 100644 src/multipart.h delete mode 100644 src/param.h delete mode 100644 src/request.h delete mode 100644 src/response.h create mode 100644 src/tools.c create mode 100644 src/tools.h delete mode 100644 src/urlenc.h create mode 100644 src/urlencoded.c create mode 100644 src/urlencoded.h (limited to 'src') diff --git a/src/cgi.c b/src/cgi.c index c3ac1c6..20df48e 100644 --- a/src/cgi.c +++ b/src/cgi.c @@ -274,21 +274,15 @@ void output_cookies(struct magi_cookie_list * list) } -int magi_response_cgi(struct magi_response * response) +int magi_response_cgi(struct magi_response *response) { output_http_params(response->http_params); output_cookies(response->cookies); fputs(response->content_type, stdout); fputs("\r\n\r\n", stdout); fputs(response->content, stdout); - return 1; -} - -int magi_response_cgi_clear(magi_response *response) -{ - int ok = magi_response_cgi(response); magi_response_destroy(response); - return ok; + return 1; } @@ -308,5 +302,5 @@ int magi_error_cgi(enum magi_error error) "" "", magi_error_message(error)); - return magi_response_cgi_clear(&res); + return magi_response_cgi(&res); } diff --git a/src/cgi.h b/src/cgi.h deleted file mode 100644 index 7e47ad0..0000000 --- a/src/cgi.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef MAGI_INCLUDED_CGI -#define MAGI_INCLUDED_CGI - -#include "error.h" -#include "request.h" -#include "response.h" - - -/* Constructs non-post part of request from environment; - * Returns null only in case of error. */ -int magi_request_cgi(magi_request *request); - -/* Complete request with post data from standard input; - * Returns null only in case of error. */ -int magi_request_resume_cgi(magi_request *request); - -/* Shortcut for applying both previous. */ -int magi_request_full_cgi(magi_request *request); - - -/* Sends response to standard output; - * Returns null only in case of error. */ -int magi_response_cgi(magi_response *response); - -/* Shortcut for magi_response_cgi with desctruction after it. */ -int magi_response_cgi_clear(magi_response *response); - - -/* Sends a standart response of Bad Request error to standard output; - * Returns null only in case of error. */ -int magi_error_cgi(magi_error error); - - -#endif diff --git a/src/cookie.h b/src/cookie.h deleted file mode 100644 index 7eb3e9d..0000000 --- a/src/cookie.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MAGI_INCLUDED_COOKIE -#define MAGI_INCLUDED_COOKIE - - -typedef struct magi_cookie { - /* All pointers must be valid as 'free' arguments. */ - char *name; - char *data; - char *path; /* Without '/' at the end. */ - char *domain; /* With dot at the begining. */ - char *max_age; /* In seconds until discard, used only in response. */ -} magi_cookie; - -typedef struct magi_cookie_list { - struct magi_cookie_list *next; /* Must be valid as 'free' arguments. */ - magi_cookie item; -} magi_cookie_list; - - -/* Addition of item to top of list. Null <=> error. */ -int magi_cookie_list_add(magi_cookie_list **list, magi_cookie *item); - -/* Data of last cookie in list: cookie.name == name, returns null if no such; - * Last cookie in list is first from request, and from RFC 2109 we know that - * first cookie is the most accurate for request's domain and path. */ -char *magi_cookie_list_get(magi_cookie_list *list, const char *name); - -/* Freeing and invalidation of list. */ -void magi_cookie_list_destroy(magi_cookie_list *list); - - -#endif diff --git a/src/cookies.c b/src/cookies.c new file mode 100644 index 0000000..9dfde7a --- /dev/null +++ b/src/cookies.c @@ -0,0 +1,290 @@ +/* * * TODO * * */ +#include "inner_cookies.h" + +#include "inner_tools.h" +#include +#include + + +enum st { + st_error = 0, + st_pre_name, + st_name, + st_post_name, + st_pre_data, + st_data, + st_post_data +}; + +enum data_type { dt_plain = 0, dt_version, dt_path, dt_domain }; + +struct automata { + struct magi_cookie_list ** list; + struct magi_cookie cookie; + char * buf; + int buf_len; + int buf_size; + int is_first; + int is_advanced; + int is_quoted; + enum data_type data_t; +}; + + +static void nulify_cookie(struct automata * a) +{ + a->cookie.name = 0; + a->cookie.data = 0; + a->cookie.path = 0; + a->cookie.domain = 0; + a->cookie.max_age = 0; +} + +static void buf_new(struct automata * a) +{ + a->buf = 0; + a->buf_len = 0; + a->buf_size = 1; +} + + +static enum data_type what_is_name(const struct automata * a) +{ + enum data_type data_t = dt_plain; + if (a->is_first && !strcmp(a->buf, "$Version")) { + data_t = dt_version; + } else if (a->is_advanced) { + if (!strcmp(a->buf, "$Path")) { + data_t = dt_path; + } else if (!strcmp(a->buf, "$Domain")) { + data_t = dt_domain; + } + } + return data_t; +} + + +static int end_name(struct automata * a) +{ + int ok = 1; + a->data_t = what_is_name(a); + if (a->data_t == dt_plain) { + if (a->cookie.name) { + ok = magi_cookie_list_add(a->list, &a->cookie); + } + nulify_cookie(a); + a->cookie.name = a->buf; + } else { + free(a->buf); + } + buf_new(a); + return ok; +} + +static int end_data(struct automata * a) +{ + int ok = 1; + switch (a->data_t) { + case dt_plain: + a->cookie.data = a->buf; + break; + case dt_path: + a->cookie.path = a->buf; + break; + case dt_domain: + a->cookie.domain = a->buf; + break; + case dt_version: + if (strcmp(a->buf, "1")) { + ok = 0; + } + } + buf_new(a); + return ok; +} + + +static enum st parse_pre_name(struct automata * a, char c) +{ + enum st state; + if (c == ' ' || c == '\t') { + state = st_name; + } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { + state = st_name; + if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { + state = st_error; + } + } else { + state = st_error; + } + return state; +} + +static enum st parse_name(struct automata * a, char c) +{ + enum st state; + if (c == '=') { + state = st_pre_data; + if (!end_name(a)) { + state = st_error; + } + } else if (c == ' ' || c == '\t') { + state = st_post_name; + if (!end_name(a)) { + state = st_error; + } + } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { + state = st_name; + if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { + state = st_error; + } + } else { + state = st_error; + } + return state; +} + +static enum st parse_post_name(struct automata * a, char c) +{ + enum st state; + if (c == '=') { + state = st_pre_data; + } else if (c == ' ' || c == '\t') { + state = st_post_name; + } else { + state = st_error; + } + return state; +} + +static enum st parse_pre_data(struct automata * a, char c) +{ + enum st state; + if (c == ' ' || c == '\t') { + state = st_pre_data; + } else if (c == '"') { + state = st_data; + a->is_quoted = 1; + } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { + state = st_data; + if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { + state = st_error; + } + } else { + state = st_error; + } + return state; +} + +static enum st parse_not_quoted_data(struct automata * a, char c) +{ + enum st state; + if (c == ';' || (c == ',' && a->is_first)) { + state = st_pre_name; + a->is_first = 0; + if (!end_data(a)) { + state = st_error; + } + } else if (c == ' ' || c == '\t') { + state = st_post_data; + if (!end_data(a)) { + state = st_error; + } + } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { + state = st_data; + if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { + state = st_error; + } + } else { + state = st_error; + } + return state; +} + +static enum st parse_data(struct automata * a, char c) +{ + enum st state; + if (a->is_quoted) { + if (c == '"') { + state = st_post_data; + a->is_quoted = 0; + if (!end_data(a)) { + state = st_error; + } + } else { + state = st_data; + } + } else { + state = parse_not_quoted_data(a, c); + } + return state; +} + +static enum st parse_post_data(struct automata * a, char c) +{ + enum st state; + if (c == ';' || (c == ',' && a->is_first)) { + state = st_pre_name; + } else if (c == ' ' || c == '\t') { + state = st_post_data; + } else { + state = st_error; + } + return state; +} + + +static void parse_end(enum magi_error * e, struct automata * a, enum st s) +{ + if (s == st_data) { + if (a->is_quoted || !a->cookie.name) { + *e = magi_error_cookies; + return; + } + if (end_data(a) && magi_cookie_list_add(a->list, &a->cookie)) { + nulify_cookie(a); + } else { + *e = magi_error_cookies; + } + } else if (s != st_post_data) { + *e = magi_error_cookies; + } +} + + +void magi_cookies(struct magi_request * request, const char * data) +{ + enum st state; + struct automata a = { 0, { 0, 0, 0, 0, 0 }, 0, 0, 1, 1, 0, 0, 0 }; + a.list = &request->cookies; + request->cookies = 0; + for (state = st_pre_name; state && *data; ++data) { + switch (state) { + case st_pre_name: + state = parse_pre_name(&a, *data); + break; + case st_name: + state = parse_name(&a, *data); + break; + case st_post_name: + state = parse_post_name(&a, *data); + break; + case st_pre_data: + state = parse_pre_data(&a, *data); + break; + case st_data: + state = parse_data(&a, *data); + break; + case st_post_data: + state = parse_post_data(&a, *data); + default: + break; + } + } + parse_end(&request->error, &a, state); + free(a.cookie.name); + free(a.cookie.data); + free(a.cookie.path); + free(a.cookie.domain); + free(a.buf); +} diff --git a/src/cookies.h b/src/cookies.h new file mode 100644 index 0000000..c336cf7 --- /dev/null +++ b/src/cookies.h @@ -0,0 +1,10 @@ +#ifndef MAGI_INCLUDED_INNER_COOKIES +#define MAGI_INCLUDED_INNER_COOKIES + +#include "request.h" + + +void magi_cookies(magi_request *request, const char *data); + + +#endif diff --git a/src/error.h b/src/error.h deleted file mode 100644 index 4a91962..0000000 --- a/src/error.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MAGI_INCLUDED_ERROR -#define MAGI_INCLUDED_ERROR - - -typedef enum magi_error { - magi_error_none = 0, - magi_error_nobound, - magi_error_unknown, - magi_error_notype, - magi_error_length, - magi_error_memory, - magi_error_cookies, - magi_error_urlenc, - magi_error_multipart, - magi_error_limit -} magi_error; - -const char *magi_error_message(magi_error error); - - -#endif diff --git a/src/fastcgi.h b/src/fastcgi.h deleted file mode 100644 index 0abd152..0000000 --- a/src/fastcgi.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef MAGI_INCLUDED_FASTCGI -#define MAGI_INCLUDED_FASTCGI - - -/* * * TODO * * */ - -enum { magi_to_avoid_warning }; - - -#endif diff --git a/src/file.h b/src/file.h deleted file mode 100644 index 4705031..0000000 --- a/src/file.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef MAGI_INCLUDED_FILE -#define MAGI_INCLUDED_FILE - -#include "param.h" - - -typedef struct magi_file { - /* All pointers must be valid as 'free' arguments. */ - char *param_name; /* Name of corresponding form field */ - char *file_name; /* File name on user's computer */ - magi_param_list *params; /* Multipart params (e.g. type) */ -} magi_file; - -typedef struct magi_file_list { - struct magi_file_list *next; /* Must be valid as 'free' argument. */ - magi_file item; -} magi_file_list; - - -/* Addition of item to top of list. Null <=> error. */ -int magi_file_list_add(magi_file_list **list, magi_file *item); - -/* First node in list: node.param_name == name; else null. */ -magi_file *magi_file_list_get(magi_file_list *list, const char *name); - -/* Freeing and invalidation of list. */ -void magi_file_list_destroy(magi_file_list *list); - - -#endif diff --git a/src/inner_cookies.c b/src/inner_cookies.c deleted file mode 100644 index 9dfde7a..0000000 --- a/src/inner_cookies.c +++ /dev/null @@ -1,290 +0,0 @@ -/* * * TODO * * */ -#include "inner_cookies.h" - -#include "inner_tools.h" -#include -#include - - -enum st { - st_error = 0, - st_pre_name, - st_name, - st_post_name, - st_pre_data, - st_data, - st_post_data -}; - -enum data_type { dt_plain = 0, dt_version, dt_path, dt_domain }; - -struct automata { - struct magi_cookie_list ** list; - struct magi_cookie cookie; - char * buf; - int buf_len; - int buf_size; - int is_first; - int is_advanced; - int is_quoted; - enum data_type data_t; -}; - - -static void nulify_cookie(struct automata * a) -{ - a->cookie.name = 0; - a->cookie.data = 0; - a->cookie.path = 0; - a->cookie.domain = 0; - a->cookie.max_age = 0; -} - -static void buf_new(struct automata * a) -{ - a->buf = 0; - a->buf_len = 0; - a->buf_size = 1; -} - - -static enum data_type what_is_name(const struct automata * a) -{ - enum data_type data_t = dt_plain; - if (a->is_first && !strcmp(a->buf, "$Version")) { - data_t = dt_version; - } else if (a->is_advanced) { - if (!strcmp(a->buf, "$Path")) { - data_t = dt_path; - } else if (!strcmp(a->buf, "$Domain")) { - data_t = dt_domain; - } - } - return data_t; -} - - -static int end_name(struct automata * a) -{ - int ok = 1; - a->data_t = what_is_name(a); - if (a->data_t == dt_plain) { - if (a->cookie.name) { - ok = magi_cookie_list_add(a->list, &a->cookie); - } - nulify_cookie(a); - a->cookie.name = a->buf; - } else { - free(a->buf); - } - buf_new(a); - return ok; -} - -static int end_data(struct automata * a) -{ - int ok = 1; - switch (a->data_t) { - case dt_plain: - a->cookie.data = a->buf; - break; - case dt_path: - a->cookie.path = a->buf; - break; - case dt_domain: - a->cookie.domain = a->buf; - break; - case dt_version: - if (strcmp(a->buf, "1")) { - ok = 0; - } - } - buf_new(a); - return ok; -} - - -static enum st parse_pre_name(struct automata * a, char c) -{ - enum st state; - if (c == ' ' || c == '\t') { - state = st_name; - } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { - state = st_name; - if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { - state = st_error; - } - } else { - state = st_error; - } - return state; -} - -static enum st parse_name(struct automata * a, char c) -{ - enum st state; - if (c == '=') { - state = st_pre_data; - if (!end_name(a)) { - state = st_error; - } - } else if (c == ' ' || c == '\t') { - state = st_post_name; - if (!end_name(a)) { - state = st_error; - } - } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { - state = st_name; - if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { - state = st_error; - } - } else { - state = st_error; - } - return state; -} - -static enum st parse_post_name(struct automata * a, char c) -{ - enum st state; - if (c == '=') { - state = st_pre_data; - } else if (c == ' ' || c == '\t') { - state = st_post_name; - } else { - state = st_error; - } - return state; -} - -static enum st parse_pre_data(struct automata * a, char c) -{ - enum st state; - if (c == ' ' || c == '\t') { - state = st_pre_data; - } else if (c == '"') { - state = st_data; - a->is_quoted = 1; - } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { - state = st_data; - if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { - state = st_error; - } - } else { - state = st_error; - } - return state; -} - -static enum st parse_not_quoted_data(struct automata * a, char c) -{ - enum st state; - if (c == ';' || (c == ',' && a->is_first)) { - state = st_pre_name; - a->is_first = 0; - if (!end_data(a)) { - state = st_error; - } - } else if (c == ' ' || c == '\t') { - state = st_post_data; - if (!end_data(a)) { - state = st_error; - } - } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { - state = st_data; - if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { - state = st_error; - } - } else { - state = st_error; - } - return state; -} - -static enum st parse_data(struct automata * a, char c) -{ - enum st state; - if (a->is_quoted) { - if (c == '"') { - state = st_post_data; - a->is_quoted = 0; - if (!end_data(a)) { - state = st_error; - } - } else { - state = st_data; - } - } else { - state = parse_not_quoted_data(a, c); - } - return state; -} - -static enum st parse_post_data(struct automata * a, char c) -{ - enum st state; - if (c == ';' || (c == ',' && a->is_first)) { - state = st_pre_name; - } else if (c == ' ' || c == '\t') { - state = st_post_data; - } else { - state = st_error; - } - return state; -} - - -static void parse_end(enum magi_error * e, struct automata * a, enum st s) -{ - if (s == st_data) { - if (a->is_quoted || !a->cookie.name) { - *e = magi_error_cookies; - return; - } - if (end_data(a) && magi_cookie_list_add(a->list, &a->cookie)) { - nulify_cookie(a); - } else { - *e = magi_error_cookies; - } - } else if (s != st_post_data) { - *e = magi_error_cookies; - } -} - - -void magi_cookies(struct magi_request * request, const char * data) -{ - enum st state; - struct automata a = { 0, { 0, 0, 0, 0, 0 }, 0, 0, 1, 1, 0, 0, 0 }; - a.list = &request->cookies; - request->cookies = 0; - for (state = st_pre_name; state && *data; ++data) { - switch (state) { - case st_pre_name: - state = parse_pre_name(&a, *data); - break; - case st_name: - state = parse_name(&a, *data); - break; - case st_post_name: - state = parse_post_name(&a, *data); - break; - case st_pre_data: - state = parse_pre_data(&a, *data); - break; - case st_data: - state = parse_data(&a, *data); - break; - case st_post_data: - state = parse_post_data(&a, *data); - default: - break; - } - } - parse_end(&request->error, &a, state); - free(a.cookie.name); - free(a.cookie.data); - free(a.cookie.path); - free(a.cookie.domain); - free(a.buf); -} diff --git a/src/inner_cookies.h b/src/inner_cookies.h deleted file mode 100644 index c336cf7..0000000 --- a/src/inner_cookies.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef MAGI_INCLUDED_INNER_COOKIES -#define MAGI_INCLUDED_INNER_COOKIES - -#include "request.h" - - -void magi_cookies(magi_request *request, const char *data); - - -#endif diff --git a/src/inner_multipart.c b/src/inner_multipart.c deleted file mode 100644 index 0187f7c..0000000 --- a/src/inner_multipart.c +++ /dev/null @@ -1,479 +0,0 @@ -/* Support for multifile controls are not provided. */ -#include "inner_multipart.h" - -#include "error.h" -#include "inner_tools.h" -#include "param.h" -#include -#include -#include -#include - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Local Shortcuts - */ -static int is_token(char c) -{ - return 32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={} \t", c); -} - -static int is_str_token(char * str) -{ - int is = str && *str; /* Empty string is not valid. */ - while (is && *str) { - is = is_token(*str); - ++str; - } - return is; -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Automata for multipart/form-data - */ -enum st { - st_error = 0, - st_begin, - st_pname_pre, - st_pname, - st_pname_end, - st_pdata, - st_data, - st_end -}; - -struct automata { - struct magi_request * request; - struct magi_file file; - struct magi_param param; - struct magi_param subparam; - char * buf; - int buf_size; - int size; - int len; - char * boundary; - int boundary_pos; - int boundary_len; - int is_end_suspected; - int is_CR_readed; - int is_quoted; - int readed; -}; - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Automata Shortcuts - */ -static char * extract_filename(char * n) -{ - n = strchr(n, '='); - if (!n) { - return 0; - } - n += strspn(n, " \t") + 1; - if (*n == '"') { - ++n; - return magi_str_create_copy(n, n - strchr(n, '"')); - } else { - return magi_str_create_copy(n, strcspn(n, " \t")); - } -} - -static int content_disposition(struct automata * a) -{ - char * n = strchr(a->subparam.data, '='); - if (!n) { - return 0; - } - n += strspn(n, " \t") + 1; - if (*n == '"') { - ++n; - a->param.name = magi_str_create_copy(n, n - strchr(n, '"')); - if (!a->param.name || !*a->param.name) { - return 0; - } - } else { - a->param.name = magi_str_create_copy(n, strcspn(n, " \t")); - if (!a->param.name || !is_str_token(a->param.name)) { - return 0; - } - } - a->file.file_name = extract_filename(n); - if (a->file.file_name) { - a->file.param_name = a->param.name; - a->file.params = 0; - a->param.name = 0; - } - free(a->subparam.name); - free(a->subparam.data); - a->subparam.name = 0; - a->subparam.data = 0; - return 1; -} - -static int subparam_end(struct automata * a) -{ - a->size = 1; - a->len = 0; - magi_str_lowercase(a->subparam.name); - if (!strcmp(a->subparam.name, "content-disposition")) { - return content_disposition(a); - } else if (magi_param_list_add(&a->file.params, &a->subparam)) { - a->subparam.name = 0; - a->subparam.data = 0; - return 1; - } - return 0; -} - -static int param_end(struct automata * a) -{ - if (a->file.file_name) { - a->request->file_callback(&a->file, a->buf, a->buf_size, 1, - a->request->file_callback_userdata); - a->readed -= a->buf_size; - a->buf_size = 0; - if (!magi_file_list_add(&a->request->files, &a->file)) { - free(a->file.file_name); - free(a->file.param_name); - magi_param_list_destroy(a->file.params); - free(a->file.params); - a->request->error = magi_error_multipart; - return 0; - } - a->file.file_name = 0; - a->file.param_name = 0; - a->file.params = 0; - a->size = 1; - a->len = 0; - return 1; - } - if (!a->param.name) { - a->request->error = magi_error_multipart; - return 0; - } - if (!magi_param_list_add(&a->request->params, &a->param)) { - free(a->param.name); - free(a->param.data); - a->request->error = magi_error_multipart; - return 0; - } - a->param.name = 0; - a->param.data = 0; - a->size = 1; - a->len = 0; - return 1; -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Boundary Interfaces - */ -static char sepget(const struct automata * a) -{ - char c; - const int pos_after = a->boundary_pos - 4 - a->boundary_len; - if (a->boundary_pos == 0) { - c = '\r'; - } else if (a->boundary_pos == 1) { - c = '\n'; - } else if (a->boundary_pos == 2 || a->boundary_pos == 3) { - c = '-'; - } else if (a->boundary_pos - 4 < a->boundary_len) { - c = a->boundary[a->boundary_pos - 4]; - } else if (pos_after == 0) { - c = '\r'; - } else if (pos_after == 1) { - c = '\n'; - } else { - c = 0; - } - return c; -} - -static int seplen(const struct automata * a) -{ - return a->boundary_len + 6; -} - -static char endget(const struct automata * a) -{ - char c; - const int pos_after = a->boundary_pos - 4 - a->boundary_len; - if (a->boundary_pos == 0) { - c = '\r'; - } else if (a->boundary_pos == 1) { - c = '\n'; - } else if (a->boundary_pos == 2 || a->boundary_pos == 3) { - c = '-'; - } else if (a->boundary_pos - 4 < a->boundary_len) { - c = a->boundary[a->boundary_pos - 4]; - } else if (pos_after == 0 || pos_after == 1) { - c = '-'; - } else if (pos_after == 2) { - c = '\r'; - } else if (pos_after == 3) { - c = '\n'; - } else { - c = 0; - } - return c; -} - -static int endlen(const struct automata * a) -{ - return a->boundary_len + 8; -} - -static int is_semiend(const struct automata * a) -{ /* Is end readed, expect last two chars, which are CR LF? */ - return a->is_end_suspected && (a->boundary_pos == endlen(a) - 2); -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Data Reading - */ -static void apply_callback(struct automata * a) -{ - int full = a->buf_size == a->request->file_callback_addon_max; - if (a->file.file_name && full) { - a->request->file_callback(&a->file, a->buf, a->buf_size, 0, - a->request->file_callback_userdata); - a->readed -= a->buf_size; - a->buf_size = 0; - } -} - -static enum st data_add_act( - struct automata * a, char c, char ** dest, int * len, int * size) -{ - int pos = a->boundary_pos; - for (a->boundary_pos = 0; a->boundary_pos < pos; ++a->boundary_pos) { - if (a->is_end_suspected) { - magi_str_add(dest, len, size, endget(a)); - } else { - magi_str_add(dest, len, size, sepget(a)); - } - apply_callback(a); - } - a->boundary_pos = 0; - a->is_end_suspected = 0; - - if (sepget(a) != c) { - magi_str_add(dest, len, size, c); - apply_callback(a); - return st_data; - } else { - a->boundary_pos++; - if (a->boundary_pos == seplen(a)) { - param_end(a); - return st_pname_pre; - } - return st_data; - } -} - -static enum st data_add(struct automata * a, char c) -{ - if (a->file.file_name) { - int max = a->request->file_callback_addon_max + 1; - return data_add_act(a, c, &a->buf, &a->buf_size, &max); - } else { - return data_add_act(a, c, &a->param.data, &a->len, &a->size); - } -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * State analysers - */ -static enum st parse_begin(struct automata * a, char c) -{ - if (sepget(a) != c) { /* 'c' is not wanted character from separator; */ - a->boundary_pos = 0; /* so nullify progress in reading separator. */ - } else { - a->boundary_pos++; - if (a->boundary_pos == seplen(a)) { - return st_pname_pre; /* Separator is completed, so move on. */ - } - } - return st_begin; -} - -static enum st parse_pname_pre(struct automata * a, char c) -{ - if (a->is_CR_readed) { - if (c != '\n') { - return st_error; - } - a->is_CR_readed = 0; - a->boundary_pos = 0; - return st_data; - } else if (c == '\r') { - a->is_CR_readed = 1; - return st_pname_pre; - } else if (is_token(c) && - magi_str_add(&a->subparam.name, &a->len, &a->size, c)) { - return st_pname; - } - return st_error; -} - -static enum st parse_pname(struct automata * a, char c) -{ - if (c == ':') { - a->len = 0; - a->size = 1; - return st_pdata; - } else if (c == ' ' || c == '\t') { - return st_pname_end; - } else if (is_token(c) && - magi_str_add(&a->subparam.name, &a->len, &a->size, c)) { - return st_pname; - } - return st_error; -} - -static enum st parse_pname_end(struct automata * a, char c) -{ - if (c == ':') { - a->len = 0; - a->size = 1; - return st_pdata; - } else if (c == ' ' || c == '\t') { - return st_pname_end; - } - return st_error; -} - -static enum st parse_pdata(struct automata * a, char c) -{ - if (a->is_CR_readed) { - a->is_CR_readed = 0; - if (c == '\n') { - if (subparam_end(a)) { - return st_pname_pre; - } - return st_error; - } else if (magi_str_add(&a->subparam.data, &a->len, &a->size, '\r') && - magi_str_add(&a->subparam.data, &a->len, &a->size, c)) { - return st_pdata; - } - return st_error; - } else if (c == '\r') { - a->is_CR_readed = 1; - return st_pdata; - } else if (magi_str_add(&a->subparam.data, &a->len, &a->size, c)) { - return st_pdata; - } - return st_error; -} - -static enum st parse_data(struct automata * a, char c) -{ - if (a->is_end_suspected) { - if (endget(a) != c) { - return data_add(a, c); - } - a->boundary_pos++; - if (a->boundary_pos == endlen(a)) { - param_end(a); - return st_end; - } - return st_data; - } else if (sepget(a) == c) { - a->boundary_pos++; - if (a->boundary_pos == seplen(a)) { - param_end(a); - return st_pname_pre; - } - return st_data; - } else if ((a->boundary_pos == seplen(a) - 2) && endget(a) == c) { - a->is_end_suspected = 1; - a->boundary_pos++; - return st_data; - } - return data_add(a, c); -} - -static enum st parse_end(struct automata * a, char c) -{ - return st_end; -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Automata Runner - */ -static void run_automata(struct automata * a, - int (*next)(void * next_userdata), - void * next_userdata) -{ - enum st state = st_begin; - int c = next(next_userdata); - while (state && state != st_end && c != EOF && - (!a->request->params_max || a->readed != a->request->params_max)) { - switch (state) { - case st_begin: - state = parse_begin(a, c); - break; - case st_pname_pre: - state = parse_pname_pre(a, c); - break; - case st_pname: - state = parse_pname(a, c); - break; - case st_pname_end: - state = parse_pname_end(a, c); - break; - case st_pdata: - state = parse_pdata(a, c); - break; - case st_data: - state = parse_data(a, c); - break; - case st_end: - state = parse_end(a, c); - default: - break; - } - c = next(next_userdata); - ++a->readed; - } - if (a->request->params_max && a->readed == a->request->params_max) { - a->request->error = magi_error_limit; - return; - } - if (state == st_data && is_semiend(a)) { - state = st_end; - param_end(a); - } - if (state != st_end) { - a->request->error = magi_error_multipart; - free(a->subparam.name); - free(a->subparam.data); - } -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Automata Interfaces - */ -void magi_multipart(struct magi_request * request, - char * boundary, - int (*next)(void * userdata), - void * next_userdata) -{ - struct automata a = { 0, { 0, 0, 0 }, { 0, 0 }, { 0, 0 }, 0, 0, 1, - 0, 0, 2, 0, 0, 0, 0 }; - a.request = request; - a.boundary = boundary; - a.boundary_len = strlen(boundary); - a.buf = malloc(request->file_callback_addon_max + 1); - if (a.buf) { - run_automata(&a, next, next_userdata); - free(a.buf); - } -} diff --git a/src/inner_multipart.h b/src/inner_multipart.h deleted file mode 100644 index c3f7b65..0000000 --- a/src/inner_multipart.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef MAGI_INCLUDED_INNER_MULTIPART -#define MAGI_INCLUDED_INNER_MULTIPART - -#include "request.h" - - -void magi_multipart(magi_request *request, - char *boundary, - int (*next)(void *userdata), - void *next_userdata); - - -#endif diff --git a/src/inner_tools.c b/src/inner_tools.c deleted file mode 100644 index d958851..0000000 --- a/src/inner_tools.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "inner_tools.h" - -#include -#include -#include - - -void magi_str_lowercase(char *str) -{ - if (!str) { - return; - } - for (; *str; ++str) { - *str = tolower(*str); - } -} - -char *magi_str_create_copy(const char *first, int len) -{ - char *copy = magi_str_create(len); - if (copy) { - memcpy(copy, first, len); - } - return copy; -} - -char *magi_str_create(int len) -{ - char *str = malloc(len + 1); - if (str) { - str[len] = 0; - } - return str; -} - -int magi_str_add(char **dest, int *len, int *size, char c) -{ - if (!*dest) { - *dest = magi_str_create(1); - } else if (*len + 1 == *size) { - *size *= 2; - *dest = realloc(*dest, *size); - } - if (!*dest) { - return 0; - } - (*dest)[*len] = c; - ++*len; - (*dest)[*len] = 0; - return 1; -} diff --git a/src/inner_tools.h b/src/inner_tools.h deleted file mode 100644 index 87489c8..0000000 --- a/src/inner_tools.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef MAGI_INCLUDED_INNER_TOOLS -#define MAGI_INCLUDED_INNER_TOOLS -/* Magi Inner Tools - * This file must not be included in header files intended for external use, - * it contains some common utility functions for use in source files. - */ - - -void magi_str_lowercase(char *str); - -/* Results of both create functions are malloced, so need to be freed. */ -char *magi_str_create_copy(const char *first, int len); -char *magi_str_create(int len); - -/* Null only in case of error; if *dest is null creates string. */ -int magi_str_add(char **dest, int *len, int *size, char c); - - -#endif diff --git a/src/inner_urlencoded.c b/src/inner_urlencoded.c deleted file mode 100644 index e3ea56a..0000000 --- a/src/inner_urlencoded.c +++ /dev/null @@ -1,144 +0,0 @@ -#include "inner_urlencoded.h" - -#include "inner_tools.h" -#include -#include -#include - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Local Shortcuts - */ -/* Call only if is_hex(c). */ -static int from_hex(char c) -{ - if (isdigit(c)) { - return c - '0'; - } else { - return toupper(c) - 'A' + 10; - } -} - -static int is_hex(char c) -{ - return isdigit(c) || strchr("ABCDEF", toupper(c)); -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * URL Decoding - */ -static int deurl(char ** data) -{ - char * val = *data; - int ti; - int ci; - if (!val) { - *data = malloc(1); - **data = 0; - return 1; - } - for (ti = 0, ci = 0; val[ci]; ++ti, ++ci) { - if (val[ci] == '%') { - if (is_hex(val[ci + 1]) && is_hex(val[ci + 2])) { - /* Since chars can be signed, arithmetics are not safe. */ - val[ti] = from_hex(val[ci + 2]); /* 0000xxxx */ - val[ti] |= from_hex(val[ci + 1]) << 4; /* XXXXxxxx */ - ci += 2; /* Two extra characters are readed from code. */ - } else { - return 0; - } - } else if (val[ci] == '+') { - val[ti] = ' '; - } else { - val[ti] = val[ci]; - } - } - val[ti] = 0; - return 1; -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Urlencoded Automata - */ -enum st { st_error = 0, st_name, st_data }; - -struct automata { - struct magi_param_list ** list; - struct magi_param param; - int size; - int len; -}; - - -static enum st parse_name(struct automata * a, char c) -{ - if (c == '&' || c == ';') { /* Impossible character means error. */ - return st_error; - } - if (c == '=') { /* Separator ends name. */ - a->size = 1; - a->len = 0; - return st_data; - } - - if (!magi_str_add(&a->param.name, &a->len, &a->size, c)) { - return st_error; - } - return st_name; -} - -static enum st end_data(struct automata * a) -{ - if (deurl(&a->param.name) && deurl(&a->param.data)) { - if (magi_param_list_add(a->list, &a->param)) { - a->size = 1; - a->len = 0; - a->param.name = 0; - a->param.data = 0; - return st_name; - } - } - return st_error; -} - -static enum st parse_data(struct automata * a, char c) -{ - if (c == '=') { /* Impossible character means error. */ - return st_error; - } - if (c == '&' || c == ';') { /* Separator ends data. */ - return end_data(a); - } - - if (!magi_str_add(&a->param.data, &a->len, &a->size, c)) { - return st_error; - } - return st_data; -} - -void magi_urlencoded(struct magi_param_list ** list, - struct magi_request * request, - const char * encoded) -{ - enum st state; - struct automata a = { 0, { 0, 0 }, 1, 0 }; - a.list = list; - *list = 0; - if (!encoded || !*encoded) { - return; - } - for (state = st_name; state && *encoded; ++encoded) { - if (state == st_name) { - state = parse_name(&a, *encoded); - } else { - state = parse_data(&a, *encoded); - } - } - if (state == st_name || !state || !end_data(&a)) { - free(a.param.name); - free(a.param.data); - request->error = magi_error_urlenc; - } -} diff --git a/src/inner_urlencoded.h b/src/inner_urlencoded.h deleted file mode 100644 index e07ed6f..0000000 --- a/src/inner_urlencoded.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef MAGI_INCLUDED_INNER_URLENCODED -#define MAGI_INCLUDED_INNER_URLENCODED - -#include "request.h" - - -/* Fills request->url_params via parsing encoded data. */ -void magi_urlencoded(magi_param_list **list, - magi_request *request, - const char *encoded); - - -#endif diff --git a/src/magi.h b/src/magi.h deleted file mode 100644 index b7df63c..0000000 --- a/src/magi.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef MAGI_INCLUDED -#define MAGI_INCLUDED -/* Magi Header Collection - * You can include this, if you don't want to think about headers. - */ -#include "cgi.h" -#include "cookie.h" -#include "error.h" -#include "fastcgi.h" -#include "file.h" -#include "param.h" -#include "request.h" -#include "response.h" -#include "urlenc.h" - -#endif diff --git a/src/multipart.c b/src/multipart.c new file mode 100644 index 0000000..0187f7c --- /dev/null +++ b/src/multipart.c @@ -0,0 +1,479 @@ +/* Support for multifile controls are not provided. */ +#include "inner_multipart.h" + +#include "error.h" +#include "inner_tools.h" +#include "param.h" +#include +#include +#include +#include + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Local Shortcuts + */ +static int is_token(char c) +{ + return 32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={} \t", c); +} + +static int is_str_token(char * str) +{ + int is = str && *str; /* Empty string is not valid. */ + while (is && *str) { + is = is_token(*str); + ++str; + } + return is; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Automata for multipart/form-data + */ +enum st { + st_error = 0, + st_begin, + st_pname_pre, + st_pname, + st_pname_end, + st_pdata, + st_data, + st_end +}; + +struct automata { + struct magi_request * request; + struct magi_file file; + struct magi_param param; + struct magi_param subparam; + char * buf; + int buf_size; + int size; + int len; + char * boundary; + int boundary_pos; + int boundary_len; + int is_end_suspected; + int is_CR_readed; + int is_quoted; + int readed; +}; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Automata Shortcuts + */ +static char * extract_filename(char * n) +{ + n = strchr(n, '='); + if (!n) { + return 0; + } + n += strspn(n, " \t") + 1; + if (*n == '"') { + ++n; + return magi_str_create_copy(n, n - strchr(n, '"')); + } else { + return magi_str_create_copy(n, strcspn(n, " \t")); + } +} + +static int content_disposition(struct automata * a) +{ + char * n = strchr(a->subparam.data, '='); + if (!n) { + return 0; + } + n += strspn(n, " \t") + 1; + if (*n == '"') { + ++n; + a->param.name = magi_str_create_copy(n, n - strchr(n, '"')); + if (!a->param.name || !*a->param.name) { + return 0; + } + } else { + a->param.name = magi_str_create_copy(n, strcspn(n, " \t")); + if (!a->param.name || !is_str_token(a->param.name)) { + return 0; + } + } + a->file.file_name = extract_filename(n); + if (a->file.file_name) { + a->file.param_name = a->param.name; + a->file.params = 0; + a->param.name = 0; + } + free(a->subparam.name); + free(a->subparam.data); + a->subparam.name = 0; + a->subparam.data = 0; + return 1; +} + +static int subparam_end(struct automata * a) +{ + a->size = 1; + a->len = 0; + magi_str_lowercase(a->subparam.name); + if (!strcmp(a->subparam.name, "content-disposition")) { + return content_disposition(a); + } else if (magi_param_list_add(&a->file.params, &a->subparam)) { + a->subparam.name = 0; + a->subparam.data = 0; + return 1; + } + return 0; +} + +static int param_end(struct automata * a) +{ + if (a->file.file_name) { + a->request->file_callback(&a->file, a->buf, a->buf_size, 1, + a->request->file_callback_userdata); + a->readed -= a->buf_size; + a->buf_size = 0; + if (!magi_file_list_add(&a->request->files, &a->file)) { + free(a->file.file_name); + free(a->file.param_name); + magi_param_list_destroy(a->file.params); + free(a->file.params); + a->request->error = magi_error_multipart; + return 0; + } + a->file.file_name = 0; + a->file.param_name = 0; + a->file.params = 0; + a->size = 1; + a->len = 0; + return 1; + } + if (!a->param.name) { + a->request->error = magi_error_multipart; + return 0; + } + if (!magi_param_list_add(&a->request->params, &a->param)) { + free(a->param.name); + free(a->param.data); + a->request->error = magi_error_multipart; + return 0; + } + a->param.name = 0; + a->param.data = 0; + a->size = 1; + a->len = 0; + return 1; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Boundary Interfaces + */ +static char sepget(const struct automata * a) +{ + char c; + const int pos_after = a->boundary_pos - 4 - a->boundary_len; + if (a->boundary_pos == 0) { + c = '\r'; + } else if (a->boundary_pos == 1) { + c = '\n'; + } else if (a->boundary_pos == 2 || a->boundary_pos == 3) { + c = '-'; + } else if (a->boundary_pos - 4 < a->boundary_len) { + c = a->boundary[a->boundary_pos - 4]; + } else if (pos_after == 0) { + c = '\r'; + } else if (pos_after == 1) { + c = '\n'; + } else { + c = 0; + } + return c; +} + +static int seplen(const struct automata * a) +{ + return a->boundary_len + 6; +} + +static char endget(const struct automata * a) +{ + char c; + const int pos_after = a->boundary_pos - 4 - a->boundary_len; + if (a->boundary_pos == 0) { + c = '\r'; + } else if (a->boundary_pos == 1) { + c = '\n'; + } else if (a->boundary_pos == 2 || a->boundary_pos == 3) { + c = '-'; + } else if (a->boundary_pos - 4 < a->boundary_len) { + c = a->boundary[a->boundary_pos - 4]; + } else if (pos_after == 0 || pos_after == 1) { + c = '-'; + } else if (pos_after == 2) { + c = '\r'; + } else if (pos_after == 3) { + c = '\n'; + } else { + c = 0; + } + return c; +} + +static int endlen(const struct automata * a) +{ + return a->boundary_len + 8; +} + +static int is_semiend(const struct automata * a) +{ /* Is end readed, expect last two chars, which are CR LF? */ + return a->is_end_suspected && (a->boundary_pos == endlen(a) - 2); +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Data Reading + */ +static void apply_callback(struct automata * a) +{ + int full = a->buf_size == a->request->file_callback_addon_max; + if (a->file.file_name && full) { + a->request->file_callback(&a->file, a->buf, a->buf_size, 0, + a->request->file_callback_userdata); + a->readed -= a->buf_size; + a->buf_size = 0; + } +} + +static enum st data_add_act( + struct automata * a, char c, char ** dest, int * len, int * size) +{ + int pos = a->boundary_pos; + for (a->boundary_pos = 0; a->boundary_pos < pos; ++a->boundary_pos) { + if (a->is_end_suspected) { + magi_str_add(dest, len, size, endget(a)); + } else { + magi_str_add(dest, len, size, sepget(a)); + } + apply_callback(a); + } + a->boundary_pos = 0; + a->is_end_suspected = 0; + + if (sepget(a) != c) { + magi_str_add(dest, len, size, c); + apply_callback(a); + return st_data; + } else { + a->boundary_pos++; + if (a->boundary_pos == seplen(a)) { + param_end(a); + return st_pname_pre; + } + return st_data; + } +} + +static enum st data_add(struct automata * a, char c) +{ + if (a->file.file_name) { + int max = a->request->file_callback_addon_max + 1; + return data_add_act(a, c, &a->buf, &a->buf_size, &max); + } else { + return data_add_act(a, c, &a->param.data, &a->len, &a->size); + } +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * State analysers + */ +static enum st parse_begin(struct automata * a, char c) +{ + if (sepget(a) != c) { /* 'c' is not wanted character from separator; */ + a->boundary_pos = 0; /* so nullify progress in reading separator. */ + } else { + a->boundary_pos++; + if (a->boundary_pos == seplen(a)) { + return st_pname_pre; /* Separator is completed, so move on. */ + } + } + return st_begin; +} + +static enum st parse_pname_pre(struct automata * a, char c) +{ + if (a->is_CR_readed) { + if (c != '\n') { + return st_error; + } + a->is_CR_readed = 0; + a->boundary_pos = 0; + return st_data; + } else if (c == '\r') { + a->is_CR_readed = 1; + return st_pname_pre; + } else if (is_token(c) && + magi_str_add(&a->subparam.name, &a->len, &a->size, c)) { + return st_pname; + } + return st_error; +} + +static enum st parse_pname(struct automata * a, char c) +{ + if (c == ':') { + a->len = 0; + a->size = 1; + return st_pdata; + } else if (c == ' ' || c == '\t') { + return st_pname_end; + } else if (is_token(c) && + magi_str_add(&a->subparam.name, &a->len, &a->size, c)) { + return st_pname; + } + return st_error; +} + +static enum st parse_pname_end(struct automata * a, char c) +{ + if (c == ':') { + a->len = 0; + a->size = 1; + return st_pdata; + } else if (c == ' ' || c == '\t') { + return st_pname_end; + } + return st_error; +} + +static enum st parse_pdata(struct automata * a, char c) +{ + if (a->is_CR_readed) { + a->is_CR_readed = 0; + if (c == '\n') { + if (subparam_end(a)) { + return st_pname_pre; + } + return st_error; + } else if (magi_str_add(&a->subparam.data, &a->len, &a->size, '\r') && + magi_str_add(&a->subparam.data, &a->len, &a->size, c)) { + return st_pdata; + } + return st_error; + } else if (c == '\r') { + a->is_CR_readed = 1; + return st_pdata; + } else if (magi_str_add(&a->subparam.data, &a->len, &a->size, c)) { + return st_pdata; + } + return st_error; +} + +static enum st parse_data(struct automata * a, char c) +{ + if (a->is_end_suspected) { + if (endget(a) != c) { + return data_add(a, c); + } + a->boundary_pos++; + if (a->boundary_pos == endlen(a)) { + param_end(a); + return st_end; + } + return st_data; + } else if (sepget(a) == c) { + a->boundary_pos++; + if (a->boundary_pos == seplen(a)) { + param_end(a); + return st_pname_pre; + } + return st_data; + } else if ((a->boundary_pos == seplen(a) - 2) && endget(a) == c) { + a->is_end_suspected = 1; + a->boundary_pos++; + return st_data; + } + return data_add(a, c); +} + +static enum st parse_end(struct automata * a, char c) +{ + return st_end; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Automata Runner + */ +static void run_automata(struct automata * a, + int (*next)(void * next_userdata), + void * next_userdata) +{ + enum st state = st_begin; + int c = next(next_userdata); + while (state && state != st_end && c != EOF && + (!a->request->params_max || a->readed != a->request->params_max)) { + switch (state) { + case st_begin: + state = parse_begin(a, c); + break; + case st_pname_pre: + state = parse_pname_pre(a, c); + break; + case st_pname: + state = parse_pname(a, c); + break; + case st_pname_end: + state = parse_pname_end(a, c); + break; + case st_pdata: + state = parse_pdata(a, c); + break; + case st_data: + state = parse_data(a, c); + break; + case st_end: + state = parse_end(a, c); + default: + break; + } + c = next(next_userdata); + ++a->readed; + } + if (a->request->params_max && a->readed == a->request->params_max) { + a->request->error = magi_error_limit; + return; + } + if (state == st_data && is_semiend(a)) { + state = st_end; + param_end(a); + } + if (state != st_end) { + a->request->error = magi_error_multipart; + free(a->subparam.name); + free(a->subparam.data); + } +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Automata Interfaces + */ +void magi_multipart(struct magi_request * request, + char * boundary, + int (*next)(void * userdata), + void * next_userdata) +{ + struct automata a = { 0, { 0, 0, 0 }, { 0, 0 }, { 0, 0 }, 0, 0, 1, + 0, 0, 2, 0, 0, 0, 0 }; + a.request = request; + a.boundary = boundary; + a.boundary_len = strlen(boundary); + a.buf = malloc(request->file_callback_addon_max + 1); + if (a.buf) { + run_automata(&a, next, next_userdata); + free(a.buf); + } +} diff --git a/src/multipart.h b/src/multipart.h new file mode 100644 index 0000000..c3f7b65 --- /dev/null +++ b/src/multipart.h @@ -0,0 +1,13 @@ +#ifndef MAGI_INCLUDED_INNER_MULTIPART +#define MAGI_INCLUDED_INNER_MULTIPART + +#include "request.h" + + +void magi_multipart(magi_request *request, + char *boundary, + int (*next)(void *userdata), + void *next_userdata); + + +#endif diff --git a/src/param.h b/src/param.h deleted file mode 100644 index bebacd7..0000000 --- a/src/param.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MAGI_INCLUDED_PARAM -#define MAGI_INCLUDED_PARAM - - -typedef struct magi_param { - /* All pointers must be valid as 'free' arguments. */ - char *name; - char *data; -} magi_param; - -typedef struct magi_param_list { - struct magi_param_list *next; /* Must be valid as 'free' argument. */ - magi_param item; -} magi_param_list; - - -/* Addition of item to top of list. Null <=> error. */ -int magi_param_list_add(magi_param_list **list, - magi_param *item); - -/* Data of the first node in list: node.name == name; else null. */ -char *magi_param_list_get(magi_param_list *list, const char *name); - -/* Freeing and invalidation of list. */ -void magi_param_list_destroy(magi_param_list *list); - - -#endif diff --git a/src/request.h b/src/request.h deleted file mode 100644 index 18d6c8e..0000000 --- a/src/request.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef MAGI_INCLUDED_REQUEST -#define MAGI_INCLUDED_REQUEST - -#include "cookie.h" -#include "error.h" -#include "file.h" -#include "param.h" - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Request - * - * Can be created via 'magi_request_{gateway interface name}', but will have - * nullified 'post'-related fields ('params' & 'files'). Reason is unlimited - * 'post' body size, with possible dependence of wanted limits from data of - * headers (e.g. session id from cookies, enabling some users to load more). - * To proceed 'post' use 'magi_request_resume_{gateway interface name}', - * specifying settings if necessary. - * - * - * Cheatsheet on environment: - * - * Request URL: http[s]://{server_name}[:{server_port}]{uri} - * example.com 80 - * uri: {document_uri}[?{urlencode(url_params)}] - * alfar=9973&veles=on - * document_uri: {script_name}{path_info} - * /bin/script /article/magic - */ -typedef struct magi_request { - /* * * Results * * */ - /* All pointers of this section must be valid as 'free' arguments. */ - - /* Parsed */ - magi_cookie_list *cookies; - magi_param_list *http_params; /* HTTP Header parameters */ - magi_param_list *url_params; /* Paramteres from URL */ - magi_param_list *params; /* Parameters from 'post' body */ - magi_file_list *files; /* 'Post' multipart files */ - - /* Environment Shortcuts */ - char *method; /* REQUEST_METHOD */ - char *uri; /* REQUEST_URI */ - char *document_root; /* DOCUMENT_ROOT */ - char *document_uri; /* DOCUMENT_URI */ - char *script_name; /* SCRIPT_NAME */ - char *script_filename; /* SCRIPT_FILENAME */ - char *remote_addr; /* REMOTE_ADDR */ - char *remote_port; /* REMOTE_PORT */ - char *server_addr; /* SERVER_ADDR */ - char *server_name; /* SERVER_NAME */ - /* server_name can be not a domain name, even if request is done with it. - * (Use http_params["HTTP_HOST"] instead.) */ - char *server_port; /* SERVER_PORT */ - char *server_protocol; /* SERVER_PROTOCOL */ - char *server_software; /* SERVER_COFTWARE */ - char *path_info; /* PATH_INFO */ - - /* Request Error Code */ - magi_error error; - - - /* * * Settings * * */ - - /* Callback for processing files */ - void (*file_callback)(magi_file *file, - char *addon, - int addon_len, - int is_addon_last, - void *userdata); - void *file_callback_userdata; - int file_callback_addon_max; - - /* Limits for memory used (null <=> unlimitted) */ - int cookies_max; - int url_params_max; - int http_params_max; - int params_max; -} magi_request; - - -/* Setup request with default settings. */ -void magi_request_setup(magi_request *request); - - -/* Destroys request. */ -void magi_request_destroy(magi_request *request); - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Tempfiles Callback - */ -typedef struct magi_tempfile { - const char *param_name; /* Form field name, in which file is expected. */ - const char *location; /* Location to load file in. */ - int maximum; /* Null <=> unlimited. */ -} magi_tempfile; - -typedef struct magi_tempfiles { - int count; - magi_tempfile *tmps; -} magi_tempfiles; - -void magi_tempfiles_add(magi_tempfiles *tmps, - const char *name, - const char *path, - int max); - -void magi_tempfiles_destroy(magi_tempfiles *tmps); - -/* Setup request callback with files loaded into corresponding to their - * parameter names locations; paths are in magi_tempfiles struct. */ -void magi_request_setup_tempfiles(magi_request *request, - magi_tempfiles *table); - - -#endif diff --git a/src/response.c b/src/response.c index 9570cd1..904b855 100644 --- a/src/response.c +++ b/src/response.c @@ -1,14 +1,10 @@ #include "response.h" -#include "inner_tools.h" -#include +#include "tools.h" #include #include #include -/* TODO: realise vsnprintf or eliminate need of it. (see musl) */ -int vsnprintf(char *s, size_t n, const char *format, va_list arg); - void magi_response_setup(magi_response *response) { @@ -46,24 +42,6 @@ void magi_response_add(magi_response *r, const char *addon) r->len += addon_len; } -void magi_response_add_format(magi_response *response, const char *addon, ...) -{ - char *act_addon; - int n; - va_list args; - va_start(args, addon); - n = vsnprintf(0, 0, addon, args); - va_end(args); - if (n >= 0) { - act_addon = malloc(n + 1); - va_start(args, addon); - vsnprintf(act_addon, n + 1, addon, args); - va_end(args); - magi_response_add(response, act_addon); - free(act_addon); - } -} - void magi_response_cookie(magi_response *response, magi_cookie *cookie) { magi_cookie_list_add(&response->cookies, cookie); diff --git a/src/response.h b/src/response.h deleted file mode 100644 index a51d4a4..0000000 --- a/src/response.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef MAGI_INCLUDED_RESPONSE -#define MAGI_INCLUDED_RESPONSE - -#include "cookie.h" -#include "param.h" - - -typedef struct magi_response { - magi_cookie_list *cookies; - magi_param_list *http_params; - char *content_type; - char *content; - int len; - int size; -} magi_response; - - -void magi_response_setup(magi_response *response); - - -void magi_response_content_type(magi_response *response, const char *type); - -void magi_response_add(magi_response *response, const char *addon); -void magi_response_add_format(magi_response *response, const char *addon, ...); - -void magi_response_cookie(magi_response *response, magi_cookie *cookie); - -void magi_response_cookie_easy(magi_response *response, - const char *name, - const char *value); - -void magi_response_cookie_discard(magi_response *response, - const char *name); - -void magi_response_http(magi_response *response, - const char *name, - const char *data); - - -void magi_response_destroy(magi_response *response); - - -#endif diff --git a/src/tools.c b/src/tools.c new file mode 100644 index 0000000..d958851 --- /dev/null +++ b/src/tools.c @@ -0,0 +1,51 @@ +#include "inner_tools.h" + +#include +#include +#include + + +void magi_str_lowercase(char *str) +{ + if (!str) { + return; + } + for (; *str; ++str) { + *str = tolower(*str); + } +} + +char *magi_str_create_copy(const char *first, int len) +{ + char *copy = magi_str_create(len); + if (copy) { + memcpy(copy, first, len); + } + return copy; +} + +char *magi_str_create(int len) +{ + char *str = malloc(len + 1); + if (str) { + str[len] = 0; + } + return str; +} + +int magi_str_add(char **dest, int *len, int *size, char c) +{ + if (!*dest) { + *dest = magi_str_create(1); + } else if (*len + 1 == *size) { + *size *= 2; + *dest = realloc(*dest, *size); + } + if (!*dest) { + return 0; + } + (*dest)[*len] = c; + ++*len; + (*dest)[*len] = 0; + return 1; +} diff --git a/src/tools.h b/src/tools.h new file mode 100644 index 0000000..8d66131 --- /dev/null +++ b/src/tools.h @@ -0,0 +1,19 @@ +#ifndef MAGI_INCLUDED_TOOLS +#define MAGI_INCLUDED_TOOLS +/* * Collection of helpful functions for internal use. + * + * blah... + */ + + +void magi_str_lowercase(char *str); + +/* Results of both create functions are malloced, so need to be freed. */ +char *magi_str_create_copy(const char *first, int len); +char *magi_str_create(int len); + +/* Null only in case of error; if *dest is null creates string. */ +int magi_str_add(char **dest, int *len, int *size, char c); + + +#endif diff --git a/src/urlenc.h b/src/urlenc.h deleted file mode 100644 index edaec08..0000000 --- a/src/urlenc.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef MAGI_INCLUDED_URLENC -#define MAGI_INCLUDED_URLENC - - -/* 'code' from urlenc must have size of at least magi_urlenc_size(plain). */ -int magi_urlenc_size(const char *plain); -void magi_urlenc(const char *plain, char *code); - - -#endif diff --git a/src/urlencoded.c b/src/urlencoded.c new file mode 100644 index 0000000..e3ea56a --- /dev/null +++ b/src/urlencoded.c @@ -0,0 +1,144 @@ +#include "inner_urlencoded.h" + +#include "inner_tools.h" +#include +#include +#include + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Local Shortcuts + */ +/* Call only if is_hex(c). */ +static int from_hex(char c) +{ + if (isdigit(c)) { + return c - '0'; + } else { + return toupper(c) - 'A' + 10; + } +} + +static int is_hex(char c) +{ + return isdigit(c) || strchr("ABCDEF", toupper(c)); +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * URL Decoding + */ +static int deurl(char ** data) +{ + char * val = *data; + int ti; + int ci; + if (!val) { + *data = malloc(1); + **data = 0; + return 1; + } + for (ti = 0, ci = 0; val[ci]; ++ti, ++ci) { + if (val[ci] == '%') { + if (is_hex(val[ci + 1]) && is_hex(val[ci + 2])) { + /* Since chars can be signed, arithmetics are not safe. */ + val[ti] = from_hex(val[ci + 2]); /* 0000xxxx */ + val[ti] |= from_hex(val[ci + 1]) << 4; /* XXXXxxxx */ + ci += 2; /* Two extra characters are readed from code. */ + } else { + return 0; + } + } else if (val[ci] == '+') { + val[ti] = ' '; + } else { + val[ti] = val[ci]; + } + } + val[ti] = 0; + return 1; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Urlencoded Automata + */ +enum st { st_error = 0, st_name, st_data }; + +struct automata { + struct magi_param_list ** list; + struct magi_param param; + int size; + int len; +}; + + +static enum st parse_name(struct automata * a, char c) +{ + if (c == '&' || c == ';') { /* Impossible character means error. */ + return st_error; + } + if (c == '=') { /* Separator ends name. */ + a->size = 1; + a->len = 0; + return st_data; + } + + if (!magi_str_add(&a->param.name, &a->len, &a->size, c)) { + return st_error; + } + return st_name; +} + +static enum st end_data(struct automata * a) +{ + if (deurl(&a->param.name) && deurl(&a->param.data)) { + if (magi_param_list_add(a->list, &a->param)) { + a->size = 1; + a->len = 0; + a->param.name = 0; + a->param.data = 0; + return st_name; + } + } + return st_error; +} + +static enum st parse_data(struct automata * a, char c) +{ + if (c == '=') { /* Impossible character means error. */ + return st_error; + } + if (c == '&' || c == ';') { /* Separator ends data. */ + return end_data(a); + } + + if (!magi_str_add(&a->param.data, &a->len, &a->size, c)) { + return st_error; + } + return st_data; +} + +void magi_urlencoded(struct magi_param_list ** list, + struct magi_request * request, + const char * encoded) +{ + enum st state; + struct automata a = { 0, { 0, 0 }, 1, 0 }; + a.list = list; + *list = 0; + if (!encoded || !*encoded) { + return; + } + for (state = st_name; state && *encoded; ++encoded) { + if (state == st_name) { + state = parse_name(&a, *encoded); + } else { + state = parse_data(&a, *encoded); + } + } + if (state == st_name || !state || !end_data(&a)) { + free(a.param.name); + free(a.param.data); + request->error = magi_error_urlenc; + } +} diff --git a/src/urlencoded.h b/src/urlencoded.h new file mode 100644 index 0000000..e07ed6f --- /dev/null +++ b/src/urlencoded.h @@ -0,0 +1,13 @@ +#ifndef MAGI_INCLUDED_INNER_URLENCODED +#define MAGI_INCLUDED_INNER_URLENCODED + +#include "request.h" + + +/* Fills request->url_params via parsing encoded data. */ +void magi_urlencoded(magi_param_list **list, + magi_request *request, + const char *encoded); + + +#endif -- cgit v1.2.3