From 07657b8a9f5c2fd9047594ec8604b9c439a999e4 Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Sat, 4 Apr 2020 19:39:38 +0300
Subject: [magi] Finalization.
---
Makefile | 6 +-
README | 10 +-
examples/append.c | 52 +++++----
examples/cookie.c | 40 ++++---
examples/echo.c | 96 ++++++++---------
examples/fastcgi.c | 33 ------
examples/upload.c | 62 ++++++-----
include/magi.h | 3 +-
include/magi/cgi.h | 18 ----
include/magi/error.h | 4 +
include/magi/fastcgi.h | 14 ---
include/magi/loadfiles.h | 2 -
include/magi/parse.h | 18 ++++
include/magi/request.h | 20 ++--
include/magi/response.h | 63 ++++-------
include/magi/session.h | 17 ---
include/magi/urlenc.h | 2 -
man/magi.3 | 7 --
src/cgi.c | 266 -----------------------------------------------
src/error.c | 19 +++-
src/fastcgi.c | 23 ----
src/parse.c | 206 ++++++++++++++++++++++++++++++++++++
src/request.c | 11 --
src/response.c | 151 +++++++++++----------------
src/session.c | 58 -----------
25 files changed, 479 insertions(+), 722 deletions(-)
delete mode 100644 examples/fastcgi.c
delete mode 100644 include/magi/cgi.h
delete mode 100644 include/magi/fastcgi.h
create mode 100644 include/magi/parse.h
delete mode 100644 include/magi/session.h
delete mode 100644 man/magi.3
delete mode 100644 src/cgi.c
delete mode 100644 src/fastcgi.c
create mode 100644 src/parse.c
delete mode 100644 src/session.c
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
#include
-void response(magi_request *r)
+void append(magi_request *r)
{
char *data = magi_request_param(r, "addon");
- magi_response(r,
- ""
- ""
- "Append to File"
- "");
- 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, "Appended!
");
+ printf("Appended!
"); /* And display success message. */
}
- magi_response(r,
- ""
- ""
- "");
+}
+
+void response(magi_request *r)
+{
+ magi_response_default(); /* Pass default headers and send body: */
+ printf(""
+ ""
+ "Append to File"
+ "");
+ append(r);
+ printf(""
+ "");
}
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
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, "
[%s]=[%s]", c->name, c->data);
+ printf("
[%s]=[%s]", c->name, c->data);
}
}
void response(magi_request *r)
{
- magi_response_cookie(r, "cookie", "monster");
- magi_response(r,
- ""
- ""
- "Cookie Listing and Setting"
- "");
+ 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(""
+ ""
+ "
Cookie Listing and Setting"
+ "");
list_cookies(r);
- magi_response(r, "
");
+ printf("
");
}
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
void list_cookies(magi_request *r)
{
magi_cookies *current = r->cookies;
- magi_response(r, "");
+ printf("
");
+ /* 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, "]
");
+ printf("]
");
}
- magi_response(r, "
");
+ printf("");
}
-void list_params(magi_request *r, magi_params *current)
+void list_params(magi_params *current)
{
- magi_response(r, "");
+ printf("
");
+ /* 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]
", p->name, p->data);
+ printf("[%s] is [%s]
", p->name, p->data);
}
- magi_response(r, "
");
+ printf("");
}
void list_files(magi_request *r)
{
magi_files *current;
- magi_response(r, "");
+ printf("
");
+ /* 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
",
- f->field, f->filename);
+ printf("[%s] was [%s] on clientside
", f->field, f->filename);
}
- magi_response(r, "
");
+ printf("");
}
void show_meta(magi_request *r)
{
- magi_response(r, "I was called ");
+ printf("
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, "]
");
+ printf("]");
}
void response(magi_request *r)
{
- magi_response(r,
- ""
- ""
- "Echo"
- "");
+ magi_response_default(); /* Pass default headers and send body: */
+ printf(""
+ ""
+ "Echo"
+ "");
- magi_response(r, "Echo CGI Script
");
+ printf("Echo CGI Script
");
show_meta(r);
- magi_response(r, "Cookies:
");
+ printf("Cookies:
");
list_cookies(r);
- magi_response(r, "Parameters:
");
- list_params(r, r->meta);
+ printf("Parameters:
");
+ list_params(r->meta);
- magi_response(r, "URL Parameters:
");
- list_params(r, r->head);
+ printf("URL Parameters:
");
+ list_params(r->head);
- magi_response(r, "Body Parameters:
");
- list_params(r, r->body);
+ printf("Body Parameters:
");
+ list_params(r->body);
- magi_response(r, "Files:
");
+ printf("Files:
");
list_files(r);
- magi_response(r, "");
+ printf("");
}
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
-
-
-void response(magi_request *r)
-{
- magi_response(r,
- ""
- ""
- "Fast CGI"
- "Hi!"
- "");
-}
-
-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
#include
-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,
- ""
- ""
- "Upload File"
- "");
- if (name && data) {
- rename("data", name);
- magi_response(r, "Uploaded!
");
+ if (name && data) { /* If file to load and its name are in the request: */
+ rename("data", name); /* Rename loaded file to designated name. */
+ printf("Uploaded!
"); /* And display success message. */
}
- magi_response(r,
- ""
- ""
- "");
+}
+
+void response(magi_request *r)
+{
+ magi_response_default(); /* Pass default headers and send body: */
+ printf(""
+ ""
+ "Upload File"
+ "");
+ upload(r);
+ printf(""
+ "");
}
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/cgi.h b/include/magi/cgi.h
deleted file mode 100644
index 0de637e..0000000
--- a/include/magi/cgi.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef MAGI_INCLUDED_CGI
-#define MAGI_INCLUDED_CGI
-/* Common Gateway Interface implementation
- */
-#include "request.h"
-
-
-/* Analyses non-post part of request from environment. True if ok. */
-int magi_cgi_head(magi_request *request);
-
-/* Complete request with post body from standard input. True if ok. */
-int magi_cgi_body(magi_request *request);
-
-/* Shortcut for analysing both head and body of request. True if ok. */
-int magi_cgi(magi_request *request);
-
-
-#endif
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/parse.h b/include/magi/parse.h
new file mode 100644
index 0000000..a71018a
--- /dev/null
+++ b/include/magi/parse.h
@@ -0,0 +1,18 @@
+#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_parse_head(magi_request *request);
+
+/* Complete request with post body from standard input. True if ok. */
+int magi_parse_body(magi_request *request);
+
+/* Shortcut for analysing both head and body of request. True if ok. */
+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
#include
-/* * * 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
diff --git a/src/cgi.c b/src/cgi.c
deleted file mode 100644
index 7b0551d..0000000
--- a/src/cgi.c
+++ /dev/null
@@ -1,266 +0,0 @@
-#include "cgi.h"
-
-#include "cookie.h"
-#include "cookies.h"
-#include "error.h"
-#include "file.h"
-#include "multipart.h"
-#include "param.h"
-#include "request.h"
-#include "response.h"
-#include "tools.h"
-#include "urlencoded.h"
-#include
-#include
-#include
-#include
-
-extern char **const environ;
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * CGI Request
- */
-static char *plain_env(char *env_name)
-{
- const char *env = getenv(env_name);
- if (!env) {
- return 0;
- }
- return magi_str_create_copy(env, strlen(env));
-}
-
-static char *lower_env(char *env_name)
-{
- char *env = plain_env(env_name);
- magi_str_lowercase(env);
- return env;
-}
-
-static void cgi_http_env(magi_request *r)
-{
- char **env;
- int len = 0;
- r->meta = 0;
- for (env = environ; *env; ++env) {
- magi_param meta;
- char *name_end = strchr(*env, '=');
- int nlen = name_end - *env;
- int dlen = strlen(name_end + 1);
- len += nlen + dlen;
- if (len > r->limits.params_meta && r->limits.params_meta) {
- r->error = magi_error_limit;
- return;
- }
- meta.name = magi_str_create_copy(*env, nlen);
- meta.data = magi_str_create_copy(name_end + 1, dlen);
- magi_params_add(&r->meta, &meta);
- }
-}
-
-static void cgi_env(magi_request *r)
-{
- cgi_http_env(r);
- r->method = plain_env("REQUEST_METHOD");
- r->document_root = plain_env("DOCUMENT_ROOT");
- r->script = plain_env("SCRIPT_NAME");
- r->host = lower_env("HTTP_HOST");
- if (!r->host) {
- r->host = lower_env("SERVER_NAME");
- }
- if (getenv("SERVER_PORT")) {
- r->port = atoi(getenv("SERVER_PORT"));
- r->is_secure = r->port == 443;
- } else {
- r->port = 0;
- r->is_secure = 0;
- }
- r->path = plain_env("PATH_INFO");
-}
-
-static void cgi_cookies(magi_request *r)
-{
- const char *env = getenv("HTTP_COOKIE");
- if (!env || !*env) {
- r->cookies = 0;
- return;
- }
- if ((int)strlen(env) > r->limits.cookies && r->limits.cookies) {
- r->error = magi_error_limit;
- return;
- }
- magi_parse_cookies(r, env);
-}
-
-static void cgi_input_get(magi_error *e, char **input, int max)
-{
- const char *env_input = getenv("QUERY_STRING");
- if (env_input) {
- int len = strlen(env_input);
- if (len >= max && max) {
- *e = magi_error_limit;
- return;
- }
- *input = magi_str_create_copy(env_input, len);
- }
-}
-
-static void cgi_url(magi_request *request)
-{
- char *in = 0;
- cgi_input_get(&request->error, &in, request->limits.params_head);
- if (!request->error) {
- magi_parse_urlencoded(&request->head, in);
- free(in);
- }
-}
-
-static void cgi_input_post(magi_error *e, char **input, int max)
-{
- int input_len = strtoul(getenv("CONTENT_LENGTH"), 0, 10);
- if (!input_len) {
- *e = magi_error_length;
- return;
- }
- if (input_len > max && max) {
- *e = magi_error_limit;
- return;
- }
- *input = magi_str_create(input_len);
- if ((int)fread(*input, 1, input_len, stdin) != input_len) {
- *e = magi_error_length;
- return;
- }
-}
-
-static char *bound(const char *type)
-{
- type = strchr(type, '=');
- if (!type) {
- return 0;
- }
- type += strspn(type, " \t") + 1;
- if (*type == '"') {
- ++type;
- return magi_str_create_copy(type, type - strchr(type, '"'));
- }
- return magi_str_create_copy(type, strcspn(type, " \t"));
-}
-
-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)
-{
- request->cookies = 0;
- request->files = 0;
- request->meta = 0;
- request->head = 0;
- request->body = 0;
- request->error = 0;
- cgi_env(request);
- cgi_cookies(request);
- cgi_url(request);
- setup_response(request);
- return !request->error;
-}
-
-int magi_cgi_body(magi_request *request)
-{
- magi_error *e = &request->error;
- request->error = magi_error_none;
- if (request->method && !strcmp(request->method, "POST")) {
- const char *t = getenv("CONTENT_TYPE");
- if (!t) {
- *e = magi_error_notype;
- return 0;
- }
- if (!strncmp(t, "multipart/form-data", 19)) {
- char *boundary = bound(t);
- if (boundary && *boundary) {
- magi_parse_multipart(request, boundary, next, 0);
- } else {
- *e = magi_error_nobound;
- }
- free(boundary);
- } else if (!strcmp(t, "application/x-www-form-urlencoded")) {
- char *in = 0;
- cgi_input_post(e, &in, request->limits.params_body);
- if (!*e) {
- magi_parse_urlencoded(&request->body, in);
- }
- free(in);
- } else {
- *e = magi_error_unknown;
- return 0;
- }
- }
- return !request->error;
-}
-
-int magi_cgi(magi_request *request)
-{
- return magi_cgi_head(request) && magi_cgi_body(request);
-}
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
+
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"
+ ""
+ ""
+ "400 Bad Request"
+ ""
+ "400 Bad Request
"
+ "", stdout);
+ fputs(magi_error_message(error), stdout);
+ fputs("
", 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/parse.c b/src/parse.c
new file mode 100644
index 0000000..58ae001
--- /dev/null
+++ b/src/parse.c
@@ -0,0 +1,206 @@
+#include "parse.h"
+
+#include "cookie.h"
+#include "cookies.h"
+#include "error.h"
+#include "file.h"
+#include "multipart.h"
+#include "param.h"
+#include "request.h"
+#include "response.h"
+#include "tools.h"
+#include "urlencoded.h"
+#include
+#include
+#include
+#include
+
+extern char **const environ;
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * CGI Request
+ */
+static char *plain_env(char *env_name)
+{
+ const char *env = getenv(env_name);
+ if (!env) {
+ return 0;
+ }
+ return magi_str_create_copy(env, strlen(env));
+}
+
+static char *lower_env(char *env_name)
+{
+ char *env = plain_env(env_name);
+ magi_str_lowercase(env);
+ return env;
+}
+
+static void cgi_http_env(magi_request *r)
+{
+ char **env;
+ int len = 0;
+ r->meta = 0;
+ for (env = environ; *env; ++env) {
+ magi_param meta;
+ char *name_end = strchr(*env, '=');
+ int nlen = name_end - *env;
+ int dlen = strlen(name_end + 1);
+ len += nlen + dlen;
+ if (len > r->limits.params_meta && r->limits.params_meta) {
+ r->error = magi_error_limit;
+ return;
+ }
+ meta.name = magi_str_create_copy(*env, nlen);
+ meta.data = magi_str_create_copy(name_end + 1, dlen);
+ magi_params_add(&r->meta, &meta);
+ }
+}
+
+static void cgi_env(magi_request *r)
+{
+ cgi_http_env(r);
+ r->method = plain_env("REQUEST_METHOD");
+ r->document_root = plain_env("DOCUMENT_ROOT");
+ r->script = plain_env("SCRIPT_NAME");
+ r->host = lower_env("HTTP_HOST");
+ if (!r->host) {
+ r->host = lower_env("SERVER_NAME");
+ }
+ if (getenv("SERVER_PORT")) {
+ r->port = atoi(getenv("SERVER_PORT"));
+ r->is_secure = r->port == 443;
+ } else {
+ r->port = 0;
+ r->is_secure = 0;
+ }
+ r->path = plain_env("PATH_INFO");
+}
+
+static void cgi_cookies(magi_request *r)
+{
+ const char *env = getenv("HTTP_COOKIE");
+ if (!env || !*env) {
+ r->cookies = 0;
+ return;
+ }
+ if ((int)strlen(env) > r->limits.cookies && r->limits.cookies) {
+ r->error = magi_error_limit;
+ return;
+ }
+ magi_parse_cookies(r, env);
+}
+
+static void cgi_input_get(magi_error *e, char **input, int max)
+{
+ const char *env_input = getenv("QUERY_STRING");
+ if (env_input) {
+ int len = strlen(env_input);
+ if (len >= max && max) {
+ *e = magi_error_limit;
+ return;
+ }
+ *input = magi_str_create_copy(env_input, len);
+ }
+}
+
+static void cgi_url(magi_request *request)
+{
+ char *in = 0;
+ cgi_input_get(&request->error, &in, request->limits.params_head);
+ if (!request->error) {
+ magi_parse_urlencoded(&request->head, in);
+ free(in);
+ }
+}
+
+static void cgi_input_post(magi_error *e, char **input, int max)
+{
+ int input_len = strtoul(getenv("CONTENT_LENGTH"), 0, 10);
+ if (!input_len) {
+ *e = magi_error_length;
+ return;
+ }
+ if (input_len > max && max) {
+ *e = magi_error_limit;
+ return;
+ }
+ *input = magi_str_create(input_len);
+ if ((int)fread(*input, 1, input_len, stdin) != input_len) {
+ *e = magi_error_length;
+ return;
+ }
+}
+
+static char *bound(const char *type)
+{
+ type = strchr(type, '=');
+ if (!type) {
+ return 0;
+ }
+ type += strspn(type, " \t") + 1;
+ if (*type == '"') {
+ ++type;
+ return magi_str_create_copy(type, type - strchr(type, '"'));
+ }
+ return magi_str_create_copy(type, strcspn(type, " \t"));
+}
+
+static int next()
+{
+ return getchar();
+}
+
+/* Interfacial CGI Request Handling */
+int magi_parse_head(magi_request *request)
+{
+ request->cookies = 0;
+ request->files = 0;
+ request->meta = 0;
+ request->head = 0;
+ request->body = 0;
+ request->error = 0;
+ cgi_env(request);
+ cgi_cookies(request);
+ cgi_url(request);
+ return !request->error;
+}
+
+int magi_parse_body(magi_request *request)
+{
+ magi_error *e = &request->error;
+ request->error = magi_error_none;
+ if (request->method && !strcmp(request->method, "POST")) {
+ const char *t = getenv("CONTENT_TYPE");
+ if (!t) {
+ *e = magi_error_notype;
+ return 0;
+ }
+ if (!strncmp(t, "multipart/form-data", 19)) {
+ char *boundary = bound(t);
+ if (boundary && *boundary) {
+ magi_parse_multipart(request, boundary, next, 0);
+ } else {
+ *e = magi_error_nobound;
+ }
+ free(boundary);
+ } else if (!strcmp(t, "application/x-www-form-urlencoded")) {
+ char *in = 0;
+ cgi_input_post(e, &in, request->limits.params_body);
+ if (!*e) {
+ magi_parse_urlencoded(&request->body, in);
+ }
+ free(in);
+ } else {
+ *e = magi_error_unknown;
+ return 0;
+ }
+ }
+ return !request->error;
+}
+
+int magi_parse(magi_request *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
@@ -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
-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,
- ""
- ""
- "400 Bad Request"
- ""
- "400 Bad Request
"
- "");
- magi_response(r, magi_error_message(r->error));
- magi_response(r, "
");
+ 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
-#include
-#include
-#include
-
-
-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);
-}
--
cgit v1.2.3