Skip to content

Commit

Permalink
Determine supported image file types by checking the file header
Browse files Browse the repository at this point in the history
Previously, the image format was determined by checking the file extension.
This commit updates the implementation to determine the image format by checking
the file header.

The format of a file is determined by its internal bit layout rather than its extension. File extensions are merely human-readable metadata and do not reflect the actual contents of a file. For instance, renaming a .jpg file to another extension does not alter its internal format and could result in a file of a completely different type, such as a harmful executable.

Relying solely on file extensions to determine file format is unreliable, as it may lead to incorrect results. Instead, file headers contain unique, format-specific signatures that provide a more accurate and stable method for identifying a file's true nature.
  • Loading branch information
Damien-Chen committed Aug 22, 2024
1 parent fd3e451 commit 0f049d4
Showing 1 changed file with 37 additions and 9 deletions.
46 changes: 37 additions & 9 deletions src/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,55 @@ typedef enum {
#undef _
} twin_image_format_t;

/* FIXME: Check the header of the given images to determine the supported image
* formats instead of parsing the filename without checking its content.
/*
* Defines the headers of supported image formats.
* Each image format has a unique header, allowing the format to be determined
* by inspecting the file header.
* Supported formats: PNG, JPEG.
* Reference:
* - PNG:
* http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature
* - JPEG:
* https://www.file-recovery.com/jpg-signature-format.htm
*/
static twin_image_format_t image_type_from_name(const char *path)
#if __BYTE_ORDER == __BIG_ENDIAN
static const uint8_t header_png[8] = {
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
};
#else
static const uint8_t header_png[8] = {
0x0A, 0x1A, 0x0A, 0x0D, 0x47, 0x4E, 0x50, 0x89,
};
#endif
static const uint8_t header_jpeg[3] = {0xFF, 0xD8, 0xFF};

static twin_image_format_t image_type_detect(const char *path)
{
twin_image_format_t type = IMAGE_TYPE_unknown;
const char *extname = strrchr(path, '.');
if (!extname)
FILE *file = fopen(path, "rb");
if (!file) {
fprintf(stderr, "Failed to open %s\n", path);
return IMAGE_TYPE_unknown;
}

uint8_t header[8];
size_t bytes_read = fread(header, 1, sizeof(header), file);
fclose(file);

if (bytes_read < 8) /* incomplete image file */
return IMAGE_TYPE_unknown;
#if LOADER_HAS(PNG)
else if (!strcasecmp(extname, ".png")) {
else if (!memcmp(header, header_png, sizeof(header_png))) {
type = IMAGE_TYPE_png;
}
#endif
#if LOADER_HAS(JPEG)
else if (!strcasecmp(extname, ".jpg") || !strcasecmp(extname, ".jpeg")) {
else if (!memcmp(header, header_jpeg, sizeof(header_jpeg))) {
type = IMAGE_TYPE_jpeg;
}
#endif
/* otherwise, unsupported format */

/* otherwise, unsupported format */
return type;
}

Expand All @@ -74,7 +102,7 @@ static loader_func_t image_loaders[] = {

twin_pixmap_t *twin_pixmap_from_file(const char *path, twin_format_t fmt)
{
loader_func_t loader = image_loaders[image_type_from_name(path)];
loader_func_t loader = image_loaders[image_type_detect(path)];
if (!loader)
return NULL;
return loader(path, fmt);
Expand Down

0 comments on commit 0f049d4

Please sign in to comment.