#define _POSIX_C_SOURCE 200809L #include "page.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PROJECT_NAME "slcl" #define PROJECT_TITLE PROJECT_NAME ", a simple and lightweight cloud" #define PROJECT_TAG "" PROJECT_TITLE "" #define DOCTYPE_TAG "\n" #define PROJECT_URL "https://gitea.privatedns.org/xavi/" PROJECT_NAME #define COMMON_HEAD \ " \n" \ " \n" \ PROJECT_TAG "\n" #define STYLE_A "" #define LOGIN_BODY \ "
\n" \ " " PROJECT_NAME "," \ " a simple and lightweight cloud\n" \ "
\n" \ "
\n" \ " \n" \ "
\n" \ " \n" \ "
\n" \ " \n" \ "
\n" #define MAXSIZEFMT "18446744073709551615.0 XiB" #define RM_FORM_ID "rm" struct element_stats { unsigned long long n_dirs, n_files; }; 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; } 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) ? "/" : ""; char *encurl = NULL; 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 (!(encurl = http_encode_url(d.str))) { fprintf(stderr, "%s: http_encode_url failed\n", __func__); goto end; } else if (html_node_add_attr(a, "href", encurl)) { 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); free(encurl); return ret; } static int size_units(const unsigned long long b, char *const buf, const size_t n) { static const char *const suffixes[] = {"B", "KiB", "MiB", "GiB", "TiB"}; float sz; size_t suffix_i; for (sz = b, suffix_i = 0; (unsigned long long)sz / 1024; suffix_i++, sz /= 1024.0f) ; if (suffix_i >= sizeof suffixes / sizeof *suffixes) { fprintf(stderr, "%s: maximum suffix exceeded\n", __func__); return -1; } const int r = suffix_i ? snprintf(buf, n, "%.1f %s", sz, suffixes[suffix_i]) : snprintf(buf, n, "%llu %s", b, suffixes[suffix_i]); if (r >= n || r < 0) { fprintf(stderr, "%s: snprintf(3) failed with %d\n", __func__, r); return -1; } 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; } 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; } 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; } 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; char *encurl = NULL; 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", dir, name)) { fprintf(stderr, "%s: dynstr_append d failed\n", __func__); goto end; } else if (!(encurl = http_encode_url(d.str))) { fprintf(stderr, "%s: http_encode_url failed\n", __func__); goto end; } dynstr_free(&d); if (dynstr_append(&d, "%s?preview=1", encurl)) { fprintf(stderr, "%s: dynstr_append encd 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); free(encurl); return ret; } static int add_element(struct html_node *const n, const char *const dir, const char *const res, const char *const name, const bool chbx, struct element_stats *const st) { int ret = -1; enum {RM_CHECKBOX, NAME, SIZE, DATE, SHARE, PREVIEW, COLUMNS}; 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; const bool parentdir = !strcmp(name, ".."); if (stat(path.str, &sb)) { fprintf(stderr, "%s: stat(2) %s: %s\n", __func__, path.str, strerror(errno)); goto end; } else if (st && !parentdir) { if (S_ISDIR(sb.st_mode)) st->n_dirs++; else if (S_ISREG(sb.st_mode)) st->n_files++; } if (chbx && !parentdir && prepare_rm_checkbox(td[RM_CHECKBOX], &sb, name)) { fprintf(stderr, "%s: prepare_rm_checkbox failed\n", __func__); goto end; } 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; } else if (prepare_share(td[SHARE], &sb, dir, name)) { fprintf(stderr, "%s: prepare_date failed\n", __func__); goto end; } else if (prepare_preview(td[PREVIEW], &sb, dir, name)) { fprintf(stderr, "%s: prepare_date failed\n", __func__); goto end; } ret = 0; end: dynstr_free(&path); return ret; } 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; } 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; } 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, "method", "post")) { fprintf(stderr, "%s: html_node_add_attr method failed\n", __func__); return -1; } else if (html_node_add_attr(form, "action", "/upload")) { fprintf(stderr, "%s: html_node_add_attr action failed\n", __func__); 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; } 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, "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; } 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; } 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; 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); struct dynstr d, pd; dynstr_init(&d); dynstr_init(&pd); if (res < 0 || res >= sizeof cur_nu) { fprintf(stderr, "%s: snprintf(3) cur_nu failed with %d\n", __func__, res); goto end; } res = snprintf(max_nu, sizeof max_nu, "%llu", q->max); 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)) { fprintf(stderr, "%s: size_units max failed\n", __func__); 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; } 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(progress, "value", cur_nu)) { fprintf(stderr, "%s: html_node_add_attr value failed\n", __func__); goto end; } else if (html_node_add_attr(progress, "max", max_nu)) { fprintf(stderr, "%s: html_node_add_attr max failed\n", __func__); goto end; } else if (dynstr_append(&pd, "%s/%s", cur, max)) { 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; } else if (dynstr_append(&d, "User quota: ")) { 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; } 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; } 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, "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; } static int common_head(struct html_node *const head, const char *const tl) { int ret = -1; struct html_node *charset, *title, *viewport, *link; struct dynstr t; dynstr_init(&t); if (!(title = html_node_add_child(head, "title"))) { 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; } 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; } else if (html_node_add_attr(charset, "charset", "UTF-8")) { fprintf(stderr, "%s: html_node_add_attr charset failed\n", __func__); goto end; } else if (tl && dynstr_append(&t, PROJECT_NAME " - %s", tl)) { fprintf(stderr, "%s: dynstr_append title failed\n", __func__); goto end; } const char *const value = tl ? t.str : PROJECT_TITLE; if (html_node_set_value(title, value)) { 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; } 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 body, struct html_node **const table) { const char *const fdir = dir + strlen("/user"); struct html_node *const html = html_node_alloc("html"), *ret = NULL, *head, *div; 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 (!(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"))) { 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; } else if (prepare_search_form(*body, fdir)) { fprintf(stderr, "%s: prepare_search_form failed\n", __func__); goto end; } 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; } else if (prepare_rm_form(*body, fdir)) { fprintf(stderr, "%s: prepare_upload_form failed\n", __func__); goto end; } else if (q && prepare_quota_form(*body, q)) { fprintf(stderr, "%s: prepare_quota_form failed\n", __func__); goto end; } else if (prepare_logout_form(*body)) { fprintf(stderr, "%s: prepare_logout_form failed\n", __func__); goto end; } 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, struct element_stats *const st) { int ret = -1; struct dirent **pde = NULL; const int n = scandir(res, &pde, NULL, alphasort); if (n < 0) { fprintf(stderr, "%s: scandir(3): %s\n", __func__, strerror(errno)); goto end; } *st = (const struct element_stats){0}; for (int i = 0; i < n; i++) { const struct dirent *const de = pde[i]; const char *const name = de->d_name; if (!strcmp(name, ".") || (!strcmp(name, "..") && !strcmp(root, res))) continue; else if (add_element(table, dir, res, name, true, st)) { fprintf(stderr, "%s: add_element failed\n", __func__); goto end; } } ret = 0; end: for (int i = 0; i < n; i++) free(pde[i]); free(pde); return ret; } static int prepare_element_stats(struct html_node *const n, const struct element_stats *const st) { int ret = -1; struct html_node *const p = html_node_add_child(n, "div"); struct dynstr d; dynstr_init(&d); if (!p) { fprintf(stderr, "%s: html_node_add_child failed\n", __func__); goto end; } else if (dynstr_append(&d, "%llu directories, %llu files", st->n_dirs, st->n_files)) { fprintf(stderr, "%s: dynstr_append failed\n", __func__); goto end; } else if (html_node_set_value(p, d.str)) { fprintf(stderr, "%s: html_node_set_value failed\n", __func__); goto end; } ret = 0; end: dynstr_free(&d); return ret; } static int list_dir(const struct page_resource *const pr) { int ret = -1; struct dynstr out; struct html_node *table, *body, *const html = resource_layout(pr->dir, pr->q, &body, &table); struct element_stats st; dynstr_init(&out); if (!html) { fprintf(stderr, "%s: resource_layout failed\n", __func__); goto end; } else if (add_elements(pr->root, pr->res, pr->dir, table, &st)) { fprintf(stderr, "%s: read_elements failed\n", __func__); goto end; } else if (prepare_element_stats(body, &st)) { fprintf(stderr, "%s: prepare_element_stats 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_prepend failed\n", __func__); goto end; } else if (html_serialize(html, &out)) { fprintf(stderr, "%s: html_serialize failed\n", __func__); goto end; } *pr->r = (const struct http_response) { .status = HTTP_STATUS_OK, .buf.rw = out.str, .n = out.len, .free = free }; if (http_response_add_header(pr->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; } static int serve_file(struct http_response *const r, const struct stat *const sb, const char *const res, const bool preview, const int fd, bool *const fdopened) { int ret = -1; FILE *f = NULL; struct dynstr b, d; char *bn; dynstr_init(&b); dynstr_init(&d); if (preview) { if (dynstr_append(&d, "inline")) { fprintf(stderr, "%s: dynstr_append inline failed\n", __func__); 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; } else if (dynstr_append(&d, "attachment; filename=\"%s\"", bn)) { fprintf(stderr, "%s: dynstr_append attachment failed\n", __func__); goto end; } if (!(f = fdopen(fd, "rb"))) { fprintf(stderr, "%s: fdopen(3): %s\n", __func__, strerror(errno)); goto end; } *fdopened = true; *r = (const struct http_response) { .status = HTTP_STATUS_OK, .f = f, .n = sb->st_size }; 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; } static int page_not_found(struct http_response *const r) { static const char body[] = DOCTYPE_TAG "\n" " " " " PROJECT_TAG "\n" " " "File or directory not found\n" ""; *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; } 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) { int ret = -1; struct stat sb; const int fd = open(pr->res, O_RDONLY); bool fdopened = false; if (fd < 0) { if (errno == ENOENT || errno == ENOTDIR) ret = page_not_found(pr->r); else fprintf(stderr, "%s: open(2) %s: %s\n", __func__, pr->res, strerror(errno)); goto end; } else if (fstat(fd, &sb)) { fprintf(stderr, "%s: fstat(2) %s: %s\n", __func__, pr->res, strerror(errno)); goto end; } const mode_t m = sb.st_mode; if (S_ISDIR(m)) { if (list_dir(pr)) { fprintf(stderr, "%s: list_dir failed\n", __func__); goto end; } } else if (S_ISREG(m)) { if (serve_file(pr->r, &sb, pr->res, preview(pr), fd, &fdopened)) { fprintf(stderr, "%s: serve_file failed\n", __func__); goto end; } } else { fprintf(stderr, "%s: unexpected st_mode %jo\n", __func__, (intmax_t)m); goto end; } ret = 0; end: if (!fdopened && fd >= 0 && close(fd)) { fprintf(stderr, "%s: close(2) %s: %s\n", __func__, pr->res, strerror(errno)); ret = -1; } return ret; } 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; } int page_public(struct http_response *const r, const char *const res) { int ret = -1; const int fd = open(res, O_RDONLY); struct stat sb; char *path = NULL; bool fdopened = false; if (fd < 0) { if (errno == ENOENT) ret = page_not_found(r); else fprintf(stderr, "%s: stat(2) %s: %s\n", __func__, res, strerror(errno)); goto end; } else if (fstat(fd, &sb)) { fprintf(stderr, "%s: fstat(2) %s: %s\n", __func__, res, strerror(errno)); goto end; } const mode_t m = sb.st_mode; if (!S_ISREG(m)) { fprintf(stderr, "%s: only regular files are supported\n", __func__); goto end; } else if (!(path = resolve_link(res))) { fprintf(stderr, "%s: resolve_link failed\n", __func__); goto end; } else if (serve_file(r, &sb, path, false, fd, &fdopened)) { fprintf(stderr, "%s: serve_file failed\n", __func__); goto end; } ret = 0; end: if (!fdopened && fd >= 0 && close(fd)) { fprintf(stderr, "%s: close(2) %s: %s\n", __func__, res, strerror(errno)); ret = -1; } free(path); return ret; } int page_failed_login(struct http_response *const r) { static const char index[] = DOCTYPE_TAG "\n" " \n" " " COMMON_HEAD "\n" " " STYLE_A "\n" " \n" "

Invalid username or password.

\n" LOGIN_BODY "\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 "\n" " \n" " " STYLE_A "\n" " " COMMON_HEAD "\n" " \n" " " LOGIN_BODY "\n" "\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 "\n" " \n" " " PROJECT_TAG "\n" " " COMMON_HEAD "\n" " \n" "Forbidden\n" ""; *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 "\n" " " " " PROJECT_TAG "\n" " " "Invalid request\n" ""; *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, const char *const path) { FILE *f = NULL; struct stat sb; if (stat(path, &sb)) { fprintf(stderr, "%s: stat(2): %s\n", __func__, strerror(errno)); goto failure; } else if (!(f = fopen(path, "rb"))) { fprintf(stderr, "%s: fopen(3) %s: %s\n", __func__, path, strerror(errno)); goto failure; } *r = (const struct http_response) { .status = HTTP_STATUS_OK, .f = f, .n = sb.st_size }; if (http_response_add_header(r, "Content-Type", "text/css")) { fprintf(stderr, "%s: http_response_add_header failed\n", __func__); goto failure; } return 0; failure: if (f && fclose(f)) fprintf(stderr, "%s: fclose(3) %s: %s\n", __func__, path, strerror(errno)); return -1; } 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; } 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; } 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, false, NULL)) { fprintf(stderr, "%s: add_element failed\n", __func__); return -1; } } return 0; } static int prepare_search_limit_exceeded(struct html_node *const n, const struct page_search *const s) { static const char msg[] = "Too many results were found. Please " "set up a more specific search term in order to provide " "fewer and more relevant results"; struct html_node *const p = html_node_add_child(n, "p"); if (!p) { fprintf(stderr, "%s: html_node_add_child p failed\n", __func__); return -1; } else if (html_node_set_value(p, msg)) { fprintf(stderr, "%s: html_node_set_value failed\n", __func__); return -1; } return 0; } static int prepare_search_result_count(struct html_node *const n, const struct page_search *const s) { int ret = -1; struct html_node *const p = html_node_add_child(n, "p"); struct dynstr d; dynstr_init(&d); if (!p) { fprintf(stderr, "%s: html_node_add_child p failed\n", __func__); goto end; } else if (s->limit_exceeded && prepare_search_limit_exceeded(n, s)) { fprintf(stderr, "%s: prepare_search_limit_exceeded msg failed\n", __func__); goto end; } else if (!s->n && html_node_set_value(p, "No results found")) { fprintf(stderr, "%s: html_node_set_value msg failed\n", __func__); goto end; } else if (s->n) { const char *const results = s->n == 1 ? "result" : "results"; if (dynstr_append(&d, "%zu %s found", s->n, results)) { fprintf(stderr, "%s: dynstr_append failed\n", __func__); goto end; } else if (html_node_set_value(p, d.str)) { fprintf(stderr, "%s: html_node_set_value p failed\n", __func__); goto end; } } ret = 0; end: dynstr_free(&d); return ret; } 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 (prepare_search_result_count(body, s)) { fprintf(stderr, "%s: prepare_limit_exceeded 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; } 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; } int page_head_resource(struct http_response *const r, const char *const res) { struct stat sb; if (stat(res, &sb)) { if (errno == ENOENT || errno == ENOTDIR) return page_not_found(r); else { fprintf(stderr, "%s: stat(2) %s: %s\n", __func__, res, strerror(errno)); return -1; } } const mode_t m = sb.st_mode; if (!S_ISDIR(m) && !S_ISREG(m)) { fprintf(stderr, "%s: unexpected st_mode %jd\n", __func__, (intmax_t)m); return -1; } *r = (const struct http_response) { .status = HTTP_STATUS_OK, .n = sb.st_size }; return 0; }