aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fiv-thumbnail.c100
1 files changed, 75 insertions, 25 deletions
diff --git a/fiv-thumbnail.c b/fiv-thumbnail.c
index 328b105..80529d6 100644
--- a/fiv-thumbnail.c
+++ b/fiv-thumbnail.c
@@ -229,6 +229,65 @@ save_thumbnail(cairo_surface_t *thumbnail, const char *path, GString *thum)
WebPDataClear(&assembled);
}
+static cairo_surface_t *
+fiv_thumbnail_prepare(
+ GFile *target, GBytes *data, gboolean *color_managed, GError **error)
+{
+ FivIoOpenContext ctx = {
+ .uri = g_file_get_uri(target),
+ .screen_profile = fiv_io_profile_new_sRGB(),
+ .screen_dpi = 96,
+ .first_frame_only = TRUE,
+ // Only using this array as a redirect.
+ .warnings = g_ptr_array_new_with_free_func(g_free),
+ };
+
+ cairo_surface_t *surface = fiv_io_open_from_data(
+ g_bytes_get_data(data, NULL), g_bytes_get_size(data), &ctx, error);
+ g_free((gchar *) ctx.uri);
+ g_ptr_array_free(ctx.warnings, TRUE);
+ if ((*color_managed = !!ctx.screen_profile))
+ fiv_io_profile_free(ctx.screen_profile);
+ g_bytes_unref(data);
+ return surface;
+}
+
+static gboolean
+fiv_thumbnail_fallback(GFile *target, FivThumbnailSize size,
+ cairo_surface_t **surface, GError **error)
+{
+ goffset filesize = 0;
+ GFileInfo *info = g_file_query_info(target,
+ G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ if (info) {
+ filesize = g_file_info_get_size(info);
+ g_object_unref(info);
+ }
+
+ // TODO(p): Try to be a bit more intelligent about this.
+ // For example, we can employ magic checks.
+ if (filesize > 10 << 20) {
+ set_error(error, "oversize, not thumbnailing");
+ return FALSE;
+ }
+
+ GBytes *data = g_file_load_bytes(target, NULL, NULL, error);
+ if (!data)
+ return FALSE;
+
+ gboolean color_managed = FALSE;
+ cairo_surface_t *result =
+ fiv_thumbnail_prepare(target, data, &color_managed, error);
+ if (!result)
+ return FALSE;
+
+ if (!*surface)
+ *surface = adjust_thumbnail(result, fiv_thumbnail_sizes[size].size);
+ cairo_surface_destroy(result);
+ return TRUE;
+}
+
gboolean
fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
cairo_surface_t **max_size_surface, GError **error)
@@ -236,42 +295,33 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
g_return_val_if_fail(max_size >= FIV_THUMBNAIL_SIZE_MIN &&
max_size <= FIV_THUMBNAIL_SIZE_MAX, FALSE);
- // Local files only, at least for now.
- // TODO(p): Support thumbnailing the trash scheme.
const gchar *path = g_file_peek_path(target);
- if (!path) {
- set_error(error, "only local files are supported");
- return FALSE;
+ if (!path || !g_file_is_native(target) /* Don't save sftp://. */) {
+ return fiv_thumbnail_fallback(
+ target, max_size, max_size_surface, error);
}
- GMappedFile *mf = g_mapped_file_new(path, FALSE, error);
- if (!mf)
- return FALSE;
-
+ // Make the TOCTTOU issue favour unnecessary reloading.
GStatBuf st = {};
if (g_stat(path, &st)) {
set_error(error, g_strerror(errno));
return FALSE;
}
- FivIoOpenContext ctx = {
- .uri = g_file_get_uri(target),
- .screen_profile = fiv_io_profile_new_sRGB(),
- .screen_dpi = 96,
- .first_frame_only = TRUE,
- // Only using this array as a redirect.
- .warnings = g_ptr_array_new_with_free_func(g_free),
- };
+ GError *e = NULL;
+ GMappedFile *mf = g_mapped_file_new(path, FALSE, &e);
+ if (!mf) {
+ g_debug("%s: %s", path, e->message);
+ g_error_free(e);
+ return fiv_thumbnail_fallback(
+ target, max_size, max_size_surface, error);
+ }
gsize filesize = g_mapped_file_get_length(mf);
- cairo_surface_t *surface = fiv_io_open_from_data(
- g_mapped_file_get_contents(mf), filesize, &ctx, error);
+ gboolean color_managed = FALSE;
+ cairo_surface_t *surface = fiv_thumbnail_prepare(
+ target, g_mapped_file_get_bytes(mf), &color_managed, error);
g_mapped_file_unref(mf);
-
- g_free((gchar *) ctx.uri);
- g_ptr_array_free(ctx.warnings, TRUE);
- if (ctx.screen_profile)
- fiv_io_profile_free(ctx.screen_profile);
if (!surface)
return FALSE;
@@ -296,7 +346,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
}
// Without a CMM, no conversion is attempted.
- if (ctx.screen_profile) {
+ if (color_managed) {
g_string_append_printf(
thum, "%s%c%s%c", THUMB_COLORSPACE, 0, THUMB_COLORSPACE_SRGB, 0);
}