diff options
| -rw-r--r-- | data.c | 19 | ||||
| -rw-r--r-- | sandbox.c | 9 | ||||
| -rw-r--r-- | snac.c | 158 | ||||
| -rw-r--r-- | snac.h | 1 |
4 files changed, 114 insertions, 73 deletions
| @@ -100,19 +100,12 @@ int srv_open(const char *basedir, int auto_upgrade) | |||
| 100 | 100 | ||
| 101 | if (auto_upgrade) | 101 | if (auto_upgrade) |
| 102 | ret = snac_upgrade(&error); | 102 | ret = snac_upgrade(&error); |
| 103 | else { | 103 | else if (xs_number_get(xs_dict_get(srv_config, "layout")) < disk_layout) |
| 104 | if (xs_number_get(xs_dict_get(srv_config, "layout")) < disk_layout) | 104 | error = xs_fmt("ERROR: disk layout changed - execute 'snac upgrade' first"); |
| 105 | error = xs_fmt("ERROR: disk layout changed - execute 'snac upgrade' first"); | 105 | else if (!check_strip_tool()) |
| 106 | else { | 106 | error = xs_fmt("ERROR: strip_exif enabled but commands not found or working"); |
| 107 | if (!check_strip_tool()) { | 107 | else |
| 108 | const char *mp = xs_dict_get(srv_config, "mogrify_path"); | 108 | ret = 1; |
| 109 | if (mp == NULL) mp = "mogrify"; | ||
| 110 | error = xs_fmt("ERROR: strip_exif enabled but '%s' not found or not working (set 'mogrify_path' in server.json)", mp); | ||
| 111 | } | ||
| 112 | else | ||
| 113 | ret = 1; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | 109 | } |
| 117 | 110 | ||
| 118 | } | 111 | } |
| @@ -13,6 +13,8 @@ void sbox_enter(const char *basedir) | |||
| 13 | return; | 13 | return; |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | const xs_val *strip_exif = xs_dict_get(srv_config, "strip_exif"); | ||
| 17 | |||
| 16 | int smail; | 18 | int smail; |
| 17 | const char *url = xs_dict_get(srv_config, "smtp_url"); | 19 | const char *url = xs_dict_get(srv_config, "smtp_url"); |
| 18 | 20 | ||
| @@ -33,6 +35,11 @@ void sbox_enter(const char *basedir) | |||
| 33 | if (*address == '/') | 35 | if (*address == '/') |
| 34 | unveil(address, "rwc"); | 36 | unveil(address, "rwc"); |
| 35 | 37 | ||
| 38 | if (strip_exif) { | ||
| 39 | unveil(xs_dict_get(srv_config, "ffmpeg_path"), "x"); | ||
| 40 | unveil(xs_dict_get(srv_config, "mogrify_path"), "x"); | ||
| 41 | } | ||
| 42 | |||
| 36 | if (smail) | 43 | if (smail) |
| 37 | unveil("/usr/sbin/sendmail", "x"); | 44 | unveil("/usr/sbin/sendmail", "x"); |
| 38 | 45 | ||
| @@ -45,7 +52,7 @@ void sbox_enter(const char *basedir) | |||
| 45 | if (*address == '/') | 52 | if (*address == '/') |
| 46 | p = xs_str_cat(p, " unix"); | 53 | p = xs_str_cat(p, " unix"); |
| 47 | 54 | ||
| 48 | if (smail) | 55 | if (smail || strip_exif) |
| 49 | p = xs_str_cat(p, " exec"); | 56 | p = xs_str_cat(p, " exec"); |
| 50 | 57 | ||
| 51 | pledge(p, NULL); | 58 | pledge(p, NULL); |
| @@ -35,7 +35,9 @@ | |||
| 35 | #include <sys/stat.h> | 35 | #include <sys/stat.h> |
| 36 | #include <sys/wait.h> | 36 | #include <sys/wait.h> |
| 37 | #include <limits.h> | 37 | #include <limits.h> |
| 38 | #include <stdio.h> | ||
| 38 | #include <stdlib.h> | 39 | #include <stdlib.h> |
| 40 | #include <unistd.h> | ||
| 39 | 41 | ||
| 40 | xs_str *srv_basedir = NULL; | 42 | xs_str *srv_basedir = NULL; |
| 41 | xs_dict *srv_config = NULL; | 43 | xs_dict *srv_config = NULL; |
| @@ -177,6 +179,46 @@ int check_password(const char *uid, const char *passwd, const char *hash) | |||
| 177 | } | 179 | } |
| 178 | 180 | ||
| 179 | 181 | ||
| 182 | char* findprog(const char *prog) | ||
| 183 | /* find a prog in PATH and return the first match */ | ||
| 184 | { | ||
| 185 | char *path_env, *path, *dir, filename[PATH_MAX]; | ||
| 186 | int len; | ||
| 187 | struct stat sbuf; | ||
| 188 | |||
| 189 | path_env = getenv("PATH"); | ||
| 190 | if (!prog || !path_env) | ||
| 191 | return NULL; | ||
| 192 | |||
| 193 | path_env = strdup(path_env); | ||
| 194 | if (!path_env) | ||
| 195 | return NULL; | ||
| 196 | path = path_env; | ||
| 197 | |||
| 198 | while ((dir = strsep(&path, ":")) != NULL) { | ||
| 199 | /* empty entries as ./ instead of / */ | ||
| 200 | if (*dir == '\0') | ||
| 201 | dir = "."; | ||
| 202 | |||
| 203 | /* strip trailing / */ | ||
| 204 | len = strlen(dir); | ||
| 205 | while (len > 0 && dir[len-1] == '/') | ||
| 206 | dir[--len] = '\0'; | ||
| 207 | |||
| 208 | len = snprintf(filename, sizeof(filename), "%s/%s", dir, prog); | ||
| 209 | if (len > 0 && len < (int) sizeof(filename) && | ||
| 210 | (stat(filename, &sbuf) == 0) && S_ISREG(sbuf.st_mode) && | ||
| 211 | access(filename, X_OK) == 0) { | ||
| 212 | free(path_env); | ||
| 213 | return strdup(filename); | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | free(path_env); | ||
| 218 | return NULL; | ||
| 219 | } | ||
| 220 | |||
| 221 | |||
| 180 | int strip_media(const char *fn) | 222 | int strip_media(const char *fn) |
| 181 | /* strips EXIF data from a file */ | 223 | /* strips EXIF data from a file */ |
| 182 | { | 224 | { |
| @@ -207,23 +249,24 @@ int strip_media(const char *fn) | |||
| 207 | xs_endswith(l_fn, ".gif") || xs_endswith(l_fn, ".bmp")) { | 249 | xs_endswith(l_fn, ".gif") || xs_endswith(l_fn, ".bmp")) { |
| 208 | 250 | ||
| 209 | const char *mp = xs_dict_get(srv_config, "mogrify_path"); | 251 | const char *mp = xs_dict_get(srv_config, "mogrify_path"); |
| 210 | if (mp == NULL) | ||
| 211 | mp = "mogrify"; | ||
| 212 | 252 | ||
| 213 | xs *cmd = xs_fmt("cd \"%s\" && %s -auto-orient -strip \"%s\" 2>/dev/null", srv_basedir, mp, r_fn); | 253 | pid_t pid = fork(); |
| 214 | 254 | if (pid == -1) { | |
| 215 | ret = system(cmd); | 255 | srv_log(xs_fmt("strip_media: cannot fork()")); |
| 256 | return -1; | ||
| 257 | } else if (pid == 0) { | ||
| 258 | chdir(srv_basedir); | ||
| 259 | execl(mp, "-auto-orient", "-strip", r_fn, (char*) NULL); | ||
| 260 | _exit(1); | ||
| 261 | } | ||
| 216 | 262 | ||
| 217 | if (ret != 0) { | 263 | if (waitpid(pid, &ret, 0) == -1) { |
| 218 | int code = 0; | 264 | srv_log(xs_fmt("strip_media: cannot waitpid()")); |
| 219 | if (WIFEXITED(ret)) | 265 | return -1; |
| 220 | code = WEXITSTATUS(ret); | ||
| 221 | |||
| 222 | if (code == 127) | ||
| 223 | srv_log(xs_fmt("strip_media: error stripping %s. '%s' not found (exit 127). Set 'mogrify_path' in server.json.", r_fn, mp)); | ||
| 224 | else | ||
| 225 | srv_log(xs_fmt("strip_media: error stripping %s %d", r_fn, ret)); | ||
| 226 | } | 266 | } |
| 267 | |||
| 268 | if (ret != 0) | ||
| 269 | srv_log(xs_fmt("strip_media: error stripping %s %d", r_fn, ret)); | ||
| 227 | else | 270 | else |
| 228 | srv_debug(1, xs_fmt("strip_media: stripped %s", r_fn)); | 271 | srv_debug(1, xs_fmt("strip_media: stripped %s", r_fn)); |
| 229 | } | 272 | } |
| @@ -234,35 +277,33 @@ int strip_media(const char *fn) | |||
| 234 | xs_endswith(l_fn, ".mkv") || xs_endswith(l_fn, ".avi")) { | 277 | xs_endswith(l_fn, ".mkv") || xs_endswith(l_fn, ".avi")) { |
| 235 | 278 | ||
| 236 | const char *fp = xs_dict_get(srv_config, "ffmpeg_path"); | 279 | const char *fp = xs_dict_get(srv_config, "ffmpeg_path"); |
| 237 | if (fp == NULL) | ||
| 238 | fp = "ffmpeg"; | ||
| 239 | 280 | ||
| 240 | /* ffmpeg cannot modify in-place, so we need a temp file */ | 281 | /* ffmpeg cannot modify in-place, so we need a temp file */ |
| 241 | /* we must preserve valid extension for ffmpeg to guess the format */ | 282 | /* we must preserve valid extension for ffmpeg to guess the format */ |
| 242 | const char *ext = strrchr(r_fn, '.'); | 283 | const char *ext = strrchr(r_fn, '.'); |
| 243 | if (ext == NULL) ext = ""; | 284 | if (ext == NULL) ext = ""; |
| 244 | xs *tmp_fn = xs_fmt("%s.tmp%s", r_fn, ext); | 285 | xs *tmp_fn = xs_fmt("%s.tmp%s", r_fn, ext); |
| 245 | |||
| 246 | /* -map_metadata -1 strips all global metadata */ | ||
| 247 | /* -c copy copies input streams without re-encoding */ | ||
| 248 | /* we don't silence stderr so we can debug issues */ | ||
| 249 | /* we explicitly cd to srv_basedir to ensure relative paths work */ | ||
| 250 | xs *cmd = xs_fmt("cd \"%s\" && %s -y -i \"%s\" -map_metadata -1 -c copy \"%s\"", srv_basedir, fp, r_fn, tmp_fn); | ||
| 251 | 286 | ||
| 252 | ret = system(cmd); | 287 | pid_t pid = fork(); |
| 288 | if (pid == -1) { | ||
| 289 | srv_log(xs_fmt("strip_media: cannot fork()")); | ||
| 290 | return -1; | ||
| 291 | } else if (pid == 0) { | ||
| 292 | chdir(srv_basedir); | ||
| 293 | /* -map_metadata -1 strips all global metadata */ | ||
| 294 | /* -c copy copies input streams without re-encoding */ | ||
| 295 | execl(fp, "-y", "-i", r_fn, "-map_metadata", "-1", "-c", "copy", tmp_fn, (char*) NULL); | ||
| 296 | _exit(1); | ||
| 297 | } | ||
| 298 | |||
| 299 | if (waitpid(pid, &ret, 0) == -1) { | ||
| 300 | srv_log(xs_fmt("strip_media: cannot waitpid()")); | ||
| 301 | return -1; | ||
| 302 | } | ||
| 253 | 303 | ||
| 254 | if (ret != 0) { | 304 | if (ret != 0) { |
| 255 | int code = 0; | 305 | srv_log(xs_fmt("strip_media: error stripping %s %d", r_fn, ret)); |
| 256 | if (WIFEXITED(ret)) | 306 | |
| 257 | code = WEXITSTATUS(ret); | ||
| 258 | |||
| 259 | if (code == 127) | ||
| 260 | srv_log(xs_fmt("strip_media: error stripping %s. '%s' not found (exit 127). Set 'ffmpeg_path' in server.json.", r_fn, fp)); | ||
| 261 | else { | ||
| 262 | srv_log(xs_fmt("strip_media: error stripping %s %d", r_fn, ret)); | ||
| 263 | srv_log(xs_fmt("strip_media: command was: %s", cmd)); | ||
| 264 | } | ||
| 265 | |||
| 266 | /* try to cleanup, just in case */ | 307 | /* try to cleanup, just in case */ |
| 267 | /* unlink needs full path too if we are not in basedir */ | 308 | /* unlink needs full path too if we are not in basedir */ |
| 268 | xs *full_tmp_fn = xs_fmt("%s/%s", srv_basedir, tmp_fn); | 309 | xs *full_tmp_fn = xs_fmt("%s/%s", srv_basedir, tmp_fn); |
| @@ -286,38 +327,37 @@ int strip_media(const char *fn) | |||
| 286 | 327 | ||
| 287 | 328 | ||
| 288 | int check_strip_tool(void) | 329 | int check_strip_tool(void) |
| 330 | /* check if strip_exif tools do exist and fix their absolute path */ | ||
| 289 | { | 331 | { |
| 290 | const xs_val *v = xs_dict_get(srv_config, "strip_exif"); | 332 | const xs_val *v = xs_dict_get(srv_config, "strip_exif"); |
| 333 | /* skip if unless strip_exif; return non-error */ | ||
| 334 | if (xs_type(v) != XSTYPE_TRUE) | ||
| 335 | return 1; | ||
| 336 | |||
| 291 | int ret = 1; | 337 | int ret = 1; |
| 338 | const char *progs[] = { "ffmpeg", "mogrify" }; | ||
| 292 | 339 | ||
| 293 | if (xs_type(v) == XSTYPE_TRUE) { | 340 | for (int i = 0; i < (int)(sizeof(progs) / sizeof(progs[0])); i++) { |
| 294 | /* check mogrify */ | 341 | xs_str *key = xs_fmt("%s_path", progs[i]); |
| 295 | { | 342 | |
| 296 | const char *mp = xs_dict_get(srv_config, "mogrify_path"); | 343 | const char *val = xs_dict_get(srv_config, key); |
| 297 | if (mp == NULL) | 344 | if (val == NULL) { |
| 298 | mp = "mogrify"; | 345 | val = findprog(progs[i]); |
| 299 | 346 | if (val != NULL) | |
| 300 | xs *cmd = xs_fmt("%s -version 2>/dev/null >/dev/null", mp); | 347 | srv_debug(1, xs_fmt("check_strip_tool: found %s in PATH at %s", progs[i], val)); |
| 301 | |||
| 302 | if (system(cmd) != 0) { | ||
| 303 | srv_log(xs_fmt("check_strip_tool: '%s' not working", mp)); | ||
| 304 | ret = 0; | ||
| 305 | } | ||
| 306 | } | 348 | } |
| 307 | 349 | ||
| 308 | /* check ffmpeg */ | 350 | if (val == NULL) { |
| 309 | if (ret) { | 351 | srv_log(xs_fmt("check_strip_tool: %s not found in PATH", progs[i])); |
| 310 | const char *fp = xs_dict_get(srv_config, "ffmpeg_path"); | 352 | ret = 0; |
| 311 | if (fp == NULL) | 353 | } else if (access(val, X_OK) != 0) { |
| 312 | fp = "ffmpeg"; | 354 | srv_log(xs_fmt("check_strip_tool: %s '%s' is not executable", progs[i], val)); |
| 313 | 355 | ret = 0; | |
| 314 | xs *cmd = xs_fmt("%s -version 2>/dev/null >/dev/null", fp); | 356 | } else { |
| 315 | 357 | srv_config = xs_dict_set(srv_config, key, val); | |
| 316 | if (system(cmd) != 0) { | ||
| 317 | srv_log(xs_fmt("check_strip_tool: '%s' not working", fp)); | ||
| 318 | ret = 0; | ||
| 319 | } | ||
| 320 | } | 358 | } |
| 359 | |||
| 360 | xs_free(key); | ||
| 321 | } | 361 | } |
| 322 | 362 | ||
| 323 | return ret; | 363 | return ret; |
| @@ -107,6 +107,7 @@ int validate_uid(const char *uid); | |||
| 107 | xs_str *hash_password(const char *uid, const char *passwd, const char *nonce); | 107 | xs_str *hash_password(const char *uid, const char *passwd, const char *nonce); |
| 108 | int check_password(const char *uid, const char *passwd, const char *hash); | 108 | int check_password(const char *uid, const char *passwd, const char *hash); |
| 109 | 109 | ||
| 110 | char* findprog(const char *prog); | ||
| 110 | int strip_media(const char *fn); | 111 | int strip_media(const char *fn); |
| 111 | int check_strip_tool(void); | 112 | int check_strip_tool(void); |
| 112 | 113 | ||