From 8f1e6faa7f548c9a8bcbcac71c8f239a6540b926 Mon Sep 17 00:00:00 2001
From: Aleksey Veresov <aleksey@veresov.pro>
Date: Wed, 11 Mar 2020 17:54:19 +0300
Subject: [magi]

---
 examples/Makefile       |   2 +-
 examples/append.c       |   2 +-
 examples/cookie.c       |   2 +-
 examples/echo.c         |  44 ++++----
 examples/upload.c       |  11 +-
 include/magi.h          |   1 +
 include/magi/response.h |   4 +-
 src/cgi.c               |  19 ++--
 src/cookies.c           | 171 +++++++++++++------------------
 src/multipart.c         | 262 +++++++++++++++++++++---------------------------
 src/param.c             |  13 ++-
 src/request.c           |  12 +--
 src/response.c          |  26 ++---
 src/tools.c             |  34 +++----
 src/tools.h             |   9 +-
 src/urlencoded.c        |  83 +++++++--------
 16 files changed, 315 insertions(+), 380 deletions(-)

diff --git a/examples/Makefile b/examples/Makefile
index 991f936..88af46d 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -3,7 +3,7 @@
 # Debug mode (allowing to debug the examples via gdb):
 # DEBUG    = yes
 # Examples to build by default:
-EXAMPLES = append cookie echo upload fastcgi
+EXAMPLES = append cookie echo upload
 # Specify your favourite C compiler here (e.g. tcc):
 CC       = gcc
 
diff --git a/examples/append.c b/examples/append.c
index 3a32287..c6cfb2b 100644
--- a/examples/append.c
+++ b/examples/append.c
@@ -26,7 +26,7 @@ void response(magi_request *r)
         "</html>");
 }
 
-int main(int argc, char const *argv[])
+int main()
 {
     magi_request request;
     magi_request_init(&request);
diff --git a/examples/cookie.c b/examples/cookie.c
index 7c55ba8..601c901 100644
--- a/examples/cookie.c
+++ b/examples/cookie.c
@@ -24,7 +24,7 @@ void response(magi_request *r)
     magi_response(r, "</p></body></html>");
 }
 
-int main(int argc, char const *argv[])
+int main()
 {
     magi_request request;
     magi_request_init(&request);
diff --git a/examples/echo.c b/examples/echo.c
index e3b90c7..7c840e2 100644
--- a/examples/echo.c
+++ b/examples/echo.c
@@ -38,33 +38,29 @@ void list_files(magi_request *r)
     for (current = r->files; current; current = current->next) {
         magi_file *f = &current->item;
         magi_response_format(r, "[%s] was [%s] on clientside<br/>",
-                             f->param_name, f->file_name);
+                             f->field, f->filename);
     }
 }
 
 void show_meta(magi_request *r)
 {
-    magi_response(r, "I was called with method [");
-    magi_response(r, r->method);
-    if (r->uri) {
-        magi_response(r, "] with URL [");
-        magi_response(r, r->uri);
-    }
-    if (r->server_name) {
-        magi_response(r, "] for server [");
-        magi_response(r, r->server_name);
+    magi_response(r, "I was called ");
+    if (r->is_secure) {
+        magi_response(r, "securely ");
     }
-    if (r->server_port) {
-        magi_response(r, "] on port [");
-        magi_response(r, r->server_port);
+    magi_response(r, "with method [");
+    magi_response(r, r->method);
+    if (r->host) {
+        magi_response(r, "] on server [");
+        magi_response(r, r->host);
     }
-    if (r->server_protocol) {
-        magi_response(r, "] with protocol [");
-        magi_response(r, r->server_protocol);
+    if (r->script) {
+        magi_response(r, "] being script on [");
+        magi_response(r, r->script);
     }
-    if (r->server_software) {
-        magi_response(r, "] and I am running on software [");
-        magi_response(r, r->server_software);
+    if (r->path) {
+        magi_response(r, "] with requested path [");
+        magi_response(r, r->path);
     }
     magi_response(r, "]<br/>");
 }
@@ -85,13 +81,13 @@ void response(magi_request *r)
     list_cookies(r);
 
     magi_response(r, "<h2>Parameters:</h2>");
-    list_params(r, r->params);
+    list_params(r, r->meta);
 
     magi_response(r, "<h2>URL Parameters:</h2>");
-    list_params(r, r->url_params);
+    list_params(r, r->head);
 
-    magi_response(r, "<h2>HTTP Parameters:</h2>");
-    list_params(r, r->http_params);
+    magi_response(r, "<h2>Body Parameters:</h2>");
+    list_params(r, r->body);
 
     magi_response(r, "<h2>Files:</h2>");
     list_files(r);
@@ -99,7 +95,7 @@ void response(magi_request *r)
     magi_response(r, "</body></html>");
 }
 
-int main(int argc, char const *argv[])
+int main()
 {
     magi_request request;
     magi_request_init(&request);
diff --git a/examples/upload.c b/examples/upload.c
index 54d3a48..a8503ea 100644
--- a/examples/upload.c
+++ b/examples/upload.c
@@ -29,18 +29,17 @@ void response(magi_request *r)
 
 void get(magi_request *r)
 {
-    magi_loadfiles rules;
-    magi_loadfiles_init(&rules);
+    magi_loadfiles rules = { 0, 0 };
     magi_loadfiles_add(&rules, "data", "data", 0);
-    magi_loadfiles_set(&r, &rules);
-    magi_cgi(&r);
+    magi_loadfiles_set(r, &rules);
+    magi_cgi(r);
     magi_loadfiles_free(&rules);
 }
 
-int main(int argc, char const *argv[])
+int main()
 {
     magi_request request;
-    magi_request_init(request);
+    magi_request_init(&request);
     get(&request);
     if (request.error) {
         magi_response_error(&request);
diff --git a/include/magi.h b/include/magi.h
index 0089878..a5b047d 100644
--- a/include/magi.h
+++ b/include/magi.h
@@ -8,6 +8,7 @@
 #include "magi/error.h"
 #include "magi/fastcgi.h"
 #include "magi/file.h"
+#include "magi/loadfile.h"
 #include "magi/param.h"
 #include "magi/request.h"
 #include "magi/response.h"
diff --git a/include/magi/response.h b/include/magi/response.h
index 52b1c9d..a9854d4 100644
--- a/include/magi/response.h
+++ b/include/magi/response.h
@@ -32,7 +32,9 @@ typedef struct magi_response_methods {
 typedef struct magi_response_implementation {
     const magi_response_methods *methods;
     void        *userdata;
-    magi_params *head[3];
+    magi_params *head_response;
+    magi_params *head_general;
+    magi_params *head_entity;
     int          head_done;
 } magi_response_implementation;
 
diff --git a/src/cgi.c b/src/cgi.c
index 00b0a06..291a57f 100644
--- a/src/cgi.c
+++ b/src/cgi.c
@@ -75,6 +75,9 @@ static void cgi_env(magi_request *r)
     if (getenv("SERVER_PORT")) {
         r->port      = atoi(getenv("SERVER_PORT"));
         r->is_secure = r->port == 443;
+    } else {
+        r->port      = 0;
+        r->is_secure = 0;
     }
     r->path = plain_env("PATH_INFO");
 }
@@ -191,13 +194,15 @@ static void setup_response(magi_request *r)
         mfile,
         mclose
     };
-    r->response            = malloc(sizeof(*r->response));
-    r->response->methods   = &m;
-    r->response->userdata  = 0;
-    r->response->head[0]   = 0;
-    r->response->head[1]   = 0;
-    r->response->head[2]   = 0;
-    r->response->head_done = 0;
+    r->response                = malloc(sizeof(*r->response));
+    r->response->methods       = &m;
+    r->response->userdata      = 0;
+    r->response->head_response = 0;
+    r->response->head_general  = 0;
+    r->response->head_entity   = 0;
+    r->response->head_done     = 0;
+    magi_response_content_type(r, "application/xhtml+xml");
+    magi_response_status(r, 200, "OK");
 }
 
 /* Interfacial CGI Request Handling */
diff --git a/src/cookies.c b/src/cookies.c
index 8b610d4..7e8248b 100644
--- a/src/cookies.c
+++ b/src/cookies.c
@@ -6,7 +6,7 @@
 #include <string.h>
 
 
-enum st {
+typedef enum st {
     st_error = 0,
     st_pre_name,
     st_name,
@@ -14,24 +14,24 @@ enum st {
     st_pre_data,
     st_data,
     st_post_data
-};
+} st;
 
-enum data_type { dt_plain = 0, dt_version, dt_path, dt_domain };
+typedef enum dt { dt_plain = 0, dt_version, dt_path, dt_domain } dt;
 
-struct automata {
-    struct magi_cookie_list ** list;
-    struct magi_cookie         cookie;
-    char *                     buf;
-    int                        buf_len;
-    int                        buf_size;
-    int                        is_first;
-    int                        is_advanced;
-    int                        is_quoted;
-    enum data_type             data_t;
-};
+typedef struct automata {
+    magi_cookies **list;
+    magi_cookie    cookie;
+    char          *buf;
+    int            buf_len;
+    int            buf_size;
+    int            is_first;
+    int            is_advanced;
+    int            is_quoted;
+    dt             datatype;
+} automata;
 
 
-static void nulify_cookie(struct automata * a)
+static void nulify_cookie(automata *a)
 {
     a->cookie.name    = 0;
     a->cookie.data    = 0;
@@ -40,7 +40,7 @@ static void nulify_cookie(struct automata * a)
     a->cookie.max_age = 0;
 }
 
-static void buf_new(struct automata * a)
+static void buf_new(automata *a)
 {
     a->buf      = 0;
     a->buf_len  = 0;
@@ -48,29 +48,28 @@ static void buf_new(struct automata * a)
 }
 
 
-static enum data_type what_is_name(const struct automata * a)
+static dt what_is_name(const automata *a)
 {
-    enum data_type data_t = dt_plain;
+    dt datatype = dt_plain;
     if (a->is_first && !strcmp(a->buf, "$Version")) {
-        data_t = dt_version;
+        datatype = dt_version;
     } else if (a->is_advanced) {
         if (!strcmp(a->buf, "$Path")) {
-            data_t = dt_path;
+            datatype = dt_path;
         } else if (!strcmp(a->buf, "$Domain")) {
-            data_t = dt_domain;
+            datatype = dt_domain;
         }
     }
-    return data_t;
+    return datatype;
 }
 
 
-static int end_name(struct automata * a)
+static void end_name(automata *a)
 {
-    int ok    = 1;
-    a->data_t = what_is_name(a);
-    if (a->data_t == dt_plain) {
+    a->datatype = what_is_name(a);
+    if (a->datatype == dt_plain) {
         if (a->cookie.name) {
-            ok = magi_cookie_list_add(a->list, &a->cookie);
+            magi_cookies_add(a->list, &a->cookie);
         }
         nulify_cookie(a);
         a->cookie.name = a->buf;
@@ -78,75 +77,56 @@ static int end_name(struct automata * a)
         free(a->buf);
     }
     buf_new(a);
-    return ok;
 }
 
-static int end_data(struct automata * a)
+static int end_data(automata *a)
 {
-    int ok = 1;
-    switch (a->data_t) {
-    case dt_plain:
-        a->cookie.data = a->buf;
-        break;
-    case dt_path:
-        a->cookie.path = a->buf;
-        break;
-    case dt_domain:
-        a->cookie.domain = a->buf;
-        break;
-    case dt_version:
-        if (strcmp(a->buf, "1")) {
-            ok = 0;
-        }
+    switch (a->datatype) {
+    case dt_plain:   a->cookie.data   = a->buf; break;
+    case dt_path:    a->cookie.path   = a->buf; break;
+    case dt_domain:  a->cookie.domain = a->buf; break;
+    case dt_version: if (strcmp(a->buf, "1")) { return 0; }
     }
     buf_new(a);
-    return ok;
+    return 1;
 }
 
 
-static enum st parse_pre_name(struct automata * a, char c)
+static st parse_pre_name(automata *a, char c)
 {
-    enum st state;
+    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;
-        }
+        magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c);
     } else {
         state = st_error;
     }
     return state;
 }
 
-static enum st parse_name(struct automata * a, char c)
+static st parse_name(automata *a, char c)
 {
-    enum st state;
+    st state;
     if (c == '=') {
         state = st_pre_data;
-        if (!end_name(a)) {
-            state = st_error;
-        }
+        end_name(a);
     } else if (c == ' ' || c == '\t') {
         state = st_post_name;
-        if (!end_name(a)) {
-            state = st_error;
-        }
+        end_name(a);
     } 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;
-        }
+        magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c);
     } else {
         state = st_error;
     }
     return state;
 }
 
-static enum st parse_post_name(struct automata * a, char c)
+static st parse_post_name(automata *a, char c)
 {
-    enum st state;
+    st state;
     if (c == '=') {
         state = st_pre_data;
     } else if (c == ' ' || c == '\t') {
@@ -157,9 +137,9 @@ static enum st parse_post_name(struct automata * a, char c)
     return state;
 }
 
-static enum st parse_pre_data(struct automata * a, char c)
+static st parse_pre_data(automata *a, char c)
 {
-    enum st state;
+    st state;
     if (c == ' ' || c == '\t') {
         state = st_pre_data;
     } else if (c == '"') {
@@ -167,18 +147,16 @@ 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 (!magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c)) {
-            state = st_error;
-        }
+        magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c);
     } else {
         state = st_error;
     }
     return state;
 }
 
-static enum st parse_not_quoted_data(struct automata * a, char c)
+static st parse_not_quoted_data(automata *a, char c)
 {
-    enum st state;
+    st state;
     if (c == ';' || (c == ',' && a->is_first)) {
         state       = st_pre_name;
         a->is_first = 0;
@@ -192,18 +170,16 @@ static enum st parse_not_quoted_data(struct automata * a, char c)
         }
     } 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;
-        }
+        magi_str_add(&a->buf, &a->buf_len, &a->buf_size, c);
     } else {
         state = st_error;
     }
     return state;
 }
 
-static enum st parse_data(struct automata * a, char c)
+static st parse_data(automata *a, char c)
 {
-    enum st state;
+    st state;
     if (a->is_quoted) {
         if (c == '"') {
             state        = st_post_data;
@@ -220,9 +196,9 @@ static enum st parse_data(struct automata * a, char c)
     return state;
 }
 
-static enum st parse_post_data(struct automata * a, char c)
+static st parse_post_data(automata *a, char c)
 {
-    enum st state;
+    st state;
     if (c == ';' || (c == ',' && a->is_first)) {
         state = st_pre_name;
     } else if (c == ' ' || c == '\t') {
@@ -234,14 +210,15 @@ static enum st parse_post_data(struct automata * a, char c)
 }
 
 
-static void parse_end(enum magi_error * e, struct automata * a, enum st s)
+static void parse_end(magi_error *e, automata *a, 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)) {
+        if (end_data(a)) {
+            magi_cookies_add(a->list, &a->cookie);
             nulify_cookie(a);
         } else {
             *e = magi_error_cookies;
@@ -252,33 +229,21 @@ static void parse_end(enum magi_error * e, struct automata * a, enum st s)
 }
 
 
-void magi_cookies(struct magi_request * request, const char * data)
+void magi_parse_cookies(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;
+    st       state;
+    automata a       = { 0, { 0, 0, 0, 0, 0 }, 0, 0, 0, 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;
+        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); break;
+        default:                                               break;
         }
     }
     parse_end(&request->error, &a, state);
diff --git a/src/multipart.c b/src/multipart.c
index 001b4d1..72fe607 100644
--- a/src/multipart.c
+++ b/src/multipart.c
@@ -18,7 +18,7 @@ static int is_token(char c)
     return 32 <= c && c <= 126 && !strchr("()<>@,;:\\\"/[]?={} \t", c);
 }
 
-static int is_str_token(char * str)
+static int is_str_token(char *str)
 {
     int is = str && *str; /* Empty string is not valid. */
     while (is && *str) {
@@ -32,7 +32,7 @@ static int is_str_token(char * str)
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Automata for multipart/form-data
  */
-enum st {
+typedef enum st {
     st_error = 0,
     st_begin,
     st_pname_pre,
@@ -41,31 +41,31 @@ enum st {
     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;
-};
+} st;
+
+typedef struct automata {
+    magi_request *request;
+    magi_file     file;
+    magi_param    param;
+    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;
 
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Automata Shortcuts
  */
-static char * extract_filename(char * n)
+static char *extract_filename(char *n)
 {
     n = strchr(n, '=');
     if (!n) {
@@ -80,9 +80,9 @@ static char * extract_filename(char * n)
     }
 }
 
-static int content_disposition(struct automata * a)
+static int content_disposition(automata *a)
 {
-    char * n = strchr(a->subparam.data, '=');
+    char *n = strchr(a->subparam.data, '=');
     if (!n) {
         return 0;
     }
@@ -99,11 +99,11 @@ static int content_disposition(struct automata * a)
             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;
+    a->file.filename = extract_filename(n);
+    if (a->file.filename) {
+        a->file.field  = a->param.name;
+        a->file.params = 0;
+        a->param.name  = 0;
     }
     free(a->subparam.name);
     free(a->subparam.data);
@@ -112,53 +112,42 @@ static int content_disposition(struct automata * a)
     return 1;
 }
 
-static int subparam_end(struct automata * a)
+static int subparam_end(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;
+    magi_params_add(&a->file.params, &a->subparam);
+    a->subparam.name = 0;
+    a->subparam.data = 0;
+    return 1;
 }
 
-static int param_end(struct automata * a)
+static int param_end(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;
+    if (a->file.filename) {
+        a->request->callback.act(a->request->callback.userdata,
+                                 &a->file, a->buf, a->buf_size);
+        a->request->callback.act(a->request->callback.userdata,
+                                 &a->file, 0, 0);
+        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;
+        magi_files_add(&a->request->files, &a->file);
+        a->file.filename = 0;
+        a->file.field    = 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;
-    }
+    magi_params_add(&a->request->body, &a->param);
     a->param.name = 0;
     a->param.data = 0;
     a->size       = 1;
@@ -170,7 +159,7 @@ static int param_end(struct automata * a)
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Boundary Interfaces
  */
-static char sepget(const struct automata * a)
+static char sepget(const automata *a)
 {
     char      c;
     const int pos_after = a->boundary_pos - 4 - a->boundary_len;
@@ -192,12 +181,9 @@ static char sepget(const struct automata * a)
     return c;
 }
 
-static int seplen(const struct automata * a)
-{
-    return a->boundary_len + 6;
-}
+static int seplen(const automata *a)  { return a->boundary_len + 6; }
 
-static char endget(const struct automata * a)
+static char endget(const automata *a)
 {
     char      c;
     const int pos_after = a->boundary_pos - 4 - a->boundary_len;
@@ -221,13 +207,13 @@ static char endget(const struct automata * a)
     return c;
 }
 
-static int endlen(const struct automata * a)
+static int endlen(const 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? */
+static int is_semiend(const automata *a)
+{  /* Is end readed, expect last two chars, which are CR LF? */
     return a->is_end_suspected && (a->boundary_pos == endlen(a) - 2);
 }
 
@@ -235,19 +221,18 @@ static int is_semiend(const struct automata * a)
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Data Reading
  */
-static void apply_callback(struct automata * a)
+static void apply_callback(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;
+    int full = a->buf_size == a->request->callback.addon_max;
+    if (a->file.filename && full) {
+        a->request->callback.act(a->request->callback.userdata,
+                                 &a->file, a->buf, a->buf_size);
+        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)
+static st data_add_act(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) {
@@ -275,10 +260,10 @@ static enum st data_add_act(
     }
 }
 
-static enum st data_add(struct automata * a, char c)
+static st data_add(automata *a, char c)
 {
-    if (a->file.file_name) {
-        int max = a->request->file_callback_addon_max + 1;
+    if (a->file.filename) {
+        int max = a->request->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);
@@ -289,20 +274,20 @@ static enum st data_add(struct automata * a, char c)
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * State analysers
  */
-static enum st parse_begin(struct automata * a, char c)
+static st parse_begin(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. */
+    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_pname_pre;  /* Separator is completed, so move on. */
         }
     }
     return st_begin;
 }
 
-static enum st parse_pname_pre(struct automata * a, char c)
+static st parse_pname_pre(automata *a, char c)
 {
     if (a->is_CR_readed) {
         if (c != '\n') {
@@ -314,14 +299,15 @@ static enum st parse_pname_pre(struct automata * a, char c)
     } 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;
+    if (!is_token(c)) {
+        return st_error;
+    }
+    magi_str_add(&a->subparam.name, &a->len, &a->size, c);
+    return st_pname;
 }
 
-static enum st parse_pname(struct automata * a, char c)
+static st parse_pname(automata *a, char c)
 {
     if (c == ':') {
         a->len  = 0;
@@ -329,14 +315,15 @@ static enum st parse_pname(struct automata * a, char c)
         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;
+    if (!is_token(c)) {
+        return st_error;
+    }
+    magi_str_add(&a->subparam.name, &a->len, &a->size, c);
+    return st_pname;
 }
 
-static enum st parse_pname_end(struct automata * a, char c)
+static st parse_pname_end(automata *a, char c)
 {
     if (c == ':') {
         a->len  = 0;
@@ -348,7 +335,7 @@ static enum st parse_pname_end(struct automata * a, char c)
     return st_error;
 }
 
-static enum st parse_pdata(struct automata * a, char c)
+static st parse_pdata(automata *a, char c)
 {
     if (a->is_CR_readed) {
         a->is_CR_readed = 0;
@@ -357,21 +344,19 @@ static enum st parse_pdata(struct automata * a, char c)
                 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;
+        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;
     } 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;
+    magi_str_add(&a->subparam.data, &a->len, &a->size, c);
+    return st_pdata;
 }
 
-static enum st parse_data(struct automata * a, char c)
+static st parse_data(automata *a, char c)
 {
     if (a->is_end_suspected) {
         if (endget(a) != c) {
@@ -398,51 +383,35 @@ static enum st parse_data(struct automata * a, char c)
     return data_add(a, c);
 }
 
-static enum st parse_end(struct automata * a, char c)
-{
-    return st_end;
-}
+static st parse_end(automata *a, char c)  { return st_end; }
 
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Automata Runner
  */
-static void run_automata(struct automata * a,
-                         int (*next)(void * next_userdata),
-                         void * next_userdata)
+static void run_automata(automata *a,
+                         int     (*next)(void *next_userdata),
+                         void     *next_userdata)
 {
-    enum st state = st_begin;
-    int     c     = next(next_userdata);
+    st  state = st_begin;
+    int c     = next(next_userdata);
+    int maxr  = a->request->limits.params_body;
     while (state && state != st_end && c != EOF &&
-           (!a->request->params_max || a->readed != a->request->params_max)) {
+           (!maxr || a->readed != maxr)) {
         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;
+        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);       break;
+        default:                                          break;
         }
         c = next(next_userdata);
         ++a->readed;
     }
-    if (a->request->params_max && a->readed == a->request->params_max) {
+    if (maxr && a->readed == maxr) {
         a->request->error = magi_error_limit;
         return;
     }
@@ -461,17 +430,18 @@ static void run_automata(struct automata * a,
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Automata Interfaces
  */
-void magi_multipart(struct magi_request * request,
-                    char *                boundary,
-                    int (*next)(void * userdata),
-                    void * next_userdata)
+void magi_parse_multipart(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);
+    automata a = {
+        0, { 0, 0, 0 }, { 0, 0 }, { 0, 0 }, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0
+    };
+    a.request      = request;
+    a.boundary     = boundary;
+    a.boundary_len = strlen(boundary);
+    a.buf          = malloc(request->callback.addon_max + 1);
     if (a.buf) {
         run_automata(&a, next, next_userdata);
         free(a.buf);
diff --git a/src/param.c b/src/param.c
index 42785fc..dd89a48 100644
--- a/src/param.c
+++ b/src/param.c
@@ -10,7 +10,18 @@ void magi_params_add(magi_params **params, magi_param *newitem)
     if (node) {
         node->next = *params;
         node->item = *newitem;
-        *params     = node;
+        *params    = node;
+    }
+}
+
+void magi_params_set(magi_params **params, magi_param *newitem)
+{
+    if (!*params) {
+        magi_params_add(params, newitem);
+    } else if (!strcmp((*params)->item.name, newitem->name)) {
+        (*params)->item.data = newitem->data;
+    } else {
+        magi_params_set(&(*params)->next, newitem);
     }
 }
 
diff --git a/src/request.c b/src/request.c
index 4132458..41b77fe 100644
--- a/src/request.c
+++ b/src/request.c
@@ -56,12 +56,12 @@ void magi_request_free(magi_request *request)
         magi_files_free(request->files);
         request->response->methods->close(request->response->userdata);
         free(request->response->userdata);
-        magi_params_free(request->response->head[0]);
-        magi_params_free(request->response->head[1]);
-        magi_params_free(request->response->head[2]);
-        free(request->response->head[0]);
-        free(request->response->head[1]);
-        free(request->response->head[2]);
+        magi_params_free(request->response->head_response);
+        magi_params_free(request->response->head_general);
+        magi_params_free(request->response->head_entity);
+        free(request->response->head_response);
+        free(request->response->head_general);
+        free(request->response->head_entity);
         request_free(request);
         request_annul(request);
     }
diff --git a/src/response.c b/src/response.c
index aea64b4..d7d2c86 100644
--- a/src/response.c
+++ b/src/response.c
@@ -21,7 +21,7 @@ void magi_response_status(magi_request *r, int code, const char *description)
     addon.data[2] = '0' + code % 10;
     addon.data[3] = ' ';
     memcpy(addon.data + 4, description, dlen + 1);
-    magi_params_set(r->response->head, &addon);
+    magi_params_set(&r->response->head_response, &addon);
 }
 
 void magi_response_cookie(magi_request *r, const char *name, const char *data)
@@ -32,7 +32,7 @@ void magi_response_cookie(magi_request *r, const char *name, const char *data)
     }
     addon.name = magi_str_create_copy("Set-Cookie", 10);
     /* TODO */
-    magi_params_add(r->response->head + 1, &addon);
+    magi_params_add(&r->response->head_general, &addon);
 }
 
 void magi_response_cookie_complex(magi_request *r, magi_cookie *c)
@@ -43,7 +43,7 @@ void magi_response_cookie_complex(magi_request *r, magi_cookie *c)
     }
     addon.name = magi_str_create_copy("Set-Cookie", 10);
     /* TODO */
-    magi_params_add(r->response->head + 1, &addon);
+    magi_params_add(&r->response->head_general, &addon);
 }
 
 void magi_response_cookie_discard(magi_request *r, const char *name)
@@ -54,7 +54,7 @@ void magi_response_cookie_discard(magi_request *r, const char *name)
     }
     addon.name = magi_str_create_copy("Set-Cookie", 10);
     /* TODO */
-    magi_params_add(r->response->head + 1, &addon);
+    magi_params_add(&r->response->head_general, &addon);
 }
 
 void magi_response_header(magi_request *r, const char *name, const char *data)
@@ -65,7 +65,7 @@ void magi_response_header(magi_request *r, const char *name, const char *data)
     }
     addon.name = magi_str_create_copy(name, strlen(name));
     addon.data = magi_str_create_copy(data, strlen(data));
-    magi_params_add(r->response->head + 1, &addon);
+    magi_params_add(&r->response->head_general, &addon);
 }
 
 void magi_response_content_length(magi_request *r, int length)
@@ -85,18 +85,18 @@ void magi_response_content_length(magi_request *r, int length)
         ++len;
     }
     addon.data[len] = 0;
-    magi_params_set(r->response->head + 2, &addon);
+    magi_params_set(&r->response->head_entity, &addon);
 }
 
 void magi_response_content_type(magi_request *r, const char *type)
 {
     magi_param addon;
-    if (r->response->head_done) {
+    if (r->response->head_done || !type) {
         return;
     }
     addon.name = magi_str_create_copy("Content-Type", 12);
     addon.data = magi_str_create_copy(type, strlen(type));
-    magi_params_set(r->response->head + 2, &addon);
+    magi_params_set(&r->response->head_entity, &addon);
 }
 
 static void response_headers(magi_response_implementation *r, magi_params *p)
@@ -108,13 +108,12 @@ static void response_headers(magi_response_implementation *r, magi_params *p)
 
 void magi_response_head(magi_request *r)
 {
-    magi_params *current;
     if (r->response->head_done) {
         return;
     }
-    response_headers(r->response, r->response->head[0]);
-    response_headers(r->response, r->response->head[1]);
-    response_headers(r->response, r->response->head[2]);
+    response_headers(r->response, r->response->head_response);
+    response_headers(r->response, r->response->head_general);
+    response_headers(r->response, r->response->head_entity);
     r->response->methods->start_body(r->response->userdata);
     r->response->head_done = 1;
 }
@@ -122,6 +121,9 @@ void magi_response_head(magi_request *r)
 void magi_response(magi_request *r, const char *addon)
 {
     magi_response_head(r);
+    if (!addon) {
+        return;
+    }
     r->response->methods->body(r->response->userdata, addon, strlen(addon));
 }
 
diff --git a/src/tools.c b/src/tools.c
index d7a90ce..d6170bc 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -19,37 +19,27 @@ char *magi_str_lowercase(char *str)
 char *magi_str_create_copy(const char *first, int len)
 {
     char *copy = magi_str_create(len);
-    if (copy) {
-        memcpy(copy, first, len);
-    }
+    memcpy(copy, first, len);
     return copy;
 }
 
 char *magi_str_create(int len)
 {
     char *str = malloc(len + 1);
-    if (str) {
-        str[len] = 0;
-    }
+    str[len] = 0;
     return str;
 }
 
-int magi_str_add(magi_str *str, char c)
+void magi_str_add(char **str, int *len, int *size, char c)
 {
-    if (!str->data) {
-        str->len  = 0;
-        str->size = 2;
-        str->data = malloc(2);
-    } else if (str->len + 1 == str->size) {
-        str->size *= 2;
-        str->data  = realloc(str->data, str->size);
-    }
-    if (!str->dest) {
-        str->len  = 0;
-        str->size = 0;
-        return 0;
+    if (!*str) {
+        *len  = 0;
+        *size = 2;
+        *str  = malloc(2);
+    } else if (*len + 1 == *size) {
+        *size *= 2;
+        *str   = realloc(*str, *size);
     }
-    str->data[str->len++] = c;
-    str->data[str->len]   = 0;
-    return 1;
+    *str[*len]   = c;
+    *str[++*len] = 0;
 }
diff --git a/src/tools.h b/src/tools.h
index 49513e8..0152f35 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -8,14 +8,7 @@ char *magi_str_lowercase(char *str);
 char *magi_str_create_copy(const char *first, int len);
 char *magi_str_create(int len);
 
-
-typedef struct magi_str {
-    char *data;
-    int   size;
-    int   len;
-} magi_str;
-
-int magi_str_add(magi_str *str, char c);
+void magi_str_add(char **str, int *len, int *size, char c);
 
 
 #endif
diff --git a/src/urlencoded.c b/src/urlencoded.c
index 9d596ed..4f9efe8 100644
--- a/src/urlencoded.c
+++ b/src/urlencoded.c
@@ -61,76 +61,77 @@ static int deurl(char **data)
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Urlencoded Automata
  */
-typedef struct automata {
-    magi_param_list **list;
-    magi_str          name;
-    magi_str          data;
-} automata;
-typedef void *(*state)(automata *a, char c);
+typedef struct automata automata;
+typedef void (*state)(automata *a, char c);
+struct automata {
+    state         s;
+    magi_params **list;
+    char         *name;
+    int           nlen;
+    int           nsize;
+    char         *data;
+    int           dlen;
+    int           dsize;
+};
 
-static void *state_parse_data(automata *a, char c);
-static void *state_parse_name(automata *a, char c)
+static void state_parse_data(automata *a, char c);
+static void state_parse_name(automata *a, char c)
 {
     if (c == '&' || c == ';') {
-        return 0;
+        a->s = 0;
+        return;
     }
     if (c == '=') {
-        if (!deurl(&a->name.data)) {
-            return 0;
+        if (!deurl(&a->name)) {
+            a->s = 0;
+            return;
         }
-        return state_parse_name;
+        a->s = state_parse_data;
     }
-    magi_str_add(&a->name, c);
-    return a->name.size ? state_parse_name : 0;
+    magi_str_add(&a->name, &a->nlen, &a->nsize, c);
 }
 
-static int add_to_list(automata *a)
+static void add_to_list(automata *a)
 {
     magi_param param;
-    param.name = a->name.data;
-    param.data = a->data.data;
-    if (!magi_param_list_add(a->list, param)) {
-        return 0;
-    }
-    a->name.data = 0;
-    a->data.data = 0;
-    return 1;
+    param.name = a->name;
+    param.data = a->data;
+    magi_params_add(a->list, &param);
+    a->name = 0;
+    a->data = 0;
 }
 
 static void state_parse_data(automata *a, char c)
 {
     if (c == '=') {
-        return 0;
+        a->s = 0;
+        return;
     }
     if (c == '&' || c == ';') {
-        if (!deurl(&a->data.data) || !add_to_list(a)) {
-            return 0;
+        if (!deurl(&a->data)) {
+            a->s = 0;
+            return;
         }
-        return state_parse_name;
+        add_to_list(a);
+        a->s = state_parse_name;
     }
-    magi_str_add(&a->data, c);
-    return a->data.size ? state_parse_data : 0;
+    magi_str_add(&a->data, &a->dlen, &a->dsize, c);
 }
 
-magi_error magi_urlencoded(magi_param_list **list, const char *encoded)
+magi_error magi_parse_urlencoded(magi_params **list, const char *encoded)
 {
-    st       state;
-    automata a = { 0, { 0, 0 }, 1, 0 };
+    automata a = { state_parse_name, 0, 0, 0, 0, 0, 0, 0 };
     a.list     = list;
     *list      = 0;
     if (!encoded || !*encoded) {
         return 0;
     }
-    for (; *encoded; ++encoded) {
-        s = s(&a, *encoded);
-        if (!s) {
-            return auto_free(a);
-        }
+    for (; *encoded && a.s; ++encoded) {
+        a.s(&a, *encoded);
     }
-    if (state == st_name || !state || !end_data(&a)) {
-        free(a.param.name);
-        free(a.param.data);
-        return auto_free(a);
+    if (a.s == state_parse_name || !a.s || !deurl(&a.data)) {
+        return magi_error_urlenc;
     }
+    add_to_list(&a);
     return 0;
 }
-- 
cgit v1.2.3