diff options
| -rw-r--r-- | fiv-thumbnail.c | 100 | 
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);  	}  | 
