/*
* 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;
}