2023-03-24 02:47:11 +01:00
|
|
|
#define _POSIX_C_SOURCE 200809L
|
|
|
|
|
2023-01-09 01:22:54 +01:00
|
|
|
#include "page.h"
|
|
|
|
#include "http.h"
|
|
|
|
#include "html.h"
|
|
|
|
#include <dynstr.h>
|
|
|
|
#include <dirent.h>
|
2023-03-16 01:49:18 +01:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <libgen.h>
|
2023-01-09 01:22:54 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2023-04-23 05:19:27 +02:00
|
|
|
#include <strings.h>
|
2023-01-09 01:22:54 +01:00
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#define PROJECT_NAME "slcl"
|
2023-03-09 01:20:18 +01:00
|
|
|
#define PROJECT_TITLE PROJECT_NAME ", a suckless cloud"
|
|
|
|
#define PROJECT_TAG "<title>" PROJECT_TITLE "</title>"
|
|
|
|
#define DOCTYPE_TAG "<!DOCTYPE html>\n"
|
2023-01-09 01:22:54 +01:00
|
|
|
#define PROJECT_URL "https://gitea.privatedns.org/Xavi92/" PROJECT_NAME
|
2023-03-08 18:52:03 +01:00
|
|
|
#define COMMON_HEAD \
|
2023-01-09 01:22:54 +01:00
|
|
|
" <meta charset=\"UTF-8\">\n" \
|
|
|
|
" <meta name=\"viewport\"\n" \
|
|
|
|
" content=\"width=device-width, initial-scale=1,\n" \
|
|
|
|
" maximum-scale=1\">\n" \
|
2023-03-09 01:20:18 +01:00
|
|
|
PROJECT_TAG "\n"
|
2023-01-09 01:22:54 +01:00
|
|
|
#define STYLE_A "<link href=\"/style.css\" rel=\"stylesheet\">"
|
|
|
|
#define LOGIN_BODY \
|
|
|
|
"<header>\n" \
|
|
|
|
" <a href=\"" PROJECT_URL "\">" PROJECT_NAME "</a>, a suckless cloud\n" \
|
|
|
|
"</header>\n" \
|
2023-06-02 09:03:57 +02:00
|
|
|
" <form class=\"loginform\" action=\"/login\" method=\"post\">\n" \
|
2023-01-09 01:22:54 +01:00
|
|
|
" <label for=\"username\">Username:</label>\n" \
|
|
|
|
" <input type=\"text\" class=\"form-control\"\n" \
|
|
|
|
" id=\"username\" name=\"username\" autofocus><br>\n" \
|
|
|
|
" <label for=\"username\">Password:</label>\n" \
|
|
|
|
" <input type=\"password\" class=\"form-control\" \n" \
|
|
|
|
" id=\"password\" name=\"password\"><br>\n" \
|
|
|
|
" <input type=\"submit\" value=\"Submit\">\n" \
|
|
|
|
" </form>\n"
|
2023-03-08 00:33:55 +01:00
|
|
|
#define MAXSIZEFMT "18446744073709551615.0 XiB"
|
2023-07-08 00:54:59 +02:00
|
|
|
#define RM_FORM_ID "rm"
|
|
|
|
|
|
|
|
static int prepare_rm_checkbox(struct html_node *const n,
|
|
|
|
const struct stat *const sb, const char *const name)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
const char *const sep = S_ISDIR(sb->st_mode) ? "/" : "";
|
|
|
|
struct html_node *input;
|
|
|
|
struct dynstr d;
|
|
|
|
|
|
|
|
dynstr_init(&d);
|
|
|
|
|
|
|
|
if (dynstr_append(&d, "%s%s", name, sep))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(input = html_node_add_child(n, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child input failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "type", "checkbox"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr type failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "form", RM_FORM_ID))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr form failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "name", "path"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr name failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "value", d.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr value failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
dynstr_free(&d);
|
|
|
|
return ret;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
|
|
|
|
static int prepare_name(struct html_node *const n, struct stat *const sb,
|
|
|
|
const char *const dir, const char *const name)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct html_node *a;
|
|
|
|
struct dynstr d, dname;
|
|
|
|
const char *const sep = S_ISDIR(sb->st_mode) ? "/" : "";
|
|
|
|
|
|
|
|
dynstr_init(&d);
|
|
|
|
dynstr_init(&dname);
|
|
|
|
|
|
|
|
if (dynstr_append(&d, "%s%s%s", dir, name, sep))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append [1] failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(a = html_node_add_child(n, "a")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(a, "href", d.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr href failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&dname, "%s%s", name, sep))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append [2] failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(a, dname.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
dynstr_free(&d);
|
|
|
|
dynstr_free(&dname);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-03-08 00:33:55 +01:00
|
|
|
static int size_units(const unsigned long long b, char *const buf,
|
|
|
|
const size_t n)
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
2023-03-08 00:33:55 +01:00
|
|
|
static const char *const suffixes[] = {"B", "KiB", "MiB", "GiB", "TiB"};
|
2023-01-09 01:22:54 +01:00
|
|
|
float sz;
|
|
|
|
size_t suffix_i;
|
|
|
|
|
2023-03-08 00:33:55 +01:00
|
|
|
for (sz = b, suffix_i = 0; (unsigned long long)sz / 1024;
|
2023-01-09 01:22:54 +01:00
|
|
|
suffix_i++, sz /= 1024.0f)
|
|
|
|
;
|
|
|
|
|
2023-03-08 00:33:55 +01:00
|
|
|
if (suffix_i >= sizeof suffixes / sizeof *suffixes)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: maximum suffix exceeded\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
|
|
|
|
const int r = suffix_i ?
|
2023-03-08 00:33:55 +01:00
|
|
|
snprintf(buf, n, "%.1f %s", sz, suffixes[suffix_i])
|
|
|
|
: snprintf(buf, n, "%llu %s", b, suffixes[suffix_i]);
|
2023-01-09 01:22:54 +01:00
|
|
|
|
2023-03-08 00:33:55 +01:00
|
|
|
if (r >= n || r < 0)
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: snprintf(3) failed with %d\n", __func__, r);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-03-08 00:33:55 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int prepare_size(struct html_node *const n, const struct stat *const sb)
|
|
|
|
{
|
|
|
|
char buf[sizeof MAXSIZEFMT] = "";
|
|
|
|
|
|
|
|
if (S_ISREG(sb->st_mode)
|
|
|
|
&& size_units(sb->st_size, buf, sizeof buf))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: size_units failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
else if (html_node_set_value(n, buf))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int prepare_date(struct html_node *const n, const struct stat *const sb)
|
|
|
|
{
|
|
|
|
struct tm tm;
|
|
|
|
|
|
|
|
if (!localtime_r(&sb->st_mtime, &tm))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: localtime_r(3): %s\n", __func__, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char date[sizeof "0000-00-00T00:00:00+0000"];
|
|
|
|
|
|
|
|
if (!strftime(date, sizeof date, "%Y-%m-%dT%H:%M:%S%z", &tm))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: strftime(3) failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(n, date))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-09 01:29:48 +01:00
|
|
|
static int prepare_share(struct html_node *const n,
|
|
|
|
const struct stat *const sb, const char *const dir, const char *const name)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
const char *const fdir = dir + strlen("/user");
|
|
|
|
struct html_node *form, *file, *submit;
|
|
|
|
struct dynstr d;
|
|
|
|
|
|
|
|
dynstr_init(&d);
|
|
|
|
|
|
|
|
if (!S_ISREG(sb->st_mode))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!(form = html_node_add_child(n, "form")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child form failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(file = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child file failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(submit = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child file failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "method", "post"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "action", "/share"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr action failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(file, "type", "hidden"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr file type failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(file, "name", "name"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr file name failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&d, "%s%s", fdir, name))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(file, "value", d.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr file value failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "type", "submit"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit type failed\n",
|
|
|
|
__func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "value", "Share"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit value failed\n",
|
|
|
|
__func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
dynstr_free(&d);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-04-23 05:19:27 +02:00
|
|
|
static int prepare_preview(struct html_node *const n,
|
|
|
|
const struct stat *const sb, const char *const dir, const char *const name)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct html_node *a;
|
|
|
|
struct dynstr d;
|
|
|
|
|
|
|
|
dynstr_init(&d);
|
|
|
|
|
|
|
|
if (!S_ISREG(sb->st_mode))
|
|
|
|
return 0;
|
|
|
|
else if (!(a = html_node_add_child(n, "a")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child form failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&d, "%s%s?preview=1", dir, name))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(a, "href", d.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(a, "Preview"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value value failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
dynstr_free(&d);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-01-09 01:22:54 +01:00
|
|
|
static int add_element(struct html_node *const n, const char *const dir,
|
|
|
|
const char *const res, const char *const name)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
2023-07-08 00:54:59 +02:00
|
|
|
enum {RM_CHECKBOX, NAME, SIZE, DATE, SHARE, PREVIEW, COLUMNS};
|
2023-01-09 01:22:54 +01:00
|
|
|
struct html_node *tr, *td[COLUMNS];
|
|
|
|
struct dynstr path;
|
|
|
|
const char *const sep = res[strlen(res) - 1] != '/' ? "/" : "";
|
|
|
|
|
|
|
|
dynstr_init(&path);
|
|
|
|
|
|
|
|
if (dynstr_append(&path, "%s%s%s", res, sep, name))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(tr = html_node_add_child(n, "tr")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child tr failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sizeof td / sizeof *td; i++)
|
|
|
|
if (!(td[i] = html_node_add_child(tr, "td")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child td[%zu] failed\n",
|
|
|
|
__func__, i);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
if (stat(path.str, &sb))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: stat(2) %s: %s\n",
|
|
|
|
__func__, path.str, strerror(errno));
|
|
|
|
goto end;
|
|
|
|
}
|
2023-07-08 00:54:59 +02:00
|
|
|
else if (strcmp(name, "..")
|
|
|
|
&& prepare_rm_checkbox(td[RM_CHECKBOX], &sb, name))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_rm_checkbox failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
else if (prepare_name(td[NAME], &sb, dir, name))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_name failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (prepare_size(td[SIZE], &sb))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_size failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (prepare_date(td[DATE], &sb))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-03-09 01:29:48 +01:00
|
|
|
else if (prepare_share(td[SHARE], &sb, dir, name))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-23 05:19:27 +02:00
|
|
|
else if (prepare_preview(td[PREVIEW], &sb, dir, name))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
dynstr_free(&path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-06-06 03:49:36 +02:00
|
|
|
static int prepare_search_form(struct html_node *const n, const char *const dir)
|
|
|
|
{
|
|
|
|
struct html_node *div, *form, *hidden, *submit, *input;
|
|
|
|
|
|
|
|
if (!(div = html_node_add_child(n, "div")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(form = html_node_add_child(div, "form")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child form failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(input = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child input failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(hidden = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child hidden failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(submit = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child submit failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(div, "class", "userform"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "method", "post"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "action", "/search"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "type", "hidden"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr hidden failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "name", "dir"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr dir failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "value", dir))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr hidden value failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "type", "submit"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "value", "Search"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit value failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "type", "text"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr text failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "name", "name"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr name failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-01-09 01:22:54 +01:00
|
|
|
static int prepare_upload_form(struct html_node *const n, const char *const dir)
|
|
|
|
{
|
|
|
|
struct html_node *div, *hidden, *form, *submit, *input;
|
|
|
|
|
|
|
|
if (!(div = html_node_add_child(n, "div")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(form = html_node_add_child(div, "form")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child form failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(hidden = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child hidden failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(input = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child input failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(submit = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child submit failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-06-02 08:59:19 +02:00
|
|
|
else if (html_node_add_attr(div, "class", "userform"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
else if (html_node_add_attr(form, "method", "post"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "action", "/upload"))
|
|
|
|
{
|
2023-03-09 02:02:06 +01:00
|
|
|
fprintf(stderr, "%s: html_node_add_attr action failed\n", __func__);
|
2023-01-09 01:22:54 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "enctype", "multipart/form-data"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr enctype failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "type", "hidden"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr hidden failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "name", "dir"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr dir failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "value", dir))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr hidden value failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "type", "submit"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "value", "Upload file"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr value failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "type", "file"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr file failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "name", "file"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr name failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "multiple", NULL))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr multiple failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int prepare_mkdir_form(struct html_node *const n, const char *const dir)
|
|
|
|
{
|
|
|
|
struct html_node *div, *form, *hidden, *submit, *input;
|
|
|
|
|
|
|
|
if (!(div = html_node_add_child(n, "div")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(form = html_node_add_child(div, "form")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child form failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(input = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child input failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(hidden = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child hidden failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(submit = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child submit failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-06-02 08:59:19 +02:00
|
|
|
else if (html_node_add_attr(div, "class", "userform"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
else if (html_node_add_attr(form, "method", "post"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "action", "/mkdir"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "type", "hidden"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr hidden failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "name", "dir"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr dir failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "value", dir))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr hidden value failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "type", "submit"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "value", "Create directory"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit value failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "type", "text"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr text failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "name", "name"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr name failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-07-08 00:54:59 +02:00
|
|
|
static int prepare_rm_form(struct html_node *const n, const char *const dir)
|
|
|
|
{
|
|
|
|
struct html_node *div, *form, *hidden, *submit;
|
|
|
|
|
|
|
|
if (!(div = html_node_add_child(n, "div")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(form = html_node_add_child(div, "form")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child form failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(hidden = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child hidden failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(submit = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child submit failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(div, "class", "userform"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "id", RM_FORM_ID))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr id failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "method", "post"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "action", "/confirm/rm"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "type", "hidden"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr hidden failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "name", "dir"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr name failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "value", dir))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr value failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "type", "submit"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "value", "Remove selected files"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit value failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
static int prepare_quota_form(struct html_node *const n,
|
|
|
|
const struct page_quota *const q)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct html_node *progress = NULL, *div, *label;
|
2023-03-08 00:33:55 +01:00
|
|
|
char cur[sizeof MAXSIZEFMT], max[sizeof cur],
|
|
|
|
cur_nu[sizeof "18446744073709551615"], max_nu[sizeof cur_nu];
|
|
|
|
int res = snprintf(cur_nu, sizeof cur_nu, "%llu", q->cur);
|
|
|
|
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
struct dynstr d, pd;
|
|
|
|
|
|
|
|
dynstr_init(&d);
|
|
|
|
dynstr_init(&pd);
|
|
|
|
|
2023-03-08 00:33:55 +01:00
|
|
|
if (res < 0 || res >= sizeof cur_nu)
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
{
|
2023-03-08 00:33:55 +01:00
|
|
|
fprintf(stderr, "%s: snprintf(3) cur_nu failed with %d\n",
|
|
|
|
__func__, res);
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2023-03-08 00:33:55 +01:00
|
|
|
res = snprintf(max_nu, sizeof max_nu, "%llu", q->max);
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
|
2023-03-08 00:33:55 +01:00
|
|
|
if (res < 0 || res >= sizeof max_nu)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: snprintf(3) max_nu failed with %d\n",
|
|
|
|
__func__, res);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (size_units(q->cur, cur, sizeof cur))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: size_units cur failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (size_units(q->max, max, sizeof max))
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
{
|
2023-03-08 00:33:55 +01:00
|
|
|
fprintf(stderr, "%s: size_units max failed\n", __func__);
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(div = html_node_add_child(n, "div")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child div failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(label = html_node_add_child(div, "label")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child label failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(progress = html_node_alloc("progress")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_alloc progress failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-06-02 08:59:19 +02:00
|
|
|
else if (html_node_add_attr(div, "class", "userform"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-03-08 00:33:55 +01:00
|
|
|
else if (html_node_add_attr(progress, "value", cur_nu))
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr value failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-03-08 00:33:55 +01:00
|
|
|
else if (html_node_add_attr(progress, "max", max_nu))
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr max failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-03-08 00:33:55 +01:00
|
|
|
else if (dynstr_append(&pd, "%s/%s", cur, max))
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append pd failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(progress, pd.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value progress failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-03-08 00:33:55 +01:00
|
|
|
else if (dynstr_append(&d, "User quota: "))
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_serialize(progress, &d))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_serialize failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&d, "%s", pd.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value_unescaped(label, d.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value_unescaped label failed\n",
|
|
|
|
__func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
html_node_free(progress);
|
|
|
|
dynstr_free(&d);
|
|
|
|
dynstr_free(&pd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-01-09 01:22:54 +01:00
|
|
|
static int prepare_logout_form(struct html_node *const n)
|
|
|
|
{
|
|
|
|
struct html_node *div, *form, *input;
|
|
|
|
|
|
|
|
if (!(div = html_node_add_child(n, "div")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(form = html_node_add_child(div, "form")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child form failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(input = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child input failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-06-02 08:59:19 +02:00
|
|
|
else if (html_node_add_attr(div, "class", "userform"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
else if (html_node_add_attr(form, "method", "post"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "action", "/logout"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr action failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "type", "submit"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr type failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(input, "value", "Logout"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr value failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int prepare_footer(struct html_node *const n)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct html_node *footer, *const a = html_node_alloc("a");
|
|
|
|
struct dynstr d;
|
|
|
|
|
|
|
|
dynstr_init(&d);
|
|
|
|
|
|
|
|
if (!a)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_alloc failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(footer = html_node_add_child(n, "footer")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child footer failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(a, "href", PROJECT_URL))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(a, PROJECT_NAME))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value a failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&d, "Powered by "))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_serialize(a, &d))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_serialize failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value_unescaped(footer, d.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child a failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
dynstr_free(&d);
|
|
|
|
html_node_free(a);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-03-09 01:20:18 +01:00
|
|
|
static int common_head(struct html_node *const head, const char *const tl)
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
2023-03-09 01:20:18 +01:00
|
|
|
int ret = -1;
|
2023-04-17 02:58:38 +02:00
|
|
|
struct html_node *charset, *title, *viewport, *link;
|
2023-03-08 00:31:40 +01:00
|
|
|
struct dynstr t;
|
2023-01-09 01:22:54 +01:00
|
|
|
|
|
|
|
dynstr_init(&t);
|
|
|
|
|
2023-03-09 01:20:18 +01:00
|
|
|
if (!(title = html_node_add_child(head, "title")))
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child title failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(charset = html_node_add_child(head, "meta")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child charset failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-17 02:58:38 +02:00
|
|
|
else if (!(link = html_node_add_child(head, "link")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child link failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(link, "href", "/style.css"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr href failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(link, "rel", "stylesheet"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr rel failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
else if (html_node_add_attr(charset, "charset", "UTF-8"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr charset failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-03-09 01:20:18 +01:00
|
|
|
else if (tl && dynstr_append(&t, PROJECT_NAME " - %s", tl))
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append title failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-03-09 01:20:18 +01:00
|
|
|
|
|
|
|
const char *const value = tl ? t.str : PROJECT_TITLE;
|
|
|
|
|
|
|
|
if (html_node_set_value(title, value))
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value title failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(viewport = html_node_add_child(head, "meta")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child viewport failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(viewport, "name", "viewport"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr name viewport failed\n",
|
|
|
|
__func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(viewport, "content",
|
|
|
|
"width=device-width, initial-scale=1, maximum-scale=1"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr name viewport failed\n",
|
|
|
|
__func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-03-09 01:20:18 +01:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
dynstr_free(&t);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct html_node *resource_layout(const char *const dir,
|
|
|
|
const struct page_quota *const q, struct html_node **const table)
|
|
|
|
{
|
|
|
|
const char *const fdir = dir + strlen("/user");
|
|
|
|
struct html_node *const html = html_node_alloc("html"),
|
2023-04-17 02:58:38 +02:00
|
|
|
*ret = NULL, *head, *body, *div;
|
2023-03-09 01:20:18 +01:00
|
|
|
|
|
|
|
if (!html)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_alloc_failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(head = html_node_add_child(html, "head")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child head failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(body = html_node_add_child(html, "body")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child body failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-17 02:58:38 +02:00
|
|
|
else if (!(div = html_node_add_child(body, "div")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child div failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(*table = html_node_add_child(div, "table")))
|
2023-03-09 01:20:18 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child table failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (common_head(head, fdir))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: common_head failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-06-06 03:49:36 +02:00
|
|
|
else if (prepare_search_form(body, fdir))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_search_form failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
else if (prepare_upload_form(body, fdir))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_upload_form failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (prepare_mkdir_form(body, fdir))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_upload_form failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-07-08 00:54:59 +02:00
|
|
|
else if (prepare_rm_form(body, fdir))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_upload_form failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
2023-03-06 05:09:56 +01:00
|
|
|
else if (q && prepare_quota_form(body, q))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_quota_form failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-01-09 01:22:54 +01:00
|
|
|
else if (prepare_logout_form(body))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_logout_form failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (prepare_footer(body))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_footer failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2023-03-08 00:31:40 +01:00
|
|
|
ret = html;
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
html_node_free(html);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_elements(const char *const root, const char *const res,
|
|
|
|
const char *const dir, struct html_node *const table)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct dirent **pde = NULL;
|
|
|
|
const int n = scandir(res, &pde, NULL, alphasort);
|
2023-01-09 01:22:54 +01:00
|
|
|
|
2023-03-08 00:31:40 +01:00
|
|
|
if (n < 0)
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
2023-03-08 00:31:40 +01:00
|
|
|
fprintf(stderr, "%s: scandir(3): %s\n", __func__, strerror(errno));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
const struct dirent *const de = pde[i];
|
2023-01-09 01:22:54 +01:00
|
|
|
const char *const name = de->d_name;
|
|
|
|
|
|
|
|
if (!strcmp(name, ".")
|
|
|
|
|| (!strcmp(name, "..") && !strcmp(root, res)))
|
|
|
|
continue;
|
|
|
|
else if (add_element(table, dir, res, name))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: add_element failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-08 00:31:40 +01:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
|
|
|
for (int i = 0; i < n; i++)
|
|
|
|
free(pde[i]);
|
|
|
|
|
|
|
|
free(pde);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-04-23 05:19:27 +02:00
|
|
|
static int list_dir(const struct page_resource *const pr)
|
2023-03-08 00:31:40 +01:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct dynstr out;
|
2023-04-23 05:19:27 +02:00
|
|
|
struct html_node *table,
|
|
|
|
*const html = resource_layout(pr->dir, pr->q, &table);
|
2023-03-08 00:31:40 +01:00
|
|
|
|
|
|
|
dynstr_init(&out);
|
|
|
|
|
|
|
|
if (!html)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: resource_layout failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-23 05:19:27 +02:00
|
|
|
else if (add_elements(pr->root, pr->res, pr->dir, table))
|
2023-03-08 00:31:40 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: read_elements failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&out, DOCTYPE_TAG))
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_prepend failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_serialize(html, &out))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_serialize failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2023-04-23 05:19:27 +02:00
|
|
|
*pr->r = (const struct http_response)
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_OK,
|
|
|
|
.buf.rw = out.str,
|
|
|
|
.n = out.len,
|
2023-03-06 00:59:07 +01:00
|
|
|
.free = free
|
2023-01-09 01:22:54 +01:00
|
|
|
};
|
|
|
|
|
2023-04-23 05:19:27 +02:00
|
|
|
if (http_response_add_header(pr->r, "Content-Type", "text/html"))
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
html_node_free(html);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
dynstr_free(&out);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int serve_file(struct http_response *const r,
|
2023-04-23 05:19:27 +02:00
|
|
|
const struct stat *const sb, const char *const res, const bool preview)
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
2023-03-16 01:49:18 +01:00
|
|
|
int ret = -1;
|
2023-01-09 01:22:54 +01:00
|
|
|
FILE *const f = fopen(res, "rb");
|
2023-03-16 01:49:18 +01:00
|
|
|
struct dynstr b, d;
|
|
|
|
char *bn;
|
|
|
|
|
|
|
|
dynstr_init(&b);
|
|
|
|
dynstr_init(&d);
|
2023-01-09 01:22:54 +01:00
|
|
|
|
|
|
|
if (!f)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: fopen(3): %s\n", __func__, strerror(errno));
|
2023-03-16 01:49:18 +01:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&b, "%s", res))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append res failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(bn = basename(b.str)))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: basename(3) failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-23 05:19:27 +02:00
|
|
|
else if (preview)
|
|
|
|
{
|
|
|
|
if (dynstr_append(&d, "inline"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append inline failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
2023-03-16 01:49:18 +01:00
|
|
|
else if (dynstr_append(&d, "attachment; filename=\"%s\"", bn))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append attachment failed\n", __func__);
|
|
|
|
goto end;
|
2023-01-09 01:22:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_OK,
|
|
|
|
.f = f,
|
|
|
|
.n = sb->st_size
|
|
|
|
};
|
|
|
|
|
2023-03-16 01:49:18 +01:00
|
|
|
if (http_response_add_header(r, "Content-Disposition", d.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
dynstr_free(&b);
|
|
|
|
dynstr_free(&d);
|
|
|
|
|
|
|
|
if (ret && f && fclose(f))
|
|
|
|
fprintf(stderr, "%s: fclose(3): %s\n", __func__, strerror(errno));
|
|
|
|
|
|
|
|
return ret;
|
2023-01-09 01:22:54 +01:00
|
|
|
}
|
|
|
|
|
2023-03-09 01:23:02 +01:00
|
|
|
static int page_not_found(struct http_response *const r)
|
|
|
|
{
|
|
|
|
static const char body[] =
|
|
|
|
DOCTYPE_TAG
|
|
|
|
"<html>\n"
|
|
|
|
" <head>"
|
|
|
|
" " PROJECT_TAG "\n"
|
|
|
|
" </head>"
|
|
|
|
"File or directory not found\n"
|
|
|
|
"</html>";
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_NOT_FOUND,
|
|
|
|
.buf.ro = body,
|
|
|
|
.n = sizeof body - 1
|
|
|
|
};
|
|
|
|
|
|
|
|
if (http_response_add_header(r, "Content-Type", "text/html"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-04-23 05:19:27 +02:00
|
|
|
static bool preview(const struct page_resource *const pr)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < pr->n_args; i++)
|
|
|
|
{
|
|
|
|
const struct http_arg *const a = &pr->args[i];
|
|
|
|
|
|
|
|
if (!strcmp(a->key, "preview")
|
|
|
|
&& (!strcmp(a->value, "1")
|
|
|
|
|| !strcasecmp(a->value, "true")))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int page_resource(const struct page_resource *const pr)
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
|
|
|
struct stat sb;
|
|
|
|
|
2023-04-23 05:19:27 +02:00
|
|
|
if (stat(pr->res, &sb))
|
2023-01-09 01:22:54 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: stat(2) %s: %s\n",
|
2023-04-23 05:19:27 +02:00
|
|
|
__func__, pr->res, strerror(errno));
|
2023-01-09 01:22:54 +01:00
|
|
|
|
2023-06-06 02:22:28 +02:00
|
|
|
if (errno == ENOENT || errno == ENOTDIR)
|
2023-04-23 05:19:27 +02:00
|
|
|
return page_not_found(pr->r);
|
2023-01-09 01:22:54 +01:00
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const mode_t m = sb.st_mode;
|
|
|
|
|
|
|
|
if (S_ISDIR(m))
|
2023-04-23 05:19:27 +02:00
|
|
|
return list_dir(pr);
|
2023-01-09 01:22:54 +01:00
|
|
|
else if (S_ISREG(m))
|
2023-04-23 05:19:27 +02:00
|
|
|
return serve_file(pr->r, &sb, pr->res, preview(pr));
|
2023-01-09 01:22:54 +01:00
|
|
|
|
|
|
|
fprintf(stderr, "%s: unexpected st_mode %jd\n", __func__, (intmax_t)m);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2023-03-16 01:49:18 +01:00
|
|
|
static char *resolve_link(const char *const res)
|
|
|
|
{
|
|
|
|
size_t len = 1;
|
|
|
|
char *p = NULL;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
char *const pp = realloc(p, len);
|
|
|
|
|
|
|
|
if (!pp)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = pp;
|
|
|
|
|
|
|
|
const ssize_t n = readlink(res, p, len);
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: readlink(2): %s\n", __func__, strerror(errno));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (n < len)
|
|
|
|
{
|
|
|
|
p[len - 1] = '\0';
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-03-09 01:29:48 +01:00
|
|
|
int page_public(struct http_response *const r, const char *const res)
|
|
|
|
{
|
2023-03-16 01:49:18 +01:00
|
|
|
int ret = -1;
|
2023-03-09 01:29:48 +01:00
|
|
|
struct stat sb;
|
2023-03-16 01:49:18 +01:00
|
|
|
char *path = NULL;
|
2023-03-09 01:29:48 +01:00
|
|
|
|
|
|
|
if (stat(res, &sb))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: stat(2) %s: %s\n",
|
|
|
|
__func__, res, strerror(errno));
|
|
|
|
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return page_not_found(r);
|
|
|
|
else
|
2023-03-16 01:49:18 +01:00
|
|
|
goto end;
|
2023-03-09 01:29:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const mode_t m = sb.st_mode;
|
|
|
|
|
|
|
|
if (!S_ISREG(m))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: only regular files are supported\n", __func__);
|
2023-03-16 01:49:18 +01:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(path = resolve_link(res)))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: resolve_link failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-23 05:19:27 +02:00
|
|
|
else if (serve_file(r, &sb, path, false))
|
2023-03-16 01:49:18 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: serve_file failed\n", __func__);
|
|
|
|
goto end;
|
2023-03-09 01:29:48 +01:00
|
|
|
}
|
|
|
|
|
2023-03-16 01:49:18 +01:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
free(path);
|
|
|
|
return ret;
|
2023-03-09 01:29:48 +01:00
|
|
|
}
|
|
|
|
|
2023-01-09 01:22:54 +01:00
|
|
|
int page_failed_login(struct http_response *const r)
|
|
|
|
{
|
|
|
|
static const char index[] =
|
|
|
|
DOCTYPE_TAG
|
|
|
|
"<html>\n"
|
|
|
|
" <head>\n"
|
2023-03-08 18:52:03 +01:00
|
|
|
" " COMMON_HEAD "\n"
|
2023-01-09 01:22:54 +01:00
|
|
|
" " STYLE_A "\n"
|
|
|
|
" </head>\n"
|
|
|
|
" <p>Invalid username or password.</p>\n"
|
|
|
|
LOGIN_BODY
|
|
|
|
"</html>\n";
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_UNAUTHORIZED,
|
|
|
|
.buf.ro = index,
|
|
|
|
.n = sizeof index - 1
|
|
|
|
};
|
|
|
|
|
|
|
|
if (http_response_add_header(r, "Content-Type", "text/html"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int page_login(struct http_response *const r)
|
|
|
|
{
|
|
|
|
static const char index[] =
|
|
|
|
DOCTYPE_TAG
|
|
|
|
"<html>\n"
|
|
|
|
" <head>\n"
|
|
|
|
" " STYLE_A "\n"
|
2023-03-08 18:52:03 +01:00
|
|
|
" " COMMON_HEAD "\n"
|
2023-01-09 01:22:54 +01:00
|
|
|
" </head>\n"
|
|
|
|
" " LOGIN_BODY "\n"
|
|
|
|
"</html>\n";
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_OK,
|
|
|
|
.buf.ro = index,
|
|
|
|
.n = sizeof index - 1
|
|
|
|
};
|
|
|
|
|
|
|
|
if (http_response_add_header(r, "Content-Type", "text/html"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int page_forbidden(struct http_response *const r)
|
|
|
|
{
|
|
|
|
static const char body[] =
|
|
|
|
DOCTYPE_TAG
|
|
|
|
"<html>\n"
|
2023-03-08 18:52:03 +01:00
|
|
|
" <head>\n"
|
2023-03-09 01:20:18 +01:00
|
|
|
" " PROJECT_TAG "\n"
|
2023-03-08 18:52:03 +01:00
|
|
|
" " COMMON_HEAD "\n"
|
|
|
|
" </head>\n"
|
2023-01-09 01:22:54 +01:00
|
|
|
"Forbidden\n"
|
|
|
|
"</html>";
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_FORBIDDEN,
|
|
|
|
.buf.ro = body,
|
|
|
|
.n = sizeof body - 1
|
|
|
|
};
|
|
|
|
|
|
|
|
if (http_response_add_header(r, "Content-Type", "text/html"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int page_bad_request(struct http_response *const r)
|
|
|
|
{
|
|
|
|
static const char body[] =
|
|
|
|
DOCTYPE_TAG
|
|
|
|
"<html>\n"
|
|
|
|
" <head>"
|
2023-03-09 01:20:18 +01:00
|
|
|
" " PROJECT_TAG "\n"
|
2023-01-09 01:22:54 +01:00
|
|
|
" </head>"
|
|
|
|
"Invalid request\n"
|
|
|
|
"</html>";
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_BAD_REQUEST,
|
|
|
|
.buf.ro = body,
|
|
|
|
.n = sizeof body - 1
|
|
|
|
};
|
|
|
|
|
|
|
|
if (http_response_add_header(r, "Content-Type", "text/html"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int page_style(struct http_response *const r)
|
|
|
|
{
|
|
|
|
static const char body[] =
|
2023-06-06 03:46:57 +02:00
|
|
|
"body\n"
|
2023-04-17 02:58:38 +02:00
|
|
|
"{\n"
|
|
|
|
" font-family: 'Courier New', Courier, monospace;\n"
|
|
|
|
"}\n"
|
|
|
|
"td\n"
|
|
|
|
"{\n"
|
|
|
|
" font-size: 14px;\n"
|
|
|
|
"}\n"
|
|
|
|
"a\n"
|
|
|
|
"{\n"
|
|
|
|
" text-decoration: none;\n"
|
|
|
|
"}\n"
|
2023-06-02 08:59:19 +02:00
|
|
|
".userform\n"
|
|
|
|
"{\n"
|
|
|
|
" padding: 4px;\n"
|
|
|
|
"}\n"
|
2023-06-02 09:03:57 +02:00
|
|
|
".loginform\n"
|
|
|
|
"{\n"
|
|
|
|
" display: grid;\n"
|
|
|
|
"}\n"
|
2023-06-06 03:46:57 +02:00
|
|
|
"form, label, table\n"
|
2023-01-09 01:22:54 +01:00
|
|
|
"{\n"
|
2023-04-17 02:58:38 +02:00
|
|
|
" margin: auto;\n"
|
|
|
|
"}\n"
|
|
|
|
"div\n"
|
|
|
|
"{\n"
|
|
|
|
" align-items: center;\n"
|
|
|
|
" display: grid;\n"
|
|
|
|
"}\n"
|
2023-06-06 03:49:36 +02:00
|
|
|
"input, .abutton\n"
|
2023-04-17 02:58:38 +02:00
|
|
|
"{\n"
|
2023-06-06 03:46:57 +02:00
|
|
|
" margin: auto;\n"
|
2023-01-09 01:22:54 +01:00
|
|
|
" border: 1px solid;\n"
|
|
|
|
" border-radius: 8px;\n"
|
2023-04-17 02:58:38 +02:00
|
|
|
"}\n"
|
|
|
|
"header, footer\n"
|
|
|
|
"{\n"
|
|
|
|
" display: flex;\n"
|
|
|
|
" justify-content: center;\n"
|
|
|
|
" text-decoration: auto;\n"
|
2023-05-30 08:16:06 +02:00
|
|
|
"}\n"
|
2023-05-30 08:34:05 +02:00
|
|
|
"table\n"
|
|
|
|
"{\n"
|
|
|
|
" max-width: 50%;\n"
|
|
|
|
"}\n"
|
2023-05-30 08:16:06 +02:00
|
|
|
"tr:nth-child(even)\n"
|
|
|
|
"{\n"
|
|
|
|
" background-color: lightgray;\n"
|
2023-01-09 01:22:54 +01:00
|
|
|
"}\n";
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_OK,
|
|
|
|
.buf.ro = body,
|
|
|
|
.n = sizeof body - 1
|
|
|
|
};
|
|
|
|
|
|
|
|
if (http_response_add_header(r, "Content-Type", "text/css"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2023-03-09 01:29:48 +01:00
|
|
|
|
|
|
|
int page_share(struct http_response *const r, const char *const path)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct dynstr out;
|
|
|
|
struct html_node *const html = html_node_alloc("html"),
|
|
|
|
*head, *a;
|
|
|
|
|
|
|
|
dynstr_init(&out);
|
|
|
|
|
|
|
|
if (!html)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_alloc failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(head = html_node_add_child(html, "head")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child head failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (common_head(head, NULL))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: common_head failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(a = html_node_add_child(html, "a")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(a, "href", path))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr href failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(a, "Copy this link"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value href failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&out, DOCTYPE_TAG))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_prepend failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_serialize(html, &out))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_serialize failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_OK,
|
|
|
|
.buf.rw = out.str,
|
|
|
|
.n = out.len,
|
|
|
|
.free = free
|
|
|
|
};
|
|
|
|
|
|
|
|
if (http_response_add_header(r, "Content-Type", "text/html"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
html_node_free(html);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
dynstr_free(&out);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2023-03-20 03:32:00 +01:00
|
|
|
|
|
|
|
int page_quota_exceeded(struct http_response *const r,
|
|
|
|
const unsigned long long len, const unsigned long long quota)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct dynstr msg, out;
|
|
|
|
char q[sizeof MAXSIZEFMT], l[sizeof q];
|
|
|
|
struct html_node *const html = html_node_alloc("html"),
|
|
|
|
*head, *body;
|
|
|
|
|
|
|
|
dynstr_init(&msg);
|
|
|
|
dynstr_init(&out);
|
|
|
|
|
|
|
|
if (!html)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_alloc failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(head = html_node_add_child(html, "head")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child head failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(body = html_node_add_child(html, "body")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child body failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (common_head(head, NULL))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: common_head failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (size_units(quota, q, sizeof q))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: size_units quota failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (size_units(len, l, sizeof l))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: size_units len failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&msg, "Maximum quota exceeded: %s "
|
|
|
|
"(requested size: %s)", q, l))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append msg failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(body, msg.str))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value msg failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&out, DOCTYPE_TAG))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_prepend failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_serialize(html, &out))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_serialize failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_PAYLOAD_TOO_LARGE,
|
|
|
|
.buf.rw = out.str,
|
|
|
|
.n = out.len,
|
|
|
|
.free = free
|
|
|
|
};
|
|
|
|
|
|
|
|
if (http_response_add_header(r, "Content-Type", "text/html"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
html_node_free(html);
|
|
|
|
dynstr_free(&msg);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
dynstr_free(&out);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2023-06-06 03:49:36 +02:00
|
|
|
|
|
|
|
static int add_search_results(struct html_node *const n,
|
|
|
|
const struct page_search *const s)
|
|
|
|
{
|
|
|
|
struct html_node *table;
|
|
|
|
|
|
|
|
if (!(table = html_node_add_child(n, "table")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child table failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < s->n; i++)
|
|
|
|
{
|
|
|
|
const struct page_search_result *const r = &s->results[i];
|
|
|
|
|
|
|
|
if (add_element(table, "/user/", s->root, r->name))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: add_element failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int prepare_back_button(struct html_node *const n)
|
|
|
|
{
|
|
|
|
struct html_node *div, *a;
|
|
|
|
|
|
|
|
if (!(div = html_node_add_child(n, "div")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(a = html_node_add_child(div, "a")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child a failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(div, "class", "userform"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(a, "class", "abutton"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr a class failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(a, "href", "/user/"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr a href failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(a, "Back"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int page_search(struct http_response *const r,
|
|
|
|
const struct page_search *const s)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct dynstr out;
|
|
|
|
struct html_node *const html = html_node_alloc("html"), *head, *body;
|
|
|
|
|
|
|
|
dynstr_init(&out);
|
|
|
|
|
|
|
|
if (!html)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_alloc failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(head = html_node_add_child(html, "head")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child head failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(body = html_node_add_child(html, "body")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child body failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (common_head(head, NULL))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: common_head failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (s->n && add_search_results(body, s))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: add_search_results failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!s->n && html_node_set_value(body, "No results found"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value msg failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (prepare_back_button(body))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_back_button failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (prepare_footer(body))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_footer failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&out, DOCTYPE_TAG))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append out failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_serialize(html, &out))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_serialize failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
2023-07-08 00:52:53 +02:00
|
|
|
.status = HTTP_STATUS_OK,
|
2023-06-06 03:49:36 +02:00
|
|
|
.buf.rw = out.str,
|
|
|
|
.n = out.len,
|
|
|
|
.free = free
|
|
|
|
};
|
|
|
|
|
|
|
|
if (http_response_add_header(r, "Content-Type", "text/html"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
html_node_free(html);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
dynstr_free(&out);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2023-07-08 00:54:59 +02:00
|
|
|
|
|
|
|
static int append_rm_items(struct html_node *const n,
|
|
|
|
const struct page_rm *const rm)
|
|
|
|
{
|
|
|
|
struct html_node *div, *form, *table, *hidden, *submit;
|
|
|
|
|
|
|
|
if (!(table = html_node_add_child(n, "table")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child table failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(div = html_node_add_child(n, "div")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child div failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(form = html_node_add_child(div, "form")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child form failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(hidden = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child hidden failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(submit = html_node_add_child(form, "input")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child submit failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "method", "post"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(form, "action", "/rm"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr action failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(div, "class", "userform"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr class failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "type", "hidden"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr hidden type failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "name", "dir"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr hidden name failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(hidden, "value", rm->dir))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr hidden value failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "type", "submit"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit type failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(submit, "value", "Confirm"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr submit value failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < rm->n; i++)
|
|
|
|
{
|
|
|
|
const char *const item = rm->items[i];
|
|
|
|
struct html_node *const tr = html_node_add_child(table, "tr"),
|
|
|
|
*const path = html_node_add_child(form, "input"), *td;
|
|
|
|
|
|
|
|
if (!tr)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child tr failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!path)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child path failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!(td = html_node_add_child(tr, "td")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child td failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(td, item))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value td failed\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(path, "type", "hidden"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr path type failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(path, "name", "path"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr path name failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (html_node_add_attr(path, "value", item))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_attr path value failed\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int page_rm(struct http_response *r, const struct page_rm *const rm)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct dynstr out;
|
|
|
|
struct html_node *const html = html_node_alloc("html"),
|
|
|
|
*p, *p2, *head, *body;
|
|
|
|
static const char question[] = "Are you sure to remove the "
|
|
|
|
"following files and/or directories? Remember that removing a "
|
|
|
|
"directory will remove all of the files and directories inside it.",
|
|
|
|
reminder[] = "This action cannot be undone.";
|
|
|
|
|
|
|
|
dynstr_init(&out);
|
|
|
|
|
|
|
|
if (!html)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_alloc failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(head = html_node_add_child(html, "head")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child head failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(body = html_node_add_child(html, "body")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child body failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(p = html_node_add_child(body, "p")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child p failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (!(p2 = html_node_add_child(body, "p")))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_add_child p2 failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (common_head(head, NULL))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: common_head failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(p, question))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value question failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_node_set_value(p2, reminder))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_node_set_value reminder failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (append_rm_items(body, rm))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: append_rm_items failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (prepare_footer(body))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: prepare_footer failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (dynstr_append(&out, DOCTYPE_TAG))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: dynstr_append out failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else if (html_serialize(html, &out))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: html_serialize failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
*r = (const struct http_response)
|
|
|
|
{
|
|
|
|
.status = HTTP_STATUS_OK,
|
|
|
|
.buf.rw = out.str,
|
|
|
|
.n = out.len,
|
|
|
|
.free = free
|
|
|
|
};
|
|
|
|
|
|
|
|
if (http_response_add_header(r, "Content-Type", "text/html"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
html_node_free(html);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
dynstr_free(&out);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|