aboutsummaryrefslogtreecommitdiff
path: root/src/multipart.c
diff options
context:
space:
mode:
authorAleksey Veresov <aleksey@veresov.pro>2019-11-29 18:50:04 +0300
committerAleksey Veresov <aleksey@veresov.pro>2019-11-29 18:50:04 +0300
commitb08db17162fddda97e6ee1ac625eae1430d22b64 (patch)
tree2c332f2db57a15c72f58704b9175cfece0ae9698 /src/multipart.c
parentea25abb295d9b4fbaf0ac23af8ffe71247970f1b (diff)
downloadmagi-b08db17162fddda97e6ee1ac625eae1430d22b64.tar
magi-b08db17162fddda97e6ee1ac625eae1430d22b64.tar.xz
magi-b08db17162fddda97e6ee1ac625eae1430d22b64.zip
[magi] Almost done!
Diffstat (limited to 'src/multipart.c')
-rw-r--r--src/multipart.c478
1 files changed, 220 insertions, 258 deletions
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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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)
{
@@ -215,213 +230,168 @@ 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;
}