commit 688c54c

Stefano Marinelli  ·  2026-01-06 10:02:36 +0000 UTC
parent b84b92d
Implement configurable EXIF stripping for uploaded media

- Add `strip_exif` configuration option to enable metadata removal.
- Add `mogrify_path` configuration to specify external tool location.
- Implement strip_media using `mogrify -strip`.
- Support multiple image formats: jpg, png, webp, heic, heif, avif, tiff, gif, bmp.
- Add strict startup check: fail to start if `strip_exif` is enabled but `mogrify` is missing/broken.
- Update documentation in `doc/snac.8`.
4 files changed,  +86, -2
M data.c
M snac.c
M snac.h
M data.c
+11, -2
 1@@ -89,8 +89,15 @@ int srv_open(const char *basedir, int auto_upgrade)
 2                 else {
 3                     if (xs_number_get(xs_dict_get(srv_config, "layout")) < disk_layout)
 4                         error = xs_fmt("ERROR: disk layout changed - execute 'snac upgrade' first");
 5-                    else
 6-                        ret = 1;
 7+                    else {
 8+                        if (!check_strip_tool()) {
 9+                            const char *mp = xs_dict_get(srv_config, "mogrify_path");
10+                            if (mp == NULL) mp = "mogrify";
11+                            error = xs_fmt("ERROR: strip_exif enabled but '%s' not found or not working (set 'mogrify_path' in server.json)", mp);
12+                        }
13+                        else
14+                            ret = 1;
15+                    }
16                 }
17             }
18 
19@@ -2710,6 +2717,8 @@ void static_put(snac *snac, const char *id, const char *data, int size)
20     if (fn && (f = fopen(fn, "wb")) != NULL) {
21         fwrite(data, size, 1, f);
22         fclose(f);
23+
24+        strip_media(fn);
25     }
26 }
27 
+8, -0
 1@@ -296,6 +296,14 @@ outgoing messages (default: 15). Anyway, whenever any incoming activity from a
 2 failed instance is detected, this counter is reset for it.
 3 .It Ic vkey
 4 Public vapid key. Used for notification on some client.
 5+.It Ic strip_exif
 6+If set to true, EXIF metadata will be stripped from uploaded images (jpg, png, webp, heic, avif, tiff, gif, bmp). This requires the
 7+.Nm mogrify
 8+tool to be installed. If
 9+.Nm snac
10+cannot find or execute the tool at startup, it will refuse to run.
11+.It Ic mogrify_path
12+Overrides the default "mogrify" command name or path. Use this if the tool is not in the system PATH or has a different name.
13 .El
14 .Pp
15 You must restart the server to make effective these changes.
M snac.c
+64, -0
 1@@ -32,6 +32,7 @@
 2 
 3 #include <sys/time.h>
 4 #include <sys/stat.h>
 5+#include <sys/wait.h>
 6 
 7 xs_str *srv_basedir = NULL;
 8 xs_dict *srv_config = NULL;
 9@@ -170,3 +171,66 @@ int check_password(const char *uid, const char *passwd, const char *hash)
10 
11     return ret;
12 }
13+
14+
15+int strip_media(const char *fn)
16+/* strips EXIF data from a file */
17+{
18+    int ret = 0;
19+    const xs_val *v = xs_dict_get(srv_config, "strip_exif");
20+
21+    if (xs_type(v) == XSTYPE_TRUE) {
22+        xs *l_fn = xs_tolower_i(xs_dup(fn));
23+
24+        /* check extensions */
25+        if (xs_endswith(l_fn, ".jpg") || xs_endswith(l_fn, ".jpeg") ||
26+            xs_endswith(l_fn, ".png") || xs_endswith(l_fn, ".webp") ||
27+            xs_endswith(l_fn, ".heic") || xs_endswith(l_fn, ".heif") ||
28+            xs_endswith(l_fn, ".avif") || xs_endswith(l_fn, ".tiff") ||
29+            xs_endswith(l_fn, ".gif")  || xs_endswith(l_fn, ".bmp")) {
30+
31+            const char *mp = xs_dict_get(srv_config, "mogrify_path");
32+            if (mp == NULL)
33+                mp = "mogrify";
34+
35+            xs *cmd = xs_fmt("%s -strip \"%s\" 2>/dev/null", mp, fn);
36+
37+            ret = system(cmd);
38+
39+            if (ret != 0) {
40+                int code = 0;
41+                if (WIFEXITED(ret))
42+                    code = WEXITSTATUS(ret);
43+                
44+                if (code == 127)
45+                    srv_log(xs_fmt("strip_media: error stripping %s. '%s' not found (exit 127). Set 'mogrify_path' in server.json.", fn, mp));
46+                else
47+                    srv_log(xs_fmt("strip_media: error stripping %s %d", fn, ret));
48+            }
49+            else
50+                srv_debug(1, xs_fmt("strip_media: stripped %s", fn));
51+        }
52+    }
53+
54+    return ret;
55+}
56+
57+
58+int check_strip_tool(void)
59+{
60+    const xs_val *v = xs_dict_get(srv_config, "strip_exif");
61+    int ret = 1;
62+
63+    if (xs_type(v) == XSTYPE_TRUE) {
64+        const char *mp = xs_dict_get(srv_config, "mogrify_path");
65+        if (mp == NULL)
66+            mp = "mogrify";
67+
68+        xs *cmd = xs_fmt("%s -version 2>/dev/null >/dev/null", mp);
69+        
70+        if (system(cmd) != 0)
71+            ret = 0;
72+    }
73+
74+    return ret;
75+}
M snac.h
+3, -0
 1@@ -105,6 +105,9 @@ int validate_uid(const char *uid);
 2 xs_str *hash_password(const char *uid, const char *passwd, const char *nonce);
 3 int check_password(const char *uid, const char *passwd, const char *hash);
 4 
 5+int strip_media(const char *fn);
 6+int check_strip_tool(void);
 7+
 8 void srv_archive(const char *direction, const char *url, xs_dict *req,
 9                  const char *payload, int p_size,
10                  int status, xs_dict *headers,