aboutsummaryrefslogtreecommitdiff
path: root/src
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
parentea25abb295d9b4fbaf0ac23af8ffe71247970f1b (diff)
downloadmagi-b08db17162fddda97e6ee1ac625eae1430d22b64.tar
magi-b08db17162fddda97e6ee1ac625eae1430d22b64.tar.xz
magi-b08db17162fddda97e6ee1ac625eae1430d22b64.zip
[magi] Almost done!
Diffstat (limited to 'src')
-rw-r--r--src/cgi.c289
-rw-r--r--src/cookie.c2
-rw-r--r--src/cookies.c117
-rw-r--r--src/cookies.h2
-rw-r--r--src/error.c9
-rw-r--r--src/error.h3
-rw-r--r--src/fastcgi.h4
-rw-r--r--src/file.c2
-rw-r--r--src/file.h2
-rw-r--r--src/multipart.c478
-rw-r--r--src/param.c4
-rw-r--r--src/request.c40
-rw-r--r--src/request.h4
-rw-r--r--src/response.c107
-rw-r--r--src/response.h29
-rw-r--r--src/urlenc.c240
-rw-r--r--src/urlenc.h6
-rw-r--r--src/utils.c15
-rw-r--r--src/utils.h8
19 files changed, 770 insertions, 591 deletions
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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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, &param);
- } else {
- ok = 0;
+ if (!param.name || !param.data ||
+ !magi_param_list_add(&r->http_params, &param)) {
+ 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,
+ "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' "
+ "'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>"
+ "<html xmlns='http://www.w3.org/1999/xhtml'>"
+ "<head><title>400 Bad Request</title></head>"
+ "<body>"
+ "<h1>400 <i>Bad Request</i></h1>"
+ "<h2>%s</h2>"
+ "</body>"
+ "</html>",
+ 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 <stdlib.h>
#include <string.h>
@@ -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 <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;
}
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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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, &param);
+}
+
+
+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 <ctype.h>
#include <stdlib.h>
#include <string.h>
@@ -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 <ctype.h>
#include <stdlib.h>
+#include <string.h>
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