From b08db17162fddda97e6ee1ac625eae1430d22b64 Mon Sep 17 00:00:00 2001 From: Aleksey Veresov Date: Fri, 29 Nov 2019 18:50:04 +0300 Subject: [magi] Almost done! --- Makefile | 5 - examples/append.c | 17 +- examples/cookie.c | 4 +- examples/echo.c | 19 ++- examples/fcgi.c | 1 + examples/upload.c | 43 ++--- src/cgi.c | 289 +++++++++++++++++++++------------ src/cookie.c | 2 +- src/cookies.c | 117 +++++-------- src/cookies.h | 2 +- src/error.c | 9 +- src/error.h | 3 +- src/fastcgi.h | 4 +- src/file.c | 2 +- src/file.h | 2 + src/multipart.c | 478 +++++++++++++++++++++++++----------------------------- src/param.c | 4 +- src/request.c | 40 ++++- src/request.h | 4 + src/response.c | 107 ++++++++++++ src/response.h | 29 ++-- src/urlenc.c | 240 +++++++++++++-------------- src/urlenc.h | 6 +- src/utils.c | 15 +- src/utils.h | 8 +- 25 files changed, 811 insertions(+), 639 deletions(-) diff --git a/Makefile b/Makefile index 9585f98..1934910 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,5 @@ # Debug mode: # DEBUG = yes -# Error logging (writes to stderr): -# ERRLOG = yes CC = gcc LIB = libmagi.a @@ -12,9 +10,6 @@ CFLAGS += -g -O0 else CFLAGS += -O3 endif -ifeq '$(ERRLOG)' 'yes' -CFLAGS += -D ERRLOG -endif SRC_DIR = src SRC = $(wildcard $(SRC_DIR)/*.c) diff --git a/examples/append.c b/examples/append.c index 3320fe0..e6e4a8a 100644 --- a/examples/append.c +++ b/examples/append.c @@ -7,6 +7,13 @@ void response_request(struct magi_request * req, struct magi_response * res) { + char * data = magi_param_list_get(req->params, "addon"); + if (data) { + FILE * file = fopen("file_to_append", "a"); + fputs(data, file); + fclose(file); + } + magi_response_content_type(res, magi_xhtml); magi_response_add( res, @@ -15,19 +22,12 @@ void response_request(struct magi_request * req, struct magi_response * res) "" "Append to File" "" - "
" + "
" "" "" "
" "" ""); - - struct magi_param * addon = magi_param_list_get(req->url_params, "addon"); - if (addon && addon->data) { - FILE * file = fopen("file_to_append", "a"); - fputs(addon->data, file); - fclose(file); - } } int main(int argc, char const * argv[]) @@ -36,6 +36,7 @@ int main(int argc, char const * argv[]) magi_request_setup(&request); if (magi_request_cgi(&request)) { struct magi_response response; + magi_response_setup(&response); response_request(&request, &response); magi_response_cgi(&response); magi_response_destroy(&response); diff --git a/examples/cookie.c b/examples/cookie.c index 01d6dea..f21cb4a 100644 --- a/examples/cookie.c +++ b/examples/cookie.c @@ -26,7 +26,6 @@ void response_request(struct magi_request * req, struct magi_response * res) magi_response_add(res, ""); magi_response_cookie_easy(res, "cookie", "monster"); - magi_response_cookie_delete(res, "monster"); } int main(int argc, char const * argv[]) @@ -35,9 +34,10 @@ int main(int argc, char const * argv[]) magi_request_setup(&request); if (magi_request_cgi(&request)) { struct magi_response response; + magi_response_setup(&response); response_request(&request, &response); magi_response_cgi(&response); - magi_response_destroy(); + magi_response_destroy(&response); } else { magi_error_cgi(request.error); } diff --git a/examples/echo.c b/examples/echo.c index 95219fc..3f72f8a 100644 --- a/examples/echo.c +++ b/examples/echo.c @@ -47,11 +47,23 @@ void proceed_params(struct magi_param_list * params, magi_response_add(response, "
"); } +void proceed_files(struct magi_file_list * files, + struct magi_response * response) +{ + while (files) { + magi_response_add_format(response, "[%s] was [%s] on userside
", + files->item.param_name, + files->item.file_name); + files = files->next; + } + magi_response_add(response, "
"); +} + void process_meta(struct magi_request * req, struct magi_response * res) { magi_response_add(res, - "

Echo CGI Script

I was called with method [%s", - req->method); + "

Echo CGI Script

I was called with method ["); + magi_response_add(res, req->method); if (req->uri) { magi_response_add(res, "] with URL ["); magi_response_add(res, req->uri); @@ -92,6 +104,8 @@ void response_request(struct magi_request * req, struct magi_response * res) proceed_params(req->url_params, res); magi_response_add(res, "

HTTP Parameters:

"); proceed_params(req->http_params, res); + magi_response_add(res, "

Files:

"); + proceed_files(req->files, res); magi_response_add(res, ""); } @@ -101,6 +115,7 @@ int main(int argc, char const * argv[]) magi_request_setup(&request); if (magi_request_cgi(&request)) { struct magi_response response; + magi_response_setup(&response); response_request(&request, &response); magi_response_cgi(&response); magi_response_destroy(&response); diff --git a/examples/fcgi.c b/examples/fcgi.c index 7d93614..06384c4 100644 --- a/examples/fcgi.c +++ b/examples/fcgi.c @@ -30,6 +30,7 @@ int main(int argc, char const * argv[]) while (magi_request_fcgi(&request, &session)) { if (!request.error) { struct magi_response response; + magi_response_setup(&response); response_request(&request, &response); magi_response_fcgi(&response, &session); magi_response_destroy(&response); diff --git a/examples/upload.c b/examples/upload.c index 25fefaf..f4350ee 100644 --- a/examples/upload.c +++ b/examples/upload.c @@ -7,30 +7,14 @@ #include -void tempfile_callback(struct magi_file * file, - char * addon, - int addon_len, - int is_addon_last, - void * unused) +void response_request(struct magi_request * req, struct magi_response * res) { - if (!strcmp(file->param_name, "data")) { - static FILE * f = 0; - if (!f) { - remove(file->param_name); - f = fopen(file->param_name, "wb"); - } - if (addon_len) { - fwrite(addon, 1, addon_len, f); - } - if (is_addon_last) { - fclose(f); - f = 0; - } + char * name = magi_param_list_get(req->params, "name"); + struct magi_file * data = magi_file_list_get(req->files, "data"); + if (name && data) { + rename("data", name); } -} -void response_request(struct magi_request * req, struct magi_response * res) -{ magi_response_content_type(res, magi_xhtml); magi_response_add( res, "" "Upload File" "" - "
" "" "" @@ -46,24 +30,21 @@ void response_request(struct magi_request * req, struct magi_response * res) "
" "" ""); - - struct magi_param * name = magi_param_list_get(req->params, "name"); - struct magi_param * data = magi_param_list_get(req->params, "data"); - if (name && name->data && data) { - rename("data", name->data); - } } int main(int argc, char const * argv[]) { - struct magi_request request; + struct magi_request request; + struct magi_tempfiles tmps = { 0, 0, 0, 0 }; magi_request_setup(&request); - request.file_callback = tempfile_callback; + magi_tempfiles_add(&tmps, "data", "data", 0); + magi_request_setup_tempfiles(&request, &tmps); if (magi_request_cgi(&request)) { struct magi_response response; + magi_response_setup(&response); response_request(&request, &response); magi_response_cgi(&response); - magi_response_destroy(); + magi_response_destroy(&response); } else { magi_error_cgi(request.error); } diff --git a/src/cgi.c b/src/cgi.c index 48b2a93..d410873 100644 --- a/src/cgi.c +++ b/src/cgi.c @@ -1,12 +1,14 @@ #include "cgi.h" #include "cookie.h" +#include "cookies.h" #include "error.h" #include "file.h" #include "multipart.h" #include "param.h" #include "request.h" #include "urlenc.h" +#include "utils.h" #include #include #include @@ -20,31 +22,30 @@ extern char ** environ; */ static int plain_env(char ** dest, char * env_name) { - int ok = 1; const char * env = getenv(env_name); if (env) { - *dest = str_alloc(strlen(env)); - if (*dest) { - strcpy(*dest, env); - } else { - ok = 0; + *dest = magi_str_create(strlen(env)); + if (!*dest) { + return 0; } + strcpy(*dest, env); } else { *dest = 0; } - return ok; + return 1; } static int lower_env(char ** dest, char * env_name) { - int ok = plain_env(dest, env_name); - lowercase(*dest); - return ok; + if (plain_env(dest, env_name)) { + magi_str_lowercase(*dest); + return 1; + } + return 0; } static int cgi_http_env(struct magi_request * r) { - int ok = 1; char ** env = environ; r->http_params = 0; while (*env) { @@ -52,87 +53,110 @@ static int cgi_http_env(struct magi_request * r) struct magi_param param; /* At least one '=' must be in *env, according to format. */ char * name_end = strchr(*env, '='); - param.name = str_alloc(name_end - *env - 5); + param.name = magi_str_create(name_end - *env - 5); if (param.name) { memcpy(param.name, *env + 5, name_end - *env - 5); - param.data = str_alloc(strlen(name_end + 1)); + param.data = magi_str_create(strlen(name_end + 1)); if (param.data) { strcpy(param.data, name_end + 1); } else { free(param.name); } } - if (param.name && param.data) { - ok = magi_param_list_add(&r->http_params, ¶m); - } else { - ok = 0; + if (!param.name || !param.data || + !magi_param_list_add(&r->http_params, ¶m)) { + r->error = magi_error_memory; + return 0; } } ++env; } - return ok; + return 1; } -static void cgi_env(enum magi_error * e, struct magi_request * r) +static void cgi_env(struct magi_request * r) { - cgi_http_env(e, r); - lower_env(e, &r->method, "REQUEST_METHOD"); - plain_env(e, &r->uri, "REQUEST_URI"); - plain_env(e, &r->document_root, "DOCUMENT_ROOT"); - plain_env(e, &r->document_uri, "DOCUMENT_URI"); - plain_env(e, &r->script_name, "SCRIPT_NAME"); - plain_env(e, &r->script_filename, "SCRIPT_FILENAME"); - plain_env(e, &r->remote_addr, "REMOTE_ADDR"); - plain_env(e, &r->remote_port, "REMOTE_PORT"); - plain_env(e, &r->server_addr, "SERVER_ADDR"); - lower_env(e, &r->server_name, "SERVER_NAME"); - plain_env(e, &r->server_port, "SERVER_PORT"); - lower_env(e, &r->server_protocol, "SERVER_PROTOCOL"); - plain_env(e, &r->server_software, "SERVER_SOFTWARE"); - plain_env(e, &r->path_info, "PATH_INFO"); + if (!cgi_http_env(r) || !lower_env(&r->method, "REQUEST_METHOD") || + !plain_env(&r->uri, "REQUEST_URI") || + !plain_env(&r->document_root, "DOCUMENT_ROOT") || + !plain_env(&r->document_uri, "DOCUMENT_URI") || + !plain_env(&r->script_name, "SCRIPT_NAME") || + !plain_env(&r->script_filename, "SCRIPT_FILENAME") || + !plain_env(&r->remote_addr, "REMOTE_ADDR") || + !plain_env(&r->remote_port, "REMOTE_PORT") || + !plain_env(&r->server_addr, "SERVER_ADDR") || + !lower_env(&r->server_name, "SERVER_NAME") || + !plain_env(&r->server_port, "SERVER_PORT") || + !lower_env(&r->server_protocol, "SERVER_PROTOCOL") || + !plain_env(&r->server_software, "SERVER_SOFTWARE") || + !plain_env(&r->path_info, "PATH_INFO")) { + r->error = magi_error_memory; + } } -static void cgi_cookies(enum magi_error * e, struct magi_cookie_list ** list) +static int cgi_cookies(struct magi_request * r) { - if (!*e) { - const char * env = getenv("HTTP_COOKIE"); - *list = 0; - if (env && *env) { - magi_parse_cookie(e, list, env); - } else { - *list = 0; - } + const char * env = getenv("HTTP_COOKIE"); + if (!env || !*env) { + r->cookies = 0; + return 1; + } + if (strlen(env) < r->cookies_max && r->cookies_max) { + magi_cookies(r, env); + } else { + r->error = magi_error_limit; } + return r->error; } -static int cgi_input_get(char ** input) +static void cgi_input_get(enum magi_error * e, char ** input, int max) { - int ok = 1; const char * env_input = getenv("QUERY_STRING"); if (env_input) { - *input = str_alloc(strlen(env_input)); - if (*input) { - strcpy(*input, env_input); - } else { - ok = 0; + int len = strlen(env_input); + if (len >= max && max) { + *e = magi_error_limit; + return; + } + *input = magi_str_create(len); + if (!*input) { + *e = magi_error_memory; + return; } + strcpy(*input, env_input); } - return ok; +} + +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); + } + free(in); } static void cgi_input_post(enum magi_error * e, char ** input, int max) { if (!*e) { int input_len = strtoul(getenv("CONTENT_LENGTH"), 0, 10); - if (input_len && (input_len < max || !max)) { - *input = str_alloc(input_len); - if (*input) { - if (fread(*input, 1, input_len, stdin) != input_len) { - *e = magi_error_length; - } - } else { - *e = magi_error_input; - } + if (!input_len) { + *e = magi_error_length; + return; + } + if (input_len >= max && max) { + *e = magi_error_limit; + return; + } + *input = magi_str_create(input_len); + if (!*input) { + *e = magi_error_memory; + return; + } + if (fread(*input, 1, input_len, stdin) != input_len) { + *e = magi_error_length; + return; } } } @@ -145,67 +169,132 @@ static char * bound(const char * type) type += strspn(type, " \t") + 1; if (*type == '"') { ++type; - res = create_str(type, strchr(type, '"')); + res = magi_str_create_copy(type, strchr(type, '"')); } else { - res = create_str(type, type + strcspn(type, " \t")); + res = magi_str_create_copy(type, type + strcspn(type, " \t")); } } return res; } -static int intput_getter(void * any) +static int next(void * any) { return getchar(); } /* Interfacial CGI Request Handling */ -int magi_request_cgi(struct magi_request * request, - void (*callback)(struct magi_field * field, - char * buffer, - int len), - int max_post) +int magi_request_cgi(struct magi_request * request) { - request->fields = 0; - request->error = magi_error_none; - enum magi_error * e = &request->error; - cgi_env(e, request); - cgi_cookies(e, &request->cookies); - if (request->method) { - if (!strcmp(request->method, "post")) { - const char * t = getenv("CONTENT_TYPE"); - if (t) { - if (!strncmp(t, "multipart/form-data", 19)) { - char * boundary = bound(t); - if (boundary && *boundary) { - magi_parse_multipart(e, &request->fields, - intput_getter, 0, boundary, - callback); - } else { - *e = magi_error_nobound; - } - free(boundary); - } else if (!strcmp(t, "application/x-www-form-urlencoded")) { - char * in = 0; - cgi_input_post(e, &in, max_post); - magi_parse_urlencoded(e, &request->fields, in); - free(in); - } else { - *e = magi_error_unknown; - } + enum magi_error * e = &request->error; + request->files = 0; + request->params = 0; + request->url_params = 0; + request->http_params = 0; + request->error = magi_error_none; + cgi_env(request); + cgi_cookies(request); + cgi_url(request); + if (request->method && !strcmp(request->method, "post")) { + const char * t = getenv("CONTENT_TYPE"); + if (!t) { + *e = magi_error_notype; + return 0; + } + if (!strncmp(t, "multipart/form-data", 19)) { + char * boundary = bound(t); + if (boundary && *boundary) { + magi_multipart(request, boundary, next, 0); } else { - *e = magi_error_notype; + *e = magi_error_nobound; } - } else if (!strcmp(request->method, "get")) { + free(boundary); + } else if (!strcmp(t, "application/x-www-form-urlencoded")) { char * in = 0; - cgi_input_get(e, &in); - magi_parse_urlencoded(e, &request->fields, in); + cgi_input_post(e, &in, request->params_max); + if (!*e) { + magi_urlenc(&request->params, request, in); + } free(in); + } else { + *e = magi_error_unknown; + return 0; } } - return request->error == magi_error_none; + return !request->error; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * CGI Response */ +void output_http_params(struct magi_param_list * list) +{ + while (list) { + fputs(list->item.name, stdout); + fputs(": ", stdout); + fputs(list->item.data, stdout); + fputs("\r\n", stdout); + list = list->next; + } +} + +void output_cookies(struct magi_cookie_list * list) +{ + while (list) { + fputs("Set-Cookie: ", stdout); + fputs(list->item.name, stdout); + fputs("=", stdout); + fputs(list->item.data, stdout); + if (list->item.path) { + fputs("; Path=", stdout); + fputs(list->item.path, stdout); + } + if (list->item.domain) { + fputs("; Domain=", stdout); + fputs(list->item.domain, stdout); + } + if (list->item.port) { + fputs("; Port=", stdout); + fputs(list->item.port, stdout); + } + fputs("\r\n", stdout); + list = list->next; + } +} + +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_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, + "" + "" + "400 Bad Request" + "" + "

400 Bad Request

" + "

%s

" + "" + "", + magi_error_message(error)); + if (magi_response_cgi(&res)) { + magi_response_destroy(&res); + return 1; + } else { + magi_response_destroy(&res); + return 0; + } +} diff --git a/src/cookie.c b/src/cookie.c index 506e4ef..eaa221f 100644 --- a/src/cookie.c +++ b/src/cookie.c @@ -13,7 +13,7 @@ int magi_cookie_list_add(struct magi_cookie_list ** list, node->item = *item; *list = node; } - return node; + return !!node; } struct magi_cookie * magi_cookie_list_get(struct magi_cookie_list * list, diff --git a/src/cookies.c b/src/cookies.c index f690ccc..4cbe659 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -1,5 +1,8 @@ +/* * * TODO * * */ #include "cookies.h" +#include "utils.h" +#include #include @@ -27,6 +30,7 @@ struct automata { enum data_type data_t; }; + static void nulify_cookie(struct automata * a) { a->cookie.name = 0; @@ -43,23 +47,6 @@ static void buf_new(struct automata * a) a->buf_size = 1; } -static int buf_add(struct automata * a, char c) -{ - int ok = 1; - if (a->buf_len == a->buf_size - 1) { - a->buf_size *= 2; - a->buf = realloc(a->buf, a->buf_size); - } - if (a->buf) { - a->buf_len++; - a->buf[a->buf_len - 1] = c; - a->buf[a->buf_len] = 0; - } else { - ok = 0; - magi_error_set("[cookie] Cannot allocate automata buffer."); - } - return ok; -} static enum data_type what_is_name(const struct automata * a) { @@ -78,6 +65,7 @@ static enum data_type what_is_name(const struct automata * a) return data_t; } + static int end_name(struct automata * a) { int ok = 1; @@ -114,14 +102,13 @@ static int end_data(struct automata * a) case dt_version: if (strcmp(a->buf, "1")) { ok = 0; - magi_error_set("[cookie] Version must be '1', readed: %s.", - a->buf); } } buf_new(a); return ok; } + static enum st parse_pre_name(struct automata * a, char c) { enum st state; @@ -129,12 +116,11 @@ static enum st parse_pre_name(struct automata * a, char c) state = st_name; } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { state = st_name; - if (!buf_add(a, c)) { + if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { state = st_error; } } else { state = st_error; - magi_error_set("[cookie] Pre-name, readed: \\%o (render: %c).", c, c); } return state; } @@ -154,13 +140,11 @@ static enum st parse_name(struct automata * a, char c) } } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { state = st_name; - if (!buf_add(a, c)) { + if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { state = st_error; } } else { state = st_error; - magi_error_set("[cookie] Reading name, readed: \\%o (render: %c).", c, - c); } return state; } @@ -174,9 +158,6 @@ static enum st parse_post_name(struct automata * a, char c) state = st_post_name; } else { state = st_error; - magi_error_set("[cookie] Waiting for name-value separator, " - "readed: \\%o (render: %c).", - c, c); } return state; } @@ -191,12 +172,11 @@ static enum st parse_pre_data(struct automata * a, char c) a->is_quoted = 1; } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { state = st_data; - if (!buf_add(a, c)) { + if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { state = st_error; } } else { state = st_error; - magi_error_set("[cookie] Pre-value, readed: \\%o (render: %c).", c, c); } return state; } @@ -217,14 +197,11 @@ static enum st parse_not_quoted_data(struct automata * a, char c) } } else if (32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={}", c)) { state = st_data; - if (!buf_add(a, c)) { + if (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) { state = st_error; } } else { state = st_error; - magi_error_set("[cookie] Reading not-quoted value, " - "readed: \\%o (render: %c).", - c, c); } return state; } @@ -257,73 +234,63 @@ static enum st parse_post_data(struct automata * a, char c) state = st_post_data; } else { state = st_error; - magi_error_set( - "[cookie] Waiting for separator between name-value pairs, " - "readed: \\%o (render: %c).", - c, c); } return state; } -static int parse_end(struct automata * a, enum st s) + +static void parse_end(enum magi_error * e, struct automata * a, enum st s) { - int ok = 0; - if (s == st_post_data) { - ok = 1; - } else if (s == st_data) { - if (!a->is_quoted) { - if (a->cookie.name) { - if (end_data(a) && magi_cookie_list_add(a->list, &a->cookie)) { - ok = 1; - nulify_cookie(a); - buf_new(a); - } - } else { - magi_error_set("[cookie] No cookies set."); - } + 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 { - magi_error_set("[cookie] In quotation when reached input end."); + *e = magi_error_cookies; } - } else if (s != st_error) { - magi_error_set("[cookie] Input ended in not correct state."); + } else if (s != st_post_data) { + *e = magi_error_cookies; } - free(a->cookie.name); - free(a->cookie.data); - free(a->cookie.path); - free(a->cookie.domain); - free(a->cookie.port); - free(a->buf); - return ok; } -int magi_parse_cookie(struct magi_cookie_list ** list, const char * input) + +void magi_cookies(struct magi_request * request, const char * data) { - struct automata a = { 0, { 0, 0, 0, 0, 0 }, 0, 0, 1, 1, 0, 0, 0 }; - enum st state = st_pre_name; - a.list = list; - while (*input && state) { - char c = *input++; + 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, c); + state = parse_pre_name(&a, *data); break; case st_name: - state = parse_name(&a, c); + state = parse_name(&a, *data); break; case st_post_name: - state = parse_post_name(&a, c); + state = parse_post_name(&a, *data); break; case st_pre_data: - state = parse_pre_data(&a, c); + state = parse_pre_data(&a, *data); break; case st_data: - state = parse_data(&a, c); + state = parse_data(&a, *data); break; case st_post_data: - state = parse_post_data(&a, c); + state = parse_post_data(&a, *data); default: break; } } - return parse_end(&a, state); + 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 index 3da16e7..98b2ada 100644 --- a/src/cookies.h +++ b/src/cookies.h @@ -1,7 +1,7 @@ #ifndef MAGI_INCLUDED_COOKIES #define MAGI_INCLUDED_COOKIES -#include "cookie.h" +#include "request.h" void magi_cookies(struct magi_request * request, const char * data); diff --git a/src/error.c b/src/error.c index 48c3c36..efe6dbf 100644 --- a/src/error.c +++ b/src/error.c @@ -3,16 +3,17 @@ const char * magi_error_message(enum magi_error error) { - static const char * const messages[] = { + const char * const messages[] = { 0, /* magi_error_none */ "No boundary for multipart.", /* magi_error_nobound */ - "Content-type is unknown.", /* magi_error_unknown */ - "Content-type is not given.", /* magi_error_notype */ + "Content-Type is unknown.", /* magi_error_unknown */ + "Content-Type is not given.", /* magi_error_notype */ "Mismatch of content length.", /* magi_error_length */ "Cannot allocate memory.", /* magi_error_memory */ "Cannot read cookies.", /* magi_error_cookies */ "Cannot decode URL.", /* magi_error_urlenc */ - "Cannot read multipart body." /* magi_error_multipart */ + "Cannot read multipart body.", /* magi_error_multipart */ + "Part of request was too big." /* magi_error_limit */ }; return messages[error]; } diff --git a/src/error.h b/src/error.h index 46a07cd..f8f0aed 100644 --- a/src/error.h +++ b/src/error.h @@ -11,7 +11,8 @@ enum magi_error { magi_error_memory, magi_error_cookies, magi_error_urlenc, - magi_error_multipart + magi_error_multipart, + magi_error_limit }; const char * magi_error_message(enum magi_error error); diff --git a/src/fastcgi.h b/src/fastcgi.h index d701fe0..0abd152 100644 --- a/src/fastcgi.h +++ b/src/fastcgi.h @@ -2,7 +2,9 @@ #define MAGI_INCLUDED_FASTCGI -/* TODO */ +/* * * TODO * * */ + +enum { magi_to_avoid_warning }; #endif diff --git a/src/file.c b/src/file.c index df6c978..9bf70c4 100644 --- a/src/file.c +++ b/src/file.c @@ -12,7 +12,7 @@ int magi_file_list_add(struct magi_file_list ** list, struct magi_file * item) node->item = *item; *list = node; } - return node; + return !!node; } struct magi_file * magi_file_list_get(struct magi_file_list * list, diff --git a/src/file.h b/src/file.h index a677e2f..887bd31 100644 --- a/src/file.h +++ b/src/file.h @@ -1,6 +1,8 @@ #ifndef MAGI_INCLUDED_FILE #define MAGI_INCLUDED_FILE +#include "param.h" + struct magi_file { /* All pointers must be valid as 'free' arguments. */ diff --git a/src/multipart.c b/src/multipart.c index c3e4a2d..5d7f261 100644 --- a/src/multipart.c +++ b/src/multipart.c @@ -3,6 +3,7 @@ #include "error.h" #include "param.h" +#include "utils.h" #include #include #include @@ -43,114 +44,128 @@ enum st { }; struct automata { - struct magi_field_list ** list; - struct magi_field field; - struct magi_param param; - 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; - void (*callback)(struct magi_field * field, char * buffer, int size); + 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; }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Automata Shortcuts */ -static int content_disposition(struct automata * a) +static char * extract_filename(char * n) { - int ok = 1; - char * name = strchr(a->param.data, '='); - if (name) { - name += strspn(name, " \t") + 1; - if (*name == '"') { - ++name; - a->field.name = create_str(name, strchr(name, '"')); - if (a->field.name == 0) { - ok = 0; - } else if (a->field.name[0] == 0) { - ok = 0; - magi_error_set( - "[multipart] Wrong content-disposition quotation."); - } - } else { - a->field.name = create_str(name, name + strcspn(name, " \t")); - if (a->field.name == 0) { - ok = 0; - } else if (!is_str_token(a->field.name)) { - ok = 0; - magi_error_set( - "[multipart] Content-disposition value is not valid, " - "readed: %s.", - a->field.name); - } - } - if (ok) { - free(a->param.name); - free(a->param.data); - a->param.name = 0; - a->param.data = 0; - } + n = strchr(n, '='); + if (!n) { + return 0; + } + n += strspn(n, " \t") + 1; + if (*n == '"') { + ++n; + return magi_str_create_copy(n, strchr(n, '"')); } else { - ok = 0; - magi_error_set("[multipart] Content-disposition has no '=' symbol."); + return magi_str_create_copy(n, n + strcspn(n, " \t")); } - return ok; } -static int param_end(struct automata * a) +static int content_disposition(struct automata * a) { - int ok = 1; - lowercase(a->param.name); - if (!strcmp(a->param.name, "content-disposition")) { - ok = content_disposition(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 { - ok = magi_param_list_add(&a->field.params, &a->param); - a->param.name = 0; - a->param.data = 0; + 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; } + 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; - return ok; + 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 field_end(struct automata * a) +static int param_end(struct automata * a) { - int ok; - if (a->field.name == 0) { - ok = 0; - magi_error_set("[multipart] Field name is empty or not specified."); - } else { - if (a->callback) { - a->callback(&a->field, a->buf, a->buf_size); - a->buf_size = 0; - } - a->field.len = a->len; - ok = magi_field_list_add(a->list, &a->field); - if (!ok) { - free(a->field.name); - free(a->field.data); - magi_param_list_destroy(a->field.params); - free(a->field.params); + if (!a->param.name) { + a->request->error = magi_error_multipart; + return 0; + } + if (a->file.file_name) { + a->request->file_callback(&a->file, a->buf, a->buf_size, 1, + a->request->file_callback_userdata); + 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->field.name = 0; - a->field.data = 0; - a->field.params = 0; - a->size = 1; - a->len = 0; + a->file.file_name = 0; + a->file.param_name = 0; + a->file.params = 0; + a->size = 1; + a->len = 0; + return 1; + } + 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; } - return ok; + a->param.name = 0; + a->param.data = 0; + a->size = 1; + a->len = 0; + return 1; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Boundary interfaces + * Boundary Interfaces */ static char sepget(const struct automata * a) { @@ -214,214 +229,169 @@ static int is_semiend(const struct automata * a) } +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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->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) { - enum st state; - if (sepget(a) == 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)) { - state = st_pname_pre; - } else { - state = st_begin; + return st_pname_pre; /* Separator is completed, so move on. */ } - } else { - state = st_begin; - a->boundary_pos = 0; } - return state; + return st_begin; } static enum st parse_pname_pre(struct automata * a, char c) { - enum st state; if (a->is_CR_readed) { - a->is_CR_readed = 0; - if (c == '\n') { - state = st_data; - a->boundary_pos = 0; - } else { - state = st_error; - magi_error_set( - "[multipart] Waiting for name, CR is readed alone."); + if (c != '\n') { + return st_error; } + a->is_CR_readed = 0; + a->boundary_pos = 0; + return st_data; } else if (c == '\r') { - state = st_pname_pre; a->is_CR_readed = 1; - } else if (is_token(c)) { - if (add(&a->param.name, &a->len, &a->size, c)) { - state = st_pname; - } else { - state = st_error; - } - } else { - state = st_error; - magi_error_set( - "[multipart] Waiting for name, readed: \\%o (render: %c).", c, c); + return st_pname_pre; + } else if (is_token(c) && + magi_str_add(&a->subparam.name, &a->len, &a->size, c)) { + return st_pname; } - return state; + return st_error; } static enum st parse_pname(struct automata * a, char c) { - enum st state; if (c == ':') { - state = st_pdata; a->len = 0; a->size = 1; + return st_pdata; } else if (c == ' ' || c == '\t') { - state = st_pname_end; - } else if (is_token(c)) { - if (add(&a->param.name, &a->len, &a->size, c)) { - state = st_pname; - } else { - state = st_error; - } - } else { - state = st_error; - magi_error_set("[multipart] Reading name, readed: \\%o (render: %c).", - c, c); + return st_pname_end; + } else if (is_token(c) && + magi_str_add(&a->subparam.name, &a->len, &a->size, c)) { + return st_pname; } - return state; + return st_error; } static enum st parse_pname_end(struct automata * a, char c) { - enum st state; if (c == ':') { - state = st_pdata; a->len = 0; a->size = 1; + return st_pdata; } else if (c == ' ' || c == '\t') { - state = st_pname_end; - } else { - state = st_error; - magi_error_set("[multipart] Waiting for name-value separator, " - "readed: \\%o (render: %c).", - c, c); + return st_pname_end; } - return state; + return st_error; } static enum st parse_pdata(struct automata * a, char c) { - enum st state; if (a->is_CR_readed) { a->is_CR_readed = 0; if (c == '\n') { - if (param_end(a)) { - state = st_pname_pre; - } else { - state = st_error; + if (subparam_end(a)) { + return st_pname_pre; } - } else if (add(&a->param.data, &a->len, &a->size, '\r')) { - if (add(&a->param.data, &a->len, &a->size, c)) { - state = st_pdata; - } else { - state = st_error; - } - } else { - state = st_error; + 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') { - state = st_pdata; a->is_CR_readed = 1; - } else { - if (add(&a->param.data, &a->len, &a->size, c)) { - state = st_pdata; - } else { - state = st_error; - } + return st_pdata; + } else if (magi_str_add(&a->subparam.data, &a->len, &a->size, c)) { + return st_pdata; } - return state; -} - -static void apply_callback(struct automata * a) -{ - if (a->callback && a->buf_size == magi_parse_multipart_callback_size) { - a->callback(&a->field, a->buf, a->buf_size); - a->buf_size = 0; - } -} - -static enum st data_add(struct automata * a, char c) -{ - static int max_buf_size = magi_parse_multipart_callback_size + 1; - enum st state; - char ** dest; - int * len; - int * size; - int pos = a->boundary_pos; - state = st_data; - a->boundary_pos = 0; - if (a->callback) { - dest = &a->buf; - len = &a->buf_size; - size = &max_buf_size; - } else { - dest = &a->field.data; - len = &a->len; - size = &a->size; - } - while (a->boundary_pos < pos) { - if (a->is_end_suspected) { - add(dest, len, size, endget(a)); - } else { - add(dest, len, size, sepget(a)); - } - apply_callback(a); - a->boundary_pos++; - } - a->boundary_pos = 0; - a->is_end_suspected = 0; - if (sepget(a) == c) { - a->boundary_pos++; - if (a->boundary_pos == seplen(a)) { - state = st_pname_pre; - field_end(a); - } else { - state = st_data; - } - } else { - add(dest, len, size, c); - apply_callback(a); - } - return state; + return st_error; } static enum st parse_data(struct automata * a, char c) { - enum st state; if (a->is_end_suspected) { - if (endget(a) == c) { - a->boundary_pos++; - if (a->boundary_pos == endlen(a)) { - state = st_end; - field_end(a); - } else { - state = st_data; - } - } else { - state = data_add(a, c); + 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)) { - state = st_pname_pre; - field_end(a); - } else { - state = st_data; + param_end(a); + return st_pname_pre; } + return st_data; } else if ((a->boundary_pos == seplen(a) - 2) && endget(a) == c) { - state = st_data; a->is_end_suspected = 1; a->boundary_pos++; - } else { - state = data_add(a, c); + return st_data; } - return state; + return data_add(a, c); } static enum st parse_end(struct automata * a, char c) @@ -433,14 +403,13 @@ static enum st parse_end(struct automata * a, char c) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Automata Runner */ -static int run_automata(struct automata * a, - int (*next)(void * thing), - void * thing) +static void run_automata(struct automata * a, + int (*next)(void * next_userdata), + void * next_userdata) { - int ok = 1; enum st state = st_begin; - int c; - for (c = next(thing); state && c != EOF; c = next(thing)) { + int c = next(next_userdata); + while (state && state != st_end && c != EOF) { switch (state) { case st_begin: state = parse_begin(a, c); @@ -465,43 +434,36 @@ static int run_automata(struct automata * a, default: break; } + c = next(next_userdata); } if (state == st_data && is_semiend(a)) { state = st_end; - field_end(a); + param_end(a); } if (state != st_end) { - ok = 0; - if (state != st_error) { - magi_error_set("[multipart] Input ended unexpectedly."); - } - free(a->field.name); - free(a->field.data); + a->request->error = magi_error_multipart; + free(a->subparam.name); + free(a->subparam.data); } - return ok; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Automata Interfaces */ -int magi_parse_multipart(struct magi_request * request, - char * boundary, - int (*get_next)(void * userdata), - void * get_next_userdata) +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, 1, 0, 0, 2, 0, 0, 0 - }; - int ok = 0; - a.list = list; - a.boundary = boundary; - a.boundary_len = strlen(boundary); - a.callback = callback; - a.buf = malloc(magi_parse_multipart_callback_size + 1); + struct automata a = { 0, { 0, 0, 0 }, { 0, 0 }, { 0, 0 }, 0, 0, 1, + 0, 0, 2, 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) { - ok = run_automata(&a, get_next, get_next_arg); + run_automata(&a, next, next_userdata); free(a.buf); } - return ok; } diff --git a/src/param.c b/src/param.c index 73005af..e9f43f8 100644 --- a/src/param.c +++ b/src/param.c @@ -13,7 +13,7 @@ int magi_param_list_add(struct magi_param_list ** list, node->item = *item; *list = node; } - return node; + return !!node; } char * magi_param_list_get(struct magi_param_list * list, const char * name) @@ -21,7 +21,7 @@ char * magi_param_list_get(struct magi_param_list * list, const char * name) if (!list || !name) { return 0; } else if (!strcmp(list->item.name, name)) { - return &list->item.data; + return list->item.data; } else { return magi_param_list_get(list->next, name); } diff --git a/src/request.c b/src/request.c index 4a1464a..99f3a2a 100644 --- a/src/request.c +++ b/src/request.c @@ -21,13 +21,39 @@ void magi_request_setup(struct magi_request * request) } } -static void tempfiles(struct magi_file * file, - char * addon, - int addon_len, - int is_addon_last, - struct magi_tempfiles * table) +void magi_tempfiles_add(struct magi_tempfiles * tmps, + const char * name, + const char * path, + int max) { - int pos; + if (!tmps) { + return; + } + if (tmps->count++) { + tmps->param_names = realloc(tmps->param_names, + sizeof(*tmps->param_names) * tmps->count); + tmps->locations = + realloc(tmps->locations, sizeof(*tmps->locations) * tmps->count); + tmps->maximums = + realloc(tmps->maximums, sizeof(*tmps->maximums) * tmps->count); + } else { + tmps->param_names = malloc(sizeof(*tmps->param_names)); + tmps->locations = malloc(sizeof(*tmps->locations)); + tmps->maximums = malloc(sizeof(*tmps->maximums)); + } + tmps->param_names[tmps->count] = name; + tmps->locations[tmps->count] = path; + tmps->maximums[tmps->count] = max; +} + +static void tempfiles(struct magi_file * file, + char * addon, + int addon_len, + int is_addon_last, + void * userdata) +{ + struct magi_tempfiles * table = userdata; + int pos; for (pos = 0; pos != table->count; ++pos) { if (!strcmp(table->param_names[pos], file->param_name)) { static FILE * f = 0; @@ -81,7 +107,7 @@ static void request_free(struct magi_request * request) free(request->path_info); } -void magi_request_annul(struct magi_request * request) +static void request_annul(struct magi_request * request) { request->cookies = 0; request->http_params = 0; diff --git a/src/request.h b/src/request.h index 5bb2070..be92370 100644 --- a/src/request.h +++ b/src/request.h @@ -88,6 +88,10 @@ struct magi_tempfiles { 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); /* 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, diff --git a/src/response.c b/src/response.c index e69de29..7e2ec0b 100644 --- a/src/response.c +++ b/src/response.c @@ -0,0 +1,107 @@ +#include "response.h" + +#include "utils.h" +#include +#include +#include +#include + +int vsnprintf(char * s, size_t n, const char * format, va_list arg); + + +void magi_response_setup(struct magi_response * response) +{ + response->cookies = 0; + response->http_params = 0; + response->content_type = 0; + 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) +{ + 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); + } +} + +void magi_response_add(struct magi_response * r, const char * addon) +{ + int addon_len; + if (!addon) { + return; + } + addon_len = strlen(addon); + if (r->len + addon_len + 1 >= r->size) { + r->content = realloc(r->content, r->len + addon_len + 1); + } + memcpy(r->content + r->len, addon, addon_len + 1); + r->len += addon_len; +} + +void magi_response_add_format(struct 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(struct magi_response * response, + struct 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) +{ + 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_list_add(&response->cookies, &cookie); +} + +void magi_response_http(struct magi_response * response, + const char * name, + const char * data) +{ + 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_list_add(&response->http_params, ¶m); +} + + +void magi_response_destroy(struct magi_response * response) +{ + if (!response) { + return; + } + magi_cookie_list_destroy(response->cookies); + magi_param_list_destroy(response->http_params); + free(response->cookies); + free(response->http_params); + free(response->content_type); + free(response->content); +} diff --git a/src/response.h b/src/response.h index 346ce3d..95f6537 100644 --- a/src/response.h +++ b/src/response.h @@ -1,36 +1,43 @@ #ifndef MAGI_INCLUDED_RESPONSE #define MAGI_INCLUDED_RESPONSE -#include "error.h" +#include "cookie.h" +#include "param.h" -enum magi_content_type { magi_xhtml }; +enum magi_content_type { magi_xhtml = 0 }; struct magi_response { struct magi_cookie_list * cookies; struct magi_param_list * http_params; - struct magi_param_list * url_params; char * content_type; char * content; + int len; + int size; }; + +void magi_response_setup(struct magi_response * response); + + void magi_response_content_type(struct magi_response * response, enum magi_content_type type); -void magi_reponse_add(struct magi_response * response, const char * addon); -void magi_reponse_add_format(struct 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(struct magi_response * response, - const struct magi_cookie * cookie); +void magi_response_cookie(struct magi_response * response, + struct magi_cookie * cookie); void magi_response_cookie_easy(struct magi_response * response, const char * name, const char * value); -void magi_response_cookie_delete(struct magi_response * response, - const char * name); +void magi_response_http(struct magi_response * response, + const char * name, + const char * data); void magi_response_destroy(struct magi_response * response); diff --git a/src/urlenc.c b/src/urlenc.c index 336dde0..8c38a0a 100644 --- a/src/urlenc.c +++ b/src/urlenc.c @@ -1,7 +1,6 @@ -#include "urlencoded.h" +#include "urlenc.h" -#include "error.h" -#include "field.h" +#include "utils.h" #include #include #include @@ -10,16 +9,21 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Local Shortcuts */ -/* Shouldn't be called with 'c' as not hex digit. */ -static char from_hex(char c) +/* Call only if is_hex(c). */ +static int from_hex(char c) { - char num; if (isdigit(c)) { - num = c - '0'; + return c - '0'; } else { - num = toupper(c) - 'A' + 10; + return toupper(c) - 'A' + 10; } - return num; +} + +/* Call only if 0 <= num && num <= 15. */ +static char to_hex(int num) +{ + static const char * const hex = "0123456789ABCDEF"; + return hex[num]; } static int is_hex(char c) @@ -27,43 +31,43 @@ 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) { - int ok = 1; - if (*data) { - char * val = *data; - int ti = 0; - int ci; - for (ci = 0; ok && 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]); /* 00xx */ - val[ti] |= from_hex(val[ci + 1]) << 4; /* XXxx */ - ci += 2; - } else { - ok = 0; - magi_error_set( - "[urlencoded] Waiting for two hex digits after '%%', " - "readed: \\%o\\%o (render: %c%c)", - val[ci + 1], val[ci + 2], val[ci + 1], val[ci + 2]); - } - } else if (val[ci] == '+') { - val[ti] = ' '; + 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 { - val[ti] = val[ci]; + return 0; } + } else if (val[ci] == '+') { + val[ti] = ' '; + } else { + val[ti] = val[ci]; } - val[ti++] = 0; - } else { - *data = malloc(1); - **data = 0; } - return ok; + val[ti] = 0; + return 1; } @@ -73,8 +77,8 @@ static int deurl(char ** data) enum st { st_error = 0, st_name, st_data }; struct automata { - struct magi_field_list ** list; - struct magi_field field; + struct magi_param_list ** list; + struct magi_param param; int size; int len; }; @@ -82,106 +86,106 @@ struct automata { static enum st parse_name(struct automata * a, char c) { - enum st state; - if (c != '&' && c != ';') { - if (c == '=') { - state = st_data; - a->size = 1; - a->len = 0; - } else { - if (a->len == a->size - 1) { - a->size *= 2; - a->field.name = realloc(a->field.name, a->size); - } - if (!a->field.name) { - state = st_error; - magi_error_set("[urlencoded] Cannot allocate field name."); - } else { - state = st_name; - a->len++; - a->field.name[a->len - 1] = c; - a->field.name[a->len] = 0; - } - } - } else { - state = st_error; - magi_error_set("[urlencoded] Reading name, readed: \\%o (render: %c).", - c, 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; } - return state; + + 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) { - enum st state = st_error; - if (deurl(&a->field.name) && deurl(&a->field.data)) { - a->field.len = strlen(a->field.data); - if (magi_field_list_add(a->list, &a->field)) { - state = st_name; + 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->field.name = 0; - a->field.data = 0; + a->param.name = 0; + a->param.data = 0; + return st_name; } } - return state; + return st_error; } static enum st parse_data(struct automata * a, char c) { - enum st state; - if (c != '=') { - if (c == '&' || c == ';') { - state = end_data(a); + 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 { - if (a->len == a->size - 1) { - a->size *= 2; - a->field.data = realloc(a->field.data, a->size); - } - if (!a->field.data) { - state = st_error; - magi_error_set("[urlencoded] Cannot allocate field data."); - } else { - state = st_data; - a->len++; - a->field.data[a->len - 1] = c; - a->field.data[a->len] = 0; - } + state = parse_data(&a, *encoded); } - } else { - state = st_error; - magi_error_set("[urlencoded] Reading data, readed: \\%o (render: %c).", - c, c); } - return state; + if (state == st_name || !state || !end_data(&a)) { + free(a.param.name); + free(a.param.data); + request->error = magi_error_urlenc; + } } -int magi_parse_urlencoded(struct magi_field_list ** list, const char * input) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * URL Encoding + */ +int magi_urlenc_size(const char * plain) { - enum st state = st_name; - struct automata a = { 0, { 0, 0, 0 }, 1, 0 }; - if (input && *input) { - a.list = list; - while (state && *input) { - switch (state) { - case st_name: - state = parse_name(&a, *input); - break; - case st_data: - state = parse_data(&a, *input); - default: - break; - } - ++input; + int size; + if (!plain) { + return 0; + } + for (size = 0; *plain; ++plain, ++size) { + if (!is_url(*plain)) { + size += 2; } - if (state == st_data) { - state = end_data(&a); - } else if (state == st_name) { - state = st_error; - magi_error_set("[urlencoded] Input ended while reading name."); + } + return size; +} + +void magi_urlencode(const char * plain, char * code) +{ + if (!plain || !code) { + return; + } + while (*plain) { + if (is_url(*plain)) { + *++code = *plain; + } else { + *++code = '%'; + *++code = to_hex(*plain & 0x0F); + *++code = to_hex(*plain >> 4); } - free(a.field.name); - free(a.field.data); + ++plain; } - return state != st_error; } diff --git a/src/urlenc.h b/src/urlenc.h index 9c14ccb..5240109 100644 --- a/src/urlenc.h +++ b/src/urlenc.h @@ -4,8 +4,12 @@ #include "request.h" -void magi_urlenc(struct magi_request * request, const char * encoded); +/* 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); diff --git a/src/utils.c b/src/utils.c index 29e8219..931d1e4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,8 +1,8 @@ #include "utils.h" -#include "error.h" #include #include +#include void magi_str_lowercase(char * str) @@ -35,17 +35,16 @@ char * magi_str_create(int len) return str; } -char * magi_str_add(char * dest, int * len, int * size, char c) +int magi_str_add(char ** dest, int * len, int * size, char c) { - int ok = 1; if (*len + 1 == *size) { *size *= 2; - dest = realloc(dest, *size); + *dest = realloc(*dest, *size); } - if (dest) { - dest[*len] = c; + if (*dest) { + (*dest)[*len] = c; ++*len; - dest[*len] = 0; + (*dest)[*len] = 0; } - return dest; + return !!*dest; } diff --git a/src/utils.h b/src/utils.h index 15904d0..f67c66c 100644 --- a/src/utils.h +++ b/src/utils.h @@ -2,10 +2,14 @@ #define MAGI_INCLUDED_UTILS -void magi_str_lowercase(char * str); +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 * begin, const char * end); char * magi_str_create(int len); -char * magi_str_add(char * dest, int * len, int * size, char c); + +/* Null only in case of error. */ +int magi_str_add(char ** dest, int * len, int * size, char c); #endif -- cgit v1.2.3