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! --- src/multipart.c | 478 ++++++++++++++++++++++++++------------------------------ 1 file changed, 220 insertions(+), 258 deletions(-) (limited to 'src/multipart.c') 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; } -- cgit v1.2.3