Skip to content

Commit 596a4f1

Browse files
committed
Determine image file types via file header check
Previously, the image format was determined by checking the file extension. 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 d ifferent 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. Therefore, I updates the implementation to determine the image format by checking the file header and remove previous checking for file extension.
1 parent fd3e451 commit 596a4f1

File tree

1 file changed

+37
-9
lines changed

1 file changed

+37
-9
lines changed

src/image.c

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,55 @@ typedef enum {
3131
#undef _
3232
} twin_image_format_t;
3333

34-
/* FIXME: Check the header of the given images to determine the supported image
35-
* formats instead of parsing the filename without checking its content.
34+
/*
35+
* Defines the headers of supported image formats.
36+
* Each image format has a unique header, allowing the format to be determined
37+
* by inspecting the file header.
38+
* Supported formats: PNG, JPEG.
39+
* Reference:
40+
* - PNG:
41+
* http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature
42+
* - JPEG:
43+
* https://www.file-recovery.com/jpg-signature-format.htm
3644
*/
37-
static twin_image_format_t image_type_from_name(const char *path)
45+
#if __BYTE_ORDER == __BIG_ENDIAN
46+
static const uint8_t header_png[8] = {
47+
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
48+
};
49+
#else
50+
static const uint8_t header_png[8] = {
51+
0x0A, 0x1A, 0x0A, 0x0D, 0x47, 0x4E, 0x50, 0x89,
52+
};
53+
#endif
54+
static const uint8_t header_jpeg[3] = {0xFF, 0xD8, 0xFF};
55+
56+
static twin_image_format_t image_type_detect(const char *path)
3857
{
3958
twin_image_format_t type = IMAGE_TYPE_unknown;
40-
const char *extname = strrchr(path, '.');
41-
if (!extname)
59+
FILE *file = fopen(path, "rb");
60+
if (!file) {
61+
fprintf(stderr, "Failed to open %s\n", path);
62+
return IMAGE_TYPE_unknown;
63+
}
64+
65+
uint8_t header[8];
66+
size_t bytes_read = fread(header, 1, sizeof(header), file);
67+
fclose(file);
68+
69+
if (bytes_read < 8) /* incomplete image file */
4270
return IMAGE_TYPE_unknown;
4371
#if LOADER_HAS(PNG)
44-
else if (!strcasecmp(extname, ".png")) {
72+
else if (!memcmp(header, header_png, sizeof(header_png))) {
4573
type = IMAGE_TYPE_png;
4674
}
4775
#endif
4876
#if LOADER_HAS(JPEG)
49-
else if (!strcasecmp(extname, ".jpg") || !strcasecmp(extname, ".jpeg")) {
77+
else if (!memcmp(header, header_jpeg, sizeof(header_jpeg))) {
5078
type = IMAGE_TYPE_jpeg;
5179
}
5280
#endif
53-
/* otherwise, unsupported format */
5481

82+
/* otherwise, unsupported format */
5583
return type;
5684
}
5785

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

75103
twin_pixmap_t *twin_pixmap_from_file(const char *path, twin_format_t fmt)
76104
{
77-
loader_func_t loader = image_loaders[image_type_from_name(path)];
105+
loader_func_t loader = image_loaders[image_type_detect(path)];
78106
if (!loader)
79107
return NULL;
80108
return loader(path, fmt);

0 commit comments

Comments
 (0)