aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Veresov <aleksey@veresov.pro>2020-04-04 19:39:38 +0300
committerAleksey Veresov <aleksey@veresov.pro>2020-04-04 19:39:38 +0300
commit07657b8a9f5c2fd9047594ec8604b9c439a999e4 (patch)
treeefc15cde781a74f5782c780422dde6ec0dfb1af4
parentcb6a86b8db5a26b92073c4fc5ceffffa8cd842c2 (diff)
downloadmagi-07657b8a9f5c2fd9047594ec8604b9c439a999e4.tar
magi-07657b8a9f5c2fd9047594ec8604b9c439a999e4.tar.xz
magi-07657b8a9f5c2fd9047594ec8604b9c439a999e4.zip
[magi] Finalization.
-rw-r--r--Makefile6
-rw-r--r--README10
-rw-r--r--examples/append.c52
-rw-r--r--examples/cookie.c40
-rw-r--r--examples/echo.c96
-rw-r--r--examples/fastcgi.c33
-rw-r--r--examples/upload.c62
-rw-r--r--include/magi.h3
-rw-r--r--include/magi/error.h4
-rw-r--r--include/magi/fastcgi.h14
-rw-r--r--include/magi/loadfiles.h2
-rw-r--r--include/magi/parse.h (renamed from include/magi/cgi.h)12
-rw-r--r--include/magi/request.h20
-rw-r--r--include/magi/response.h63
-rw-r--r--include/magi/session.h17
-rw-r--r--include/magi/urlenc.h2
-rw-r--r--man/magi.37
-rw-r--r--src/error.c19
-rw-r--r--src/fastcgi.c23
-rw-r--r--src/parse.c (renamed from src/cgi.c)70
-rw-r--r--src/request.c11
-rw-r--r--src/response.c151
-rw-r--r--src/session.c58
23 files changed, 266 insertions, 509 deletions
diff --git a/Makefile b/Makefile
index 18fd89b..6d2c8bc 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/README b/README
index f363b11..1e05137 100644
--- a/README
+++ b/README
@@ -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 = &current->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 = &current->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 = &current->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 = &current->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);
-}
diff --git a/src/cgi.c b/src/parse.c
index 7b0551d..58ae001 100644
--- a/src/cgi.c
+++ b/src/parse.c
@@ -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);
-}