From 8ef310a22a54dca7a80947f83f03bc296ab298dd Mon Sep 17 00:00:00 2001 From: Aleksey Veresov Date: Tue, 4 Feb 2020 18:14:54 +0300 Subject: [magi] --- src/cgi.c | 38 ++-- src/cgi.h | 15 +- src/cookie.h | 34 ++-- src/cookies.c | 296 ------------------------------ src/cookies.h | 10 -- src/error.h | 6 +- src/file.h | 25 ++- src/inner_cookies.c | 296 ++++++++++++++++++++++++++++++ src/inner_cookies.h | 10 ++ src/inner_multipart.c | 479 +++++++++++++++++++++++++++++++++++++++++++++++++ src/inner_multipart.h | 13 ++ 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 | 24 +-- src/request.h | 102 ++++++----- src/response.c | 64 +++---- src/response.h | 47 +++-- src/urlenc.c | 135 -------------- src/urlenc.h | 13 +- 22 files changed, 1164 insertions(+), 1108 deletions(-) delete mode 100644 src/cookies.c delete mode 100644 src/cookies.h create mode 100644 src/inner_cookies.c create mode 100644 src/inner_cookies.h create mode 100644 src/inner_multipart.c create mode 100644 src/inner_multipart.h create mode 100644 src/inner_urlencoded.c create mode 100644 src/inner_urlencoded.h create mode 100644 src/magi.h delete mode 100644 src/multipart.c delete mode 100644 src/multipart.h (limited to 'src') diff --git a/src/cgi.c b/src/cgi.c index 7563613..a0661dc 100644 --- a/src/cgi.c +++ b/src/cgi.c @@ -1,14 +1,14 @@ #include "cgi.h" #include "cookie.h" -#include "cookies.h" #include "error.h" #include "file.h" -#include "multipart.h" +#include "inner_cookies.h" +#include "inner_multipart.h" +#include "inner_tools.h" +#include "inner_urlencoded.h" #include "param.h" #include "request.h" -#include "urlenc.h" -#include "utils.h" #include #include #include @@ -132,7 +132,7 @@ static void cgi_url(struct magi_request * request) char * in = 0; cgi_input_get(&request->error, &in, request->url_params_max); if (!request->error) { - magi_urlenc(&request->url_params, request, in); + magi_urlencoded(&request->url_params, request, in); } free(in); } @@ -218,7 +218,7 @@ int magi_request_resume_cgi(struct magi_request * request) char * in = 0; cgi_input_post(e, &in, request->params_max); if (!*e) { - magi_urlenc(&request->params, request, in); + magi_urlencoded(&request->params, request, in); } free(in); } else { @@ -229,6 +229,11 @@ int magi_request_resume_cgi(struct magi_request * request) return !request->error; } +int magi_request_full_cgi(magi_request *request) +{ + return magi_request_cgi(request) && magi_request_resume_cgi(request); +} + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * CGI Response @@ -268,6 +273,7 @@ void output_cookies(struct magi_cookie_list * list) } } + int magi_response_cgi(struct magi_response * response) { output_http_params(response->http_params); @@ -278,14 +284,20 @@ int magi_response_cgi(struct magi_response * response) return 1; } +int magi_response_cgi_clear(magi_response *response) +{ + int ok = magi_response_cgi(response); + magi_response_destroy(response); + return ok; +} + + int magi_error_cgi(enum magi_error error) { struct magi_response res; magi_response_setup(&res); magi_response_http(&res, "Status", "400 Bad Request"); - magi_response_content_type(&res, magi_xhtml); - magi_response_add_format( - &res, + magi_response_add_format(&res, "" "" @@ -296,11 +308,5 @@ int magi_error_cgi(enum magi_error error) "" "", magi_error_message(error)); - if (magi_response_cgi(&res)) { - magi_response_destroy(&res); - return 1; - } else { - magi_response_destroy(&res); - return 0; - } + return magi_response_cgi_clear(&res); } diff --git a/src/cgi.h b/src/cgi.h index 6101de6..7e47ad0 100644 --- a/src/cgi.h +++ b/src/cgi.h @@ -8,20 +8,27 @@ /* Constructs non-post part of request from environment; * Returns null only in case of error. */ -int magi_request_cgi(struct magi_request * request); +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(struct magi_request * request); +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(struct magi_response * response); +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(enum magi_error error); +int magi_error_cgi(magi_error error); #endif diff --git a/src/cookie.h b/src/cookie.h index 82c914e..7eb3e9d 100644 --- a/src/cookie.h +++ b/src/cookie.h @@ -2,31 +2,31 @@ #define MAGI_INCLUDED_COOKIE -struct magi_cookie { +typedef struct magi_cookie { /* All pointers must be valid as 'free' arguments. */ - char * name; - char * data; - char * path; - char * domain; - char * port; -}; + 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; -struct magi_cookie_list { - struct magi_cookie_list * next; /* Must be valid as 'free' arguments. */ - struct magi_cookie item; -}; +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(struct magi_cookie_list ** list, - struct magi_cookie * item); +int magi_cookie_list_add(magi_cookie_list **list, magi_cookie *item); -/* First node in list: node.name == name; else null. */ -struct magi_cookie * magi_cookie_list_get(struct magi_cookie_list * list, - const char * name); +/* 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(struct magi_cookie_list * list); +void magi_cookie_list_destroy(magi_cookie_list *list); #endif diff --git a/src/cookies.c b/src/cookies.c deleted file mode 100644 index 4cbe659..0000000 --- a/src/cookies.c +++ /dev/null @@ -1,296 +0,0 @@ -/* * * TODO * * */ -#include "cookies.h" - -#include "utils.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, dt_port }; - -struct automata { - struct magi_cookie_list ** list; - struct magi_cookie cookie; - char * buf; - int buf_len; - int buf_size; - int is_first; - int is_cookie2; - 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.port = 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_cookie2) { - if (!strcmp(a->buf, "$Path")) { - data_t = dt_path; - } else if (!strcmp(a->buf, "$Domain")) { - data_t = dt_domain; - } else if (!strcmp(a->buf, "$Port")) { - data_t = dt_port; - } - } - 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_port: - a->cookie.port = 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.cookie.port); - free(a.buf); -} diff --git a/src/cookies.h b/src/cookies.h deleted file mode 100644 index 98b2ada..0000000 --- a/src/cookies.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef MAGI_INCLUDED_COOKIES -#define MAGI_INCLUDED_COOKIES - -#include "request.h" - - -void magi_cookies(struct magi_request * request, const char * data); - - -#endif diff --git a/src/error.h b/src/error.h index f8f0aed..4a91962 100644 --- a/src/error.h +++ b/src/error.h @@ -2,7 +2,7 @@ #define MAGI_INCLUDED_ERROR -enum magi_error { +typedef enum magi_error { magi_error_none = 0, magi_error_nobound, magi_error_unknown, @@ -13,9 +13,9 @@ enum magi_error { magi_error_urlenc, magi_error_multipart, magi_error_limit -}; +} magi_error; -const char * magi_error_message(enum magi_error error); +const char *magi_error_message(magi_error error); #endif diff --git a/src/file.h b/src/file.h index 887bd31..4705031 100644 --- a/src/file.h +++ b/src/file.h @@ -4,28 +4,27 @@ #include "param.h" -struct magi_file { +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 */ - struct magi_param_list * params; /* Multipart params (e.g. type) */ -}; + 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; -struct magi_file_list { - struct magi_file_list * next; /* Must be valid as 'free' argument. */ - struct magi_file item; -}; +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(struct magi_file_list ** list, struct magi_file * item); +int magi_file_list_add(magi_file_list **list, magi_file *item); /* First node in list: node.param_name == name; else null. */ -struct magi_file * magi_file_list_get(struct magi_file_list * list, - const char * name); +magi_file *magi_file_list_get(magi_file_list *list, const char *name); /* Freeing and invalidation of list. */ -void magi_file_list_destroy(struct magi_file_list * list); +void magi_file_list_destroy(magi_file_list *list); #endif diff --git a/src/inner_cookies.c b/src/inner_cookies.c new file mode 100644 index 0000000..d9035c4 --- /dev/null +++ b/src/inner_cookies.c @@ -0,0 +1,296 @@ +/* * * TODO * * */ +#include "inner_cookies.h" + +#include "utils.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, dt_port }; + +struct automata { + struct magi_cookie_list ** list; + struct magi_cookie cookie; + char * buf; + int buf_len; + int buf_size; + int is_first; + int is_cookie2; + 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.port = 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_cookie2) { + if (!strcmp(a->buf, "$Path")) { + data_t = dt_path; + } else if (!strcmp(a->buf, "$Domain")) { + data_t = dt_domain; + } else if (!strcmp(a->buf, "$Port")) { + data_t = dt_port; + } + } + 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_port: + a->cookie.port = 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.cookie.port); + free(a.buf); +} diff --git a/src/inner_cookies.h b/src/inner_cookies.h new file mode 100644 index 0000000..c336cf7 --- /dev/null +++ b/src/inner_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/inner_multipart.c b/src/inner_multipart.c new file mode 100644 index 0000000..65252e5 --- /dev/null +++ b/src/inner_multipart.c @@ -0,0 +1,479 @@ +/* Support for multifile controls are not provided. */ +#include "inner_multipart.h" + +#include "error.h" +#include "param.h" +#include "utils.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, strchr(n, '"')); + } else { + return magi_str_create_copy(n, 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, strchr(n, '"')); + if (!a->param.name || !*a->param.name) { + return 0; + } + } else { + a->param.name = magi_str_create_copy(n, 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 new file mode 100644 index 0000000..c3f7b65 --- /dev/null +++ b/src/inner_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/inner_urlencoded.c b/src/inner_urlencoded.c new file mode 100644 index 0000000..e3ea56a --- /dev/null +++ b/src/inner_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/inner_urlencoded.h b/src/inner_urlencoded.h new file mode 100644 index 0000000..e07ed6f --- /dev/null +++ b/src/inner_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 diff --git a/src/magi.h b/src/magi.h new file mode 100644 index 0000000..b7df63c --- /dev/null +++ b/src/magi.h @@ -0,0 +1,16 @@ +#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 deleted file mode 100644 index 7b829d8..0000000 --- a/src/multipart.c +++ /dev/null @@ -1,479 +0,0 @@ -/* Support for multifile controls are not provided. */ -#include "multipart.h" - -#include "error.h" -#include "param.h" -#include "utils.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, strchr(n, '"')); - } else { - return magi_str_create_copy(n, 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, strchr(n, '"')); - if (!a->param.name || !*a->param.name) { - return 0; - } - } else { - a->param.name = magi_str_create_copy(n, 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 deleted file mode 100644 index 47cfefe..0000000 --- a/src/multipart.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef MAGI_INCLUDED_MULTIPART -#define MAGI_INCLUDED_MULTIPART - -#include "request.h" - - -void magi_multipart(struct magi_request * request, - char * boundary, - int (*next)(void * userdata), - void * next_userdata); - - -#endif diff --git a/src/param.h b/src/param.h index 3f8cefe..bebacd7 100644 --- a/src/param.h +++ b/src/param.h @@ -2,27 +2,27 @@ #define MAGI_INCLUDED_PARAM -struct magi_param { +typedef struct magi_param { /* All pointers must be valid as 'free' arguments. */ - char * name; - char * data; -}; + char *name; + char *data; +} magi_param; -struct magi_param_list { - struct magi_param_list * next; /* Must be valid as 'free' argument. */ - struct magi_param item; -}; +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(struct magi_param_list ** list, - struct magi_param * item); +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(struct magi_param_list * list, const char * name); +char *magi_param_list_get(magi_param_list *list, const char *name); /* Freeing and invalidation of list. */ -void magi_param_list_destroy(struct magi_param_list * list); +void magi_param_list_destroy(magi_param_list *list); #endif diff --git a/src/request.h b/src/request.h index be92370..ffa239f 100644 --- a/src/request.h +++ b/src/request.h @@ -27,75 +27,91 @@ * document_uri: {script_name}{path_info} * /bin/script /article/magic */ -struct magi_request { +typedef struct magi_request { /* * * Results * * */ /* All pointers of this section must be valid as 'free' arguments. */ /* Parsed */ - struct magi_cookie_list * cookies; - struct magi_param_list * http_params; /* HTTP Header parameters */ - struct magi_param_list * url_params; /* Urlencoded paramteres from URL */ - struct magi_param_list * params; /* Parameters from 'post' body */ - struct magi_file_list * files; /* 'Post' multipart files */ + 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 */ + 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 */ + char *server_port; /* SERVER_PORT */ + char *server_protocol; /* SERVER_PROTOCOL */ + char *server_software; /* SERVER_COFTWARE */ + char *path_info; /* PATH_INFO */ /* Request Error Code */ - enum magi_error error; + magi_error error; /* * * Settings * * */ /* Callback for processing files */ - void (*file_callback)(struct magi_file * file, - char * addon, - int addon_len, - int is_addon_last, - void * userdata); - void * file_callback_userdata; - int file_callback_addon_max; + 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(struct magi_request * request); - -struct magi_tempfiles { - int count; - const char ** param_names; - const char ** locations; - int * maximums; /* Null maximums[i] <=> unlimited tempfiles[i]. */ -}; -void magi_tempfiles_add(struct magi_tempfiles * tmps, - const char * name, - const char * path, - int max); +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(struct magi_request * request, - struct magi_tempfiles * table); +void magi_request_setup_tempfiles(magi_request *request, + magi_tempfiles *table); /* Destroys request. */ void magi_request_destroy(struct magi_request * request); diff --git a/src/response.c b/src/response.c index 7e2ec0b..e4cd342 100644 --- a/src/response.c +++ b/src/response.c @@ -6,33 +6,33 @@ #include #include -int vsnprintf(char * s, size_t n, const char * format, va_list arg); +/* 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(struct magi_response * response) +void magi_response_setup(magi_response *response) { response->cookies = 0; response->http_params = 0; response->content_type = 0; + magi_response_content_type(response, "application/xhtml+xml"); response->content = magi_str_create(0); response->len = 0; response->size = 1; } - -void magi_response_content_type(struct magi_response * response, - enum magi_content_type type) +void magi_response_content_type(magi_response *response, const char *type) { - const char * const messages[] = { - "Content-Type: application/xhtml+xml", /* magi_xhtml */ - }; - if (!response->content_type) { - const char * end = messages[type] + strlen(messages[type]); - response->content_type = magi_str_create_copy(messages[type], end); - } + static const char *const ct = "Content-Type: "; + static const int ctlen = 15; + const int len = strlen(type); + free(response->content_type); + response->content_type = malloc(ctlen + len + 1); + memcpy(response->content_type, ct, ctlen); + memcpy(response->content_type + ctlen, type, len + 1); } -void magi_response_add(struct magi_response * r, const char * addon) +void magi_response_add(magi_response *r, const char *addon) { int addon_len; if (!addon) { @@ -46,11 +46,9 @@ void magi_response_add(struct magi_response * r, const char * addon) r->len += addon_len; } -void magi_response_add_format(struct magi_response * response, - const char * addon, - ...) +void magi_response_add_format(magi_response *response, const char *addon, ...) { - char * act_addon; + char *act_addon; int n; va_list args; va_start(args, addon); @@ -66,34 +64,36 @@ void magi_response_add_format(struct magi_response * response, } } -void magi_response_cookie(struct magi_response * response, - struct magi_cookie * cookie) +void magi_response_cookie(magi_response *response, magi_cookie *cookie) { magi_cookie_list_add(&response->cookies, cookie); } -void magi_response_cookie_easy(struct magi_response * response, - const char * name, - const char * value) +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) { - struct magi_cookie cookie = { 0, 0, 0, 0, 0 }; - cookie.name = magi_str_create_copy(name, name + strlen(name)); - cookie.data = magi_str_create_copy(value, value + strlen(value)); + magi_cookie cookie = { 0, 0, 0, 0, 0 }; + cookie.name = magi_str_create_copy(name, strlen(name)); + cookie.max_age = magi_str_create(1); + cookie.max_age[0] = '0'; magi_cookie_list_add(&response->cookies, &cookie); } -void magi_response_http(struct magi_response * response, - const char * name, - const char * data) +void magi_response_http(magi_response *response, + const char *name, { - struct magi_param param = { 0, 0 }; - param.name = magi_str_create_copy(name, name + strlen(name)); - param.data = magi_str_create_copy(data, data + strlen(data)); + magi_param param = { 0, 0 }; + param.name = magi_str_create_copy(name, strlen(name)); + param.data = magi_str_create_copy(data, strlen(data)); magi_param_list_add(&response->http_params, ¶m); } -void magi_response_destroy(struct magi_response * response) +void magi_response_destroy(magi_response *response) { if (!response) { return; diff --git a/src/response.h b/src/response.h index 95f6537..a51d4a4 100644 --- a/src/response.h +++ b/src/response.h @@ -5,42 +5,39 @@ #include "param.h" -enum magi_content_type { magi_xhtml = 0 }; +typedef struct magi_response { + magi_cookie_list *cookies; + magi_param_list *http_params; + char *content_type; + char *content; + int len; + int size; +} magi_response; -struct magi_response { - struct magi_cookie_list * cookies; - struct magi_param_list * http_params; - char * content_type; - char * content; - int len; - int size; -}; +void magi_response_setup(magi_response *response); -void magi_response_setup(struct magi_response * response); +void magi_response_content_type(magi_response *response, const char *type); -void magi_response_content_type(struct magi_response * response, - enum magi_content_type 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_add(struct magi_response * response, const char * addon); -void magi_response_add_format(struct magi_response * response, - const char * addon, - ...); +void magi_response_cookie(magi_response *response, magi_cookie *cookie); -void magi_response_cookie(struct magi_response * response, - struct magi_cookie * cookie); +void magi_response_cookie_easy(magi_response *response, + const char *name, + const char *value); -void magi_response_cookie_easy(struct magi_response * response, - const char * name, - const char * value); +void magi_response_cookie_discard(magi_response *response, + const char *name); -void magi_response_http(struct magi_response * response, - const char * name, - const char * data); +void magi_response_http(magi_response *response, + const char *name, + const char *data); -void magi_response_destroy(struct magi_response * response); +void magi_response_destroy(magi_response *response); #endif diff --git a/src/urlenc.c b/src/urlenc.c index 8c38a0a..bb0bada 100644 --- a/src/urlenc.c +++ b/src/urlenc.c @@ -2,23 +2,12 @@ #include "utils.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; - } -} - /* Call only if 0 <= num && num <= 15. */ static char to_hex(int num) { @@ -26,136 +15,12 @@ static char to_hex(int num) return hex[num]; } -static int is_hex(char c) -{ - return isdigit(c) || strchr("ABCDEF", toupper(c)); -} - static int is_url(char c) { return isalnum(c) || strchr("$-_.+ !*'(),", 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_urlenc(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; - } -} - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * URL Encoding */ diff --git a/src/urlenc.h b/src/urlenc.h index 5240109..edaec08 100644 --- a/src/urlenc.h +++ b/src/urlenc.h @@ -1,17 +1,10 @@ #ifndef MAGI_INCLUDED_URLENC #define MAGI_INCLUDED_URLENC -#include "request.h" - -/* Fills request->url_params via parsing encoded data. */ -void magi_urlenc(struct magi_param_list ** list, - struct magi_request * request, - const char * encoded); - -/* 'code' from urlencode must have size of at least magi_urlenc_size(plain). */ -int magi_urlenc_size(const char * plain); -void magi_urlencode(const char * plain, char * code); +/* '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 -- cgit v1.2.3