aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-07-21 01:07:57 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-07-21 01:40:55 +0200
commit7fe639b3ba7a253627d2cd34f3b97bd95b0a90b3 (patch)
treeab50067686bfdc87b35ddfa29112af5ca0440574
parent1d4480c0f36f9d4e75c00e58c065b4c43e53b4f3 (diff)
downloadslcl-7fe639b3ba7a253627d2cd34f3b97bd95b0a90b3.tar.gz
Remove files now provided by slweb
-rw-r--r--handler.c315
-rw-r--r--handler.h24
-rw-r--r--html.c301
-rw-r--r--html.h15
-rw-r--r--http.c1964
-rw-r--r--http.h106
-rw-r--r--server.c373
-rw-r--r--server.h15
8 files changed, 0 insertions, 3113 deletions
diff --git a/handler.c b/handler.c
deleted file mode 100644
index 1d7e922..0000000
--- a/handler.c
+++ /dev/null
@@ -1,315 +0,0 @@
-#define _POSIX_C_SOURCE 200809L
-
-#include "handler.h"
-#include "http.h"
-#include "server.h"
-#include "wildcard_cmp.h"
-#include <errno.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-struct handler
-{
- struct handler_cfg cfg;
- struct elem
- {
- char *url;
- enum http_op op;
- handler_fn f;
- void *user;
- } *elem;
-
- struct server *server;
- struct client
- {
- struct handler *h;
- struct server_client *c;
- struct http_ctx *http;
- struct client *next;
- } *clients;
-
- size_t n_cfg;
-};
-
-static int on_read(void *const buf, const size_t n, void *const user)
-{
- struct client *const c = user;
-
- return server_read(buf, n, c->c);
-}
-
-static int on_write(const void *const buf, const size_t n, void *const user)
-{
- struct client *const c = user;
-
- return server_write(buf, n, c->c);
-}
-
-static int on_payload(const struct http_payload *const p,
- struct http_response *const r, void *const user)
-{
- struct client *const c = user;
- struct handler *const h = c->h;
-
- for (size_t i = 0; i < h->n_cfg; i++)
- {
- const struct elem *const e = &h->elem[i];
-
- if (e->op == p->op && !wildcard_cmp(p->resource, e->url, true))
- return e->f(p, r, e->user);
- }
-
- fprintf(stderr, "Not found: %s\n", p->resource);
-
- *r = (const struct http_response)
- {
- .status = HTTP_STATUS_NOT_FOUND
- };
-
- return 0;
-}
-
-static int on_length(const unsigned long long len,
- const struct http_cookie *const c, struct http_response *const r,
- void *const user)
-{
- struct client *const cl = user;
- struct handler *const h = cl->h;
-
- if (h->cfg.length)
- return h->cfg.length(len, c, r, h->cfg.user);
-
- return 0;
-}
-
-static struct client *find_or_alloc_client(struct handler *const h,
- struct server_client *const c)
-{
- for (struct client *cl = h->clients; cl; cl = cl->next)
- {
- if (cl->c == c)
- return cl;
- }
-
- struct client *const ret = malloc(sizeof *ret);
-
- if (!ret)
- {
- fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
- return NULL;
- }
-
- const struct http_cfg cfg =
- {
- .read = on_read,
- .write = on_write,
- .payload = on_payload,
- .length = on_length,
- .user = ret,
- .tmpdir = h->cfg.tmpdir
- };
-
- *ret = (const struct client)
- {
- .c = c,
- .h = h,
- .http = http_alloc(&cfg)
- };
-
- if (!ret->http)
- {
- fprintf(stderr, "%s: http_alloc failed\n", __func__);
- return NULL;
- }
-
- if (!h->clients)
- h->clients = ret;
- else
- {
- for (struct client *c = h->clients; c; c = c->next)
- if (!c->next)
- {
- c->next = ret;
- break;
- }
- }
-
- return ret;
-}
-
-static void client_free(struct client *const c)
-{
- if (c)
- http_free(c->http);
-
- free(c);
-}
-
-static int remove_client_from_list(struct handler *const h,
- struct client *const c)
-{
- int ret = -1;
-
- if (server_client_close(h->server, c->c))
- {
- fprintf(stderr, "%s: server_client_close failed\n",
- __func__);
- goto end;
- }
-
- for (struct client *cl = h->clients, *prev = NULL; cl;
- prev = cl, cl = cl->next)
- {
- if (cl == c)
- {
- if (!prev)
- h->clients = c->next;
- else
- prev->next = cl->next;
-
- break;
- }
- }
-
- ret = 0;
-
-end:
- client_free(c);
- return ret;
-}
-
-int handler_listen(struct handler *const h, const short port)
-{
- if (!(h->server = server_init(port)))
- {
- fprintf(stderr, "%s: server_init failed\n", __func__);
- return -1;
- }
-
- for (;;)
- {
- bool exit, io;
- struct server_client *const c = server_poll(h->server, &io, &exit);
-
- if (exit)
- {
- printf("Exiting...\n");
- break;
- }
- else if (!c)
- {
- fprintf(stderr, "%s: server_poll failed\n", __func__);
- return -1;
- }
-
- struct client *const cl = find_or_alloc_client(h, c);
-
- if (!cl)
- {
- fprintf(stderr, "%s: find_or_alloc_client failed\n", __func__);
- return -1;
- }
- else if (io)
- {
- bool write, close;
- const int res = http_update(cl->http, &write, &close);
-
- if (res || close)
- {
- if (res < 0)
- {
- fprintf(stderr, "%s: http_update failed\n", __func__);
- return -1;
- }
- else if (remove_client_from_list(h, cl))
- {
- fprintf(stderr, "%s: remove_client_from_list failed\n",
- __func__);
- return -1;
- }
- }
- else
- server_client_write_pending(cl->c, write);
- }
- }
-
- return 0;
-}
-
-static void free_clients(struct handler *const h)
-{
- for (struct client *c = h->clients; c;)
- {
- struct client *const next = c->next;
-
- server_client_close(h->server, c->c);
- client_free(c);
- c = next;
- }
-}
-
-void handler_free(struct handler *const h)
-{
- if (h)
- {
- for (size_t i = 0; i < h->n_cfg; i++)
- free(h->elem[i].url);
-
- free(h->elem);
- free_clients(h);
- server_close(h->server);
- }
-
- free(h);
-}
-
-struct handler *handler_alloc(const struct handler_cfg *const cfg)
-{
- struct handler *const h = malloc(sizeof *h);
-
- if (!h)
- {
- fprintf(stderr, "%s: malloc(3) handler: %s\n",
- __func__, strerror(errno));
- return NULL;
- }
-
- *h = (const struct handler){.cfg = *cfg};
- return h;
-}
-
-int handler_add(struct handler *const h, const char *url,
- const enum http_op op, const handler_fn f, void *const user)
-{
- const size_t n = h->n_cfg + 1;
- struct elem *const elem = realloc(h->elem, n * sizeof *h->elem);
-
- if (!elem)
- {
- fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- struct elem *const e = &elem[h->n_cfg];
-
- *e = (const struct elem)
- {
- .url = strdup(url),
- .op = op,
- .f = f,
- .user = user
- };
-
- if (!e->url)
- {
- fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- h->elem = elem;
- h->n_cfg = n;
- return 0;
-}
diff --git a/handler.h b/handler.h
deleted file mode 100644
index 38de97b..0000000
--- a/handler.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef HANDLER_H
-#define HANDLER_H
-
-#include "http.h"
-#include <stddef.h>
-
-typedef int (*handler_fn)(const struct http_payload *p,
- struct http_response *r, void *user);
-
-struct handler_cfg
-{
- const char *tmpdir;
- int (*length)(unsigned long long len, const struct http_cookie *c,
- struct http_response *r, void *user);
- void *user;
-};
-
-struct handler *handler_alloc(const struct handler_cfg *cfg);
-void handler_free(struct handler *h);
-int handler_add(struct handler *h, const char *url, enum http_op op,
- handler_fn f, void *user);
-int handler_listen(struct handler *h, short port);
-
-#endif /* HANDLER_H */
diff --git a/html.c b/html.c
deleted file mode 100644
index 41ce387..0000000
--- a/html.c
+++ /dev/null
@@ -1,301 +0,0 @@
-#define _POSIX_C_SOURCE 200809L
-
-#include "html.h"
-#include <dynstr.h>
-#include <errno.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-struct html_node
-{
- struct html_attribute
- {
- char *attr, *value;
- } *attrs;
-
- char *element, *value;
- size_t n;
- struct html_node *child, *sibling;
-};
-
-static char *html_encode(const char *s)
-{
- struct dynstr d;
-
- dynstr_init(&d);
-
- if (!*s && dynstr_append(&d, ""))
- {
- fprintf(stderr, "%s: dynstr_append empty failed\n", __func__);
- goto failure;
- }
-
- while (*s)
- {
- static const struct esc
- {
- char c;
- const char *str;
- } esc[] =
- {
- {.c = '<', .str = "&gt;"},
- {.c = '>', .str = "&lt;"},
- {.c = '&', .str = "&amp;"},
- {.c = '\"', .str = "&quot;"},
- {.c = '\'', .str = "&apos;"}
- };
-
- char buf[sizeof "a"] = {0};
- const char *str = NULL;
-
- for (size_t i = 0; i < sizeof esc / sizeof *esc; i++)
- {
- const struct esc *const e = &esc[i];
-
- if (*s == e->c)
- {
- str = e->str;
- break;
- }
- }
-
- if (!str)
- {
- *buf = *s;
- str = buf;
- }
-
- if (dynstr_append(&d, "%s", str))
- {
- fprintf(stderr, "%s: dynstr_append failed\n", __func__);
- goto failure;
- }
-
- s++;
- }
-
- return d.str;
-
-failure:
- dynstr_free(&d);
- return NULL;
-}
-
-int html_node_set_value(struct html_node *const n, const char *const val)
-{
- if (!(n->value = html_encode(val)))
- {
- fprintf(stderr, "%s: html_encode failed\n", __func__);
- return -1;
- }
-
- return 0;
-}
-
-int html_node_set_value_unescaped(struct html_node *const n,
- const char *const val)
-{
- if (!(n->value = strdup(val)))
- {
- fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-int html_node_add_attr(struct html_node *const n, const char *const attr,
- const char *const val)
-{
- const size_t el = n->n + 1;
- struct html_attribute *const attrs = realloc(n->attrs,
- el * sizeof *n->attrs), *a = NULL;
-
- if (!attrs)
- {
- fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- a = &attrs[n->n];
- *a = (const struct html_attribute){0};
-
- if (!(a->attr = strdup(attr))
- || (val && !(a->value = strdup(val))))
- {
- fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno));
- free(a->attr);
- free(a->value);
- return -1;
- }
-
- n->attrs = attrs;
- n->n = el;
- return 0;
-}
-
-void html_node_add_sibling(struct html_node *const n,
- struct html_node *const sibling)
-{
- for (struct html_node *c = n; c; c = c->sibling)
- if (!c->sibling)
- {
- c->sibling = sibling;
- break;
- }
-}
-
-struct html_node *html_node_add_child(struct html_node *const n,
- const char *const element)
-{
- struct html_node *const child = html_node_alloc(element);
-
- if (!child)
- return NULL;
- else if (n->child)
- html_node_add_sibling(n->child, child);
- else
- n->child = child;
-
- return child;
-}
-
-int serialize_node(struct dynstr *const d, const struct html_node *const n,
- const unsigned level)
-{
- for (unsigned i = 0; i < level; i++)
- dynstr_append(d, "\t");
-
- dynstr_append_or_ret_nonzero(d, "<%s", n->element);
-
- if (n->n)
- dynstr_append_or_ret_nonzero(d, " ");
-
- for (size_t i = 0; i < n->n; i++)
- {
- const struct html_attribute *const a = &n->attrs[i];
-
- if (a->value)
- dynstr_append_or_ret_nonzero(d, "%s=\"%s\"", a->attr, a->value);
- else
- dynstr_append_or_ret_nonzero(d, "%s", a->attr);
-
- if (i + 1 < n->n)
- dynstr_append_or_ret_nonzero(d, " ");
- }
-
- if (!n->value && !n->child)
- dynstr_append_or_ret_nonzero(d, "/>");
- else
- {
- dynstr_append_or_ret_nonzero(d, ">");
-
- if (n->value)
- dynstr_append_or_ret_nonzero(d, "%s", n->value);
-
- if (n->child)
- {
- dynstr_append_or_ret_nonzero(d, "\n");
-
- if (serialize_node(d, n->child, level + 1))
- {
- fprintf(stderr, "%s: serialize_node failed\n", __func__);
- return -1;
- }
-
- for (unsigned i = 0; i < level; i++)
- dynstr_append(d, "\t");
- }
-
- dynstr_append_or_ret_nonzero(d, "</%s>", n->element);
- }
-
- /* TODO: print siblings */
-
- dynstr_append_or_ret_nonzero(d, "\n");
-
- if (n->sibling)
- return serialize_node(d, n->sibling, level);
-
- return 0;
-}
-
-int html_serialize(const struct html_node *const n, struct dynstr *const d)
-{
- return serialize_node(d, n, 0);
-}
-
-static void html_attribute_free(struct html_attribute *const a)
-{
- if (a)
- {
- free(a->attr);
- free(a->value);
- }
-}
-
-void html_node_free(struct html_node *const n)
-{
- if (n)
- {
- struct html_node *s = n->sibling;
-
- html_node_free(n->child);
-
- while (s)
- {
- struct html_node *const next = s->sibling;
-
- html_node_free(s->child);
- free(s->element);
- free(s->value);
-
- for (size_t i = 0 ; i < s->n; i++)
- html_attribute_free(&s->attrs[i]);
-
- free(s->attrs);
- free(s);
- s = next;
- }
-
- free(n->element);
- free(n->value);
-
- for (size_t i = 0 ; i < n->n; i++)
- html_attribute_free(&n->attrs[i]);
-
- free(n->attrs);
- }
-
- free(n);
-}
-
-struct html_node *html_node_alloc(const char *const element)
-{
- struct html_node *const n = malloc(sizeof *n);
-
- if (!n)
- {
- fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
- goto failure;
- }
-
- *n = (const struct html_node)
- {
- .element = strdup(element)
- };
-
- if (!n->element)
- {
- fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
- goto failure;
- }
-
- return n;
-
-failure:
- html_node_free(n);
- return NULL;
-}
diff --git a/html.h b/html.h
deleted file mode 100644
index e62575e..0000000
--- a/html.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef HTML_H
-#define HTML_H
-
-#include <dynstr.h>
-
-struct html_node *html_node_alloc(const char *element);
-void html_node_free(struct html_node *n);
-int html_node_set_value(struct html_node *n, const char *val);
-int html_node_set_value_unescaped(struct html_node *n, const char *val);
-int html_node_add_attr(struct html_node *n, const char *attr, const char *val);
-struct html_node *html_node_add_child(struct html_node *n, const char *elem);
-void html_node_add_sibling(struct html_node *n, struct html_node *sibling);
-int html_serialize(const struct html_node *n, struct dynstr *d);
-
-#endif /* HTML_H */
diff --git a/http.c b/http.c
deleted file mode 100644
index cb28ee5..0000000
--- a/http.c
+++ /dev/null
@@ -1,1964 +0,0 @@
-#define _POSIX_C_SOURCE 200809L
-
-#include "http.h"
-#include <dynstr.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <strings.h>
-#include <time.h>
-
-#define HTTP_VERSION "HTTP/1.1"
-
-struct http_ctx
-{
- struct ctx
- {
- enum state
- {
- START_LINE,
- HEADER_CR_LINE,
- BODY_LINE
- } state;
-
- enum
- {
- LINE_CR,
- LINE_LF
- } lstate;
-
- enum http_op op;
- char *resource, *field, *value, *boundary;
- size_t len;
-
- struct post
- {
- char *path;
- unsigned long long len, read;
- } post;
-
- union
- {
- struct start_line
- {
- enum
- {
- START_LINE_OP,
- START_LINE_RESOURCE,
- START_LINE_PROTOCOL
- } state;
- } sl;
-
- struct multiform
- {
- enum mf_state
- {
- MF_START_BOUNDARY,
- MF_HEADER_CR_LINE,
- MF_BODY_BOUNDARY_LINE,
- MF_END_BOUNDARY_CR_LINE
- } state;
-
- enum
- {
- BODY_CR,
- BODY_LF,
- BODY_DATA
- } bstate;
-
- off_t len, written;
- char *boundary;
- const char *dir;
- size_t blen, nforms, nfiles;
- int fd;
- struct http_post_file *files;
-
- struct form
- {
- char *name, *filename, *tmpname, *value;
- } *forms;
- } mf;
- } u;
-
- struct http_arg *args;
- size_t n_args;
- } ctx;
-
- struct write_ctx
- {
- bool pending, close;
- enum state state;
- struct http_response r;
- off_t n;
- struct dynstr d;
- } wctx;
-
- /* From RFC9112, section 3 (Request line):
- * It is RECOMMENDED that all HTTP senders and recipients support,
- * at a minimum, request-line lengths of 8000 octets. */
- char line[8000];
- struct http_cfg cfg;
-};
-
-static void arg_free(struct http_arg *const a)
-{
- if (a)
- {
- free(a->key);
- free(a->value);
- }
-}
-
-static size_t chrcnt(const char *s, const int c)
-{
- size_t ret = 0;
-
- while (*s++ == c)
- ret++;
-
- return ret;
-}
-
-static int parse_arg(struct ctx *const c, const char *const arg,
- const size_t n)
-{
- int ret = -1;
- struct http_arg a = {0}, *args = NULL;
- const char *sep = memchr(arg, '=', n);
- char *enckey = NULL, *encvalue = NULL;
-
- if (!sep)
- {
- fprintf(stderr, "%s: expected '='\n", __func__);
- ret = 1;
- goto end;
- }
- else if (sep == arg)
- {
- fprintf(stderr, "%s: expected key\n", __func__);
- ret = 1;
- goto end;
- }
-
- const char *const value = sep + 1;
-
- if (!*value)
- {
- fprintf(stderr, "%s: missing value: %.*s\n", __func__, (int)n, arg);
- ret = 1;
- goto end;
- }
-
- const size_t keylen = sep - arg, valuelen = n - keylen - 1;
-
- if (!(enckey = strndup(arg, keylen)))
- {
- fprintf(stderr, "%s: strndup(3) key: %s\n", __func__, strerror(errno));
- goto end;
- }
- else if (!(encvalue = strndup(value, valuelen)))
- {
- fprintf(stderr, "%s: strndup(3) value: %s\n",
- __func__, strerror(errno));
- goto end;
- }
-
- /* URL parameters use '+' for whitespace, rather than %20. */
- a = (const struct http_arg)
- {
- .key = http_decode_url(enckey, true),
- .value = http_decode_url(encvalue, true)
- };
-
- if (!a.key)
- {
- fprintf(stderr, "%s: http_decode_url key failed\n", __func__);
- goto end;
- }
- else if (!a.value)
- {
- fprintf(stderr, "%s: http_decode_url value failed\n", __func__);
- goto end;
- }
- else if (!(args = realloc(c->args, (c->n_args + 1) * sizeof *args)))
- {
- fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
- goto end;
- }
-
- args[c->n_args++] = a;
- c->args = args;
- ret = 0;
-
-end:
- if (ret)
- arg_free(&a);
-
- free(enckey);
- free(encvalue);
- return ret;
-}
-
-static int parse_first_arg(struct ctx *const c, const char *const arg,
- const char *const ad_arg, const char *const res)
-{
- int error;
- const char *const next = arg + 1;
-
- if (chrcnt(next, '?'))
- {
- fprintf(stderr, "%s: more than one argument indicator '?' found: %s\n",
- __func__, res);
- return 1;
- }
-
- const size_t n = ad_arg ? ad_arg - next : strlen(next);
-
- if (!n)
- {
- fprintf(stderr, "%s: unterminated argument: %s\n", __func__, res);
- return 1;
- }
- else if ((error = parse_arg(c, next, n)))
- {
- fprintf(stderr, "%s: parse_arg failed: %s\n", __func__, res);
- return error;
- }
-
- return 0;
-}
-
-static int parse_adargs(struct ctx *const c, const char *const start,
- const char *const res)
-{
- for (const char *arg = start, *next; arg; arg = next)
- {
- next = strchr(++arg, '&');
-
- int error;
- const size_t n = next ? next - arg : strlen(arg);
-
- if ((error = parse_arg(c, arg, n)))
- {
- fprintf(stderr, "%s: parse_arg failed: %s\n", __func__, res);
- return error;
- }
- }
-
- return 0;
-}
-
-static int parse_args(struct ctx *const c, const char *const res,
- size_t *const reslen)
-{
- int error;
- const char *const tmp_arg_start = strrchr(res, '?'),
- *const arg_start = tmp_arg_start && *(tmp_arg_start + 1) ?
- tmp_arg_start : NULL,
- *const ad_arg = arg_start ? strchr(arg_start, '&') : NULL;
-
- if (!arg_start)
- {
- if (!ad_arg)
- {
- *reslen = strlen(res);
- return 0;
- }
- else
- {
- fprintf(stderr, "%s: expected argument indicator '?': %s\n",
- __func__, res);
- return 1;
- }
- }
- else if (arg_start == res)
- {
- fprintf(stderr, "%s: expected resource: %s\n", __func__, res);
- return 1;
- }
- else if (ad_arg && ad_arg <= arg_start)
- {
- fprintf(stderr, "%s: expected '?' before '&': %s\n", __func__, res);
- return 1;
- }
- else if ((error = parse_first_arg(c, arg_start, ad_arg, res)))
- {
- fprintf(stderr, "%s: parse_first_arg failed\n", __func__);
- return error;
- }
- else if ((error = parse_adargs(c, ad_arg, res)))
- {
- fprintf(stderr, "%s: parse_adargs failed\n", __func__);
- return error;
- }
-
- *reslen = arg_start - res;
- return 0;
-}
-
-static int parse_resource(struct ctx *const c, const char *const enc_res)
-{
- int ret = -1, error;
- size_t reslen;
- char *trimmed_encres = NULL, *resource = NULL;
-
- if ((error = parse_args(c, enc_res, &reslen)))
- {
- fprintf(stderr, "%s: parse_args failed\n", __func__);
- ret = error;
- goto end;
- }
- else if (!(trimmed_encres = strndup(enc_res, reslen)))
- {
- fprintf(stderr, "%s: strndup(3): %s\n", __func__, strerror(errno));
- goto end;
- }
- else if (!(resource = http_decode_url(trimmed_encres, false)))
- {
- fprintf(stderr, "%s: http_decode_url failed\n", __func__);
- goto end;
- }
-
- c->resource = resource;
- ret = 0;
-
-end:
- free(trimmed_encres);
- return ret;
-}
-
-static int start_line(struct http_ctx *const h)
-{
- const char *const line = (const char *)h->line;
-
- if (!*line)
- {
- fprintf(stderr, "%s: expected non-empty line\n", __func__);
- return 1;
- }
-
- const char *const op = strchr(line, ' ');
-
- if (!op || op == line)
- {
- fprintf(stderr, "%s: expected resource\n", __func__);
- return 1;
- }
-
- struct ctx *const c = &h->ctx;
- const size_t n = op - line;
-
- if (!strncmp(line, "GET", n))
- c->op = HTTP_OP_GET;
- else if (!strncmp(line, "POST", n))
- c->op = HTTP_OP_POST;
- else
- {
- fprintf(stderr, "%s: unsupported HTTP op %.*s\n",
- __func__, (int)n, line);
- return 1;
- }
-
- const char *const resource = op + 1,
- *const res_end = strchr(resource, ' ');
-
- if (!res_end)
- {
- fprintf(stderr, "%s: expected protocol version\n", __func__);
- return 1;
- }
-
- const size_t res_n = res_end - resource;
-
- if (memchr(resource, '*', res_n))
- {
- fprintf(stderr, "%s: illegal character * in resource %.*s\n",
- __func__, (int)res_n, resource);
- return 1;
- }
-
- const char *const protocol = res_end + 1;
-
- if (!*protocol)
- {
- fprintf(stderr, "%s: expected protocol version\n", __func__);
- return 1;
- }
- else if (strchr(protocol, ' '))
- {
- fprintf(stderr, "%s: unexpected field after protocol version\n",
- __func__);
- return 1;
- }
-
- int ret = 1, error;
- char *enc_res = NULL;
-
- if (strcmp(protocol, HTTP_VERSION))
- {
- fprintf(stderr, "%s: unsupported protocol %s\n", __func__, protocol);
- goto end;
- }
- else if (!(enc_res = strndup(resource, res_n)))
- {
- fprintf(stderr, "%s: strndup(3): %s\n", __func__, strerror(errno));
- ret = -1;
- goto end;
- }
- else if ((error = parse_resource(c, enc_res)))
- {
- fprintf(stderr, "%s: parse_resource failed\n", __func__);
- ret = error;
- goto end;
- }
-
- printf("%.*s %s %s\n", (int)n, line, c->resource, protocol);
- ret = 0;
- c->state = HEADER_CR_LINE;
-
-end:
- free(enc_res);
- return ret;
-}
-
-static void ctx_free(struct ctx *const c)
-{
- if (c->boundary)
- {
- struct multiform *const m = &c->u.mf;
-
- free(m->files);
- free(m->boundary);
-
- if (m->fd >= 0 && close(m->fd))
- fprintf(stderr, "%s: close(2) m->fd: %s\n",
- __func__, strerror(errno));
-
- for (size_t i = 0; i < m->nforms; i++)
- {
- struct form *const f = &m->forms[i];
-
- free(f->name);
- free(f->filename);
- free(f->value);
-
- if (f->tmpname && remove(f->tmpname) && errno != ENOENT)
- fprintf(stderr, "%s: remove(3) %s: %s\n",
- __func__, f->tmpname, strerror(errno));
-
- free(f->tmpname);
- }
-
- free(m->forms);
- }
-
- free(c->field);
- free(c->value);
- free(c->resource);
- free(c->boundary);
-
- for (size_t i = 0; i < c->n_args; i++)
- arg_free(&c->args[i]);
-
- free(c->args);
- *c = (const struct ctx){0};
-}
-
-static int prepare_headers(struct http_ctx *const h)
-{
- struct write_ctx *const w = &h->wctx;
- struct dynstr *const d = &w->d;
-
- dynstr_init(d);
-
- for (size_t i = 0; i < w->r.n_headers; i++)
- {
- const struct http_header *const hdr = &w->r.headers[i];
-
- dynstr_append_or_ret_nonzero(d, "%s: %s\r\n", hdr->header, hdr->value);
- free(hdr->header);
- free(hdr->value);
- }
-
- free(w->r.headers);
- dynstr_append_or_ret_nonzero(d, "\r\n");
- return 0;
-}
-
-static int rw_error(const int r, bool *const close)
-{
- if (r < 0)
- {
- switch (errno)
- {
- case EPIPE:
- /* Fall through. */
- case ECONNRESET:
- *close = true;
- return 1;
-
- case EAGAIN:
- return 0;
-
- default:
- break;
- }
-
- fprintf(stderr, "%s: %s\n", __func__, strerror(errno));
- return -1;
- }
- else if (!r)
- {
- *close = true;
- return 0;
- }
-
- fprintf(stderr, "%s: unexpected value %d\n", __func__, r);
- return -1;
-}
-
-static int write_start_line(struct http_ctx *const h, bool *const close)
-{
- struct write_ctx *const w = &h->wctx;
- struct dynstr *const d = &w->d;
- const size_t rem = d->len - w->n;
- const int res = h->cfg.write(d->str + w->n, rem, h->cfg.user);
-
- if (res <= 0)
- return rw_error(res, close);
- else if ((w->n += res) >= d->len)
- {
- char len[sizeof "18446744073709551615"];
- const int res = snprintf(len, sizeof len, "%llu", w->r.n);
-
- dynstr_free(d);
-
- if (res < 0 || res >= sizeof len)
- {
- fprintf(stderr, "%s: snprintf(3) failed\n", __func__);
- return -1;
- }
- else if (http_response_add_header(&w->r, "Content-Length", len))
- {
- fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
- return -1;
- }
- else if (prepare_headers(h))
- {
- fprintf(stderr, "%s: prepare_headers failed\n", __func__);
- return -1;
- }
-
- w->state = HEADER_CR_LINE;
- w->n = 0;
- }
-
- return 0;
-}
-
-static int write_ctx_free(struct write_ctx *const w)
-{
- int ret = 0;
- const struct http_response *const r = &w->r;
-
- if (r->free)
- r->free(r->buf.rw);
-
- if (r->f && (ret = fclose(r->f)))
- fprintf(stderr, "%s: fclose(3): %s\n", __func__, strerror(errno));
-
- dynstr_free(&w->d);
- *w = (const struct write_ctx){0};
- return ret;
-}
-
-static int write_header_cr_line(struct http_ctx *const h, bool *const close)
-{
- struct write_ctx *const w = &h->wctx;
- struct dynstr *const d = &w->d;
- const size_t rem = d->len - w->n;
- const int res = h->cfg.write(d->str + w->n, rem, h->cfg.user);
-
- if (res <= 0)
- return rw_error(res, close);
- else if ((w->n += res) >= d->len)
- {
- const bool close_pending = w->close;
-
- dynstr_free(d);
-
- if (w->r.n)
- {
- w->state = BODY_LINE;
- w->n = 0;
- }
- else if (write_ctx_free(w))
- {
- fprintf(stderr, "%s: write_ctx_free failed\n", __func__);
- return -1;
- }
- else if (close_pending)
- *close = true;
- }
-
- return 0;
-}
-
-static int write_body_mem(struct http_ctx *const h, bool *const close)
-{
- struct write_ctx *const w = &h->wctx;
- const struct http_response *const r = &w->r;
- const size_t rem = r->n - w->n;
- const int res = h->cfg.write((const char *)r->buf.ro + w->n, rem,
- h->cfg.user);
-
- if (res <= 0)
- return rw_error(res, close);
- else if ((w->n += res) >= r->n)
- {
- const bool close_pending = w->close;
-
- if (write_ctx_free(w))
- {
- fprintf(stderr, "%s: write_ctx_free failed\n", __func__);
- return -1;
- }
- else if (close_pending)
- *close = true;
-
- return res;
- }
-
- return 0;
-}
-
-static int write_body_file(struct http_ctx *const h, bool *const close)
-{
- struct write_ctx *const w = &h->wctx;
- const struct http_response *const r = &w->r;
- const unsigned long long left = r->n - w->n;
- char buf[1024];
- const size_t rem = left > sizeof buf ? sizeof buf : left;
-
- if (!fread(buf, 1, rem, r->f))
- {
- fprintf(stderr, "%s: fread(3) failed, ferror=%d, feof=%d\n",
- __func__, ferror(r->f), feof(r->f));
- return -1;
- }
-
- const int res = h->cfg.write(buf, rem, h->cfg.user);
-
- if (res <= 0)
- return rw_error(res, close);
- else if ((w->n += res) >= r->n)
- {
- const bool close_pending = w->close;
-
- if (write_ctx_free(w))
- {
- fprintf(stderr, "%s: write_ctx_free failed\n", __func__);
- return -1;
- }
- else if (close_pending)
- *close = true;
- }
-
- return 0;
-}
-
-static int write_body_line(struct http_ctx *const h, bool *const close)
-{
- const struct http_response *const r = &h->wctx.r;
-
- if (r->buf.ro)
- return write_body_mem(h, close);
- else if (r->f)
- return write_body_file(h, close);
-
- fprintf(stderr, "%s: expected either buffer or file path\n", __func__);
- return -1;
-}
-
-static int http_write(struct http_ctx *const h, bool *const close)
-{
- static int (*const fn[])(struct http_ctx *, bool *) =
- {
- [START_LINE] = write_start_line,
- [HEADER_CR_LINE] = write_header_cr_line,
- [BODY_LINE] = write_body_line,
- };
-
- struct write_ctx *const w = &h->wctx;
-
- const int ret = fn[w->state](h, close);
-
- if (ret)
- write_ctx_free(w);
-
- return ret;
-}
-
-int http_response_add_header(struct http_response *const r,
- const char *const header, const char *const value)
-{
- const size_t n = r->n_headers + 1;
- struct http_header *const headers = realloc(r->headers,
- n * sizeof *r->headers), *h = NULL;
-
- if (!headers)
- {
- fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- h = &headers[r->n_headers];
-
- *h = (const struct http_header)
- {
- .header = strdup(header),
- .value = strdup(value)
- };
-
- if (!h->header || !h->value)
- {
- fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno));
- free(h->header);
- free(h->value);
- return -1;
- }
-
- r->headers = headers;
- r->n_headers = n;
- return 0;
-}
-
-static int start_response(struct http_ctx *const h)
-{
- static const struct code
- {
- const char *descr;
- int code;
- } codes[] =
- {
-#define X(x, y, z) [HTTP_STATUS_##x] = {.descr = y, .code = z},
- HTTP_STATUSES
-#undef X
- };
-
- struct write_ctx *const w = &h->wctx;
- const struct code *const c = &codes[w->r.status];
-
- w->pending = true;
- dynstr_init(&w->d);
- dynstr_append_or_ret_nonzero(&w->d, HTTP_VERSION " %d %s\r\n",
- c->code, c->descr);
- return 0;
-}
-
-static int set_cookie(struct http_ctx *const h, const char *const cookie)
-{
- struct ctx *const c = &h->ctx;
- const char *const value = strchr(cookie, '=');
-
- if (!value)
- {
- fprintf(stderr, "%s: expected field=value for cookie %s\n",
- __func__, cookie);
- goto failure;
- }
- else if (!*(value + 1))
- {
- fprintf(stderr, "%s: expected non-empty value for cookie %s\n",
- __func__, cookie);
- goto failure;
- }
- else if (!(c->field = strndup(cookie, value - cookie)))
- {
- fprintf(stderr, "%s: malloc(3) field: %s\n", __func__, strerror(errno));
- goto failure;
- }
- else if (!(c->value = strdup(value + 1)))
- {
- fprintf(stderr, "%s: malloc(3) value: %s\n", __func__, strerror(errno));
- goto failure;
- }
-
- return 0;
-
-failure:
- free(c->field);
- free(c->value);
- return -1;
-}
-
-static int set_length(struct http_ctx *const h, const char *const len)
-{
- char *end;
-
- errno = 0;
- h->ctx.post.len = strtoull(len, &end, 10);
-
- if (errno || *end != '\0')
- {
- fprintf(stderr, "%s: invalid length %s: %s\n",
- __func__, len, strerror(errno));
- return 1;
- }
-
- return 0;
-}
-
-static int set_content_type(struct http_ctx *const h, const char *const type)
-{
- const char *const sep = strchr(type, ';');
-
- if (!sep)
- /* No multipart/form-data expected. */
- return 0;
-
- const size_t n = sep - type;
-
- if (strncmp(type, "multipart/form-data", n))
- {
- fprintf(stderr, "%s: unsupported Content-Type %.*s\n",
- __func__, (int)n, type);
- return 1;
- }
-
- const char *boundary = sep + 1;
-
- while (*boundary == ' ')
- boundary++;
-
- if (!*boundary)
- {
- fprintf(stderr, "%s: expected boundary\n", __func__);
- return 1;
- }
-
- const char *const eq = strchr(boundary, '=');
-
- if (!eq)
- {
- fprintf(stderr, "%s: expected = after boundary\n", __func__);
- return 1;
- }
-
- const size_t bn = eq - boundary;
-
- if (strncmp(boundary, "boundary", bn))
- {
- fprintf(stderr, "%s: expected boundary, got %.*s\n",
- __func__, (int)bn, boundary);
- return 1;
- }
-
- const char *val = eq + 1;
-
- while (*val == ' ')
- val++;
-
- if (!*val)
- {
- fprintf(stderr, "%s: expected value after boundary\n", __func__);
- return 1;
- }
-
- struct ctx *const c = &h->ctx;
- struct dynstr b;
-
- dynstr_init(&b);
-
- if (dynstr_append(&b, "\r\n--%s", val))
- {
- fprintf(stderr, "%s: dynstr_append failed\n", __func__);
- return -1;
- }
-
- c->boundary = b.str;
- c->u.mf = (const struct multiform){.fd = -1};
- return 0;
-}
-
-static struct http_payload ctx_to_payload(const struct ctx *const c)
-{
- return (const struct http_payload)
- {
- .cookie =
- {
- .field = c->field,
- .value = c->value
- },
-
- .op = c->op,
- .resource = c->resource,
- .args = c->args,
- .n_args = c->n_args
- };
-}
-
-static int payload_get(struct http_ctx *const h, const char *const line)
-{
- struct ctx *const c = &h->ctx;
- const struct http_payload p = ctx_to_payload(c);
- const int ret = h->cfg.payload(&p, &h->wctx.r, h->cfg.user);
-
- ctx_free(c);
-
- if (ret)
- return ret;
-
- return start_response(h);
-}
-
-static int payload_post(struct http_ctx *const h, const char *const line)
-{
- struct ctx *const c = &h->ctx;
- const struct http_payload pl = ctx_to_payload(c);
- const int ret = h->cfg.payload(&pl, &h->wctx.r, h->cfg.user);
-
- ctx_free(c);
-
- if (ret)
- return ret;
-
- return start_response(h);
-}
-
-static int get_field_value(const char *const line, size_t *const n,
- const char **const value)
-{
- const char *const field = strchr(line, ':');
-
- if (!field || line == field)
- {
- fprintf(stderr, "%s: expected field:value\n", __func__);
- return 1;
- }
-
- *n = field - line;
- *value = field + 1;
-
- if (!**value)
- {
- fprintf(stderr, "%s: expected value\n", __func__);
- return 1;
- }
-
- while (**value == ' ')
- (*value)++;
-
- return 0;
-}
-
-static int expect(struct http_ctx *const h, const char *const value)
-{
- if (!strcmp(value, "100-continue"))
- {
- struct ctx *const c = &h->ctx;
- const struct http_payload p =
- {
- .u.post.expect_continue = true,
- .cookie =
- {
- .field = c->field,
- .value = c->value
- },
-
- .op = c->op,
- .resource = c->resource
- };
-
- const int ret = h->cfg.payload(&p, &h->wctx.r, h->cfg.user);
-
- if (ret)
- return ret;
-
- return start_response(h);
- }
-
- return 0;
-}
-
-static int process_header(struct http_ctx *const h, const char *const line,
- const size_t n, const char *const value)
-{
- static const struct header
- {
- const char *header;
- int (*f)(struct http_ctx *, const char *);
- } headers[] =
- {
- {
- .header = "Cookie",
- .f = set_cookie
- },
-
- {
- .header = "Content-Length",
- .f = set_length
- },
-
- {
- .header = "Expect",
- .f = expect
- },
-
- {
- .header = "Content-Type",
- .f = set_content_type
- }
- };
-
- for (size_t i = 0; i < sizeof headers / sizeof *headers; i++)
- {
- const struct header *const hdr = &headers[i];
- int ret;
-
- if (!strncasecmp(line, hdr->header, n) && (ret = hdr->f(h, value)))
- return ret;
- }
-
- return 0;
-}
-
-static int check_length(struct http_ctx *const h)
-{
- struct ctx *const c = &h->ctx;
- const struct http_cookie cookie =
- {
- .field = c->field,
- .value = c->value
- };
-
- return h->cfg.length(c->post.len, &cookie, &h->wctx.r, h->cfg.user);
-}
-
-static int header_cr_line(struct http_ctx *const h)
-{
- const char *const line = (const char *)h->line;
- struct ctx *const c = &h->ctx;
-
- if (!*line)
- {
- switch (c->op)
- {
- case HTTP_OP_GET:
- return payload_get(h, line);
-
- case HTTP_OP_POST:
- {
- if (!c->post.len)
- return payload_post(h, line);
- else if (c->boundary)
- {
- const int res = check_length(h);
-
- if (res)
- {
- h->wctx.close = true;
- return start_response(h);
- }
- }
-
- c->state = BODY_LINE;
- return 0;
- }
- }
- }
-
- const char *value;
- size_t n;
- const int ret = get_field_value(line, &n, &value);
-
- if (ret)
- return ret;
-
- return process_header(h, line, n, value);
-}
-
-static int send_payload(struct http_ctx *const h,
- const struct http_payload *const p)
-{
- struct ctx *const c = &h->ctx;
- const int ret = h->cfg.payload(p, &h->wctx.r, h->cfg.user);
-
- ctx_free(c);
-
- if (ret)
- return ret;
-
- return start_response(h);
-}
-
-static int update_lstate(struct http_ctx *const h, bool *const close,
- int (*const f)(struct http_ctx *), const char b)
-{
- int ret = 1;
- struct ctx *const c = &h->ctx;
-
- switch (c->lstate)
- {
- case LINE_CR:
- if (b == '\r')
- c->lstate = LINE_LF;
- else if (c->len < sizeof h->line - 2)
- h->line[c->len++] = b;
- else
- {
- fprintf(stderr, "%s: line too long\n", __func__);
- goto failure;
- }
-
- break;
-
- case LINE_LF:
- if (b == '\n')
- {
- h->line[c->len] = '\0';
-
- if ((ret = f(h)))
- goto failure;
-
- c->len = 0;
- }
- else if (c->len < sizeof h->line - 3)
- {
- h->line[c->len++] = '\r';
- h->line[c->len++] = b;
- }
- else
- {
- fprintf(stderr, "%s: line too long\n", __func__);
- goto failure;
- }
-
- c->lstate = LINE_CR;
- break;
- }
-
- return 0;
-
-failure:
- ctx_free(c);
- return ret;
-}
-
-static int start_boundary_line(struct http_ctx *const h)
-{
- struct ctx *const c = &h->ctx;
- struct multiform *const m = &c->u.mf;
- const char *const line = h->line;
-
- if (strcmp(line, c->boundary + strlen("\r\n")))
- {
- fprintf(stderr, "%s: expected boundary %s, got %s\n",
- __func__, c->boundary, line);
- return 1;
- }
-
- m->state = MF_HEADER_CR_LINE;
- m->len = strlen(line) + strlen("\r\n");
- return 0;
-}
-
-static int cd_fields(struct http_ctx *const h, struct form *const f,
- const char *sep)
-{
- do
- {
- while (*++sep == ' ')
- ;
-
- if (!*sep)
- break;
-
- const char *const op = strchr(sep, '=');
-
- if (!op)
- {
- fprintf(stderr, "%s: expected attr=value\n", __func__);
- return 1;
- }
-
- const char *const value = op + 1, *const end = strchr(value, ';');
- const size_t vlen = end ? end - value : strlen(value);
-
- if (*value != '\"' || value[vlen - 1] != '\"')
- {
- fprintf(stderr, "%s: expected \"-enclosed value, got %.*s\n",
- __func__, (int)vlen, value);
- return 1;
- }
-
- const char *const evalue = value + 1;
- const size_t evlen = vlen - 2;
-
- if (!strncmp(sep, "name", op - sep))
- {
- if (!evlen)
- {
- fprintf(stderr, "%s: expected non-empty name\n", __func__);
- return 1;
- }
- else if (!(f->name = strndup(evalue, evlen)))
- {
- fprintf(stderr, "%s: strndup(3): %s\n",
- __func__, strerror(errno));
- return -1;
- }
- }
- else if (!strncmp(sep, "filename", op - sep))
- {
- if (!evlen)
- {
- fprintf(stderr, "%s: expected non-empty filename\n", __func__);
- return 1;
- }
- else if (!(f->filename = strndup(evalue, evlen)))
- {
- fprintf(stderr, "%s: strndup(3): %s\n", __func__, strerror(errno));
- return -1;
- }
- else if (!strcmp(f->filename, ".")
- || !strcmp(f->filename, "..")
- || strpbrk(f->filename, "/*"))
- {
- fprintf(stderr, "%s: invalid filename %s\n",
- __func__, f->filename);
- return 1;
- }
- }
- } while ((sep = strchr(sep, ';')));
-
- if (!f->name)
- {
- fprintf(stderr, "%s: expected name\n", __func__);
- return 1;
- }
-
- return 0;
-}
-
-static int set_content_disposition(struct http_ctx *const h,
- const char *const c)
-{
- const char *const sep = strchr(c, ';');
- struct multiform *const m = &h->ctx.u.mf;
-
- if (!sep)
- {
- fprintf(stderr, "%s: no fields found\n", __func__);
- return 1;
- }
- else if (strncmp(c, "form-data", sep - c))
- {
- fprintf(stderr, "%s: expected form-data, got %.*s\n",
- __func__, (int)(sep - c), c);
- return 1;
- }
-
- const size_t n = m->nforms + 1;
- struct form *const forms = realloc(m->forms, n * sizeof *m->forms);
-
- if (!forms)
- {
- fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- struct form *const f = &forms[m->nforms];
-
- *f = (const struct form){0};
- m->nforms = n;
- m->forms = forms;
- return cd_fields(h, f, sep);
-}
-
-static int mf_header_cr_line(struct http_ctx *const h)
-{
- struct ctx *const c = &h->ctx;
- struct multiform *const m = &c->u.mf;
- const char *const line = h->line;
-
- m->len += strlen(line) + strlen("\r\n");
-
- if (!*line)
- {
- const size_t n = strlen("\r\n") + strlen(c->boundary) + 1;
-
- if (!m->boundary && !(m->boundary = calloc(1, n)))
- {
- fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- m->state = MF_BODY_BOUNDARY_LINE;
- return 0;
- }
-
- const char *value;
- size_t n;
- int ret = get_field_value(line, &n, &value);
-
- if (ret)
- return ret;
- else if (!strncasecmp(line, "Content-Disposition", n)
- && (ret = set_content_disposition(h, value)))
- return ret;
-
- return 0;
-}
-
-static int end_boundary_line(struct http_ctx *const h)
-{
- const char *const line = h->line;
-
- if (!*line)
- {
- h->ctx.u.mf.state = MF_HEADER_CR_LINE;
- return 0;
- }
- else if (!strcmp(line, "--"))
- {
- /* Found end boundary. */
- struct ctx *const c = &h->ctx;
- struct multiform *const m = &c->u.mf;
-
- const struct http_payload p =
- {
- .cookie =
- {
- .field = c->field,
- .value = c->value
- },
-
- .op = c->op,
- .resource = c->resource,
- .u.post =
- {
- .dir = m->dir,
- .files = m->files,
- .n = m->nfiles
- }
- };
-
- return send_payload(h, &p);
- }
-
- fprintf(stderr, "%s: unexpected line after boundary: %s\n",
- __func__, line);
- return 1;
-}
-
-static int process_mf_line(struct http_ctx *const h)
-{
- static int (*const state[])(struct http_ctx *) =
- {
- [MF_START_BOUNDARY] = start_boundary_line,
- [MF_HEADER_CR_LINE] = mf_header_cr_line,
- [MF_END_BOUNDARY_CR_LINE] = end_boundary_line
- };
-
- h->ctx.post.read += strlen(h->line) + strlen("\r\n");
- return state[h->ctx.u.mf.state](h);
-}
-
-static char *get_tmp(const char *const tmpdir)
-{
- struct dynstr d;
-
- dynstr_init(&d);
-
- if (dynstr_append(&d, "%s/tmp.XXXXXX", tmpdir))
- {
- fprintf(stderr, "%s: dynstr_append failed\n", __func__);
- return NULL;
- }
-
- return d.str;
-}
-
-static int generate_mf_file(struct http_ctx *const h)
-{
- struct multiform *const m = &h->ctx.u.mf;
- struct form *const f = &m->forms[m->nforms - 1];
-
- if (!(f->tmpname = get_tmp(h->cfg.tmpdir)))
- {
- fprintf(stderr, "%s: get_tmp failed\n", __func__);
- return -1;
- }
- else if ((m->fd = mkstemp(f->tmpname)) < 0)
- {
- fprintf(stderr, "%s: mkstemp(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-static int read_mf_body_to_mem(struct http_ctx *const h, const void *const buf,
- const size_t n)
-{
- struct ctx *const c = &h->ctx;
- struct multiform *const m = &c->u.mf;
-
- if (m->written + n > sizeof h->line)
- {
- fprintf(stderr, "%s: maximum length exceeded\n", __func__);
- return 1;
- }
-
- memcpy(&h->line[m->written], buf, n);
- m->written += n;
- m->len += n;
- c->post.read += n;
- return 0;
-}
-
-static int read_mf_body_to_file(struct http_ctx *const h, const void *const buf,
- const size_t n)
-{
- struct ctx *const c = &h->ctx;
- struct multiform *const m = &c->u.mf;
- ssize_t res;
-
- if (m->fd < 0 && generate_mf_file(h))
- {
- fprintf(stderr, "%s: generate_mf_file failed\n", __func__);
- return -1;
- }
- else if ((res = pwrite(m->fd, buf, n, m->written)) < 0)
- {
- fprintf(stderr, "%s: pwrite(2): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- m->written += res;
- m->len += res;
- c->post.read += res;
- return 0;
-}
-
-static int reset_boundary(struct http_ctx *const h, const void *const buf,
- const size_t n)
-{
- struct multiform *const m = &h->ctx.u.mf;
- struct form *const f = &m->forms[m->nforms - 1];
- const size_t len = strlen(m->boundary);
- int (*const read_mf)(struct http_ctx *, const void *, size_t) =
- f->filename ? read_mf_body_to_file : read_mf_body_to_mem;
- const int res = read_mf(h, m->boundary, len);
-
- if (res)
- return res;
-
- memset(m->boundary, '\0', len);
- m->blen = 0;
- return read_mf(h, buf, n);
-}
-
-static int apply_from_file(struct http_ctx *const h, struct form *const f)
-{
- struct multiform *const m = &h->ctx.u.mf;
-
- if (close(m->fd))
- {
- fprintf(stderr, "%s: close(2): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- m->fd = -1;
-
- const size_t n = m->nfiles + 1;
- struct http_post_file *const files = realloc(m->files,
- n * sizeof *m->files);
-
- if (!files)
- {
- fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- struct http_post_file *const pf = &files[m->nfiles];
-
- *pf = (const struct http_post_file)
- {
- .tmpname = f->tmpname,
- .filename = f->filename
- };
-
- m->files = files;
- m->nfiles = n;
- return 0;
-}
-
-static int apply_from_mem(struct http_ctx *const h, struct form *const f)
-{
- struct multiform *const m = &h->ctx.u.mf;
-
- if (!(f->value = strndup(h->line, m->written)))
- {
- fprintf(stderr, "%s: strndup(3): %s\n", __func__, strerror(errno));
- return -1;
- }
- else if (!strcmp(f->name, "dir"))
- {
- if (m->dir)
- {
- fprintf(stderr, "%s: \"dir\" defined more than once\n", __func__);
- return 1;
- }
-
- m->dir = f->value;
- }
-
- return 0;
-}
-
-static int read_mf_body_boundary_byte(struct http_ctx *const h, const char b,
- const size_t len)
-{
- struct ctx *const c = &h->ctx;
- struct multiform *const m = &c->u.mf;
-
- if (b == c->boundary[m->blen])
- {
- m->boundary[len] = b;
-
- if (++m->blen >= strlen(c->boundary))
- {
- /* Found intermediate boundary. */
- struct form *const f = &m->forms[m->nforms - 1];
- const int ret = f->filename ? apply_from_file(h, f)
- : apply_from_mem(h, f);
-
- memset(m->boundary, '\0', len + 1);
- m->blen = 0;
- m->state = MF_END_BOUNDARY_CR_LINE;
- m->written = 0;
- return ret;
- }
- }
-
- return 0;
-}
-
-/* Similar to memmem(3), provided here to avoid the use of GNU extensions. */
-static const char *http_memmem(const char *const a, const void *const b,
- const size_t n)
-{
- const size_t len = strlen(a);
-
- if (len > n)
- return NULL;
-
- const char *s = a, *st = NULL;
-
- for (size_t i = 0; i < n; i++)
- {
- const char *bc = b;
- const char c = bc[i];
-
- if (*s == c)
- {
- if (!st)
- st = &bc[i];
-
- if (!*++s)
- return st;
- }
- else if (*(s = a) != c)
- st = NULL;
- else
- {
- st = &bc[i];
-
- if (!*++s)
- return st;
- }
- }
-
- return NULL;
-}
-
-static int read_mf_body_boundary(struct http_ctx *const h,
- const char **const buf, size_t *const n)
-{
- struct ctx *const c = &h->ctx;
- struct multiform *const m = &c->u.mf;
- const char *const boundary = http_memmem(&c->boundary[m->blen], *buf, *n);
- int res;
-
- if (!boundary)
- {
- if ((res = reset_boundary(h, *buf, *n)))
- return res;
-
- *n = 0;
- return 0;
- }
-
- const size_t prev = boundary - *buf;
-
- if ((res = reset_boundary(h, *buf, prev)))
- return res;
-
- *buf += prev;
- *n -= prev;
-
- const size_t len = strlen(m->boundary),
- rem = strlen(c->boundary) - len,
- r = rem > *n ? *n : rem;
-
- for (size_t i = 0; i < r; i++)
- {
- const char *const b = *buf;
-
- if ((res = read_mf_body_boundary_byte(h, b[i], len)))
- return res;
- }
-
- *buf += r;
- *n -= r;
- return 0;
-}
-
-static int read_multiform_n(struct http_ctx *const h, bool *const close,
- const char *buf, size_t n)
-{
- struct multiform *const m = &h->ctx.u.mf;
-
- while (n)
- {
- int res;
-
- switch (m->state)
- {
- case MF_START_BOUNDARY:
- /* Fall through. */
- case MF_HEADER_CR_LINE:
- /* Fall through. */
- case MF_END_BOUNDARY_CR_LINE:
- {
- if ((res = update_lstate(h, close, process_mf_line, *buf)))
- return res;
-
- buf++;
- n--;
- }
-
- break;
-
- case MF_BODY_BOUNDARY_LINE:
- if ((res = read_mf_body_boundary(h, &buf, &n)))
- return res;
- }
- }
-
- return 0;
-}
-
-static int read_multiform(struct http_ctx *const h, bool *const close)
-{
- /* Note: the larger the buffer below, the less CPU load. */
- char buf[sizeof h->line];
- struct post *const p = &h->ctx.post;
- const unsigned long long left = p->len - p->read;
- const size_t rem = left > sizeof buf ? sizeof buf : left;
- const int r = h->cfg.read(buf, rem, h->cfg.user);
-
- if (r <= 0)
- return rw_error(r, close);
-
- return read_multiform_n(h, close, buf, r);
-}
-
-static int read_body_to_mem(struct http_ctx *const h, bool *const close)
-{
- char b;
- const int r = h->cfg.read(&b, sizeof b, h->cfg.user);
-
- if (r <= 0)
- return rw_error(r, close);
-
- struct ctx *const c = &h->ctx;
- struct post *const p = &c->post;
-
- if (p->read >= sizeof h->line)
- {
- fprintf(stderr, "%s: exceeded maximum length\n", __func__);
- return 1;
- }
-
- h->line[p->read++] = b;
-
- if (p->read >= p->len)
- {
- const struct http_payload pl =
- {
- .cookie =
- {
- .field = c->field,
- .value = c->value
- },
-
- .op = c->op,
- .resource = c->resource,
- .u.post =
- {
- .data = h->line,
- .n = p->len
- }
- };
-
- return send_payload(h, &pl);
- }
-
- return 0;
-}
-
-static int read_body(struct http_ctx *const h, bool *const close)
-{
- return h->ctx.boundary ? read_multiform(h, close)
- : read_body_to_mem(h, close);
-}
-
-static int process_line(struct http_ctx *const h)
-{
- static int (*const state[])(struct http_ctx *) =
- {
- [START_LINE] = start_line,
- [HEADER_CR_LINE] = header_cr_line
- };
-
- return state[h->ctx.state](h);
-}
-
-static int http_read(struct http_ctx *const h, bool *const close)
-{
- switch (h->ctx.state)
- {
- case START_LINE:
- /* Fall through. */
- case HEADER_CR_LINE:
- {
- char b;
- const int r = h->cfg.read(&b, sizeof b, h->cfg.user);
-
- if (r <= 0)
- return rw_error(r, close);
-
- return update_lstate(h, close, process_line, b);
- }
-
- case BODY_LINE:
- return read_body(h, close);
- }
-
- fprintf(stderr, "%s: unexpected state %d\n", __func__, h->ctx.state);
- return -1;
-}
-
-static int append_expire(struct dynstr *const d)
-{
- time_t t = time(NULL);
-
- if (t == (time_t)-1)
- {
- fprintf(stderr, "%s: time(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- t += 365 * 24 * 60 * 60;
-
- struct tm tm;
-
- if (!localtime_r(&t, &tm))
- {
- fprintf(stderr, "%s: localtime_r(3): %s\n", __func__, strerror(errno));
- return -1;
- }
-
- char s[sizeof "Thu, 01 Jan 1970 00:00:00 GMT"];
-
- if (!strftime(s, sizeof s, "%a, %d %b %Y %H:%M:%S GMT", &tm))
- {
- fprintf(stderr, "%s: strftime(3) failed\n", __func__);
- return -1;
- }
-
- dynstr_append_or_ret_nonzero(d, "; Expires=%s", s);
- return 0;
-}
-
-char *http_cookie_create(const char *const key, const char *const value)
-{
- struct dynstr d;
-
- dynstr_init(&d);
- dynstr_append_or_ret_null(&d, "%s=%s; HttpOnly", key, value);
-
- if (append_expire(&d))
- {
- dynstr_free(&d);
- return NULL;
- }
-
- return d.str;
-}
-
-int http_update(struct http_ctx *const h, bool *const write, bool *const close)
-{
- *close = false;
-
- struct write_ctx *const w = &h->wctx;
- const int ret = w->pending ? http_write(h, close) : http_read(h, close);
-
- *write = w->pending;
- return ret;
-}
-
-void http_free(struct http_ctx *const h)
-{
- if (h)
- {
- ctx_free(&h->ctx);
- write_ctx_free(&h->wctx);
- }
-
- free(h);
-}
-
-struct http_ctx *http_alloc(const struct http_cfg *const cfg)
-{
- struct http_ctx *const h = malloc(sizeof *h);
-
- if (!h)
- {
- fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
- goto failure;
- }
-
- *h = (const struct http_ctx)
- {
- .cfg = *cfg
- };
-
- return h;
-
-failure:
- http_free(h);
- return NULL;
-}
-
-char *http_encode_url(const char *url)
-{
- struct dynstr d;
- char c;
-
- dynstr_init(&d);
-
- while ((c = *url++))
- {
- /* Unreserved characters must not be percent-encoded. */
- if ((c >= 'A' && c <= 'Z')
- || (c >= 'a' && c <= 'z')
- || (c == '-')
- || (c == '_')
- || (c == '.')
- || (c == '/')
- || (c == '~'))
- {
- if (dynstr_append(&d, "%c", c))
- {
- fprintf(stderr, "%s: dynstr_append failed\n", __func__);
- goto failure;
- }
- }
- else if (dynstr_append(&d, "%%%hhx", c))
- {
- fprintf(stderr, "%s: dynstr_append failed\n", __func__);
- goto failure;
- }
- }
-
- return d.str;
-
-failure:
- dynstr_free(&d);
- return NULL;
-}
-
-char *http_decode_url(const char *url, const bool spaces)
-{
- char *ret = NULL;
- size_t n = 0;
-
- while (*url)
- {
- char *const r = realloc(ret, n + 1);
-
- if (!r)
- {
- fprintf(stderr, "%s: realloc(3) loop: %s\n",
- __func__, strerror(errno));
- goto failure;
- }
-
- ret = r;
-
- if (spaces && *url == '+')
- {
- ret[n++] = ' ';
- url++;
- }
- else if (*url != '%')
- ret[n++] = *url++;
- else if (*(url + 1) && *(url + 2))
- {
- const char buf[sizeof "00"] = {*(url + 1), *(url + 2)};
- char *endptr;
- const unsigned long res = strtoul(buf, &endptr, 16);
-
- if (*endptr)
- {
- fprintf(stderr, "%s: invalid number %s\n", __func__, buf);
- goto failure;
- }
-
- url += 3;
- ret[n++] = res;
- }
- else
- {
- fprintf(stderr, "%s: unterminated %%\n", __func__);
- goto failure;
- }
- }
-
- char *const r = realloc(ret, n + 1);
-
- if (!r)
- {
- fprintf(stderr, "%s: realloc(3) end: %s\n", __func__, strerror(errno));
- goto failure;
- }
-
- ret = r;
- ret[n] = '\0';
- return ret;
-
-failure:
- free(ret);
- return NULL;
-}
diff --git a/http.h b/http.h
deleted file mode 100644
index ac1f51a..0000000
--- a/http.h
+++ /dev/null
@@ -1,106 +0,0 @@
-#ifndef HTTP_H
-#define HTTP_H
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-
-struct http_payload
-{
- enum http_op
- {
- HTTP_OP_GET,
- HTTP_OP_POST
- } op;
-
- const char *resource;
-
- struct http_cookie
- {
- const char *field, *value;
- } cookie;
-
- union
- {
- struct http_post
- {
- bool expect_continue;
- const void *data;
- size_t n;
- const char *dir;
-
- const struct http_post_file
- {
- const char *tmpname, *filename;
- } *files;
- } post;
- } u;
-
- const struct http_arg
- {
- char *key, *value;
- } *args;
-
- size_t n_args;
-};
-
-#define HTTP_STATUSES \
- X(CONTINUE, "Continue", 100) \
- X(OK, "OK", 200) \
- X(SEE_OTHER, "See other", 303) \
- X(BAD_REQUEST, "Bad Request", 400) \
- X(UNAUTHORIZED, "Unauthorized", 401) \
- X(FORBIDDEN, "Forbidden", 403) \
- X(NOT_FOUND, "Not found", 404) \
- X(PAYLOAD_TOO_LARGE, "Payload too large", 413) \
- X(INTERNAL_ERROR, "Internal Server Error", 500)
-
-struct http_response
-{
- enum http_status
- {
-#define X(x, y, z) HTTP_STATUS_##x,
- HTTP_STATUSES
-#undef X
- } status;
-
- struct http_header
- {
- char *header, *value;
- } *headers;
-
- union
- {
- const void *ro;
- void *rw;
- } buf;
-
- FILE *f;
- unsigned long long n;
- size_t n_headers;
- void (*free)(void *);
-};
-
-struct http_cfg
-{
- int (*read)(void *buf , size_t n, void *user);
- int (*write)(const void *buf, size_t n, void *user);
- int (*payload)(const struct http_payload *p, struct http_response *r,
- void *user);
- int (*length)(unsigned long long len, const struct http_cookie *c,
- struct http_response *r, void *user);
- const char *tmpdir;
- void *user;
-};
-
-struct http_ctx *http_alloc(const struct http_cfg *cfg);
-void http_free(struct http_ctx *h);
-/* Positive return value: user input error, negative: fatal error. */
-int http_update(struct http_ctx *h, bool *write, bool *close);
-int http_response_add_header(struct http_response *r, const char *header,
- const char *value);
-char *http_cookie_create(const char *key, const char *value);
-char *http_encode_url(const char *url);
-char *http_decode_url(const char *url, bool spaces);
-
-#endif /* HTTP_H */
diff --git a/server.c b/server.c
deleted file mode 100644
index 22615b2..0000000
--- a/server.c
+++ /dev/null
@@ -1,373 +0,0 @@
-#define _POSIX_C_SOURCE 200809L
-
-#include "server.h"
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <poll.h>
-#include <unistd.h>
-#include <errno.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-struct server
-{
- int fd;
-
- struct server_client
- {
- int fd;
- bool write;
- struct server_client *prev, *next;
- } *c;
-};
-
-int server_close(struct server *const s)
-{
- int ret = 0;
-
- if (!s)
- return 0;
- else if (s->fd >= 0)
- ret = close(s->fd);
-
- free(s);
- return ret;
-}
-
-int server_client_close(struct server *const s, struct server_client *const c)
-{
- int ret = 0;
-
- for (struct server_client *ref = s->c; ref; ref = ref->next)
- {
- if (c == ref)
- {
- struct server_client *const next = ref->next;
-
- if ((ret = close(c->fd)))
- {
- fprintf(stderr, "%s: close(2): %s\n",
- __func__, strerror(errno));
- return -1;
- }
- else if (ref->prev)
- ref->prev->next = next;
- else
- s->c = next;
-
- if (next)
- next->prev = ref->prev;
-
- free(ref);
- break;
- }
- }
-
- return ret;
-}
-
-int server_read(void *const buf, const size_t n, struct server_client *const c)
-{
- const ssize_t r = read(c->fd, buf, n);
-
- if (r < 0)
- fprintf(stderr, "%s: read(2): %s\n", __func__, strerror(errno));
-
- return r;
-}
-
-int server_write(const void *const buf, const size_t n,
- struct server_client *const c)
-{
- const ssize_t w = write(c->fd, buf, n);
-
- if (w < 0)
- fprintf(stderr, "%s: write(2): %s\n", __func__, strerror(errno));
-
- return w;
-}
-
-static struct server_client *alloc_client(struct server *const s)
-{
- struct sockaddr_in addr;
- socklen_t sz = sizeof addr;
- const int fd = accept(s->fd, (struct sockaddr *)&addr, &sz);
-
- if (fd < 0)
- {
- fprintf(stderr, "%s: accept(2): %s\n",
- __func__, strerror(errno));
- return NULL;
- }
-
- const int flags = fcntl(fd, F_GETFL);
-
- if (flags < 0)
- {
- fprintf(stderr, "%s: fcntl(2) F_GETFL: %s\n",
- __func__, strerror(errno));
- return NULL;
- }
- else if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
- {
- fprintf(stderr, "%s: fcntl(2) F_SETFL: %s\n",
- __func__, strerror(errno));
- return NULL;
- }
-
- struct server_client *const c = malloc(sizeof *c);
-
- if (!c)
- {
- fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
- return NULL;
- }
-
- *c = (const struct server_client)
- {
- .fd = fd
- };
-
- if (!s->c)
- s->c = c;
- else
- for (struct server_client *ref = s->c; ref; ref = ref->next)
- if (!ref->next)
- {
- ref->next = c;
- c->prev = ref;
- break;
- }
-
- return c;
-}
-
-void server_client_write_pending(struct server_client *const c,
- const bool write)
-{
- c->write = write;
-}
-
-static volatile sig_atomic_t do_exit;
-
-static void handle_signal(const int signum)
-{
- switch (signum)
- {
- case SIGINT:
- /* Fall through. */
- case SIGTERM:
- do_exit = 1;
- break;
-
- default:
- break;
- }
-}
-
-static size_t get_clients(const struct server *const s)
-{
- size_t ret = 0;
-
- for (const struct server_client *c = s->c; c; c = c->next)
- ret++;
-
- return ret;
-}
-
-struct server_client *server_poll(struct server *const s, bool *const io,
- bool *const exit)
-{
- struct server_client *ret = NULL;
- const size_t n_clients = get_clients(s);
- const nfds_t n = n_clients + 1;
- struct pollfd *const fds = malloc(n * sizeof *fds);
-
- if (!fds)
- {
- fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
- goto end;
- }
-
- struct pollfd *const sfd = &fds[0];
-
- *io = *exit = false;
- *sfd = (const struct pollfd)
- {
- .fd = s->fd,
- .events = POLLIN
- };
-
- for (struct {const struct server_client *c; size_t j;}
- _ = {.c = s->c, .j = 1}; _.c; _.c = _.c->next, _.j++)
- {
- struct pollfd *const p = &fds[_.j];
- const int fd = _.c->fd;
-
- *p = (const struct pollfd)
- {
- .fd = fd,
- .events = POLLIN
- };
-
- if (_.c->write)
- p->events |= POLLOUT;
- }
-
- int res;
-
-again:
-
- res = poll(fds, n, -1);
-
- if (res < 0)
- {
- if (do_exit)
- {
- *exit = true;
- goto end;
- }
-
- switch (errno)
- {
- case EAGAIN:
- /* Fall through. */
- case EINTR:
- goto again;
-
- default:
- fprintf(stderr, "%s: poll(2): %s\n", __func__, strerror(errno));
- break;
- }
-
- goto end;
- }
- else if (!res)
- {
- fprintf(stderr, "%s: poll(2) returned zero\n", __func__);
- goto end;
- }
- else if (sfd->revents)
- {
- ret = alloc_client(s);
- goto end;
- }
-
- for (struct {struct server_client *c; size_t j;}
- _ = {.c = s->c, .j = 1}; _.c; _.c = _.c->next, _.j++)
- {
- const struct pollfd *const p = &fds[_.j];
-
- if (p->revents)
- {
- *io = true;
- ret = _.c;
- goto end;
- }
- }
-
- fprintf(stderr, "%s: unlisted fd\n", __func__);
-
-end:
- free(fds);
- return ret;
-}
-
-static int init_signals(void)
-{
- struct sigaction sa =
- {
- .sa_handler = handle_signal,
- .sa_flags = SA_RESTART
- };
-
- sigemptyset(&sa.sa_mask);
-
- if (sigaction(SIGINT, &sa, NULL))
- {
- fprintf(stderr, "%s: sigaction(2) SIGINT: %s\n",
- __func__, strerror(errno));
- return -1;
- }
- else if (sigaction(SIGTERM, &sa, NULL))
- {
- fprintf(stderr, "%s: sigaction(2) SIGTERM: %s\n",
- __func__, strerror(errno));
- return -1;
- }
- else if (sigaction(SIGPIPE, &sa, NULL))
- {
- fprintf(stderr, "%s: sigaction(2) SIGPIPE: %s\n",
- __func__, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-struct server *server_init(const unsigned short port)
-{
- struct server *const s = malloc(sizeof *s);
-
- if (!s)
- {
- fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
- goto failure;
- }
-
- *s = (const struct server)
- {
- .fd = socket(AF_INET, SOCK_STREAM, 0)
- };
-
- if (s->fd < 0)
- {
- fprintf(stderr, "%s: socket(2): %s\n", __func__, strerror(errno));
- goto failure;
- }
- else if (init_signals())
- {
- fprintf(stderr, "%s: init_signals failed\n", __func__);
- goto failure;
- }
-
- const struct sockaddr_in addr =
- {
- .sin_family = AF_INET,
- .sin_port = htons(port)
- };
-
- enum {QUEUE_LEN = 10};
-
- if (bind(s->fd, (const struct sockaddr *)&addr, sizeof addr))
- {
- fprintf(stderr, "%s: bind(2): %s\n", __func__, strerror(errno));
- goto failure;
- }
- else if (listen(s->fd, QUEUE_LEN))
- {
- fprintf(stderr, "%s: listen(2): %s\n", __func__, strerror(errno));
- goto failure;
- }
-
- struct sockaddr_in in;
- socklen_t sz = sizeof in;
-
- if (getsockname(s->fd, (struct sockaddr *)&in, &sz))
- {
- fprintf(stderr, "%s: getsockname(2): %s\n", __func__, strerror(errno));
- goto failure;
- }
-
- printf("Listening on port %hu\n", ntohs(in.sin_port));
- return s;
-
-failure:
- server_close(s);
- return NULL;
-}
diff --git a/server.h b/server.h
deleted file mode 100644
index 74f06ae..0000000
--- a/server.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef SERVER_H
-#define SERVER_H
-
-#include <stdbool.h>
-#include <stddef.h>
-
-struct server *server_init(unsigned short port);
-struct server_client *server_poll(struct server *s, bool *io, bool *exit);
-int server_read(void *buf, size_t n, struct server_client *c);
-int server_write(const void *buf, size_t n, struct server_client *c);
-int server_close(struct server *s);
-int server_client_close(struct server *s, struct server_client *c);
-void server_client_write_pending(struct server_client *c, bool write);
-
-#endif /* SERVER_H */