summaryrefslogtreecommitdiff
path: root/clients/ui.c
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-21 11:45:54 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-21 11:45:54 +0200
commite1dd7112e0346914710566660625da0f6b866e8a (patch)
treec4b0d5c7f6f62c1c4c38245e7c001b3fd4b03e74 /clients/ui.c
parent182f0a592b6fe97fc1ff87add227f9d52a4ef902 (diff)
downloadbokf-e1dd7112e0346914710566660625da0f6b866e8a.tar.gz
bokf-e1dd7112e0346914710566660625da0f6b866e8a.zip
tui: payroll screens, Företag/System menu and settings splitv0.1.55
Diffstat (limited to 'clients/ui.c')
-rw-r--r--clients/ui.c85
1 files changed, 85 insertions, 0 deletions
diff --git a/clients/ui.c b/clients/ui.c
index ff573c9..a95ce44 100644
--- a/clients/ui.c
+++ b/clients/ui.c
@@ -442,6 +442,91 @@ static int bytes_look_text(const unsigned char *p, size_t n)
return 1;
}
+/* Directory for generated PDFs. */
+static void pdf_cache_dir(char *buf, size_t n)
+{
+ const char *cache = getenv("XDG_CACHE_HOME");
+ if (cache && *cache) {
+ snprintf(buf, n, "%s/bokf", cache);
+ return;
+ }
+ snprintf(buf, n, "/tmp");
+}
+
+/* Keep a user-supplied name from escaping the directory. */
+static void safe_fname(const char *in, char *out, size_t n)
+{
+ size_t o = 0;
+ for (const char *p = in ? in : ""; *p && o + 1 < n; p++) {
+ unsigned char c = (unsigned char)*p;
+ if (c < 32 || c == '/')
+ c = '-';
+ out[o++] = (char)c;
+ }
+ out[o] = '\0';
+}
+
+static int write_pdf_b64(const char *b64, const char *path)
+{
+ unsigned char *data = NULL;
+ size_t len = 0;
+ if (!b64 || util_b64_decode(b64, strlen(b64), &data, &len) != 0)
+ return -1;
+ FILE *fp = fopen(path, "wb");
+ if (!fp) {
+ free(data);
+ return -1;
+ }
+ size_t wrote = fwrite(data, 1, len, fp);
+ fclose(fp);
+ free(data);
+ return wrote == len ? 0 : -1;
+}
+
+/* Open a saved PDF with xdg-open when a desktop session is present;
+ otherwise (or on fork failure) show where it was saved. */
+static void open_pdf(const char *path, const char *title)
+{
+ const char *display = getenv("DISPLAY");
+ const char *wayland = getenv("WAYLAND_DISPLAY");
+ if ((!display || !*display) && (!wayland || !*wayland)) {
+ tui_message(title, "Sparad: %s", path);
+ return;
+ }
+ pid_t pid = fork();
+ if (pid == 0) {
+ pid_t gc = fork();
+ if (gc == 0) {
+ setsid();
+ execlp("xdg-open", "xdg-open", path, (char *)NULL);
+ _exit(127);
+ }
+ _exit(gc < 0 ? 1 : 0);
+ }
+ if (pid > 0)
+ waitpid(pid, NULL, 0);
+ else
+ tui_message(title, "Sparad: %s", path);
+}
+
+void pdf_save_and_open(const char *b64, const char *filename,
+ const char *title)
+{
+ char dir[512], path[700], safe[600];
+ pdf_cache_dir(dir, sizeof dir);
+ safe_fname(filename, safe, sizeof safe);
+ if (snprintf(path, sizeof path, "%s/%s", dir, safe) >= (int)sizeof path) {
+ tui_message(title, "Sökvägen blir för lång.");
+ return;
+ }
+ config_mkdirs(path);
+ if (write_pdf_b64(b64, path) != 0) {
+ tui_message(title, "Kunde inte spara %s", path);
+ return;
+ }
+ open_pdf(path, title);
+}
+
/* Fetch one attachment, save it to a prompted path and verify its hash.
Text attachments are shown inline after saving. */
void attachment_download(struct app *a, int64_t att_id)