Unverified Commit 125556cb authored by Birte Kristina Friesel's avatar Birte Kristina Friesel Committed by GitHub
Browse files

Merge pull request #645 from eworm-de/magic

use libmagic to detect valid file formats
parents 617e1f3a 26cd770c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ Dependencies

 * Imlib2
 * libcurl (disable with make curl=0)
 * libmagic (disable with make magic=0)
 * libpng
 * libX11
 * libXinerama (disable with make xinerama=0)
@@ -91,6 +92,7 @@ indicates that the corresponding feature is enabled by default.
| help | 0 | include help text (refers to the manpage otherwise) |
| inotify | 0 | enable inotify, needed for `--auto-reload` |
| stat64 | 0 | Support CIFS shares from 64bit hosts on 32bit machines |
| magic | 1 | Build against libmagic to filter unsupported file formats |
| mkstemps | 1 | Whether your libc provides `mkstemps()`. If set to 0, feh will be unable to load gif images via libcurl |
| verscmp | 1 | Whether your libc provides `strvercmp()`. If set to 0, feh will use an internal implementation. |
| xinerama | 1 | Support Xinerama/XRandR multiscreen setups |
+6 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ curl ?= 1
debug ?= 0
exif ?= 0
help ?= 0
magic ?= 1
mkstemps ?= 1
verscmp ?= 1
xinerama ?= 1
@@ -68,6 +69,11 @@ ifeq (${mkstemps},1)
	CFLAGS += -DHAVE_MKSTEMPS
endif

ifeq (${magic},1)
	CFLAGS += -DHAVE_LIBMAGIC
	LDLIBS += -lmagic
endif

ifeq (${verscmp},1)
	CFLAGS += -DHAVE_STRVERSCMP
endif
+4 −0
Original line number Diff line number Diff line
@@ -145,6 +145,10 @@ void init_slideshow_mode(void);
void init_list_mode(void);
void init_loadables_mode(void);
void init_unloadables_mode(void);
#ifdef HAVE_LIBMAGIC
void uninit_magic(void);
void init_magic(void);
#endif
void feh_clean_exit(void);
int feh_should_ignore_image(Imlib_Image * im);
int feh_load_image(Imlib_Image * im, feh_file * file);
+64 −82
Original line number Diff line number Diff line
@@ -44,6 +44,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "exif.h"
#endif

#ifdef HAVE_LIBMAGIC
#include <magic.h>

magic_t magic = NULL;
#endif

Display *disp = NULL;
Visual *vis = NULL;
Screen *scr = NULL;
@@ -235,6 +241,33 @@ void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum fe
	}
}

#ifdef HAVE_LIBMAGIC
void uninit_magic(void)
{
	if (!magic) {
		return;
	}

	magic_close(magic);
	magic = NULL;
}
void init_magic(void)
{
	if (getenv("FEH_SKIP_MAGIC")) {
		return;
	}

	if (!(magic = magic_open(MAGIC_NONE))) {
		weprintf("unable to initialize magic library\n");
		return;
	}

	if (magic_load(magic, NULL) != 0) {
		weprintf("cannot load magic database: %s\n", magic_error(magic));
		uninit_magic();
	}
}

/*
 * This is a workaround for an Imlib2 regression, causing unloadable image
 * detection to be excessively slow (and, thus, causing feh to hang for a while
@@ -242,99 +275,48 @@ void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum fe
 * avoid calling Imlib2 for files it probably cannot handle. See
 * <https://phab.enlightenment.org/T8739> and
 * <https://github.com/derf/feh/issues/505>.
 *
 * Note that this drops support for bz2-compressed files, unless
 * FEH_SKIP_MAGIC is set
 */
int feh_is_image(feh_file * file)
int feh_is_image(feh_file * file, int magic_flags)
{
	unsigned char buf[16];
	FILE *fh = fopen(file->filename, "r");
	if (!fh) {
		return 0;
	}
	// Files smaller than buf will be padded with zeroes
	memset(buf, 0, sizeof(buf));
	if (fread(buf, 1, 16, fh) <= 0) {
		fclose(fh);
		return 0;
	}
	fclose(fh);
	const char * mime_type = NULL;

	if (buf[0] == 0xff && buf[1] == 0xd8) {
		// JPEG
		return 1;
	}
	if (!memcmp(buf, "\x89PNG\x0d\x0a\x1a\x0a", 8)) {
		// PNG
		return 1;
	}
	if (buf[0] == 'A' && buf[1] == 'R' && buf[2] == 'G' && buf[3] == 'B') {
		// ARGB
		return 1;
	}
	if (buf[0] == 'B' && buf[1] == 'M') {
		// BMP
		return 1;
	}
	if (!memcmp(buf, "farbfeld", 8)) {
		// farbfeld
	if (!magic) {
		return 1;
	}
	if (buf[0] == 'G' && buf[1] == 'I' && buf[2] == 'F') {
		// GIF
		return 1;
	}
	if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] <= 0x02 && buf[3] == 0x00) {
		// ICO
		return 1;
	}
	if (!memcmp(buf, "FORM", 4)) {
		// Amiga IFF ILBM
		return 1;
	}
	if (buf[0] == 'P' && buf[1] >= '1' && buf[1] <= '7') {
		// PNM et al.
		return 1;
	}
	if (strstr(file->filename, ".tga")) {
		// TGA
		return 1;
	}
	if (!memcmp(buf, "II\x2a\x00", 4) || !memcmp(buf, "MM\x00\x2a", 4)) {
		// TIFF
		return 1;
	}
	if (!memcmp(buf, "RIFF", 4)) {
		// might be webp
		return 1;

	magic_setflags(magic, MAGIC_MIME_TYPE | MAGIC_SYMLINK | magic_flags);
	mime_type = magic_file(magic, file->filename);

	if (!mime_type) {
		return 0;
	}
	if (!memcmp(buf + 4, "ftyphei", 7) || !memcmp(buf + 4, "ftypmif1", 8)) {
		// HEIC/HEIF - note that this is only supported in imlib2-heic. Ordinary
		// imlib2 releases do not support heic/heif images as of 2021-01.

	D(("file %s has mime type: %s\n", file->filename, mime_type));

	if (strncmp(mime_type, "image/", 6) == 0) {
		return 1;
	}
	if ((buf[0] == 0xff && buf[1] == 0x0a) || !memcmp(buf, "\x00\x00\x00\x0cJXL \x0d\x0a\x87\x0a", 12)) {
		// JXL - note that this is only supported in imlib2-jxl. Ordinary
		// imlib2 releases do not support JXL images as of 2021-06.
		return 1;

	/* no infinite loop on compressed content, please */
	if (magic_flags) {
		return 0;
	}
	buf[15] = 0;
	if (strstr((char *)buf, "XPM")) {
		// XPM
		return 1;

	/* imlib2 supports loading compressed images, let's have a look inside */
	if (strcmp(mime_type, "application/gzip") == 0 ||
	    strcmp(mime_type, "application/x-bzip2") == 0 ||
	    strcmp(mime_type, "application/x-xz") == 0) {
		return feh_is_image(file, MAGIC_COMPRESS);
	}
	if (strstr(file->filename, ".bz2") || strstr(file->filename, ".gz")) {
		// Imlib2 supports compressed images. It relies on the filename to
		// determine the appropriate loader and does not use magic bytes here.
		return 1;

	return 0;
}
	// moved to the end as this variable won't be set in most cases
	if (getenv("FEH_SKIP_MAGIC")) {
#else
int feh_is_image(__attribute__((unused)) feh_file * file, __attribute__((unused)) int magic_flags)
{
	return 1;
}
	return 0;
}
#endif

int feh_load_image(Imlib_Image * im, feh_file * file)
{
@@ -358,7 +340,7 @@ int feh_load_image(Imlib_Image * im, feh_file * file)
		}
	}
	else {
		if (feh_is_image(file)) {
		if (feh_is_image(file, 0)) {
			*im = imlib_load_image_with_error_return(file->filename, &err);
		} else {
			feh_err = LOAD_ERROR_MAGICBYTES;
+9 −0
Original line number Diff line number Diff line
@@ -49,6 +49,11 @@ int main(int argc, char **argv)
	srandom(getpid() * time(NULL) % ((unsigned int) -1));

	setup_signal_handlers();

#ifdef HAVE_LIBMAGIC
	init_magic();
#endif

	init_parse_options(argc, argv);

	init_imlib_fonts();
@@ -262,6 +267,10 @@ void feh_clean_exit(void)
	if(disp)
		XCloseDisplay(disp);

#ifdef HAVE_LIBMAGIC
	uninit_magic();
#endif

	/*
	 * Only restore the old terminal settings if
	 * - we changed them in the first place