Commit c1956f35 authored by Bastien Nocera's avatar Bastien Nocera

thumbnail: Split off running the script

Move most of the script command generation to a separate file, making
the function return a GBytes from a successful thumbnailer run, so as to
avoid having to clean up temporary files from the thumbnailer run.

Note that it changes a few subtle things which shouldn't be a problem in
practice, but, as a corner case, might have been used by applications:

- Thumbnailers must output PNG images. pixbuf_new_from_bytes() could
have been made more complicated to handle all images, and then we would
restrict the thumbnailer output format separately, but it makes no sense
to write complicated code to remove it in the next commit.
- URIs which have no backing path are not supported. This will likely
cause problems for thumbnailing remote shares on OSes which lack
gvfsd-fuse. Support could be re-added in the future.

https://bugzilla.gnome.org/show_bug.cgi?id=774497
parent 3b298f60
......@@ -42,7 +42,9 @@ libgnome_desktop_3_la_SOURCES = \
gnome-datetime-source.c \
gnome-rr-private.h \
default-input-sources.h \
meta-xrandr-shared.h
meta-xrandr-shared.h \
gnome-desktop-thumbnail-script.c\
gnome-desktop-thumbnail-script.h
dbus_xrandr_built_sources = meta-dbus-xrandr.c meta-dbus-xrandr.h
......
/*
* Copyright (C) 2002, 2017 Red Hat, Inc.
* Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
*
* This file is part of the Gnome Library.
*
* The Gnome Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The Gnome Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with the Gnome Library; see the file COPYING.LIB. If not,
* write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* Authors: Alexander Larsson <alexl@redhat.com>
* Carlos Garcia Campos <carlosgc@gnome.org>
* Bastien Nocera <hadess@hadess.net>
*/
#include "config.h"
#include <gio/gio.h>
#include <glib/gstdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "gnome-desktop-thumbnail-script.h"
typedef struct {
char *infile;
char *outfile;
} ScriptExec;
static char *
expand_thumbnailing_elem (const char *elem,
const int size,
const char *inuri,
const char *outfile,
gboolean *got_input,
gboolean *got_output)
{
GString *str;
const char *p, *last;
char *localfile;
str = g_string_new (NULL);
last = elem;
while ((p = strchr (last, '%')) != NULL)
{
g_string_append_len (str, last, p - last);
p++;
switch (*p) {
case 'u':
g_string_append (str, inuri);
*got_input = TRUE;
p++;
break;
case 'i':
localfile = g_filename_from_uri (inuri, NULL, NULL);
if (localfile)
{
g_string_append (str, localfile);
*got_input = TRUE;
g_free (localfile);
}
p++;
break;
case 'o':
g_string_append (str, outfile);
*got_output = TRUE;
p++;
break;
case 's':
g_string_append_printf (str, "%d", size);
p++;
break;
case '%':
g_string_append_c (str, '%');
p++;
break;
case 0:
default:
break;
}
last = p;
}
g_string_append (str, last);
return g_string_free (str, FALSE);
}
static char **
expand_thumbnailing_script (const char *cmd,
ScriptExec *script,
int size,
GError **error)
{
GPtrArray *array;
g_auto(GStrv) cmd_elems = NULL;
guint i;
gboolean got_in, got_out;
g_autofree char *sandboxed_path = NULL;
if (!g_shell_parse_argv (cmd, NULL, &cmd_elems, error))
return NULL;
array = g_ptr_array_new_with_free_func (g_free);
got_in = got_out = FALSE;
for (i = 0; cmd_elems[i] != NULL; i++)
{
char *expanded;
expanded = expand_thumbnailing_elem (cmd_elems[i],
size,
script->infile,
script->outfile,
&got_in,
&got_out);
g_ptr_array_add (array, expanded);
}
if (!got_in)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Input file could not be set");
goto bail;
}
else if (!got_out)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Output file could not be set");
goto bail;
}
g_ptr_array_add (array, NULL);
return (char **) g_ptr_array_free (array, FALSE);
bail:
g_ptr_array_free (array, TRUE);
return NULL;
}
static void
script_exec_free (ScriptExec *exec)
{
g_free (exec->infile);
if (exec->outfile)
{
g_unlink (exec->outfile);
g_free (exec->outfile);
}
g_free (exec);
}
static ScriptExec *
script_exec_new (const char *uri)
{
ScriptExec *exec;
g_autoptr(GFile) file = NULL;
int fd;
g_autofree char *tmpname = NULL;
exec = g_new0 (ScriptExec, 1);
file = g_file_new_for_uri (uri);
exec->infile = g_file_get_path (file);
if (!exec->infile)
goto bail;
fd = g_file_open_tmp (".gnome_desktop_thumbnail.XXXXXX", &tmpname, NULL);
if (fd == -1)
goto bail;
close (fd);
exec->outfile = g_steal_pointer (&tmpname);
return exec;
bail:
script_exec_free (exec);
return NULL;
}
GBytes *
gnome_desktop_thumbnail_script_exec (const char *cmd,
int size,
const char *uri,
GError **error)
{
g_autofree char *error_out = NULL;
g_auto(GStrv) expanded_script = NULL;
int exit_status;
gboolean ret;
GBytes *image = NULL;
ScriptExec *exec;
exec = script_exec_new (uri);
expanded_script = expand_thumbnailing_script (cmd, exec, size, error);
if (expanded_script == NULL)
goto out;
#if 0
guint i;
g_print ("About to launch script: ");
for (i = 0; expanded_script[i]; i++)
g_print ("%s ", expanded_script[i]);
g_print ("\n");
#endif
ret = g_spawn_sync (NULL, expanded_script, NULL, G_SPAWN_SEARCH_PATH,
NULL, NULL, NULL, &error_out,
&exit_status, error);
if (ret && g_spawn_check_exit_status (exit_status, error))
{
char *contents;
gsize length;
if (g_file_get_contents (exec->outfile, &contents, &length, error))
image = g_bytes_new_take (contents, length);
}
else
{
g_debug ("Failed to launch script: %s", !ret ? (*error)->message : error_out);
}
out:
script_exec_free (exec);
return image;
}
/*
* gnome-thumbnail.h: Utilities for handling thumbnails
*
* Copyright (C) 2002, 2017 Red Hat, Inc.
*
* This file is part of the Gnome Library.
*
* The Gnome Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The Gnome Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with the Gnome Library; see the file COPYING.LIB. If not,
* write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* Authors: Alexander Larsson <alexl@redhat.com>
* Bastien Nocera <hadess@hadess.net>
*/
#ifndef GNOME_DESKTOP_THUMBNAIL_SCRIPT_H
#define GNOME_DESKTOP_THUMBNAIL_SCRIPT_H
#include <glib.h>
GBytes *
gnome_desktop_thumbnail_script_exec (const char *cmd,
int size,
const char *uri,
GError **error);
#endif /* GNOME_DESKTOP_THUMBNAIL_SCRIPT_H */
......@@ -127,9 +127,12 @@
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#define GNOME_DESKTOP_USE_UNSTABLE_API
#include "gnome-desktop-thumbnail.h"
#include "gnome-desktop-thumbnail-script.h"
static void
thumbnailers_directory_changed (GFileMonitor *monitor,
......@@ -929,120 +932,6 @@ gnome_desktop_thumbnail_factory_can_thumbnail (GnomeDesktopThumbnailFactory *fac
return FALSE;
}
static char *
expand_thumbnailing_elem (const char *elem,
const int size,
const char *inuri,
const char *outfile,
gboolean *got_input,
gboolean *got_output)
{
GString *str;
const char *p, *last;
char *localfile;
str = g_string_new (NULL);
last = elem;
while ((p = strchr (last, '%')) != NULL)
{
g_string_append_len (str, last, p - last);
p++;
switch (*p) {
case 'u':
g_string_append (str, inuri);
*got_input = TRUE;
p++;
break;
case 'i':
localfile = g_filename_from_uri (inuri, NULL, NULL);
if (localfile)
{
g_string_append (str, localfile);
*got_input = TRUE;
g_free (localfile);
}
p++;
break;
case 'o':
g_string_append (str, outfile);
*got_output = TRUE;
p++;
break;
case 's':
g_string_append_printf (str, "%d", size);
p++;
break;
case '%':
g_string_append_c (str, '%');
p++;
break;
case 0:
default:
break;
}
last = p;
}
g_string_append (str, last);
return g_string_free (str, FALSE);
}
static char **
expand_thumbnailing_script (const char *script,
const int size,
const char *inuri,
const char *outfile,
GError **error)
{
GPtrArray *array;
char **script_elems;
guint i;
gboolean got_in, got_out;
if (!g_shell_parse_argv (script, NULL, &script_elems, error))
return NULL;
array = g_ptr_array_new_with_free_func (g_free);
got_in = got_out = FALSE;
for (i = 0; script_elems[i] != NULL; i++)
{
char *expanded;
expanded = expand_thumbnailing_elem (script_elems[i],
size,
inuri,
outfile,
&got_in,
&got_out);
g_ptr_array_add (array, expanded);
}
if (!got_in)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Input file could not be set");
goto bail;
}
else if (!got_out)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Output file could not be set");
goto bail;
}
g_ptr_array_add (array, NULL);
return (char **) g_ptr_array_free (array, FALSE);
bail:
g_ptr_array_free (array, TRUE);
return NULL;
}
static GdkPixbuf *
get_preview_thumbnail (const char *uri,
int size)
......@@ -1099,6 +988,30 @@ get_preview_thumbnail (const char *uri,
return pixbuf;
}
static GdkPixbuf *
pixbuf_new_from_bytes (GBytes *bytes,
GError **error)
{
g_autoptr(GdkPixbufLoader) loader = NULL;
loader = gdk_pixbuf_loader_new_with_mime_type ("image/png", error);
if (!loader)
return NULL;
if (!gdk_pixbuf_loader_write (loader,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes),
error))
{
return NULL;
}
if (!gdk_pixbuf_loader_close (loader, error))
return NULL;
return g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
}
/**
* gnome_desktop_thumbnail_factory_generate_thumbnail:
* @factory: a #GnomeDesktopThumbnailFactory
......@@ -1122,8 +1035,6 @@ gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory
GdkPixbuf *pixbuf;
char *script;
int size;
int exit_status;
char *tmpname;
g_return_val_if_fail (uri != NULL, NULL);
g_return_val_if_fail (mime_type != NULL, NULL);
......@@ -1154,42 +1065,18 @@ gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory
if (script)
{
int fd;
fd = g_file_open_tmp (".gnome_desktop_thumbnail.XXXXXX", &tmpname, NULL);
if (fd != -1)
{
char **expanded_script;
GError *error = NULL;
close (fd);
expanded_script = expand_thumbnailing_script (script, size, uri, tmpname, &error);
if (expanded_script == NULL)
{
g_warning ("Failed to expand script '%s': %s", script, error->message);
g_error_free (error);
}
else
{
gboolean ret;
GBytes *data;
ret = g_spawn_sync (NULL, expanded_script, NULL, G_SPAWN_SEARCH_PATH,
NULL, NULL, NULL, NULL, &exit_status, NULL);
if (ret && exit_status == 0)
pixbuf = gdk_pixbuf_new_from_file (tmpname, NULL);
g_strfreev (expanded_script);
}
g_unlink (tmpname);
g_free (tmpname);
}
g_free (script);
data = gnome_desktop_thumbnail_script_exec (script, size, uri, NULL);
if (data)
{
pixbuf = pixbuf_new_from_bytes (data, NULL);
g_bytes_unref (data);
}
}
g_free (script);
return pixbuf;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment