From a93c561921b79e5f27b986292fe70dce1879f91e Mon Sep 17 00:00:00 2001 From: Aleksey Veresov Date: Fri, 21 Feb 2020 18:09:22 +0300 Subject: [magi] --- Makefile | 33 ++++++------ examples/Makefile | 11 ++-- examples/fcgi.c | 2 +- include/magi/loadfile.h | 31 +++++++++++ include/magi/request.h | 27 ---------- readme | 9 ++-- src/cgi.c | 8 +-- src/cookies.c | 4 +- src/error.c | 4 +- src/loadfile.c | 77 ++++++++++++++++++++++++++ src/multipart.c | 4 +- src/request.c | 77 -------------------------- src/tools.c | 25 +++++---- src/tools.h | 14 ++--- src/urlenc.c | 2 +- src/urlencoded.c | 140 +++++++++++++++++++++++------------------------- src/urlencoded.h | 13 +++-- 17 files changed, 237 insertions(+), 244 deletions(-) create mode 100644 include/magi/loadfile.h create mode 100644 src/loadfile.c diff --git a/Makefile b/Makefile index f25e658..93f4a2d 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ -### TODO: Partial compilation (e.g. cgi+urlenc, but without fcgi) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Compilation Options # Debug mode (allowing to debug the library via gdb): # DEBUG = yes -# Specify your favourite C compiler here (e.g. tcc): +# Optional modules (remove unwanted ones): +MODULES = cgi fastcgi loadfile urlenc +# Specify your favourite C compiler here: CC = gcc @@ -20,9 +21,17 @@ else CFLAGS += -O3 endif +# Interfacial files to compile: +INTER = cookie error file param request response $(MODULES) + # Object files listing: +INC_DIR = include/magi SRC_DIR = src -SRC = $(wildcard $(SRC_DIR)/*.c) +INTER_H = $(foreach name,$(INTER),$(INC_DIR)/$(name).h) +INTER_C = $(foreach name,$(INTER),$(SRC_DIR)/$(name).c) +INNER_H = $(wildcard $(SRC_DIR)/*.h) +INNER_C = $(INNER_H:.h=.c) +SRC = $(INTER_C) $(INNER_C) OBJ = $(SRC:.c=.o) @@ -33,27 +42,15 @@ default: $(LIB) # Cleaning means removing everything automatically produced: clean: - rm -f $(OBJ) $(LIB) deps.mk + rm -f $(OBJ) $(LIB) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Compilation # Compile object files from corresponding source and header: -%.o: %.c %.h - $(CC) $(CFLAGS) -c $< -o $@ +%.o: %.c + $(CC) $(CFLAGS) -I $(INC_DIR) -c $< -o $@ # Packing object files into library: $(LIB): $(OBJ) ar rcs $@ $^ - - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# 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 8f5282c..28e9bed 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -2,14 +2,14 @@ # Compilation Options # Debug mode (allowing to debug the examples via gdb): # DEBUG = yes +# Examples to build by default: +EXAMPLES = append cookie upload echo # 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: @@ -22,8 +22,7 @@ endif # Including magi library headers and setting linker to use it: INCLUDE = -I ../include LFLAGS = -L.. -lmagi - -# Specification of library file to produce it, if not provided: +# Specify library file to set it as a prerequisite for compilation: MAGI = ../libmagi.a @@ -42,7 +41,3 @@ clean: # Compile executables from corresponding sources and library: %: %.c $(MAGI) $(CC) $(CFLAGS) $(INCLUDE) $< $(LFLAGS) -o $@ - -# Run make for library to produce it: -$(MAGI): - cd ..; $(MAKE) diff --git a/examples/fcgi.c b/examples/fcgi.c index bb5645e..de2f525 100644 --- a/examples/fcgi.c +++ b/examples/fcgi.c @@ -1,4 +1,4 @@ -/* * * TODO * * */ +/* * * TODO -- not valid yet * * */ #include #include #include diff --git a/include/magi/loadfile.h b/include/magi/loadfile.h new file mode 100644 index 0000000..942c298 --- /dev/null +++ b/include/magi/loadfile.h @@ -0,0 +1,31 @@ +#ifndef MAGI_INCLUDED_LOADFILE +#define MAGI_INCLUDED_LOADFILE + +#include "request.h" + + +typedef struct magi_loadfile { + const char *param_name; /* Form field name, in which file is expected. */ + const char *location; /* Location to load file in. */ + int maximum; /* Limit in bytes. Null <=> unlimited. */ +} magi_loadfile; + +typedef struct magi_loadfiles { + int count; + magi_loadfile *files; +} magi_loadfiles; + +void magi_loadfiles_add(magi_loadfiles *table, + const char *name, + const char *path, + int max); + +void magi_loadfiles_destroy(magi_loadfiles *table); + +/* Setup request callback with files loaded into corresponding to their + * parameter names locations; paths are in magi_loadfiles struct. */ +void magi_request_setup_loadfiles(magi_request *request, + magi_loadfiles *table); + + +#endif diff --git a/include/magi/request.h b/include/magi/request.h index b15f085..47acb27 100644 --- a/include/magi/request.h +++ b/include/magi/request.h @@ -89,31 +89,4 @@ void magi_request_setup(magi_request *request); void magi_request_destroy(magi_request *request); -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Tempfiles Callback - */ -typedef struct magi_tempfile { - const char *param_name; /* Form field name, in which file is expected. */ - const char *location; /* Location to load file in. */ - int maximum; /* Null <=> unlimited. */ -} magi_tempfile; - -typedef struct magi_tempfiles { - int count; - magi_tempfile *tmps; -} magi_tempfiles; - -void magi_tempfiles_add(magi_tempfiles *tmps, - const char *name, - const char *path, - int max); - -void magi_tempfiles_destroy(magi_tempfiles *tmps); - -/* Setup request callback with files loaded into corresponding to their - * parameter names locations; paths are in magi_tempfiles struct. */ -void magi_request_setup_tempfiles(magi_request *request, - magi_tempfiles *table); - - #endif diff --git a/readme b/readme index fb36636..3160155 100644 --- a/readme +++ b/readme @@ -4,11 +4,12 @@ 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. +library. All source and inner header files are located in the 'src' directory. The 'include' directory is intended to be used in your include environment. -Headers themselves are in 'magi' subdirectory of include, due to their short -and simple names. If you are not about choosing which header to include and -just want everything to be included you can use 'magi.h'. +Headers in it contain library interface and its documentation in comments. +They are in subdirectory 'include/magi', due to their short and simple names. +If you are not about choosing which header to include and just want everything +to be included you can '#include '. Compiling Compilation and its options are described in the 'Makefile'. diff --git a/src/cgi.c b/src/cgi.c index 20df48e..efb05c2 100644 --- a/src/cgi.c +++ b/src/cgi.c @@ -1,14 +1,14 @@ #include "cgi.h" #include "cookie.h" +#include "cookies.h" #include "error.h" #include "file.h" -#include "inner_cookies.h" -#include "inner_tools.h" -#include "inner_multipart.h" -#include "inner_urlencoded.h" +#include "multipart.h" #include "param.h" #include "request.h" +#include "tools.h" +#include "urlencoded.h" #include #include #include diff --git a/src/cookies.c b/src/cookies.c index 9dfde7a..8b610d4 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -1,7 +1,7 @@ /* * * TODO * * */ -#include "inner_cookies.h" +#include "cookies.h" -#include "inner_tools.h" +#include "tools.h" #include #include diff --git a/src/error.c b/src/error.c index efe6dbf..a1c3153 100644 --- a/src/error.c +++ b/src/error.c @@ -1,9 +1,9 @@ #include "error.h" -const char * magi_error_message(enum magi_error error) +const char *magi_error_message(magi_error error) { - 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 */ diff --git a/src/loadfile.c b/src/loadfile.c new file mode 100644 index 0000000..e8e0655 --- /dev/null +++ b/src/loadfile.c @@ -0,0 +1,77 @@ +#include "loadfile.h" + +#include +#include + + +void magi_loadfiles_add(magi_loadfiles *table, + const char *name, + const char *path, + int max) +{ + static const int size = sizeof(*table->files); + if (!table) { + return; + } + if (table->count) { + table->files = realloc(table->files, size * table->count + size); + } else { + table->files = malloc(size); + } + table->files[table->count].param_name = name; + table->files[table->count].location = path; + table->files[table->count].maximum = max; + table->count++; +} + +void magi_loadfiles_destroy(magi_loadfiles *table) +{ + if (!table) { + return; + } + free(table->table); +} + +static void loadfiles(magi_file *file, + char *addon, + int addon_len, + int is_addon_last, + void *userdata) +{ + magi_loadfiles *table = userdata; + int pos; + if (!file->file_name || !strcmp(file->file_name, "")) { + return; + } + for (pos = 0; pos != table->count; ++pos) { + if (!strcmp(table->files[pos].param_name, file->param_name)) { + static FILE *f = 0; + static int unlimited; + static int left; + if (!f) { + const char * loc = table->files[pos].location; + f = fopen(loc, "wb"); + left = table->files[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_loadfiles(magi_request *request, magi_loadfiles *table) +{ + request->file_callback = loadfiles; + request->file_callback_userdata = table; +} diff --git a/src/multipart.c b/src/multipart.c index 0187f7c..001b4d1 100644 --- a/src/multipart.c +++ b/src/multipart.c @@ -1,9 +1,9 @@ /* Support for multifile controls are not provided. */ -#include "inner_multipart.h" +#include "multipart.h" #include "error.h" -#include "inner_tools.h" #include "param.h" +#include "tools.h" #include #include #include diff --git a/src/request.c b/src/request.c index 8c3a101..3677eb7 100644 --- a/src/request.c +++ b/src/request.c @@ -3,9 +3,7 @@ #include "cookie.h" #include "file.h" #include "param.h" -#include #include -#include void magi_request_setup(struct magi_request * request) @@ -80,78 +78,3 @@ 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/tools.c b/src/tools.c index d958851..ecfcd6c 100644 --- a/src/tools.c +++ b/src/tools.c @@ -1,4 +1,4 @@ -#include "inner_tools.h" +#include "tools.h" #include #include @@ -33,19 +33,22 @@ char *magi_str_create(int len) return str; } -int magi_str_add(char **dest, int *len, int *size, char c) +int magi_str_add(magi_str *str, char c) { - if (!*dest) { - *dest = magi_str_create(1); - } else if (*len + 1 == *size) { - *size *= 2; - *dest = realloc(*dest, *size); + 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 (!*dest) { + if (!str->dest) { + str->len = 0; + str->size = 0; return 0; } - (*dest)[*len] = c; - ++*len; - (*dest)[*len] = 0; + str->data[str->len++] = c; + str->data[str->len] = 0; return 1; } diff --git a/src/tools.h b/src/tools.h index 8d66131..f7e701d 100644 --- a/src/tools.h +++ b/src/tools.h @@ -1,9 +1,5 @@ #ifndef MAGI_INCLUDED_TOOLS #define MAGI_INCLUDED_TOOLS -/* * Collection of helpful functions for internal use. - * - * blah... - */ void magi_str_lowercase(char *str); @@ -12,8 +8,14 @@ void magi_str_lowercase(char *str); 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); + +typedef struct magi_str { + char *data; + int size; + int len; +} magi_str; + +int magi_str_add(magi_str *str, char c); #endif diff --git a/src/urlenc.c b/src/urlenc.c index 64a087e..28fbd48 100644 --- a/src/urlenc.c +++ b/src/urlenc.c @@ -1,6 +1,6 @@ #include "urlenc.h" -#include "inner_tools.h" +#include "tools.h" #include #include diff --git a/src/urlencoded.c b/src/urlencoded.c index e3ea56a..9d596ed 100644 --- a/src/urlencoded.c +++ b/src/urlencoded.c @@ -1,6 +1,6 @@ -#include "inner_urlencoded.h" +#include "urlencoded.h" -#include "inner_tools.h" +#include "tools.h" #include #include #include @@ -9,30 +9,30 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Local Shortcuts */ + static int is_hex(char c) +{ + return isdigit(c) || strchr("abcdef", tolower(c)); +} + /* Call only if is_hex(c). */ static int from_hex(char c) { if (isdigit(c)) { return c - '0'; } else { - return toupper(c) - 'A' + 10; + return tolower(c) - 'a' + 10; } } -static int is_hex(char c) -{ - return isdigit(c) || strchr("ABCDEF", toupper(c)); -} - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * URL Decoding */ -static int deurl(char ** data) +static int deurl(char **data) { - char * val = *data; - int ti; - int ci; + char *val = *data; + int ti; + int ci; if (!val) { *data = malloc(1); **data = 0; @@ -40,14 +40,13 @@ static int deurl(char ** data) } 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 { + if (!is_hex(val[ci + 1]) || !is_hex(val[ci + 2])) { return 0; } + /* Since chars can be signed, arithmetics are not safe. */ + val[ti] = from_hex(val[ci + 2]) | /* 0000xxxx */ + from_hex(val[ci + 1]) << 4; /* XXXXxxxx */ + ci += 2; /* Two extra characters are readed from code. */ } else if (val[ci] == '+') { val[ti] = ' '; } else { @@ -62,83 +61,76 @@ static int deurl(char ** data) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Urlencoded Automata */ -enum st { st_error = 0, st_name, st_data }; - -struct automata { - struct magi_param_list ** list; - struct magi_param param; - int size; - int len; -}; - - -static enum st parse_name(struct automata * a, char c) +typedef struct automata { + magi_param_list **list; + magi_str name; + magi_str data; +} automata; +typedef void *(*state)(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 == ';') { /* Impossible character means error. */ - return st_error; + if (c == '&' || c == ';') { + return 0; } - if (c == '=') { /* Separator ends name. */ - a->size = 1; - a->len = 0; - return st_data; - } - - if (!magi_str_add(&a->param.name, &a->len, &a->size, c)) { - return st_error; + if (c == '=') { + if (!deurl(&a->name.data)) { + return 0; + } + return state_parse_name; } - return st_name; + magi_str_add(&a->name, c); + return a->name.size ? state_parse_name : 0; } -static enum st end_data(struct automata * a) +static int add_to_list(automata *a) { - 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->param.name = 0; - a->param.data = 0; - return st_name; - } + magi_param param; + param.name = a->name.data; + param.data = a->data.data; + if (!magi_param_list_add(a->list, param)) { + return 0; } - return st_error; + a->name.data = 0; + a->data.data = 0; + return 1; } -static enum st parse_data(struct automata * a, char c) +static void state_parse_data(automata *a, char c) { - if (c == '=') { /* Impossible character means error. */ - return st_error; + if (c == '=') { + return 0; } - 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; + if (c == '&' || c == ';') { + if (!deurl(&a->data.data) || !add_to_list(a)) { + return 0; + } + return state_parse_name; } - return st_data; + magi_str_add(&a->data, c); + return a->data.size ? state_parse_data : 0; } -void magi_urlencoded(struct magi_param_list ** list, - struct magi_request * request, - const char * encoded) +magi_error magi_urlencoded(magi_param_list **list, const char *encoded) { - enum st state; - struct automata a = { 0, { 0, 0 }, 1, 0 }; - a.list = list; - *list = 0; + st state; + automata a = { 0, { 0, 0 }, 1, 0 }; + a.list = list; + *list = 0; if (!encoded || !*encoded) { - return; + return 0; } - for (state = st_name; state && *encoded; ++encoded) { - if (state == st_name) { - state = parse_name(&a, *encoded); - } else { - state = parse_data(&a, *encoded); + for (; *encoded; ++encoded) { + s = s(&a, *encoded); + if (!s) { + return auto_free(a); } } if (state == st_name || !state || !end_data(&a)) { free(a.param.name); free(a.param.data); - request->error = magi_error_urlenc; + return auto_free(a); } + return 0; } diff --git a/src/urlencoded.h b/src/urlencoded.h index e07ed6f..319f01e 100644 --- a/src/urlencoded.h +++ b/src/urlencoded.h @@ -1,13 +1,12 @@ -#ifndef MAGI_INCLUDED_INNER_URLENCODED -#define MAGI_INCLUDED_INNER_URLENCODED +#ifndef MAGI_INCLUDED_URLENCODED +#define MAGI_INCLUDED_URLENCODED -#include "request.h" +#include "error.h" +#include "param.h" -/* Fills request->url_params via parsing encoded data. */ -void magi_urlencoded(magi_param_list **list, - magi_request *request, - const char *encoded); +/* Add decoded params from 'encoded' to 'list'. */ +magi_error magi_urlencoded(magi_param_list **list, const char *encoded); #endif -- cgit v1.2.3