ctpv/ctpv.c

390 lines
8.1 KiB
C
Raw Normal View History

2022-05-25 23:57:50 +02:00
#include <stdio.h>
#include <magic.h>
2022-06-08 01:27:30 +02:00
#include <fcntl.h>
2022-05-22 09:55:04 +02:00
#include <stdlib.h>
#include <string.h>
2022-05-25 23:57:50 +02:00
#include <signal.h>
2022-05-22 09:55:04 +02:00
#include <unistd.h>
2022-05-26 01:48:27 +02:00
#include <sys/stat.h>
#include <openssl/md5.h>
2022-05-22 09:55:04 +02:00
#include "error.h"
2022-06-01 18:17:47 +02:00
#include "utils.h"
2022-06-08 01:27:30 +02:00
#include "config.h"
2022-05-25 23:57:50 +02:00
#include "server.h"
#include "preview.h"
2022-05-22 09:55:04 +02:00
#include "previews.h"
2022-05-22 22:07:08 +02:00
#define ANY_TYPE "*"
2022-06-08 01:27:30 +02:00
static char any_type[] = ANY_TYPE;
2022-05-22 22:07:08 +02:00
2022-05-22 09:55:04 +02:00
static magic_t magic;
static struct {
2022-05-22 22:07:08 +02:00
enum {
MODE_PREVIEW,
MODE_SERVER,
2022-05-25 23:57:50 +02:00
MODE_CLEAR,
MODE_END,
2022-05-22 22:07:08 +02:00
MODE_LIST,
2022-05-23 03:32:09 +02:00
MODE_MIME,
2022-05-22 22:07:08 +02:00
} mode;
2022-05-25 23:57:50 +02:00
char *server_id_s;
} ctpv = { .mode = MODE_PREVIEW };
2022-05-22 09:55:04 +02:00
2022-06-08 01:27:30 +02:00
static VectorPreview *previews;
2022-06-02 02:20:42 +02:00
static void cleanup(void)
{
2022-05-22 09:55:04 +02:00
cleanup_previews();
2022-06-08 01:27:30 +02:00
config_cleanup();
2022-05-22 09:55:04 +02:00
if (magic != NULL)
magic_close(magic);
2022-06-08 01:27:30 +02:00
if (previews)
vectorPreview_free(previews);
2022-05-22 09:55:04 +02:00
}
2022-06-02 02:20:42 +02:00
static int init_magic(void)
2022-05-23 03:32:09 +02:00
{
ERRCHK_RET(!(magic = magic_open(MAGIC_MIME_TYPE)), FUNCFAILED("magic_open"),
magic_error(magic));
2022-05-22 09:55:04 +02:00
ERRCHK_RET(magic_load(magic, NULL) != 0, FUNCFAILED("magic_load"),
2022-05-22 09:55:04 +02:00
magic_error(magic));
return OK;
}
2022-06-08 01:27:30 +02:00
static int create_dir(char *buf, size_t len)
{
char dir[len];
strncpy(dir, buf, LEN(dir) - 1);
ERRCHK_RET(mkpath(dir, 0700) == -1, FUNCFAILED("mkpath"), ERRNOS);
return OK;
}
static int get_config_file(char *buf, size_t len)
{
ERRCHK_RET_OK(get_config_dir(buf, len, "ctpv/"));
ERRCHK_RET_OK(create_dir(buf, len));
strncat(buf, "config", len - 1);
if (access(buf, F_OK) != 0)
close(creat(buf, 0600));
return OK;
}
static int init_previews_v(void)
2022-05-22 22:07:08 +02:00
{
2022-06-08 01:27:30 +02:00
previews = vectorPreview_new(LEN(b_previews));
vectorPreview_append_arr(previews, b_previews, LEN(b_previews));
char config_file[FILENAME_MAX];
get_config_file(config_file, LEN(config_file));
ERRCHK_RET_OK(config_load(previews, config_file, any_type));
init_previews(previews->buf, previews->len);
return OK;
2022-05-22 22:07:08 +02:00
}
2022-06-02 01:37:43 +02:00
static const char *get_mimetype(const char *path)
2022-05-23 03:32:09 +02:00
{
2022-05-22 09:55:04 +02:00
const char *r = magic_file(magic, path);
if (!r) {
PRINTINTERR(FUNCFAILED("magic_file"), magic_error(magic));
2022-05-22 09:55:04 +02:00
return NULL;
}
return r;
}
2022-06-02 01:37:43 +02:00
static const char *get_ext(const char *path)
2022-05-23 03:32:09 +02:00
{
const char *base;
2022-05-23 03:32:09 +02:00
if ((base = strrchr(path, '/')))
base += sizeof(*base);
else
base = path;
2022-05-23 23:40:46 +02:00
const char *dot = strchr(base, '.');
if (!dot || dot == base)
2022-05-22 09:55:04 +02:00
return NULL;
2022-05-23 03:32:09 +02:00
return &dot[1];
}
2022-06-02 01:37:43 +02:00
static int check_file(const char *f)
2022-05-23 03:32:09 +02:00
{
if (!f) {
print_error("file not given");
return ERR;
}
if (access(f, R_OK) != 0) {
print_errorf("failed to access '%s': %s", f, ERRNOS);
return ERR;
}
2022-05-23 03:32:09 +02:00
return OK;
2022-05-22 09:55:04 +02:00
}
static int is_newer(int *resp, char *f1, char *f2)
{
struct stat stat1, stat2;
ERRCHK_RET(stat(f1, &stat1) == -1, FUNCFAILED("stat"), ERRNOS);
ERRCHK_RET(stat(f2, &stat2) == -1, FUNCFAILED("stat"), ERRNOS);
2022-05-22 09:55:04 +02:00
int sec_d = stat1.st_mtim.tv_sec - stat2.st_mtim.tv_sec;
if (sec_d < 0)
goto older;
else if (sec_d == 0 && stat1.st_mtim.tv_nsec <= stat2.st_mtim.tv_nsec)
goto older;
*resp = 1;
return OK;
older:
*resp = 0;
return OK;
}
static void md5_string(char *buf, size_t len, char *s)
2022-05-22 09:55:04 +02:00
{
unsigned char out[MD5_DIGEST_LENGTH];
char b[16];
MD5((const unsigned char *)s, strlen(s), out);
buf[0] = '\0';
for(unsigned int i = 0; i < LEN(out); i++) {
snprintf(b, LEN(b)-1, "%02x", out[i]);
strncat(buf, b, len);
}
}
static int get_cache_file(char *buf, size_t len, char *file)
{
ERRCHK_RET_OK(get_cache_dir(buf, len, "ctpv/"));
2022-06-08 01:27:30 +02:00
ERRCHK_RET_OK(create_dir(buf, len));
char name[64];
md5_string(name, LEN(name) - 1, file);
strncat(buf, name, len - 1);
return OK;
}
static int check_cache(int *resp, char *file, char *cache_file)
{
if (access(cache_file, F_OK) != 0) {
*resp = 0;
return OK;
}
return is_newer(resp, cache_file, file);
}
#define GET_PARG(a, i) (a) = argc > (i) ? argv[i] : NULL
static int preview(int argc, char *argv[])
{
2022-05-28 22:06:41 +02:00
char *f, *w, *h, *x, *y, *id;
2022-05-22 09:55:04 +02:00
GET_PARG(f, 0);
GET_PARG(w, 1);
GET_PARG(h, 2);
GET_PARG(x, 3);
GET_PARG(y, 4);
2022-05-28 22:06:41 +02:00
GET_PARG(id, 5);
2022-05-22 09:55:04 +02:00
2022-05-23 03:32:09 +02:00
ERRCHK_RET_OK(check_file(f));
2022-05-22 09:55:04 +02:00
ERRCHK_RET_OK(init_magic());
2022-06-08 01:27:30 +02:00
ERRCHK_RET_OK(init_previews_v());
2022-05-22 09:55:04 +02:00
2022-05-24 03:26:02 +02:00
const char *mimetype;
ERRCHK_RET(!(mimetype = get_mimetype(f)));
2022-05-22 09:55:04 +02:00
char cache_file[FILENAME_MAX];
ERRCHK_RET_OK(get_cache_file(cache_file, LEN(cache_file), f));
int cache_valid;
ERRCHK_RET_OK(check_cache(&cache_valid, f, cache_file));
PreviewArgs args = {
.f = f, .w = w, .h = h, .x = x, .y = y, .id = id,
.cache_file = cache_file, .cache_valid = cache_valid,
};
2022-05-22 09:55:04 +02:00
2022-05-25 23:57:50 +02:00
return run_preview(get_ext(f), mimetype, &args);
}
2022-05-22 09:55:04 +02:00
2022-05-29 20:34:21 +02:00
static int server(void)
2022-05-25 23:57:50 +02:00
{
2022-05-29 20:34:21 +02:00
return server_listen(ctpv.server_id_s);
2022-05-22 09:55:04 +02:00
}
2022-05-29 20:34:21 +02:00
static int clear(void)
2022-05-22 22:07:08 +02:00
{
2022-05-29 20:34:21 +02:00
return server_clear(ctpv.server_id_s);
2022-05-25 23:57:50 +02:00
}
2022-05-29 20:34:21 +02:00
static int end(void)
2022-05-25 23:57:50 +02:00
{
2022-05-29 20:34:21 +02:00
return server_end(ctpv.server_id_s);
2022-05-22 22:07:08 +02:00
}
static int list(void)
{
2022-06-08 01:27:30 +02:00
ERRCHK_RET_OK(init_previews_v());
2022-05-22 22:07:08 +02:00
size_t len;
Preview p, **list = get_previews_list(&len);
2022-06-01 18:17:47 +02:00
const char *n, *e, *t, *s;
const char header_name[] = "Name", header_ext[] = "Extension",
header_mime[] = "MIME type";
int width_name = 0, width_ext = 0;
for (size_t i = 0; i < len + 1; i++) {
if (i < len) {
p = *list[i];
n = p.name;
e = p.ext;
} else {
n = header_name;
e = header_ext;
}
int name_len = strlennull(n);
int ext_len = strlennull(e);
width_name = MAX(width_name, name_len);
width_ext = MAX(width_ext, ext_len);
}
width_name += 2, width_ext += 2;
2022-05-22 22:07:08 +02:00
puts("List of available previews:");
2022-06-01 18:17:47 +02:00
printf("\t%-*s %-*s %s\n", width_name, header_name, width_ext, header_ext,
header_mime);
2022-05-22 22:07:08 +02:00
for (size_t i = 0; i < len; i++) {
p = *list[i];
e = p.ext;
2022-05-22 22:07:08 +02:00
t = p.type;
s = p.subtype;
if (!e)
e = any_type;
2022-05-22 22:07:08 +02:00
if (!t) {
t = any_type;
s = any_type;
} else if (!s) {
s = any_type;
}
2022-06-01 18:17:47 +02:00
printf("\t%-*s .%-*s %s/%s\n", width_name, p.name, width_ext - 1, e, t,
s);
2022-05-22 22:07:08 +02:00
}
puts("\nNote: '" ANY_TYPE "' means that it matches any.");
2022-06-01 18:17:47 +02:00
2022-05-22 22:07:08 +02:00
return OK;
}
2022-05-23 03:32:09 +02:00
static int mime(int argc, char *argv[])
{
2022-06-02 01:37:43 +02:00
const char *f, *mimetype;
2022-05-23 03:32:09 +02:00
for (int i = 0; i < argc; i++) {
f = argv[i];
ERRCHK_RET_OK(check_file(f));
ERRCHK_RET_OK(init_magic());
mimetype = get_mimetype(f);
ERRCHK_RET(!mimetype);
if (argc > 1)
printf("%s: ", f);
2022-05-26 01:48:27 +02:00
printf(".%s ", get_ext(f));
2022-05-23 03:32:09 +02:00
puts(mimetype);
}
return OK;
}
2022-05-22 09:55:04 +02:00
int main(int argc, char *argv[])
{
program = argc > 0 ? argv[0] : "ctpv";
2022-05-22 09:55:04 +02:00
int c;
while ((c = getopt(argc, argv, "s:c:e:lm")) != -1) {
2022-05-22 09:55:04 +02:00
switch (c) {
case 's':
2022-05-22 22:07:08 +02:00
ctpv.mode = MODE_SERVER;
2022-05-25 23:57:50 +02:00
ctpv.server_id_s = optarg;
break;
case 'c':
ctpv.mode = MODE_CLEAR;
ctpv.server_id_s = optarg;
2022-05-25 23:57:50 +02:00
break;
case 'e':
ctpv.mode = MODE_END;
ctpv.server_id_s = optarg;
2022-05-22 22:07:08 +02:00
break;
case 'l':
ctpv.mode = MODE_LIST;
2022-05-22 09:55:04 +02:00
break;
2022-05-23 03:32:09 +02:00
case 'm':
ctpv.mode = MODE_MIME;
break;
2022-05-22 09:55:04 +02:00
default:
return EXIT_FAILURE;
}
}
2022-05-23 03:32:09 +02:00
argc -= optind;
argv = &argv[optind];
2022-05-22 09:55:04 +02:00
int ret;
2022-05-22 22:07:08 +02:00
switch (ctpv.mode) {
case MODE_PREVIEW:
2022-05-23 03:32:09 +02:00
ret = preview(argc, argv);
2022-05-22 22:07:08 +02:00
break;
case MODE_SERVER:
2022-05-29 20:34:21 +02:00
ret = server();
2022-05-25 23:57:50 +02:00
break;
case MODE_CLEAR:
2022-05-29 20:34:21 +02:00
ret = clear();
2022-05-25 23:57:50 +02:00
break;
case MODE_END:
2022-05-29 20:34:21 +02:00
ret = end();
2022-05-22 22:07:08 +02:00
break;
case MODE_LIST:
ret = list();
break;
2022-05-23 03:32:09 +02:00
case MODE_MIME:
ret = mime(argc, argv);
break;
2022-05-22 22:07:08 +02:00
default:
PRINTINTERR("unknowm mode: %d", ctpv.mode);
2022-05-22 22:07:08 +02:00
ret = ERR;
break;
}
2022-05-22 09:55:04 +02:00
cleanup();
return ret == OK ? EXIT_SUCCESS : EXIT_FAILURE;
}