diff options
author | Aleksey Veresov <aleksey@veresov.pro> | 2020-02-06 11:27:59 +0300 |
---|---|---|
committer | Aleksey Veresov <aleksey@veresov.pro> | 2020-02-06 11:27:59 +0300 |
commit | fca83975899b01c62ede4258f175346b7039be3d (patch) | |
tree | 37c2bb8faf95de1252a97942a68183f795eb82a0 | |
parent | 8ef310a22a54dca7a80947f83f03bc296ab298dd (diff) | |
parent | 746c3f3076fe5994f08d729aa1b958465231b0c8 (diff) | |
download | magi-fca83975899b01c62ede4258f175346b7039be3d.tar magi-fca83975899b01c62ede4258f175346b7039be3d.tar.xz magi-fca83975899b01c62ede4258f175346b7039be3d.zip |
Merge branch 'master' into test
-rw-r--r-- | Makefile | 41 | ||||
-rw-r--r-- | examples/Makefile | 31 | ||||
-rw-r--r-- | examples/echo.c | 4 | ||||
-rw-r--r-- | examples/upload.c | 1 | ||||
-rw-r--r-- | license | 2 | ||||
-rw-r--r-- | readme | 50 | ||||
-rw-r--r-- | src/cgi.c | 19 | ||||
-rw-r--r-- | src/cookie.c | 17 | ||||
-rw-r--r-- | src/inner_cookies.c | 24 | ||||
-rw-r--r-- | src/inner_multipart.c | 10 | ||||
-rw-r--r-- | src/inner_tools.c | 51 | ||||
-rw-r--r-- | src/inner_tools.h | 19 | ||||
-rw-r--r-- | src/request.c | 142 | ||||
-rw-r--r-- | src/request.h | 3 | ||||
-rw-r--r-- | src/response.c | 12 | ||||
-rw-r--r-- | src/urlenc.c | 2 | ||||
-rw-r--r-- | src/utils.c | 50 | ||||
-rw-r--r-- | src/utils.h | 15 |
18 files changed, 299 insertions, 194 deletions
@@ -1,35 +1,58 @@ -# Debug mode: +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Compilation Options +# Debug mode (allowing to debug the library via gdb): # DEBUG = yes - +# Specify your favourite C compiler here (e.g. tcc): CC = gcc + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Preparations LIB = libmagi.a +# Compile under the most strict conditions: CFLAGS = -xc -ansi -pedantic -Wall +# Debug and optimisation are not compatible: ifeq '$(DEBUG)' 'yes' CFLAGS += -g -O0 else CFLAGS += -O3 endif +# Object files listing: SRC_DIR = src SRC = $(wildcard $(SRC_DIR)/*.c) OBJ = $(SRC:.c=.o) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Targets +# 'make' produces library by default: default: $(LIB) -ifneq "clean" "$(MAKECMDGOALS)" --include deps.mk -endif +# Cleaning means removing everything automatically produced: +clean: + rm -f $(OBJ) $(LIB) deps.mk -deps.mk: $(SRC) - $(CC) -MM $^ > $@ +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Compilation +# Compile object files from corresponding source and header: %.o: %.c %.h $(CC) $(CFLAGS) -c $< -o $@ +# Packing object files into library: $(LIB): $(OBJ) ar rcs $@ $^ -clean: - rm -f $(OBJ) $(LIB) deps.mk + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Dependencies +# Generating dependencies description file: +deps.mk: $(SRC) + $(CC) -MM $^ > $@ + +# While cleaning we need to remove it, not include: +ifneq "clean" "$(MAKECMDGOALS)" +-include deps.mk +endif diff --git a/examples/Makefile b/examples/Makefile index d93176b..2f57b1e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,29 +1,48 @@ -# Uncomment following to enable debug mode: +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Compilation Options +# Debug mode (allowing to debug the examples via gdb): # DEBUG = yes - +# Specify your favourite C compiler here (e.g. tcc): CC = gcc + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Preparations EXAMPLES = append cookie upload echo +# Compile under the most strict conditions: CFLAGS = -xc -ansi -pedantic -Wall +# Debug and optimisation are not compatible: ifeq '$(DEBUG)' 'yes' CFLAGS += -g -O0 else CFLAGS += -O3 endif +# Including magi library headers and setting linker to use it: INCLUDE = -I ../src LFLAGS = -L.. -lmagi +# Specification of library file to produce it, if not provided: MAGI = ../libmagi.a +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Targets +# 'make' produces all examples by default: default: $(EXAMPLES) -$(MAGI): - cd ..; $(MAKE) +# Cleaning means removing everything automatically produced: +clean: + rm -f $(EXAMPLES) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Compilation +# Compile executables from corresponding sources and library: %: %.c $(MAGI) $(CC) $(CFLAGS) $(INCLUDE) $< $(LFLAGS) -o $@ -clean: - rm -f $(EXAMPLES) +# Run make for library to produce it: +$(MAGI): + cd ..; $(MAKE) diff --git a/examples/echo.c b/examples/echo.c index 81bdcf4..f665cd0 100644 --- a/examples/echo.c +++ b/examples/echo.c @@ -16,10 +16,6 @@ void proceed_cookies(magi_cookie_list *cookies, magi_response *response) magi_response_add(response, "] for domain ["); magi_response_add(response, cookies->item.domain); } - if (cookies->item.port) { - magi_response_add(response, "] for port ["); - magi_response_add(response, cookies->item.port); - } if (cookies->item.path) { magi_response_add(response, "] for path ["); magi_response_add(response, cookies->item.path); diff --git a/examples/upload.c b/examples/upload.c index 6a3a7a4..19e045c 100644 --- a/examples/upload.c +++ b/examples/upload.c @@ -43,6 +43,7 @@ int main(int argc, char const *argv[]) } else { magi_error_cgi(request.error); } + magi_tempfiles_destroy(&tmps); magi_request_destroy(&request); return 0; } @@ -1,4 +1,4 @@ -Copyright 2019 Aleksey Veresov +Copyright 2019-2020 Aleksey Veresov This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from @@ -0,0 +1,50 @@ + Description +Magi Library (libmagi) implements Gateway Interfaces, namely CGI and FastCGI. + + Overview +Magi are free and open-source software: legal info is in the 'license' file. +They are written in ANSI C, without any dependecies, except for C standard +library. All source and header files are located in the 'src' directory. +Files with 'inner' part in their names are not intended for external use. +Since filenames in the project are short and simple consider placing +header files in subdirectory 'magi' of your include. + + Compiling +Compilation and its options are described in the 'Makefile'. +Running 'make' produces 'libmagi.a' file, which is precompiled library itself, +ready to be statically linked into your project. + + Examples +Compilation and its options are described in the local 'examples/Makefile'. +Running 'make' in the 'examples' directory produces 'append', 'cookie', +'upload', and 'echo' CGI executables. Descriptions and details are in +corresponding source files. + + Usage +magi_request analyses incoming request, general workflow is to: +1. Prepare request for analysis, setup defaults via 'magi_request_setup'; +2. Process non-post part of the CGI request via 'magi_request_cgi'; + (The division of post and non-post analysis comes from the fact that + post body can be really huge, so you probably will want to specify + some constraints on it, based on non-post info. For example session id + from non-post part allowes some users (e.g. admins) to load more in post.) +3. Process post part of the CGI request via 'magi_request_resume_cgi'; +4. Now you have the full request analysed, so do your work; +5. And finally, free memory via 'magi_request_destroy'. +For more see 'src/request.h'. + +magi_response provides output capabilities. You can form the response with +'magi_response_setup' used to setup defaults, and the following functions to +fill in your data ('...' means 'magi_response_'): + ...content_type: set Content Type (by default XHTML); + ...cookie: set HTTP Cookie; +...cookie_discard: discard HTTP Cookie; + ...http: setup arbitarary HTTP header; + ...add: add something to the body; + ...add_format: add comething to the body, using format (similar to printf); +Outputting itself can be done with 'magi_response_cgi'. +To correctly free all occupied memory call 'magi_response_destroy'. +For more see 'src/response.h'. + + Motivation +Web must be fun. @@ -3,12 +3,11 @@ #include "cookie.h" #include "error.h" #include "file.h" -#include "inner_cookies.h" -#include "inner_multipart.h" #include "inner_tools.h" -#include "inner_urlencoded.h" +#include "multipart.h" #include "param.h" #include "request.h" +#include "urlenc.h" #include <ctype.h> #include <stdio.h> #include <stdlib.h> @@ -169,9 +168,9 @@ static char * bound(const char * type) type += strspn(type, " \t") + 1; if (*type == '"') { ++type; - res = magi_str_create_copy(type, strchr(type, '"')); + res = magi_str_create_copy(type, type - strchr(type, '"')); } else { - res = magi_str_create_copy(type, type + strcspn(type, " \t")); + res = magi_str_create_copy(type, strcspn(type, " \t")); } } return res; @@ -185,6 +184,8 @@ static int next(void * any) /* Interfacial CGI Request Handling */ int magi_request_cgi(struct magi_request * request) { + request->files = 0; + request->params = 0; request->url_params = 0; request->http_params = 0; request->error = magi_error_none; @@ -197,8 +198,6 @@ int magi_request_cgi(struct magi_request * request) int magi_request_resume_cgi(struct magi_request * request) { enum magi_error * e = &request->error; - request->files = 0; - request->params = 0; request->error = magi_error_none; if (request->method && !strcmp(request->method, "post")) { const char * t = getenv("CONTENT_TYPE"); @@ -264,9 +263,9 @@ void output_cookies(struct magi_cookie_list * list) fputs("; Domain=", stdout); fputs(list->item.domain, stdout); } - if (list->item.port) { - fputs("; Port=", stdout); - fputs(list->item.port, stdout); + if (list->item.max_age) { + fputs("; Max-Age=", stdout); + fputs(list->item.max_age, stdout); } fputs("\r\n", stdout); list = list->next; diff --git a/src/cookie.c b/src/cookie.c index eaa221f..d1d82b3 100644 --- a/src/cookie.c +++ b/src/cookie.c @@ -16,16 +16,19 @@ int magi_cookie_list_add(struct magi_cookie_list ** list, return !!node; } -struct magi_cookie * magi_cookie_list_get(struct magi_cookie_list * list, - const char * name) +char * magi_cookie_list_get(struct magi_cookie_list * list, const char * name) { + char * res = 0; if (!list || !name) { return 0; - } else if (!strcmp(list->item.name, name)) { - return &list->item; - } else { - return magi_cookie_list_get(list->next, name); } + while (list) { + if (!strcmp(list->item.name, name)) { + res = list->item.data; + } + list = list->next; + } + return res; } void magi_cookie_list_destroy(struct magi_cookie_list * list) @@ -37,6 +40,6 @@ void magi_cookie_list_destroy(struct magi_cookie_list * list) free(list->item.data); free(list->item.path); free(list->item.domain); - free(list->item.port); + free(list->item.max_age); } } diff --git a/src/inner_cookies.c b/src/inner_cookies.c index d9035c4..9dfde7a 100644 --- a/src/inner_cookies.c +++ b/src/inner_cookies.c @@ -1,7 +1,7 @@ /* * * TODO * * */ #include "inner_cookies.h" -#include "utils.h" +#include "inner_tools.h" #include <stdlib.h> #include <string.h> @@ -16,7 +16,7 @@ enum st { st_post_data }; -enum data_type { dt_plain = 0, dt_version, dt_path, dt_domain, dt_port }; +enum data_type { dt_plain = 0, dt_version, dt_path, dt_domain }; struct automata { struct magi_cookie_list ** list; @@ -25,7 +25,7 @@ struct automata { int buf_len; int buf_size; int is_first; - int is_cookie2; + int is_advanced; int is_quoted; enum data_type data_t; }; @@ -33,11 +33,11 @@ struct automata { static void nulify_cookie(struct automata * a) { - a->cookie.name = 0; - a->cookie.data = 0; - a->cookie.path = 0; - a->cookie.domain = 0; - a->cookie.port = 0; + a->cookie.name = 0; + a->cookie.data = 0; + a->cookie.path = 0; + a->cookie.domain = 0; + a->cookie.max_age = 0; } static void buf_new(struct automata * a) @@ -53,13 +53,11 @@ static enum data_type what_is_name(const struct automata * a) enum data_type data_t = dt_plain; if (a->is_first && !strcmp(a->buf, "$Version")) { data_t = dt_version; - } else if (a->is_cookie2) { + } else if (a->is_advanced) { if (!strcmp(a->buf, "$Path")) { data_t = dt_path; } else if (!strcmp(a->buf, "$Domain")) { data_t = dt_domain; - } else if (!strcmp(a->buf, "$Port")) { - data_t = dt_port; } } return data_t; @@ -96,9 +94,6 @@ static int end_data(struct automata * a) case dt_domain: a->cookie.domain = a->buf; break; - case dt_port: - a->cookie.port = a->buf; - break; case dt_version: if (strcmp(a->buf, "1")) { ok = 0; @@ -291,6 +286,5 @@ void magi_cookies(struct magi_request * request, const char * data) free(a.cookie.data); free(a.cookie.path); free(a.cookie.domain); - free(a.cookie.port); free(a.buf); } diff --git a/src/inner_multipart.c b/src/inner_multipart.c index 65252e5..0187f7c 100644 --- a/src/inner_multipart.c +++ b/src/inner_multipart.c @@ -2,8 +2,8 @@ #include "inner_multipart.h" #include "error.h" +#include "inner_tools.h" #include "param.h" -#include "utils.h" #include <ctype.h> #include <stdio.h> #include <stdlib.h> @@ -74,9 +74,9 @@ static char * extract_filename(char * n) n += strspn(n, " \t") + 1; if (*n == '"') { ++n; - return magi_str_create_copy(n, strchr(n, '"')); + return magi_str_create_copy(n, n - strchr(n, '"')); } else { - return magi_str_create_copy(n, n + strcspn(n, " \t")); + return magi_str_create_copy(n, strcspn(n, " \t")); } } @@ -89,12 +89,12 @@ static int content_disposition(struct automata * a) n += strspn(n, " \t") + 1; if (*n == '"') { ++n; - a->param.name = magi_str_create_copy(n, strchr(n, '"')); + a->param.name = magi_str_create_copy(n, n - strchr(n, '"')); if (!a->param.name || !*a->param.name) { return 0; } } else { - a->param.name = magi_str_create_copy(n, n + strcspn(n, " \t")); + a->param.name = magi_str_create_copy(n, strcspn(n, " \t")); if (!a->param.name || !is_str_token(a->param.name)) { return 0; } diff --git a/src/inner_tools.c b/src/inner_tools.c new file mode 100644 index 0000000..d958851 --- /dev/null +++ b/src/inner_tools.c @@ -0,0 +1,51 @@ +#include "inner_tools.h" + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + + +void magi_str_lowercase(char *str) +{ + if (!str) { + return; + } + for (; *str; ++str) { + *str = tolower(*str); + } +} + +char *magi_str_create_copy(const char *first, int len) +{ + char *copy = magi_str_create(len); + if (copy) { + memcpy(copy, first, len); + } + return copy; +} + +char *magi_str_create(int len) +{ + char *str = malloc(len + 1); + if (str) { + str[len] = 0; + } + return str; +} + +int magi_str_add(char **dest, int *len, int *size, char c) +{ + if (!*dest) { + *dest = magi_str_create(1); + } else if (*len + 1 == *size) { + *size *= 2; + *dest = realloc(*dest, *size); + } + if (!*dest) { + return 0; + } + (*dest)[*len] = c; + ++*len; + (*dest)[*len] = 0; + return 1; +} diff --git a/src/inner_tools.h b/src/inner_tools.h new file mode 100644 index 0000000..87489c8 --- /dev/null +++ b/src/inner_tools.h @@ -0,0 +1,19 @@ +#ifndef MAGI_INCLUDED_INNER_TOOLS +#define MAGI_INCLUDED_INNER_TOOLS +/* Magi Inner Tools + * This file must not be included in header files intended for external use, + * it contains some common utility functions for use in source files. + */ + + +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 *first, int len); +char *magi_str_create(int len); + +/* Null only in case of error; if *dest is null creates string. */ +int magi_str_add(char **dest, int *len, int *size, char c); + + +#endif diff --git a/src/request.c b/src/request.c index e905e53..8c3a101 100644 --- a/src/request.c +++ b/src/request.c @@ -21,73 +21,6 @@ void magi_request_setup(struct magi_request * request) } } -void magi_tempfiles_add(struct magi_tempfiles * tmps, - const char * name, - const char * path, - int max) -{ - 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 - 1] = name; - tmps->locations[tmps->count - 1] = path; - tmps->maximums[tmps->count - 1] = 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; - static int unlimited; - static int left; - if (!f) { - const char * loc = table->locations[pos]; - f = fopen(loc, "wb"); - left = table->maximums[pos]; - unlimited = !left; - } - if (unlimited) { - fwrite(addon, 1, addon_len, f); - } else { - int min = left < addon_len ? left : addon_len; - fwrite(addon, 1, min, f); - left -= min; - } - if (is_addon_last) { - fclose(f); - f = 0; - } - return; - } - } -} - -void magi_request_setup_tempfiles(struct magi_request * request, - struct magi_tempfiles * table) -{ - request->file_callback = tempfiles; - request->file_callback_userdata = table; -} - static void request_free(struct magi_request * request) { @@ -147,3 +80,78 @@ void magi_request_destroy(struct magi_request * request) request_annul(request); } } + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Tempfiles Callback + */ +void magi_tempfiles_add(struct magi_tempfiles * tmps, + const char * name, + const char * path, + int max) +{ + if (!tmps) { + return; + } + if (tmps->count++) { + tmps->tmps = realloc(tmps->tmps, sizeof(*tmps->tmps) * tmps->count); + } else { + tmps->tmps = malloc(sizeof(*tmps->tmps)); + } + tmps->tmps[tmps->count - 1].param_name = name; + tmps->tmps[tmps->count - 1].location = path; + tmps->tmps[tmps->count - 1].maximum = max; +} + +void magi_tempfiles_destroy(struct magi_tempfiles * tmps) +{ + if (!tmps) { + return; + } + free(tmps->tmps); +} + +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; + if (!file->file_name || !strcmp(file->file_name, "")) { + return; + } + for (pos = 0; pos != table->count; ++pos) { + if (!strcmp(table->tmps[pos].param_name, file->param_name)) { + static FILE * f = 0; + static int unlimited; + static int left; + if (!f) { + const char * loc = table->tmps[pos].location; + f = fopen(loc, "wb"); + left = table->tmps[pos].maximum; + unlimited = !left; + } + if (unlimited) { + fwrite(addon, 1, addon_len, f); + } else { + int min = left < addon_len ? left : addon_len; + fwrite(addon, 1, min, f); + left -= min; + } + if (is_addon_last) { + fclose(f); + f = 0; + } + return; + } + } +} + +void magi_request_setup_tempfiles(struct magi_request * request, + struct magi_tempfiles * table) +{ + request->file_callback = tempfiles; + request->file_callback_userdata = table; +} diff --git a/src/request.h b/src/request.h index ffa239f..18d6c8e 100644 --- a/src/request.h +++ b/src/request.h @@ -113,8 +113,5 @@ void magi_tempfiles_destroy(magi_tempfiles *tmps); void magi_request_setup_tempfiles(magi_request *request, magi_tempfiles *table); -/* Destroys request. */ -void magi_request_destroy(struct magi_request * request); - #endif diff --git a/src/response.c b/src/response.c index e4cd342..fdeba34 100644 --- a/src/response.c +++ b/src/response.c @@ -1,6 +1,6 @@ #include "response.h" -#include "utils.h" +#include "inner_tools.h" #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -23,6 +23,7 @@ void magi_response_setup(magi_response *response) void magi_response_content_type(magi_response *response, const char *type) { +<<<<<<< HEAD static const char *const ct = "Content-Type: "; static const int ctlen = 15; const int len = strlen(type); @@ -30,6 +31,15 @@ void magi_response_content_type(magi_response *response, const char *type) response->content_type = malloc(ctlen + len + 1); memcpy(response->content_type, ct, ctlen); memcpy(response->content_type + ctlen, type, len + 1); +======= + const char * const messages[] = { + "Content-Type: application/xhtml+xml", /* magi_xhtml */ + }; + if (!response->content_type) { + response->content_type = magi_str_create_copy(messages[type], + strlen(messages[type])); + } +>>>>>>> master } void magi_response_add(magi_response *r, const char *addon) diff --git a/src/urlenc.c b/src/urlenc.c index bb0bada..64a087e 100644 --- a/src/urlenc.c +++ b/src/urlenc.c @@ -1,6 +1,6 @@ #include "urlenc.h" -#include "utils.h" +#include "inner_tools.h" #include <ctype.h> #include <string.h> diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 931d1e4..0000000 --- a/src/utils.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "utils.h" - -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - - -void magi_str_lowercase(char * str) -{ - if (str) { - while (*str) { - *str = tolower(*str); - ++str; - } - } -} - -char * magi_str_create_copy(const char * begin, const char * end) -{ - char * res; - res = malloc(end - begin + 1); - if (res) { - memcpy(res, begin, end - begin); - res[end - begin] = 0; - } - return res; -} - -char * magi_str_create(int len) -{ - char * str = malloc(len + 1); - if (str) { - str[len] = 0; - } - return str; -} - -int magi_str_add(char ** dest, int * len, int * size, char c) -{ - if (*len + 1 == *size) { - *size *= 2; - *dest = realloc(*dest, *size); - } - if (*dest) { - (*dest)[*len] = c; - ++*len; - (*dest)[*len] = 0; - } - return !!*dest; -} diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index f67c66c..0000000 --- a/src/utils.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef MAGI_INCLUDED_UTILS -#define MAGI_INCLUDED_UTILS - - -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); - -/* Null only in case of error. */ -int magi_str_add(char ** dest, int * len, int * size, char c); - - -#endif |