summaryrefslogtreecommitdiff
path: root/clients/bokftui.c
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-17 21:26:20 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-17 21:26:20 +0200
commited1c15929d2eb2dbc6432986c26661bf1549964a (patch)
tree4f04934edef686b19d16e1bb2f79bb5c15e142de /clients/bokftui.c
parent380195f7cd5e57acf2c1cf2bc41069e6b0b979ed (diff)
downloadbokf-0.1.1.tar.gz
bokf-0.1.1.zip
Add native TLS transport, TLS clients and lego cert sidecarv0.1.1
- bokfd: optional TLS listener (OpenSSL), certificate reload on change - clients: tls:host:port targets with chain and host verification - compose: port 8788 and an INWX/lego renewal sidecar - Makefile: header dependency tracking (-MMD -MP)
Diffstat (limited to 'clients/bokftui.c')
-rw-r--r--clients/bokftui.c112
1 files changed, 58 insertions, 54 deletions
diff --git a/clients/bokftui.c b/clients/bokftui.c
index 1939eeb..c26d188 100644
--- a/clients/bokftui.c
+++ b/clients/bokftui.c
@@ -32,7 +32,7 @@ static int ui_getch(void)
}
struct app {
- int fd;
+ struct client_conn conn;
char socket[256];
char session[128];
char username[64];
@@ -895,10 +895,10 @@ static void app_refresh_context(struct app *a)
{
char args[64];
snprintf(args, sizeof args, "{\"org\":%lld}", (long long)a->org);
- char *resp = client_rpc(a->fd, "session.use_org", a->session, 0, args);
+ char *resp = client_rpc(&a->conn, "session.use_org", a->session, 0, args);
free(resp);
- resp = client_rpc(a->fd, "org.get", a->session, a->org, "{}");
+ resp = client_rpc(&a->conn, "org.get", a->session, a->org, "{}");
if (resp && client_ok(resp)) {
char *name = jstr_dup(resp, "result.name");
if (name) {
@@ -908,7 +908,7 @@ static void app_refresh_context(struct app *a)
}
free(resp);
- resp = client_rpc(a->fd, "org.list", a->session, 0, "{}");
+ resp = client_rpc(&a->conn, "org.list", a->session, 0, "{}");
if (resp) {
size_t n = jarr_size(resp, "result.items");
for (size_t i = 0; i < n; i++) {
@@ -928,7 +928,7 @@ static void app_refresh_context(struct app *a)
snprintf(a->default_series, sizeof a->default_series, "%s", "A");
a->attachment_dir[0] = '\0';
- resp = client_rpc(a->fd, "settings.get", a->session, a->org, "{}");
+ resp = client_rpc(&a->conn, "settings.get", a->session, a->org, "{}");
if (resp && client_ok(resp)) {
char *ser = jstr_dup(resp, "result.default_series");
if (ser && *ser)
@@ -942,7 +942,7 @@ static void app_refresh_context(struct app *a)
free(resp);
a->max_attachment_bytes = 10 * 1024 * 1024;
- resp = client_rpc(a->fd, "meta", NULL, 0, NULL);
+ resp = client_rpc(&a->conn, "meta", NULL, 0, NULL);
if (resp && client_ok(resp)) {
int64_t v =
jint_val(resp, "result.limits.max_attachment_bytes", 0);
@@ -951,7 +951,7 @@ static void app_refresh_context(struct app *a)
}
free(resp);
- resp = client_rpc(a->fd, "fiscal_year.get", a->session, a->org, "{}");
+ resp = client_rpc(&a->conn, "fiscal_year.get", a->session, a->org, "{}");
if (resp && client_ok(resp)) {
a->fy = jint_val(resp, "result.id", 0);
char *label = jstr_dup(resp, "result.label");
@@ -983,7 +983,7 @@ static int64_t fy_new_form(struct app *a)
{
char label[32] = "", start[16] = "", end[16] = "";
char *resp =
- client_rpc(a->fd, "fiscal_year.list", a->session, a->org, "{}");
+ client_rpc(&a->conn, "fiscal_year.list", a->session, a->org, "{}");
if (resp && client_ok(resp)) {
size_t n = jarr_size(resp, "result.items");
char max_end[16] = "";
@@ -1098,7 +1098,7 @@ static int64_t fy_new_form(struct app *a)
yyjson_mut_obj_add_strcpy(d, o, "end_date", end);
char *args = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
- resp = client_rpc(a->fd, "fiscal_year.open", a->session, a->org,
+ resp = client_rpc(&a->conn, "fiscal_year.open", a->session, a->org,
args);
free(args);
if (resp && client_ok(resp)) {
@@ -1131,7 +1131,7 @@ static void select_fiscal_year(struct app *a)
if (g_quit)
return;
char *resp =
- client_rpc(a->fd, "fiscal_year.list", a->session, a->org, "{}");
+ client_rpc(&a->conn, "fiscal_year.list", a->session, a->org, "{}");
if (!resp || !client_ok(resp)) {
show_error("Räkenskapsår", resp);
free(resp);
@@ -1375,7 +1375,7 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
snprintf(args, sizeof args, "{\"id\":%lld}", (long long)id);
for (;;) {
char *resp =
- client_rpc(a->fd, "voucher.get", a->session, a->org, args);
+ client_rpc(&a->conn, "voucher.get", a->session, a->org, args);
if (!resp || !client_ok(resp)) {
show_error("Verifikat", resp);
free(resp);
@@ -1494,7 +1494,7 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
yyjson_mut_obj_add_strcpy(d, o, "client_ref", ref);
char *cargs = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
- char *r = client_rpc(a->fd, "voucher.correct", a->session,
+ char *r = client_rpc(&a->conn, "voucher.correct", a->session,
a->org, cargs);
if (r && client_ok(r)) {
int64_t new_id = jint_val(r, "result.id", 0);
@@ -1612,7 +1612,7 @@ static void acct_cache_load(struct app *a)
if (g_accts.loaded && g_accts.org == a->org)
return;
acct_cache_free();
- char *resp = client_rpc(a->fd, "account.list", a->session, a->org,
+ char *resp = client_rpc(&a->conn, "account.list", a->session, a->org,
"{\"active_only\":true}");
if (!resp || !client_ok(resp)) {
free(resp);
@@ -1844,7 +1844,7 @@ static int64_t vouchers_new(struct app *a)
}
if (ch == KEY_F(4)) {
char tname[128] = "";
- char *lresp = client_rpc(a->fd, "template.list", a->session,
+ char *lresp = client_rpc(&a->conn, "template.list", a->session,
a->org, "{\"active_only\":true}");
if (!lresp || !client_ok(lresp)) {
show_error("Mallar", lresp);
@@ -1901,7 +1901,7 @@ static int64_t vouchers_new(struct app *a)
yyjson_mut_obj_add_strcpy(d, to, "name", tbuf);
char *targs = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
- char *resp = client_rpc(a->fd, "template.get", a->session,
+ char *resp = client_rpc(&a->conn, "template.get", a->session,
a->org, targs);
free(targs);
if (!resp || !client_ok(resp)) {
@@ -2030,8 +2030,8 @@ static int64_t vouchers_new(struct app *a)
a->session, (long long)a->org, args);
free(args);
char *r = NULL;
- if (client_send_line(a->fd, raw) == 0)
- r = client_read_line(a->fd);
+ if (client_send_line(&a->conn, raw) == 0)
+ r = client_read_line(&a->conn);
free(raw);
if (r && client_ok(r)) {
att_ids[natt] = jint_val(r, "result.id", 0);
@@ -2107,8 +2107,8 @@ static int64_t vouchers_new(struct app *a)
"\"session\":\"%s\",\"org\":%lld,\"dry_run\":true,"
"\"args\":%s}",
a->session, (long long)a->org, args);
- if (client_send_line(a->fd, raw) == 0) {
- char *r = client_read_line(a->fd);
+ if (client_send_line(&a->conn, raw) == 0) {
+ char *r = client_read_line(&a->conn);
if (r && client_ok(r)) {
int64_t num = jint_val(r, "result.number", 0);
message("Validering OK",
@@ -2122,7 +2122,7 @@ static int64_t vouchers_new(struct app *a)
}
free(raw);
} else {
- char *r = client_rpc(a->fd, "voucher.post", a->session, a->org,
+ char *r = client_rpc(&a->conn, "voucher.post", a->session, a->org,
args);
if (r && client_ok(r)) {
int64_t id = jint_val(r, "result.id", 0);
@@ -2221,7 +2221,7 @@ static void vouchers_screen(struct app *a)
snprintf(largs, sizeof largs,
"{\"limit\":200,\"fiscal_year\":%lld}",
(long long)a->fy);
- char *resp = client_rpc(a->fd, "voucher.list", a->session, a->org,
+ char *resp = client_rpc(&a->conn, "voucher.list", a->session, a->org,
largs);
if (!resp || !client_ok(resp)) {
show_error("Verifikat", resp);
@@ -2401,7 +2401,7 @@ static const char *vat_display(const char *name, const char *vat_code,
static void accounts_report(struct app *a)
{
for (;;) {
- char *resp = client_rpc(a->fd, "account.list", a->session, a->org,
+ char *resp = client_rpc(&a->conn, "account.list", a->session, a->org,
"{\"active_only\":false}");
if (!resp || !client_ok(resp)) {
show_error("Kontolista", resp);
@@ -2523,7 +2523,7 @@ static void reports_screen(struct app *a)
}
}
for (;;) {
- char *resp = client_rpc(a->fd, cmd, a->session, a->org, args);
+ char *resp = client_rpc(&a->conn, cmd, a->session, a->org, args);
if (!resp || !client_ok(resp)) {
show_error("Rapport", resp);
free(resp);
@@ -2550,7 +2550,7 @@ static void inbox_screen(struct app *a)
for (;;) {
if (g_quit)
return;
- char *resp = client_rpc(a->fd, "attachment.list", a->session, a->org,
+ char *resp = client_rpc(&a->conn, "attachment.list", a->session, a->org,
"{\"unlinked\":true,\"limit\":200}");
if (!resp || !client_ok(resp)) {
show_error("Underlag", resp);
@@ -2639,8 +2639,8 @@ static void inbox_screen(struct app *a)
a->session, (long long)a->org, args);
free(args);
char *r = NULL;
- if (client_send_line(a->fd, raw) == 0)
- r = client_read_line(a->fd);
+ if (client_send_line(&a->conn, raw) == 0)
+ r = client_read_line(&a->conn);
free(raw);
if (r && client_ok(r))
message("Underlag", "Sparat.");
@@ -2663,7 +2663,7 @@ static void audit_screen(struct app *a)
return;
if (sel == 0) {
char *resp =
- client_rpc(a->fd, "audit.verify", a->session, a->org, "{}");
+ client_rpc(&a->conn, "audit.verify", a->session, a->org, "{}");
if (!resp || !client_ok(resp)) {
show_error("Revision", resp);
free(resp);
@@ -2677,7 +2677,7 @@ static void audit_screen(struct app *a)
free(resp);
} else {
for (;;) {
- char *resp = client_rpc(a->fd, "audit.list", a->session,
+ char *resp = client_rpc(&a->conn, "audit.list", a->session,
a->org, "{\"limit\":200}");
if (!resp || !client_ok(resp)) {
show_error("Revision", resp);
@@ -2779,7 +2779,7 @@ static int template_form(struct app *a, const char *load_name)
char *args = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
char *resp =
- client_rpc(a->fd, "template.get", a->session, a->org, args);
+ client_rpc(&a->conn, "template.get", a->session, a->org, args);
free(args);
if (!resp || !client_ok(resp)) {
show_error("Mall", resp);
@@ -3037,7 +3037,7 @@ static int template_form(struct app *a, const char *load_name)
char *args = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
const char *cmd = tpl_id ? "template.update" : "template.create";
- char *resp = client_rpc(a->fd, cmd, a->session, a->org, args);
+ char *resp = client_rpc(&a->conn, cmd, a->session, a->org, args);
free(args);
if (resp && client_ok(resp)) {
if (ch == KEY_F(5)) {
@@ -3106,7 +3106,7 @@ static void template_archive_ui(struct app *a)
char *args = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
char *resp =
- client_rpc(a->fd, "template.archive", a->session, a->org, args);
+ client_rpc(&a->conn, "template.archive", a->session, a->org, args);
free(args);
if (resp && client_ok(resp))
message("Mall", "Mallen '%s' arkiverad.", name);
@@ -3170,7 +3170,7 @@ static int ib_load(struct app *a, char ***out_acc, int64_t **out_amt,
"\"limit\":200}",
(long long)a->fy);
char *resp =
- client_rpc(a->fd, "voucher.list", a->session, a->org, args);
+ client_rpc(&a->conn, "voucher.list", a->session, a->org, args);
if (!resp || !client_ok(resp)) {
show_error("Ingående balans", resp);
free(resp);
@@ -3188,7 +3188,7 @@ static int ib_load(struct app *a, char ***out_acc, int64_t **out_amt,
continue;
char vargs[64];
snprintf(vargs, sizeof vargs, "{\"id\":%lld}", (long long)id);
- char *v = client_rpc(a->fd, "voucher.get", a->session, a->org, vargs);
+ char *v = client_rpc(&a->conn, "voucher.get", a->session, a->org, vargs);
if (!v || !client_ok(v)) {
free(v);
continue;
@@ -3474,7 +3474,7 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
char *args = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
char *resp =
- client_rpc(a->fd, "voucher.post", a->session, a->org, args);
+ client_rpc(&a->conn, "voucher.post", a->session, a->org, args);
free(args);
if (resp && client_ok(resp)) {
int64_t num = jint_val(resp, "result.number", 0);
@@ -3618,7 +3618,7 @@ static void settings_screen(struct app *a)
if (g_quit)
return;
char *resp =
- client_rpc(a->fd, "settings.get", a->session, a->org, "{}");
+ client_rpc(&a->conn, "settings.get", a->session, a->org, "{}");
if (!resp || !client_ok(resp)) {
show_error("Inställningar", resp);
free(resp);
@@ -3673,7 +3673,7 @@ static void settings_screen(struct app *a)
yyjson_mut_obj_add_strcpy(d, o, "value", val);
char *args = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
- char *r = client_rpc(a->fd, "settings.set", a->session,
+ char *r = client_rpc(&a->conn, "settings.set", a->session,
a->org, args);
free(args);
if (r && client_ok(r)) {
@@ -3731,7 +3731,7 @@ static void templates_screen(struct app *a)
continue;
}
for (;;) {
- char *resp = client_rpc(a->fd, "template.list", a->session,
+ char *resp = client_rpc(&a->conn, "template.list", a->session,
a->org, "{\"active_only\":true}");
if (!resp || !client_ok(resp)) {
show_error("Mallar", resp);
@@ -3936,22 +3936,26 @@ static int login_screen(struct app *a)
/* attempt login */
snprintf(a->socket, sizeof a->socket, "%s", socket_path);
- a->fd = client_connect(a->socket);
- if (a->fd < 0) {
- message("Fel", "Kunde inte ansluta till %s", a->socket);
+ if (client_connect(a->socket, &a->conn) != 0) {
+ message("Fel", "Kunde inte ansluta till %s: %s", a->socket,
+ client_last_error());
continue;
}
char *err = NULL, *session = NULL;
- if (client_login(a->fd, user, pass, &session, &err) != 0) {
- char *code = jstr_dup(err, "error.code");
- char *msg = jstr_dup(err, "error.message");
- message("Inloggning misslyckades", "%s: %s",
- code ? code : "fel", msg ? msg : "okänt fel");
- free(code);
- free(msg);
+ if (client_login(&a->conn, user, pass, &session, &err) != 0) {
+ if (err && err[0] == '{') {
+ char *code = jstr_dup(err, "error.code");
+ char *msg = jstr_dup(err, "error.message");
+ message("Inloggning misslyckades", "%s: %s",
+ code ? code : "fel", msg ? msg : "okänt fel");
+ free(code);
+ free(msg);
+ } else {
+ message("Inloggning misslyckades", "%s",
+ err ? err : "transportfel");
+ }
free(err);
- close(a->fd);
- a->fd = -1;
+ client_close(&a->conn);
continue;
}
snprintf(a->session, sizeof a->session, "%s", session);
@@ -3963,7 +3967,7 @@ static int login_screen(struct app *a)
static int select_org(struct app *a)
{
- char *resp = client_rpc(a->fd, "session.list_orgs", a->session, 0, "{}");
+ char *resp = client_rpc(&a->conn, "session.list_orgs", a->session, 0, "{}");
if (!resp || !client_ok(resp)) {
show_error("Organisationer", resp);
free(resp);
@@ -4010,7 +4014,7 @@ static void usage(FILE *f)
{
fprintf(f,
"usage: bokftui [options]\n"
- " --socket TARGET unix socket or tcp:host:port\n"
+ " --socket TARGET unix path, tcp:host:port or tls:host:port\n"
" --user NAME prefill username\n"
" --org ID select org directly\n"
" --version\n");
@@ -4020,7 +4024,7 @@ int main(int argc, char **argv)
{
struct app app;
memset(&app, 0, sizeof app);
- app.fd = -1;
+ app.conn.fd = -1;
const char *socket = getenv("BOKFD_SOCKET");
if (!socket)
socket = "/run/bokfd/bokfd.sock";
@@ -4068,9 +4072,9 @@ int main(int argc, char **argv)
update_status(&app);
dashboard(&app);
- client_rpc(app.fd, "session.close", app.session, 0, "{}");
+ client_rpc(&app.conn, "session.close", app.session, 0, "{}");
acct_cache_free();
- close(app.fd);
+ client_close(&app.conn);
endwin();
return 0;
}