diff options
Diffstat (limited to 'snac.c')
| -rw-r--r-- | snac.c | 158 |
1 files changed, 99 insertions, 59 deletions
| @@ -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; |