Skip to content

Commit

Permalink
Determine image file formats by checking file header (#36)
Browse files Browse the repository at this point in the history
The image format was determined by checking the file extension parsed
from the image path. However, a file's format is defined by its internal
bit layout, not its extension. File extensions are merely human-readable
metadata and do not necessarily reflect the actual contents of a file.
For example, renaming a .jpg file to a different extension does not
change its internal format and could potentially misrepresent the file
as something else, such as a harmful executable.

Relying solely on filenames to determine file formats is unreliable and
may lead to incorrect results. In contrast, file headers contain unique,
format-specific signatures that offer a more accurate and reliable
method for identifying a file's true nature. Therefore, the handler has
been updated to determine the image format by checking the file header,
and the previous reliance on file extensions has been removed.
  • Loading branch information
Damien-Chen authored Aug 23, 2024
1 parent a6a9970 commit bf70e8d
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 bf70e8d

Please sign in to comment.