diff options
author | Aleksey Veresov <aleksey@veresov.pro> | 2020-04-04 19:39:38 +0300 |
---|---|---|
committer | Aleksey Veresov <aleksey@veresov.pro> | 2020-04-04 19:39:38 +0300 |
commit | 07657b8a9f5c2fd9047594ec8604b9c439a999e4 (patch) | |
tree | efc15cde781a74f5782c780422dde6ec0dfb1af4 | |
parent | cb6a86b8db5a26b92073c4fc5ceffffa8cd842c2 (diff) | |
download | magi-07657b8a9f5c2fd9047594ec8604b9c439a999e4.tar magi-07657b8a9f5c2fd9047594ec8604b9c439a999e4.tar.xz magi-07657b8a9f5c2fd9047594ec8604b9c439a999e4.zip |
[magi] Finalization.
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | README | 10 | ||||
-rw-r--r-- | examples/append.c | 52 | ||||
-rw-r--r-- | examples/cookie.c | 40 | ||||
-rw-r--r-- | examples/echo.c | 96 | ||||
-rw-r--r-- | examples/fastcgi.c | 33 | ||||
-rw-r--r-- | examples/upload.c | 62 | ||||
-rw-r--r-- | include/magi.h | 3 | ||||
-rw-r--r-- | include/magi/error.h | 4 | ||||
-rw-r--r-- | include/magi/fastcgi.h | 14 | ||||
-rw-r--r-- | include/magi/loadfiles.h | 2 | ||||
-rw-r--r-- | include/magi/parse.h (renamed from include/magi/cgi.h) | 12 | ||||
-rw-r--r-- | include/magi/request.h | 20 | ||||
-rw-r--r-- | include/magi/response.h | 63 | ||||
-rw-r--r-- | include/magi/session.h | 17 | ||||
-rw-r--r-- | include/magi/urlenc.h | 2 | ||||
-rw-r--r-- | man/magi.3 | 7 | ||||
-rw-r--r-- | src/error.c | 19 | ||||
-rw-r--r-- | src/fastcgi.c | 23 | ||||
-rw-r--r-- | src/parse.c (renamed from src/cgi.c) | 70 | ||||
-rw-r--r-- | src/request.c | 11 | ||||
-rw-r--r-- | src/response.c | 151 | ||||
-rw-r--r-- | src/session.c | 58 |
23 files changed, 266 insertions, 509 deletions
@@ -2,8 +2,6 @@ # Compilation Options # Debug mode [yes/no] (allowing to debug the library via gdb): DEBUG ?= no -# Optional modules (remove unwanted ones): -MODULES ?= cgi fastcgi loadfiles urlenc # Specify your favourite C compiler here: CC ?= gcc @@ -30,8 +28,8 @@ EXADIR = examples # Library itself: LIB = libmagi.a # Modules: -EXTERNAL = cookie error file param request response session $(MODULES) -INTERNAL = cookies multipart tools urlencoded +EXTERNAL = $(foreach x,$(notdir $(wildcard $(INCLUDE)/magi/*.h)),$(x:.h=)) +INTERNAL = $(foreach x,$(notdir $(wildcard $(SRCDIR)/*.h)),$(x:.h=)) # Default target is library: TARGET = $(BUILD)/$(LIB) @@ -1,5 +1,5 @@ Description -Magi Library (libmagi) implements Gateway Interfaces, namely CGI and FastCGI. +Magi Library (libmagi) implements Common Gateway Interface. Overview Magi are free and open-source software: legal info is in the 'LICENSE' file. @@ -22,12 +22,12 @@ Running 'make examples' produces executable for each example in 'examples'. Descriptions and details are in corresponding source files. Usage -magi_request handles incoming request, general workflow for CGI is to: +magi_request handles incoming request, general workflow is to: 1. prepare request for analysis, setup defaults via magi_request_init; -2. analyse CGI request via magi_cgi; +2. analyse CGI request via magi_parse; 4. do your work, and response request: - 1. specify headers (e.g. magi_response_cookie), - 2. fill response body (e.g. magi_response); + 1. specify and send headers (magi_response_...), + 2. response body (through stdout); 5. finally, free memory via magi_request_free. Motivation diff --git a/examples/append.c b/examples/append.c index 8417796..fe85492 100644 --- a/examples/append.c +++ b/examples/append.c @@ -1,40 +1,46 @@ +/* This is the very basic example, showing work with form fields to append + * specified by user in form addon into some file (i.e. "file_to_append"). + */ #include <magi.h> #include <stdio.h> -void response(magi_request *r) +void append(magi_request *r) { char *data = magi_request_param(r, "addon"); - magi_response(r, - "<!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>Append to File</title></head>" - "<body>"); - if (data) { + if (data) { /* If field "addon" was in request: */ FILE *file = fopen("file_to_append", "a"); - fputs(data, file); + fputs(data, file); /* Append addon into the file. */ fclose(file); - magi_response(r, "<p>Appended!</p>"); + printf("<p>Appended!</p>"); /* And display success message. */ } - magi_response(r, - "<form action='/cgi-bin/append' method='post'><fieldset>" - "<input type='text' name='addon' value='Whatever you want to add.' />" - "<input type='submit' value='Append' />" - "</fieldset></form>" - "</body>" - "</html>"); +} + +void response(magi_request *r) +{ + magi_response_default(); /* Pass default headers and send body: */ + printf("<!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>Append to File</title></head>" + "<body>"); + append(r); + printf("<form action='/cgi-bin/append' method='post'><fieldset>" + "<input type='text' name='addon' value='Whatever you want to add.' />" + "<input type='submit' value='Append' />" + "</fieldset></form>" + "</body></html>"); } int main() { magi_request request; - magi_request_init(&request); - if (magi_cgi(&request)) { - response(&request); - } else { - magi_response_error(&request); + magi_request_init(&request); /* Setting defaults. */ + if (magi_parse(&request)) { /* If parsing was done successful */ + response(&request); /* we need to response the request. */ + } else { /* And display error overwise: */ + magi_error_response(request.error); } - magi_request_free(&request); + magi_request_free(&request); /* Don't forget to free everything after. */ return 0; } diff --git a/examples/cookie.c b/examples/cookie.c index e18c100..c64ca0c 100644 --- a/examples/cookie.c +++ b/examples/cookie.c @@ -1,38 +1,46 @@ +/* Basic example of cookie handling through response headers. + * It lists all recieved cookies and set one via magi_response, + * showing work with headers in magi. + */ #include <magi.h> void list_cookies(magi_request *r) { magi_cookies *current; - magi_response(r, "Cookies:"); + printf("Cookies:"); + /* Iterate through all cookies in request to show them in body: */ for (current = r->cookies; current; current = current->next) { magi_cookie *c = ¤t->item; - magi_response_format(r, "<br />[%s]=[%s]", c->name, c->data); + printf("<br />[%s]=[%s]", c->name, c->data); } } void response(magi_request *r) { - magi_response_cookie(r, "cookie", "monster"); - magi_response(r, - "<!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>Cookie Listing and Setting</title></head>" - "<body><p>"); + magi_response head; + magi_response_init(&head); /* Setting defaults. */ + /* Set cookie "cookie" with value "monster" on clientside: */ + magi_response_cookie(&head, "cookie", "monster"); + magi_response_free(&head); /* Send headers and start sending body: */ + printf("<!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>Cookie Listing and Setting</title></head>" + "<body><p>"); list_cookies(r); - magi_response(r, "</p></body></html>"); + printf("</p></body></html>"); } int main() { magi_request request; - magi_request_init(&request); - if (magi_cgi(&request)) { - response(&request); - } else { - magi_response_error(&request); + magi_request_init(&request); /* Setting defaults. */ + if (magi_parse(&request)) { /* If parsing was done successful */ + response(&request); /* we need to response the request. */ + } else { /* And display error overwise: */ + magi_error_response(request.error); } - magi_request_free(&request); + magi_request_free(&request); /* Don't forget to free everything after. */ return 0; } diff --git a/examples/echo.c b/examples/echo.c index 7cb4a3b..4f6975a 100644 --- a/examples/echo.c +++ b/examples/echo.c @@ -1,111 +1,111 @@ +/* This is useful example echoing request data in response. + */ #include <magi.h> void list_cookies(magi_request *r) { magi_cookies *current = r->cookies; - magi_response(r, "<p>"); + printf("<p>"); + /* Iterate through all cookies in request to show them in body: */ for (current = r->cookies; current; current = current->next) { magi_cookie *c = ¤t->item; - magi_response(r, "Cookie with name ["); - magi_response(r, c->name); + printf("Cookie with name [%s", c->name); if (c->data) { - magi_response(r, "] is ["); - magi_response(r, c->data); + printf("] is [%s", c->data); } if (c->domain) { - magi_response(r, "] for domain ["); - magi_response(r, c->domain); + printf("] for domain [%s", c->domain); } if (c->path) { - magi_response(r, "] for path ["); - magi_response(r, c->path); + printf("] for path [%s", c->path); } - magi_response(r, "]<br />"); + printf("]<br />"); } - magi_response(r, "</p>"); + printf("</p>"); } -void list_params(magi_request *r, magi_params *current) +void list_params(magi_params *current) { - magi_response(r, "<p>"); + printf("<p>"); + /* Iterate through specified params to show them in body: */ for (; current; current = current->next) { magi_param *p = ¤t->item; - magi_response_format(r, "[%s] is [%s]<br />", p->name, p->data); + printf("[%s] is [%s]<br />", p->name, p->data); } - magi_response(r, "</p>"); + printf("</p>"); } void list_files(magi_request *r) { magi_files *current; - magi_response(r, "<p>"); + printf("<p>"); + /* Iterate through all field files in request to show them in body: */ for (current = r->files; current; current = current->next) { magi_file *f = ¤t->item; - magi_response_format(r, "[%s] was [%s] on clientside<br />", - f->field, f->filename); + printf("[%s] was [%s] on clientside<br />", f->field, f->filename); } - magi_response(r, "</p>"); + printf("</p>"); } void show_meta(magi_request *r) { - magi_response(r, "<p>I was called "); + printf("<p>I was called "); if (r->is_secure) { - magi_response(r, "securely "); + printf("securely "); } - magi_response_format(r, "with method [%s", r->method); + printf("with method [%s", r->method); if (r->host) { - magi_response_format(r, "] on server [%s", r->host); + printf("] on server [%s", r->host); } if (r->script) { - magi_response_format(r, "] being script on [%s", r->script); + printf("] being script on [%s", r->script); } if (r->path) { - magi_response_format(r, "] with requested path [%s", r->path); + printf("] with requested path [%s", r->path); } - magi_response(r, "]</p>"); + printf("]</p>"); } void response(magi_request *r) { - magi_response(r, - "<!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>Echo</title></head>" - "<body>"); + magi_response_default(); /* Pass default headers and send body: */ + printf("<!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>Echo</title></head>" + "<body>"); - magi_response(r, "<h1>Echo CGI Script</h1>"); + printf("<h1>Echo CGI Script</h1>"); show_meta(r); - magi_response(r, "<h2>Cookies:</h2>"); + printf("<h2>Cookies:</h2>"); list_cookies(r); - magi_response(r, "<h2>Parameters:</h2>"); - list_params(r, r->meta); + printf("<h2>Parameters:</h2>"); + list_params(r->meta); - magi_response(r, "<h2>URL Parameters:</h2>"); - list_params(r, r->head); + printf("<h2>URL Parameters:</h2>"); + list_params(r->head); - magi_response(r, "<h2>Body Parameters:</h2>"); - list_params(r, r->body); + printf("<h2>Body Parameters:</h2>"); + list_params(r->body); - magi_response(r, "<h2>Files:</h2>"); + printf("<h2>Files:</h2>"); list_files(r); - magi_response(r, "</body></html>"); + printf("</body></html>"); } int main() { magi_request request; - magi_request_init(&request); - if (magi_cgi(&request)) { - response(&request); - } else { - magi_response_error(&request); + magi_request_init(&request); /* Setting defaults. */ + if (magi_parse(&request)) { /* If parsing was done successful */ + response(&request); /* we need to response the request. */ + } else { /* And display error overwise: */ + magi_error_response(request.error); } - magi_request_free(&request); + magi_request_free(&request); /* Don't forget to free everything after. */ return 0; } diff --git a/examples/fastcgi.c b/examples/fastcgi.c deleted file mode 100644 index 16668f3..0000000 --- a/examples/fastcgi.c +++ /dev/null @@ -1,33 +0,0 @@ -#include <magi.h> - - -void response(magi_request *r) -{ - magi_response(r, - "<!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>Fast CGI</title></head>" - "<body>Hi!</body>" - "</html>"); -} - -int main() -{ - magi_session session; - magi_request request; - magi_request_init(&request); - magi_session_init(&session); - magi_session_unix(&session, "unix.sock"); - while (magi_fastcgi(&session, &request)) { - if (request.error) { - magi_response_error(&request); - } else { - response(&request); - } - magi_request_free(&request); - } - magi_request_free(&request); - magi_session_free(&session); - return 0; -} diff --git a/examples/upload.c b/examples/upload.c index bf8eed1..d07db16 100644 --- a/examples/upload.c +++ b/examples/upload.c @@ -1,51 +1,59 @@ +/* This is basic example of handling file uploading. + * It is using magi_loadfiles callback to load file temporarily and change its + * name into the one specified by user. + */ #include <magi.h> #include <stdio.h> -void response(magi_request *r) +void upload(magi_request *r) { char *name = magi_request_param(r, "name"); const magi_file *data = magi_request_file(r, "data"); - magi_response(r, - "<!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>Upload File</title></head>" - "<body>"); - if (name && data) { - rename("data", name); - magi_response(r, "<p>Uploaded!</p>"); + if (name && data) { /* If file to load and its name are in the request: */ + rename("data", name); /* Rename loaded file to designated name. */ + printf("<p>Uploaded!</p>"); /* And display success message. */ } - magi_response(r, - "<form action='/cgi-bin/upload' method='post' " - "enctype='multipart/form-data'><fieldset>" - "<input type='text' name='name' value='filename' />" - "<input type='file' name='data' />" - "<input type='submit' value='Upload' />" - "</fieldset></form>" - "</body>" - "</html>"); +} + +void response(magi_request *r) +{ + magi_response_default(); /* Pass default headers and send body: */ + printf("<!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>Upload File</title></head>" + "<body>"); + upload(r); + printf("<form action='/cgi-bin/upload' method='post' " + "enctype='multipart/form-data'><fieldset>" + "<input type='text' name='name' value='filename' />" + "<input type='file' name='data' />" + "<input type='submit' value='Upload' />" + "</fieldset></form>" + "</body></html>"); } void get(magi_request *r) { magi_loadfiles rules = { 0, 0 }; + /* Setup callback to load file from "data" field into file "data": */ magi_loadfiles_add(&rules, "data", "data", 0); - magi_loadfiles_set(r, &rules); - magi_cgi(r); - magi_loadfiles_free(&rules); + magi_loadfiles_set(r, &rules); /* Setup request to use the callback. */ + magi_parse(r); + magi_loadfiles_free(&rules); /* Free data of callback. */ } int main() { magi_request request; magi_request_init(&request); - get(&request); - if (request.error) { - magi_response_error(&request); + get(&request); /* Parse request. */ + if (request.error) { /* If error occurred display error message: */ + magi_error_response(request.error); } else { - response(&request); + response(&request); /* Overwise response request. */ } - magi_request_free(&request); + magi_request_free(&request); /* Don't forget to free everything after. */ return 0; } diff --git a/include/magi.h b/include/magi.h index d8e1245..e89a322 100644 --- a/include/magi.h +++ b/include/magi.h @@ -3,13 +3,12 @@ /* All headers are included here. * You can include this, if you don't want to think about headers. */ -#include "magi/cgi.h" #include "magi/cookie.h" #include "magi/error.h" -#include "magi/fastcgi.h" #include "magi/file.h" #include "magi/loadfiles.h" #include "magi/param.h" +#include "magi/parse.h" #include "magi/request.h" #include "magi/response.h" #include "magi/urlenc.h" diff --git a/include/magi/error.h b/include/magi/error.h index e928ed2..19538ae 100644 --- a/include/magi/error.h +++ b/include/magi/error.h @@ -1,6 +1,7 @@ #ifndef MAGI_INCLUDED_ERROR #define MAGI_INCLUDED_ERROR /* Error codes and messages + * You can output default error message via magi_error_response. */ @@ -20,4 +21,7 @@ typedef enum magi_error { const char *magi_error_message(magi_error error); +void magi_error_response(magi_error error); + + #endif diff --git a/include/magi/fastcgi.h b/include/magi/fastcgi.h deleted file mode 100644 index 3d91b7b..0000000 --- a/include/magi/fastcgi.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MAGI_INCLUDED_FASTCGI -#define MAGI_INCLUDED_FASTCGI -/* Fast CGI implementation - */ -#include "session.h" -#include "request.h" - - -int magi_fastcgi_head(magi_session *s, magi_request *r); -int magi_fastcgi_body(magi_session *s, magi_request *r); -int magi_fastcgi(magi_session *s, magi_request *r); - - -#endif diff --git a/include/magi/loadfiles.h b/include/magi/loadfiles.h index bf3421c..d6cdaeb 100644 --- a/include/magi/loadfiles.h +++ b/include/magi/loadfiles.h @@ -6,8 +6,6 @@ * via magi_loadfiles_add, specifying which file-parameter to load into which * path, and what are size limitations for it. When table is complete, setup * your request to use this callback with magi_loadfiles_set. - * - * This module is optional. */ #include "request.h" diff --git a/include/magi/cgi.h b/include/magi/parse.h index 0de637e..a71018a 100644 --- a/include/magi/cgi.h +++ b/include/magi/parse.h @@ -1,18 +1,18 @@ -#ifndef MAGI_INCLUDED_CGI -#define MAGI_INCLUDED_CGI -/* Common Gateway Interface implementation +#ifndef MAGI_INCLUDED_PARSE +#define MAGI_INCLUDED_PARSE +/* Common Gateway Interface request parsing implementation */ #include "request.h" /* Analyses non-post part of request from environment. True if ok. */ -int magi_cgi_head(magi_request *request); +int magi_parse_head(magi_request *request); /* Complete request with post body from standard input. True if ok. */ -int magi_cgi_body(magi_request *request); +int magi_parse_body(magi_request *request); /* Shortcut for analysing both head and body of request. True if ok. */ -int magi_cgi(magi_request *request); +int magi_parse(magi_request *request); #endif diff --git a/include/magi/request.h b/include/magi/request.h index 7245feb..32d31b5 100644 --- a/include/magi/request.h +++ b/include/magi/request.h @@ -2,13 +2,13 @@ #define MAGI_INCLUDED_REQUEST /* Request handler * - * Can be created via magi_{gateway interface name}_head, but will have - * nullified POST-related fields (params & files). Reason is unlimited - * POST body size, with possible dependence of wanted limits from data of - * headers (e.g. session id from cookies, enabling some users to load more). - * To proceed POST use magi_{gateway interface name}_body, specifying - * settings if necessary. - * Or just use shortcut magi_{gateway interface name} to do both parts. + * Can be created via magi_parse_head, but will have nullified POST-related + * fields (params & files). Reason is unlimited POST body size, + * with possible dependence of wanted limits from data of headers + * (e.g. session id from cookies, enabling some users to load more); and also + * dependence of place to load files by callback from head data. + * To proceed POST use magi_parse_body, specifying settings if necessary. + * Or just use shortcut magi_parse to do both parts. */ #include "cookie.h" #include "error.h" @@ -44,12 +44,6 @@ typedef struct magi_request { magi_file_callback callback; /* Callback to actually load files. */ magi_request_limits limits; - - /* Implementation of response for this request. - * Especially helpful in case of FastCGI, - * when you can handle many requests simultaniously, - * and need to know how to response each of them. */ - struct magi_response_implementation *response; } magi_request; diff --git a/include/magi/response.h b/include/magi/response.h index 11acf75..e67b279 100644 --- a/include/magi/response.h +++ b/include/magi/response.h @@ -1,66 +1,41 @@ #ifndef MAGI_INCLUDED_RESPONSE #define MAGI_INCLUDED_RESPONSE -/* General response functionality for magi_request. +/* General CGI headers response functionality. * - * There are two parts of response, namely header and body. - * You can directly dive into filling the body, since default headers are set. - * Defult content-type is HTML, status is 200 (OK). - * - * Use body related functions only after dealing with headers. - * (Since storing possibly large body in memory is a bad idea, - * all headers should be sent before anything from the body.) + * There are two parts of response, namely head and body. + * You need to output head first, it is done with magi_response_free or with + * magi_response_default. Default content-type is HTML, status is 200 (Ok). + * Body can be outputed after head into stdout. */ -#include "request.h" +#include "cookie.h" +#include "param.h" #include <stdio.h> #include <stdarg.h> -/* * * TODO: Comments * * */ -typedef void (*magi_response_method_head)(void *ud, magi_param *header); -typedef void (*magi_response_method_start_body)(void *ud); -typedef void (*magi_response_method_body)(void *ud, const char *data, int len); -typedef void (*magi_response_method_fmt)(void *ud, const char *f, va_list a); -typedef void (*magi_response_method_file)(void *ud, FILE *file); -typedef void (*magi_response_method_close)(void *ud); - -typedef struct magi_response_methods { - magi_response_method_head head; - magi_response_method_start_body start_body; - magi_response_method_body body; - magi_response_method_fmt format; - magi_response_method_file file; - magi_response_method_close close; -} magi_response_methods; - -typedef struct magi_response_implementation { - const magi_response_methods *methods; - void *userdata; +typedef struct magi_response { magi_params *head_response; magi_params *head_general; magi_params *head_entity; - int head_done; -} magi_response_implementation; - +} magi_response; -void magi_response_status(magi_request *r, int code, const char *description); -void magi_response_cookie(magi_request *r, const char *name, const char *data); -void magi_response_cookie_complex(magi_request *r, magi_cookie *c); -void magi_response_cookie_discard(magi_request *r, const char *name); +void magi_response_init(magi_response *r); +void magi_response_free(magi_response *r); -void magi_response_header(magi_request *r, const char *name, const char *data); +void magi_response_default(); -void magi_response_content_length(magi_request *r, int length); -void magi_response_content_type(magi_request *r, const char *type); -void magi_response_head(magi_request *r); +void magi_response_status(magi_response *r, int code, const char *description); -void magi_response(magi_request *r, const char *addon); -void magi_response_format(magi_request *r, const char *format, ...); -void magi_response_file(magi_request *r, FILE *file); +void magi_response_cookie(magi_response *r, const char *n, const char *d); +void magi_response_cookie_complex(magi_response *r, magi_cookie *c); +void magi_response_cookie_discard(magi_response *r, const char *name); +void magi_response_header(magi_response *r, const char *n, const char *d); -void magi_response_error(magi_request *r); +void magi_response_content_length(magi_response *r, int length); +void magi_response_content_type(magi_response *r, const char *type); #endif diff --git a/include/magi/session.h b/include/magi/session.h deleted file mode 100644 index c1c5903..0000000 --- a/include/magi/session.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef MAGI_INCLUDED_SESSION -#define MAGI_INCLUDED_SESSION - - -typedef struct magi_session { - int socket; -} magi_session; - - -void magi_session_init(magi_session *s); -void magi_session_free(magi_session *s); - -int magi_session_inet(magi_session *s, const char *address, int port); -int magi_session_unix(magi_session *s, const char *path); - - -#endif diff --git a/include/magi/urlenc.h b/include/magi/urlenc.h index dadebe7..1b629f9 100644 --- a/include/magi/urlenc.h +++ b/include/magi/urlenc.h @@ -9,8 +9,6 @@ * RFC 3986 describes URL-encoding. Briefly it is changing every space into * plus sign and every not alpha-numerical and not @c "~-._" character into * percent sign followed by hexadecimal representation of given character. - * - * This module is optional. */ diff --git a/man/magi.3 b/man/magi.3 deleted file mode 100644 index 0c7ed1a..0000000 --- a/man/magi.3 +++ /dev/null @@ -1,7 +0,0 @@ -.TH MAGI 3 "2020-03-18" "0.1" "Magi Library" -.SH NAME -magi \- ANSI C dependency-free CGI and FastCGI library -.SH LIBRARY -library "libmagi" -.SH SYNOPSIS -.In #include <magi.h> diff --git a/src/error.c b/src/error.c index a1c3153..eb16eb6 100644 --- a/src/error.c +++ b/src/error.c @@ -1,9 +1,11 @@ #include "error.h" +#include <stdio.h> + const char *magi_error_message(magi_error error) { - const char *const messages[] = { + static const char *const messages[] = { 0, /* magi_error_none */ "No boundary for multipart.", /* magi_error_nobound */ "Content-Type is unknown.", /* magi_error_unknown */ @@ -17,3 +19,18 @@ const char *magi_error_message(magi_error error) }; return messages[error]; } + +void magi_error_response(magi_error error) +{ + fputs("Status: 400 Bad Request\r\n" + "Content-Type: text/html\r\n\r\n" + "<!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>", stdout); + fputs(magi_error_message(error), stdout); + fputs("</h2></body></html>", stdout); +} diff --git a/src/fastcgi.c b/src/fastcgi.c deleted file mode 100644 index 8959c14..0000000 --- a/src/fastcgi.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "fastcgi.h" - - -int magi_fastcgi_head(magi_session *s, magi_request *r) -{ - (void)s; - (void)r; - return 1; -} - - -int magi_fastcgi_body(magi_session *s, magi_request *r) -{ - (void)s; - (void)r; - return 1; -} - - -int magi_fastcgi(magi_session *s, magi_request *r) -{ - return magi_fastcgi_head(s, r) && magi_fastcgi_body(s, r); -} @@ -1,4 +1,4 @@ -#include "cgi.h" +#include "parse.h" #include "cookie.h" #include "cookies.h" @@ -152,67 +152,8 @@ static int next() return getchar(); } -static void mhead(void *any, magi_param *header) -{ - (void)any; - fputs(header->name, stdout); - fputs(": ", stdout); - fputs(header->data, stdout); - fputs("\r\n", stdout); -} - -static void mstart_body() -{ - fputs("\r\n", stdout); -} - -static void mbody(void *any, const char *data, int len) -{ - (void)any; - fwrite(data, 1, len, stdout); -} - -static void mformat(void *any, const char *format, va_list args) -{ - (void)any; - vprintf(format, args); -} - -static void mfile(void *any, FILE *file) -{ - (void)any; - while (!feof(file)) { - char buf[64]; - int len = fread(buf, 1, 64, file); - fwrite(buf, 1, len, stdout); - } -} - -static void mclose() {} - -static void setup_response(magi_request *r) -{ - static const magi_response_methods m = { - mhead, - mstart_body, - mbody, - mformat, - mfile, - mclose - }; - r->response = malloc(sizeof(*r->response)); - r->response->methods = &m; - r->response->userdata = 0; - r->response->head_response = 0; - r->response->head_general = 0; - r->response->head_entity = 0; - r->response->head_done = 0; - magi_response_content_type(r, "text/html"); - magi_response_status(r, 200, "OK"); -} - /* Interfacial CGI Request Handling */ -int magi_cgi_head(magi_request *request) +int magi_parse_head(magi_request *request) { request->cookies = 0; request->files = 0; @@ -223,11 +164,10 @@ int magi_cgi_head(magi_request *request) cgi_env(request); cgi_cookies(request); cgi_url(request); - setup_response(request); return !request->error; } -int magi_cgi_body(magi_request *request) +int magi_parse_body(magi_request *request) { magi_error *e = &request->error; request->error = magi_error_none; @@ -260,7 +200,7 @@ int magi_cgi_body(magi_request *request) return !request->error; } -int magi_cgi(magi_request *request) +int magi_parse(magi_request *request) { - return magi_cgi_head(request) && magi_cgi_body(request); + return magi_parse_head(request) && magi_parse_body(request); } diff --git a/src/request.c b/src/request.c index d80bf20..bf8fc1d 100644 --- a/src/request.c +++ b/src/request.c @@ -1,6 +1,5 @@ #include "request.h" -#include "response.h" #include <stdlib.h> @@ -30,7 +29,6 @@ static void request_free(magi_request *request) free(request->host); free(request->script); free(request->path); - free(request->response); } static void request_annul(magi_request *request) @@ -44,7 +42,6 @@ static void request_annul(magi_request *request) request->host = 0; request->script = 0; request->path = 0; - request->response = 0; } void magi_request_free(magi_request *request) @@ -55,14 +52,6 @@ void magi_request_free(magi_request *request) magi_params_free(request->head); magi_params_free(request->body); magi_files_free(request->files); - request->response->methods->close(request->response->userdata); - free(request->response->userdata); - magi_params_free(request->response->head_response); - magi_params_free(request->response->head_general); - magi_params_free(request->response->head_entity); - free(request->response->head_response); - free(request->response->head_general); - free(request->response->head_entity); request_free(request); request_annul(request); } diff --git a/src/response.c b/src/response.c index dc36bb0..c5b7575 100644 --- a/src/response.c +++ b/src/response.c @@ -6,11 +6,55 @@ #include <string.h> -void magi_response_status(magi_request *r, int code, const char *description) +void magi_response_init(magi_response *r) +{ + r->head_response = 0; + r->head_general = 0; + r->head_entity = 0; + magi_response_status(r, 200, "OK"); + magi_response_content_type(r, "text/html"); +} + +static void response_headers(magi_params *p) +{ + for (; p; p = p->next) { + fputs(p->item.name, stdout); + fputs(": ", stdout); + fputs(p->item.data, stdout); + fputs("\r\n", stdout); + } +} + +void magi_response_free(magi_response *r) +{ + response_headers(r->head_response); + response_headers(r->head_general); + response_headers(r->head_entity); + fputs("\r\n", stdout); + magi_params_free(r->head_response); + magi_params_free(r->head_general); + magi_params_free(r->head_entity); + free(r->head_response); + free(r->head_general); + free(r->head_entity); + r->head_response = 0; + r->head_general = 0; + r->head_entity = 0; +} + + +void magi_response_default() +{ + fputs("Status: 200 Ok\r\n" + "Content-Type: text/html\r\n\r\n", stdout); +} + + +void magi_response_status(magi_response *r, int code, const char *description) { int dlen; magi_param addon; - if (r->response->head_done || code <= 99 || 600 <= code) { + if (code <= 99 || 600 <= code) { return; } dlen = strlen(description); @@ -21,15 +65,15 @@ void magi_response_status(magi_request *r, int code, const char *description) addon.data[2] = '0' + code % 10; addon.data[3] = ' '; memcpy(addon.data + 4, description, dlen + 1); - magi_params_set(&r->response->head_response, &addon); + magi_params_set(&r->head_response, &addon); } -void magi_response_cookie(magi_request *r, const char *name, const char *data) +void magi_response_cookie(magi_response *r, const char *name, const char *data) { magi_param addon; int nlen; int dlen; - if (r->response->head_done || !name || !data) { + if (!name || !data) { return; } nlen = strlen(name); @@ -39,10 +83,10 @@ void magi_response_cookie(magi_request *r, const char *name, const char *data) memcpy(addon.data, name, nlen); addon.data[nlen] = '='; memcpy(addon.data + nlen + 1, data, dlen + 1); - magi_params_add(&r->response->head_general, &addon); + magi_params_add(&r->head_general, &addon); } -void magi_response_cookie_complex(magi_request *r, magi_cookie *c) +void magi_response_cookie_complex(magi_response *r, magi_cookie *c) { magi_param addon; char *pointer; @@ -50,7 +94,7 @@ void magi_response_cookie_complex(magi_request *r, magi_cookie *c) const int cdsize = 9; const int cpsize = 7; const int cmsize = 10; - if (r->response->head_done || !c->name) { + if (!c->name) { return; } nlen = strlen(c->name); @@ -83,14 +127,14 @@ void magi_response_cookie_complex(magi_request *r, magi_cookie *c) memcpy(pointer, "; Max-Age=", cmsize); memcpy(pointer + cmsize, c->max_age, msize - cmsize); } - magi_params_add(&r->response->head_general, &addon); + magi_params_add(&r->head_general, &addon); } -void magi_response_cookie_discard(magi_request *r, const char *name) +void magi_response_cookie_discard(magi_response *r, const char *name) { magi_param addon; int len; - if (r->response->head_done || !name) { + if (!name) { return; } len = strlen(name); @@ -98,29 +142,22 @@ void magi_response_cookie_discard(magi_request *r, const char *name) addon.data = malloc(len + 13); memcpy(addon.data, name, len); memcpy(addon.data + len, "=; Max-Age=1", 13); - magi_params_add(&r->response->head_general, &addon); + magi_params_add(&r->head_general, &addon); } -void magi_response_header(magi_request *r, const char *name, const char *data) +void magi_response_header(magi_response *r, const char *name, const char *data) { magi_param addon; - if (r->response->head_done) { - return; - } addon.name = magi_str_create_copy(name, strlen(name)); addon.data = magi_str_create_copy(data, strlen(data)); - magi_params_add(&r->response->head_general, &addon); + magi_params_add(&r->head_general, &addon); } -void magi_response_content_length(magi_request *r, int length) +void magi_response_content_length(magi_response *r, int length) { magi_param addon; - int len; - if (r->response->head_done) { - return; - } + int len = 1; addon.name = magi_str_create_copy("Content-Length", 14); - len = 1; addon.data = malloc(len + 1); *addon.data = '0' + length % 10; while (length /= 10) { @@ -129,75 +166,13 @@ void magi_response_content_length(magi_request *r, int length) ++len; } addon.data[len] = 0; - magi_params_set(&r->response->head_entity, &addon); + magi_params_set(&r->head_entity, &addon); } -void magi_response_content_type(magi_request *r, const char *type) +void magi_response_content_type(magi_response *r, const char *type) { magi_param addon; - if (r->response->head_done) { - return; - } addon.name = magi_str_create_copy("Content-Type", 12); addon.data = magi_str_create_copy(type, strlen(type)); - magi_params_set(&r->response->head_entity, &addon); -} - -static void response_headers(magi_response_implementation *r, magi_params *p) -{ - for (; p; p = p->next) { - r->methods->head(r->userdata, &p->item); - } -} - -void magi_response_head(magi_request *r) -{ - if (r->response->head_done) { - return; - } - response_headers(r->response, r->response->head_response); - response_headers(r->response, r->response->head_general); - response_headers(r->response, r->response->head_entity); - r->response->methods->start_body(r->response->userdata); - r->response->head_done = 1; -} - -void magi_response(magi_request *r, const char *addon) -{ - magi_response_head(r); - if (!addon) { - return; - } - r->response->methods->body(r->response->userdata, addon, strlen(addon)); -} - -void magi_response_format(magi_request *r, const char *format, ...) -{ - va_list args; - magi_response_head(r); - va_start(args, format); - r->response->methods->format(r->response->userdata, format, args); - va_end(args); -} - -void magi_response_file(magi_request *r, FILE *file) -{ - magi_response_head(r); - r->response->methods->file(r->response->userdata, file); -} - - -void magi_response_error(magi_request *r) -{ - magi_response_status(r, 400, "Bad Request"); - magi_response(r, - "<!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>"); - magi_response(r, magi_error_message(r->error)); - magi_response(r, "</h2></body></html>"); + magi_params_set(&r->head_entity, &addon); } diff --git a/src/session.c b/src/session.c deleted file mode 100644 index aec0e6d..0000000 --- a/src/session.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "session.h" - -#include <arpa/inet.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/un.h> - - -enum { listen_backlog = 64 }; - -void magi_session_init(magi_session *s) -{ - s->socket = 0; -} - -void magi_session_free(magi_session *s) -{ - if (s->socket) { - shutdown(s->socket, SHUT_RDWR); - s->socket = 0; - } -} - - -int magi_session_inet(magi_session *s, const char *address, int port) -{ - struct sockaddr_in addr; - if (s->socket) { - return 0; - } - s->socket = socket(AF_INET, SOCK_STREAM, 0); - if (s->socket == -1) { - s->socket = 0; - return 0; - } - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = inet_addr(address); - return !bind(s->socket, (struct sockaddr *)&addr, sizeof(addr)) && - !listen(s->socket, listen_backlog); -} - -int magi_session_unix(magi_session *s, const char *path) -{ - struct sockaddr_un addr; - if (s->socket) { - return 0; - } - s->socket = socket(AF_UNIX, SOCK_STREAM, 0); - if (s->socket == -1) { - s->socket = 0; - return 0; - } - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); - return !bind(s->socket, (struct sockaddr *)&addr, sizeof(addr)) && - !listen(s->socket, listen_backlog); -} |