/* * nanobbs, a tiny forums software. * Copyright (C) 2025-2026 Xavier Del Campo Romero * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "endpoints.h" #include "db.h" #include "defs.h" #include "auth.h" #include "form.h" #include #include #include #include #include static int setup_passwd(struct html_node *const n, const char *const username) { int ret = -1; struct dynstr ud; struct html_node *div, *user, *form, *lold, *iold, *lnew, *inew, *lcnew, *icnew, *submit; dynstr_init(&ud); if (dynstr_append(&ud, "User: %s", username)) { fprintf(stderr, "%s: dynstr_append failed\n", __func__); goto end; } else if (!(div = html_node_add_child(n, "div")) || !(form = html_node_add_child(div, "form")) || !(user = html_node_add_child(form, "label")) || !(lold = html_node_add_child(form, "label")) || !(iold = html_node_add_child(form, "input")) || !(lnew = html_node_add_child(form, "label")) || !(inew = html_node_add_child(form, "input")) || !(lcnew = html_node_add_child(form, "label")) || !(icnew = html_node_add_child(form, "input")) || !(submit = html_node_add_child(form, "input"))) { fprintf(stderr, "%s: html_node_add_child failed\n", __func__); goto end; } else if (html_node_add_attr(form, "action", "/passwd") || html_node_add_attr(form, "form", "passwdform") || html_node_add_attr(form, "method", "post") || html_node_add_attr(lold, "for", "old") || html_node_add_attr(lnew, "for", "new") || html_node_add_attr(lcnew, "for", "cnew") || html_node_add_attr(iold, "type", "password") || html_node_add_attr(iold, "id", "old") || html_node_add_attr(iold, "name", "old") || html_node_add_attr(inew, "type", "password") || html_node_add_attr(inew, "id", "new") || html_node_add_attr(inew, "name", "new") || html_node_add_attr(icnew, "type", "password") || html_node_add_attr(icnew, "id", "cnew") || html_node_add_attr(icnew, "name", "cnew") || html_node_add_attr(submit, "type", "submit") || html_node_add_attr(submit, "value", "Change password")) { fprintf(stderr, "%s: html_node_add_attr failed\n", __func__); goto end; } else if (html_node_set_value(user, ud.str) || html_node_set_value(lold, "Current password:") || html_node_set_value(lnew, "New password:") || html_node_set_value(lcnew, "Confirm new password:")) { fprintf(stderr, "%s: html_node_set_value failed\n", __func__); goto end; } ret = 0; end: dynstr_free(&ud); return ret; } static int setup_useract(struct html_node *const div) { struct f { const char *text, *id, *url; struct html_node *luser, *iuser, *form, *submit; } fs[] = { {.text = "Ban user", .id = "banform", .url = "/confirm/ban"}, {.text = "Delete user", .id = "delform", .url = "/confirm/deluser"} }; for (size_t i = 0; i < sizeof fs / sizeof *fs; i++) { struct f *const f = &fs[i]; if (!(f->form = html_node_add_child(div, "form")) || !(f->luser = html_node_add_child(f->form, "label")) || !(f->iuser = html_node_add_child(f->form, "input")) || !(f->submit = html_node_add_child(f->form, "input"))) { fprintf(stderr, "%s: html_node_add_child failed\n", __func__); return -1; } else if (html_node_add_attr(f->form, "action", f->url) || html_node_add_attr(f->form, "form", f->id) || html_node_add_attr(f->form, "method", "post") || html_node_add_attr(f->luser, "for", "user") || html_node_add_attr(f->iuser, "type", "text") || html_node_add_attr(f->iuser, "id", "user") || html_node_add_attr(f->iuser, "name", "user") || html_node_add_attr(f->submit, "type", "submit") || html_node_add_attr(f->submit, "value", f->text)) { fprintf(stderr, "%s: html_node_add_attr failed\n", __func__); return -1; } else if (html_node_set_value(f->luser, "Username:")) { fprintf(stderr, "%s: html_node_set_value failed\n", __func__); return -1; } } return 0; } static int setup(const struct http_payload *const p, struct http_response *const r, void *const user, sqlite3 *const db, const struct auth_user *const u) { int ret = -1, error; struct html_node *root = NULL, *body, *div; struct dynstr d; dynstr_init(&d); if (!u) { ret = form_unauthorized("Login required", r); goto end; } else if (u->role <= AUTH_ROLE_BANNED) { ret = form_unauthorized("Banned account", r); goto end; } else if (!(root = html_node_alloc("html"))) { fprintf(stderr, "%s: html_node_alloc failed\n", __func__); goto end; } else if (form_head(root)) { fprintf(stderr, "%s: form_head failed\n", __func__); goto end; } else if (!(body = html_node_add_child(root, "body")) || !(div = html_node_add_child(body, "div"))) { fprintf(stderr, "%s: html_node_add_child failed\n", __func__); goto end; } else if (setup_passwd(div, u->username)) { fprintf(stderr, "%s: setup_passwd failed\n", __func__); goto end; } else if (u->role >= AUTH_ROLE_MOD && setup_useract(div)) { fprintf(stderr, "%s: setup_useract failed\n", __func__); goto end; } else if (form_footer(body, p->resource)) { fprintf(stderr, "%s: form_footer failed\n", __func__); goto end; } else if (dynstr_append(&d, "%s", DOCTYPE_TAG)) { fprintf(stderr, "%s: dynstr_append failed\n", __func__); goto end; } else if (html_serialize(root, &d)) { fprintf(stderr, "%s: html_serialize failed\n", __func__); goto end; } *r = (const struct http_response) { .status = HTTP_STATUS_OK, .buf.rw = d.str, .n = d.len, .free = free }; ret = 0; end: if ((error = sqlite3_close(db)) != SQLITE_OK) { fprintf(stderr, "%s: sqlite3_close: %s\n", __func__, sqlite3_errstr(error)); ret = -1; } if (ret) dynstr_free(&d); html_node_free(root); return ret; } int ep_ucp(const struct http_payload *const p, struct http_response *const r, void *const user) { const int n = auth_validate(p, r, user, setup); if (n < 0) fprintf(stderr, "%s: auth_validate failed\n", __func__); else if (n) return setup(p, r, user, NULL, NULL); return n; }