From 36dda991898ce621a0b0f7103f763690e73fa0ff Mon Sep 17 00:00:00 2001 From: Aleksey Veresov Date: Sat, 18 Jul 2020 07:56:11 +0300 Subject: Docs were extended, consts were added. --- Makefile | 24 +++---- README | 4 +- examples/upload.c | 2 +- include/magi.hpp | 7 +++ include/magi/cookie.h | 6 +- include/magi/file.h | 14 ++--- include/magi/param.h | 11 ++-- include/magi/response.h | 6 +- man/magi.3 | 164 +++++++++++++++++++++++++++++++++++++++--------- man/magi_cookie.3 | 0 man/magi_error.3 | 0 man/magi_file.3 | 0 man/magi_loadfiles.3 | 0 man/magi_param.3 | 8 --- man/magi_parse.3 | 0 man/magi_request.3 | 0 man/magi_response.3 | 0 man/magi_urlenc.3 | 51 --------------- src/cookie.c | 4 +- src/file.c | 5 +- src/loadfiles.c | 10 +-- src/multipart.c | 6 +- src/param.c | 6 +- src/parse.c | 4 +- src/request.c | 2 +- src/response.c | 6 +- 26 files changed, 199 insertions(+), 141 deletions(-) create mode 100644 include/magi.hpp delete mode 100644 man/magi_cookie.3 delete mode 100644 man/magi_error.3 delete mode 100644 man/magi_file.3 delete mode 100644 man/magi_loadfiles.3 delete mode 100644 man/magi_param.3 delete mode 100644 man/magi_parse.3 delete mode 100644 man/magi_request.3 delete mode 100644 man/magi_response.3 delete mode 100644 man/magi_urlenc.3 diff --git a/Makefile b/Makefile index 6803d5a..eaadd6f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Compilation Options # Debug mode [yes/no] (allowing to debug the library via gdb): DEBUG ?= no @@ -6,7 +6,7 @@ DEBUG ?= no CC ?= gcc -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Preparations # Compile as ANSI C code: CFLAGS = -xc -ansi -Wall @@ -41,30 +41,30 @@ INTER_C = $(INTER_H:.h=.c) SRC = $(INTER_C) $(EXTER_C) OBJ = $(foreach o,$(SRC:.c=.o),$(BUILD)/$(o)) # Example executables: -EXASRC = $(wildcard $(EXADIR)/*.c) -EXAMPLES = $(foreach x,$(EXASRC:.c=),$(BUILD)/$(x)) +XSRC = $(wildcard $(EXADIR)/*.c) +EXAMPLES = $(foreach x,$(XSRC:.c=),$(BUILD)/$(x)) # Dependency file: DEPS = deps.mk SRCINC = -I$(INCLUDE)/magi -EXAINC = -I$(INCLUDE) +XINC = -I$(INCLUDE) SRCBUILD = $(BUILD)/$(SRCDIR) -EXABUILD = $(BUILD)/$(EXADIR) +XBLD = $(BUILD)/$(EXADIR) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Targets .PHONY: all examples clean all: $(SRCBUILD) $(TARGET) -examples: all $(EXABUILD) $(EXAMPLES) +examples: all $(XBLD) $(EXAMPLES) clean: rm -f $(TARGET) $(OBJ) $(EXAMPLES) $(DEPS) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Compilation -include $(DEPS) @@ -78,13 +78,13 @@ $(BUILD)/%.o: %.c # Compile executables from corresponding sources and library: $(BUILD)/%: %.c $(TARGET) - $(CC) $(CFLAGS) $(EXAINC) $< $(LFLAGS) -o $@ + $(CC) $(CFLAGS) $(XINC) $< $(LFLAGS) -o $@ # Create build directories, if no such: -$(SRCBUILD) $(EXABUILD): +$(SRCBUILD) $(XBLD): mkdir -p $@ # Generate dependency file, adding corresponding build prefixes: $(DEPS): $(SRC) $(EXASRC) $(EXTER_H) $(INTER_H) $(INCLUDE)/magi.h $(CC) $(SRCINC) $(SRC) -MM | sed '/^ /!s#^#$(SRCBUILD)/#' > $@ - $(CC) $(EXAINC) $(EXASRC) -MM | sed '/^ /!s#^#$(EXABUILD)/#;s/\.o//' >> $@ + $(CC) $(XINC) $(XSRC) -MM | sed '/^ /!s#^#$(XBLD)/#;s/\.o//' >> $@ diff --git a/README b/README index 1e05137..ffc70d0 100644 --- a/README +++ b/README @@ -3,8 +3,8 @@ Magi Library (libmagi) implements Common Gateway Interface. 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 inner header files are located in the 'src' directory. +They are written in ANSI C without any dependecies except for C standard +library. All source and inner header files are in the 'src' directory. The 'include' directory is intended to be used in your include environment. Headers in it contain library interface and its description in comments. They are in subdirectory 'include/magi', due to their short and simple names. diff --git a/examples/upload.c b/examples/upload.c index 8efceb7..c9752b6 100644 --- a/examples/upload.c +++ b/examples/upload.c @@ -10,7 +10,7 @@ void upload(struct magi_request *r) { char *name = magi_request_param(r, "name"); const struct magi_file *data = magi_request_file(r, "data"); - if (name && data) { /* If file to load and its name are in the request: */ + if (name && data) { /* If file and its name are in the request: */ rename("data", name); /* Rename loaded file to designated name. */ printf("

Uploaded!

"); /* And display success message. */ } diff --git a/include/magi.hpp b/include/magi.hpp new file mode 100644 index 0000000..b3547b3 --- /dev/null +++ b/include/magi.hpp @@ -0,0 +1,7 @@ +#ifndef MAGI_INCLUDED_EVERYTHING +/* Shortcut to use magi in C++, eliminates manual use of 'extern "C"'. + */ +extern "C" { +#include "magi.h" +} +#endif diff --git a/include/magi/cookie.h b/include/magi/cookie.h index 68cbe3f..40b9175 100644 --- a/include/magi/cookie.h +++ b/include/magi/cookie.h @@ -8,7 +8,7 @@ struct magi_cookie { char *name; /* Cookie name. */ char *data; /* Cookie value. */ - char *path; /* Path on which cookie is set. Without '/' at the end. */ + char *path; /* Path on which cookie is set, without '/' at the end. */ char *domain; /* Domain in wich cookie is set. * With dot at the begining. */ char *max_age; /* In seconds until discard (response only). */ @@ -25,8 +25,8 @@ struct magi_cookies { void magi_cookies_free(struct magi_cookies *cookies); /* Add newitem onto top of cookies. */ -void magi_cookies_add(struct magi_cookies **cookies, - struct magi_cookie *newitem); +void magi_cookies_add(struct magi_cookies **cookies, + const struct magi_cookie *newitem); /* Get first cookie with given name, null if no such cookie. * First cookie is the most accurate in terms of domain and path. */ diff --git a/include/magi/file.h b/include/magi/file.h index 615d8b7..a655855 100644 --- a/include/magi/file.h +++ b/include/magi/file.h @@ -22,8 +22,8 @@ struct magi_files { void magi_files_free(struct magi_files *files); /* Add newitem onto top of files. */ -void magi_files_add(struct magi_files **files, - struct magi_file *newitem); +void magi_files_add(struct magi_files **files, + const struct magi_file *newitem); /* Get first from top of files file with name, null if no such file. */ const struct magi_file *magi_files_get(const struct magi_files *files, @@ -36,11 +36,11 @@ const struct magi_file *magi_files_get(const struct magi_files *files, * and null if file is ended. * newfile flag is setted up in the beginning of new file. * Files are passed sequentialy, one by one. */ -typedef void (*magi_file_callback_act)(void *userdata, - int newfile, - struct magi_file *file, - char *addon, - int addon_len); +typedef void (*magi_file_callback_act)(void *userdata, + int newfile, + const struct magi_file *file, + const char *addon, + int addon_len); struct magi_file_callback { magi_file_callback_act act; diff --git a/include/magi/param.h b/include/magi/param.h index 44ea384..b5bb27e 100644 --- a/include/magi/param.h +++ b/include/magi/param.h @@ -20,14 +20,15 @@ struct magi_params { void magi_params_free(struct magi_params *params); /* Add newitem onto top of params. */ -void magi_params_add(struct magi_params **params, - struct magi_param *newitem); +void magi_params_add(struct magi_params **params, + const struct magi_param *newitem); /* Set newitem in params. - * If param with name of newitem is in params it will be replaced with newitem, + * If param with name of newitem is in params + * it will be replaced with newitem, * otherwise newitem will be added into the end of params. */ -void magi_params_set(struct magi_params **params, - struct magi_param *newitem); +void magi_params_set(struct magi_params **params, + const struct magi_param *newitem); /* Get data of the first from top of params parameter with name, * null if no such parameter. */ diff --git a/include/magi/response.h b/include/magi/response.h index 3ef053a..6a49a0c 100644 --- a/include/magi/response.h +++ b/include/magi/response.h @@ -22,7 +22,7 @@ struct magi_response { /* Response initialiser, setup defaults. */ void magi_response_init(struct magi_response *r); /* Send response headers. */ -void magi_response_send(struct magi_response *r); +void magi_response_send(const struct magi_response *r); /* Free memory used by response headers. */ void magi_response_free(struct magi_response *r); @@ -40,8 +40,8 @@ void magi_response_cookie(struct magi_response *r, const char *n, const char *d); /* Add cookie with additional information to response. */ -void magi_response_cookie_complex(struct magi_response *r, - struct magi_cookie *c); +void magi_response_cookie_complex(struct magi_response *r, + const struct magi_cookie *c); /* Add request to discard cookie to response. */ void magi_response_cookie_discard(struct magi_response *r, const char *name); diff --git a/man/magi.3 b/man/magi.3 index 2db61b4..1188dca 100644 --- a/man/magi.3 +++ b/man/magi.3 @@ -1,4 +1,4 @@ -.TH MAGI 3 2020-05-02 v0.0.1 "Library Manual" +.TH MAGI 3 2020-07-14 v0.0.1 "Library Manual" .SH NAME .B magi \- Common Gateway Interface (CGI) library @@ -11,7 +11,8 @@ library gives the user a way to interact with the web server through CGI as described in .IR "RFC 3875" . The library is free and open source software. -.SS Usage +It is written in ANSI C without any dependencies except C standard library. +.SS Usage overview A program using .B magi library must be linked with the @@ -22,38 +23,129 @@ Main task of the .B magi library is to analyse CGI request and represent it to the user in .B magi_request -data structure. This is done by calling +data structure. +CGI request is passed in environment variables and standard input. +You can access environment directly even then using magi, +however reading from standard input will cause a mess. +.P +The library also provides response functionality with +.B magi_response +and its methods. +Since body can be everything and only headers have specific CGI-related +structure, +.B magi +provides response functionality only for them. +Body can be outputted directly into standard output with any method you want +after forming and sending CGI headers via +.BR magi_response . +.SH REQUEST +Request structure initializtion is done with +.BR magi_request_init . +Don't forget to free everything in it then the work is done via +.BR magi_request_free . +.P +.RS +.nf +struct magi_request request; +magi_request_init(&request); +/* do your work with request */ +magi_request_free(&request); +.fi +.RE +.P +Obtaining of request is done in two parts, i.e. head via .B magi_parse_head +and body via +.BR magi_parse_body . +Analysis of head will fill everything in +.B magi_request +structure, except +.I body and -.B magi_parse_body -(such separation allows to determine body processing on head content, -if there is no need to do so you can use shortcut -.BR magi_parse ) -on initialized via -.B magi_request_init -request variable. After initialization the user is able to configure -limits on the input -.RB ( magi_request_limits , -everything is unlimited as default) -and setup callback to process files loaded as -.I multipart/form-data -.RB ( magi_file_callback , -with default addon_max of 1KB). +.I files +fields. +Such separtion has place since body processing can depend on data from head, +e.g. in case of loading files, if file size is defined by user group. +If analysis succeeded they will return 1, otherwise 0 as error mark. +More disctinct error code will be placed in +.IR request.error . +.P +.RS +.nf +if (!magi_parse_head(&request)) { + /* handle error and exit */ +} +/* configure body processing using head data */ +if (magi_parse_body(&request)) { + /* send response using request data */ +} else { + /* handle error */ +} +.fi +.RE +.P +If your case is not as advanced, you can use shortuct +.B magi_parse +to analyse both head and body. +.BR magi_parse (&request) +is literally +.BR magi_parse_head (&request) +&& +.BR magi_parse_body (&request). +.P +.RS +.nf +if (magi_parse(&request)) { + /* send response using request data */ +} else { + /* handle error */ +} +.fi +.RE +.SS What's in request? +.SS Loading files +.SH RESPONSE +.SS URL encoding +It is described in +.IR "RFC 3986" . +Briefly it is replacement of every space into plus sign and every not +alpha-numerical or not one of "~-._" character inte percent sign +followed by hexademical representation of given character byte. +.P The .B magi -library comes with -.B magi_loadfiles -callback usable for the majority of cases. +library provides functions to form url-encoded strings, which is very useful +in forming response. Use +.B magi_urlenc_size +to find what the size of code will be and then call +.B magi_urlenc +for encoding itself. .P -CGI response utilities are also provided. The CGI headers are -outputted with -.B magi_response -and its methods. You can output defaults (Content-Type: text/html; -Status: 200 Ok) by calling -.BR magi_response_default . -Utility to form urlencoded strings is provided via -.BR magi_urlenc . -.SS Error handling +For example, lets form URL to search in DuckDuckGo for provided char +.RI * query +in char +.RI * url . +.P +.RS +.nf +const char *prefix = "http://duckduckgo.com/?q="; +const int prelen = strlen(prefix); +const int urlencsize = magi_urlenc_size(query); +url = malloc(prelen + urlencsize + 1); +strcpy(url, prefix); +magi_urlenc(query, url + prelen); +url[prelen + urlencsize] = 0; +.fi +.RE +.P +Note that second argument of +.B magi_urlenc +which is encoding destination should be at least size of +.B magi_urlenc_size +of plain data, and that +.B magi_urlenc +doesn't write zero to form null-terminated string in its output. +.SH ERRORS If function is returning pointer, error is only in case of null returned. If function is returning .I int @@ -65,6 +157,20 @@ code is in field of .B magi_request structure. For other modules error codes seem to be overkill. +.SH COMPATIBILTY +The +.B magi +library runs on any UNIX environment, +i.e. Linux, OpenBSD, FreeBSD, macOS, etc. +Windows is currently not supported. +.P +The library is compatible with C++, since it avoid using typedefs for structs. +However it doesn't have 'extern "C"' in the headers, so you need to wrap your +includes with it manually or use +.B #include +which is a shortcut for include of +.I magi.h +wrapped in 'extern "C"' construct. .SH AUTHORS AND LICENSE Copyrigth 2019-2020 .B Aleksey Veresov diff --git a/man/magi_cookie.3 b/man/magi_cookie.3 deleted file mode 100644 index e69de29..0000000 diff --git a/man/magi_error.3 b/man/magi_error.3 deleted file mode 100644 index e69de29..0000000 diff --git a/man/magi_file.3 b/man/magi_file.3 deleted file mode 100644 index e69de29..0000000 diff --git a/man/magi_loadfiles.3 b/man/magi_loadfiles.3 deleted file mode 100644 index e69de29..0000000 diff --git a/man/magi_param.3 b/man/magi_param.3 deleted file mode 100644 index 0044993..0000000 --- a/man/magi_param.3 +++ /dev/null @@ -1,8 +0,0 @@ -.TH MAGI 3 2020-05-02 v0.0.1 "Library Manual" -.SH NAME -.B magi_param -\- parameter as key-value pair -.SH SYNOPSYS -.SH DESCRIPTION -.SH RETURN VALUES -.SH EXAMPLES diff --git a/man/magi_parse.3 b/man/magi_parse.3 deleted file mode 100644 index e69de29..0000000 diff --git a/man/magi_request.3 b/man/magi_request.3 deleted file mode 100644 index e69de29..0000000 diff --git a/man/magi_response.3 b/man/magi_response.3 deleted file mode 100644 index e69de29..0000000 diff --git a/man/magi_urlenc.3 b/man/magi_urlenc.3 deleted file mode 100644 index ed919eb..0000000 --- a/man/magi_urlenc.3 +++ /dev/null @@ -1,51 +0,0 @@ -.TH MAGI 3 2020-05-02 v0.0.1 "Library Manual" -.SH NAME -.B magi_urlenc -\- implementation of url encoding -.SH SYNOPSYS -.B #include -.P -int -.BR magi_urlenc_size "(const char *" plain ); -.P -void -.BR magi_urlenc "(const char *" plain ", char *" code ); -.SH DESCRIPTION -Can be helpful in forming urls in response. Use -.B magi_urlenc_size -to find what the size of code will be and then call -.B magi_urlenc -for encoding itself. -.P -URL encoding is described in -.IR "RFC 3986" . -Briefly it is replacement of every space into plus sign and every not -alpha-numerical or not one of "~-._" character into percent sign -followed by hexademical representation of given character byte. -.P -.B code -passed to -.B magi_urlenc -should be at least size of -.BR magi_urlenc_size ( plain ). -.SH RETURN VALUES -.BR magi_urlenc_size : -size of url encoding of -.BR plain . -.SH EXAMPLES -The following forms URL to search in DuckDuckGo for provided -.RB "char *" query -in -.RB "char *" url . -.P -.RS -.nf -const char *prefix = "http://duckduckgo.com/?q="; -const int prelen = strlen(prefix); -const int urlencsize = magi_urlenc_size(query); -url = malloc(prelen + urlencsize + 1); -strcpy(url, prefix); -magi_urlenc(query, url + prelen); -url[prelen + urlencsize] = 0; -.fi -.RE diff --git a/src/cookie.c b/src/cookie.c index ebca599..c6a4956 100644 --- a/src/cookie.c +++ b/src/cookie.c @@ -17,8 +17,8 @@ void magi_cookies_free(struct magi_cookies *cookies) } } -void magi_cookies_add(struct magi_cookies **cookies, - struct magi_cookie *newitem) +void magi_cookies_add(struct magi_cookies **cookies, + const struct magi_cookie *newitem) { struct magi_cookies *node = malloc(sizeof(*node)); if (node) { diff --git a/src/file.c b/src/file.c index 30b085c..b3727f1 100644 --- a/src/file.c +++ b/src/file.c @@ -4,7 +4,8 @@ #include -void magi_files_add(struct magi_files **files, struct magi_file *newitem) +void magi_files_add(struct magi_files **files, + const struct magi_file *newitem) { struct magi_files *node = malloc(sizeof(*node)); if (node) { @@ -15,7 +16,7 @@ void magi_files_add(struct magi_files **files, struct magi_file *newitem) } const struct magi_file *magi_files_get(const struct magi_files *files, - const char *name) + const char *name) { if (!files || !name) { return 0; diff --git a/src/loadfiles.c b/src/loadfiles.c index 963e351..49cf503 100644 --- a/src/loadfiles.c +++ b/src/loadfiles.c @@ -34,11 +34,11 @@ void magi_loadfiles_free(struct magi_loadfiles *table) table->count = 0; } -static void loadfiles_callback(void *userdata, - int newfile, - struct magi_file *file, - char *addon, - int addon_len) +static void loadfiles_callback(void *userdata, + int newfile, + const struct magi_file *file, + const char *addon, + int addon_len) { int pos; struct magi_loadfiles *table = userdata; diff --git a/src/multipart.c b/src/multipart.c index a412603..75cbc25 100644 --- a/src/multipart.c +++ b/src/multipart.c @@ -124,7 +124,7 @@ static int param_end(struct automata *a) a->newfile, &a->file, a->buf, a->buf_size); a->request->callback.act(a->request->callback.userdata, 0, &a->file, 0, 0); - a->newfile = 1; + a->newfile = 1; a->readed -= a->buf_size; a->buf_size = 0; magi_files_add(&a->request->files, &a->file); @@ -215,7 +215,7 @@ static void apply_callback(struct automata *a) if (a->file.filename && full) { a->request->callback.act(a->request->callback.userdata, a->newfile, &a->file, a->buf, a->buf_size); - a->newfile = 0; + a->newfile = 0; a->readed -= a->buf_size; a->buf_size = 0; } @@ -270,7 +270,7 @@ static void *data_add(struct automata *a, char c) */ static void *state_begin(struct automata *a, char c) { - if (sepget(a) != c) { /* 'c' is not wanted character from 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++; diff --git a/src/param.c b/src/param.c index 5854231..adc2b8b 100644 --- a/src/param.c +++ b/src/param.c @@ -4,7 +4,8 @@ #include -void magi_params_add(struct magi_params **params, struct magi_param *newitem) +void magi_params_add(struct magi_params **params, + const struct magi_param *newitem) { struct magi_params *node = malloc(sizeof(*node)); if (node) { @@ -14,7 +15,8 @@ void magi_params_add(struct magi_params **params, struct magi_param *newitem) } } -void magi_params_set(struct magi_params **params, struct magi_param *newitem) +void magi_params_set(struct magi_params **params, + const struct magi_param *newitem) { if (!*params) { magi_params_add(params, newitem); diff --git a/src/parse.c b/src/parse.c index fa8b54a..0ca1cd9 100644 --- a/src/parse.c +++ b/src/parse.c @@ -164,7 +164,7 @@ static int next(void *userdata) if (last) { free(buffer); buffer = 0; - return EOF; + return EOF; } left = fread(buffer, 1, size, stdin); last = left != size; @@ -203,7 +203,7 @@ int magi_parse_body(struct magi_request *request) char *boundary = bound(t); if (boundary && *boundary) { magi_parse_multipart(request, boundary, next, - &request->limits.read_buffer); + &request->limits.read_buffer); } else { *e = magi_error_nobound; } diff --git a/src/request.c b/src/request.c index e957889..d401e67 100644 --- a/src/request.c +++ b/src/request.c @@ -13,7 +13,7 @@ void magi_request_init(struct magi_request *request) request->limits.params_meta = 0; request->limits.params_head = 0; request->limits.params_body = 0; - request->limits.read_buffer = 65536; + request->limits.read_buffer = 65536; } } diff --git a/src/response.c b/src/response.c index 43da57e..8809837 100644 --- a/src/response.c +++ b/src/response.c @@ -25,7 +25,7 @@ static void response_headers(struct magi_params *p) } } -void magi_response_send(struct magi_response *r) +void magi_response_send(const struct magi_response *r) { response_headers(r->head_response); response_headers(r->head_general); @@ -94,8 +94,8 @@ void magi_response_cookie(struct magi_response *r, magi_params_add(&r->head_general, &addon); } -void magi_response_cookie_complex(struct magi_response *r, - struct magi_cookie *c) +void magi_response_cookie_complex(struct magi_response *r, + const struct magi_cookie *c) { char *pointer; struct magi_param addon; -- cgit v1.2.3