diff options
Diffstat (limited to 'src/hotplug-sniffer')
-rw-r--r-- | src/hotplug-sniffer/hotplug-mimetypes.h | 141 | ||||
-rw-r--r-- | src/hotplug-sniffer/hotplug-sniffer.c | 298 | ||||
-rw-r--r-- | src/hotplug-sniffer/meson.build | 22 | ||||
-rw-r--r-- | src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service.in | 3 | ||||
-rw-r--r-- | src/hotplug-sniffer/shell-mime-sniffer.c | 590 | ||||
-rw-r--r-- | src/hotplug-sniffer/shell-mime-sniffer.h | 46 |
6 files changed, 1100 insertions, 0 deletions
diff --git a/src/hotplug-sniffer/hotplug-mimetypes.h b/src/hotplug-sniffer/hotplug-mimetypes.h new file mode 100644 index 0000000..b034020 --- /dev/null +++ b/src/hotplug-sniffer/hotplug-mimetypes.h @@ -0,0 +1,141 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#ifndef __HOTPLUG_MIMETYPES_H__ +#define __HOTPLUG_MIMETYPES_H__ + +#include <glib.h> + +G_GNUC_UNUSED static const gchar *docs_mimetypes[] = { + "application/vnd.oasis.opendocument.text", + "application/vnd.oasis.opendocument.presentation", + "application/vnd.oasis.opendocument.spreadsheet", + "application/msword", + "application/vnd.ms-excel", + "application/vnd.ms-powerpoint", + "application/rtf", + "application/pdf", + "application/x-bzpdf", + "application/x-gzpdf", + "application/x-xzpdf", + "application/postscript", + "application/x-bzpostscript", + "application/x-gzpostscript", + "image/x-eps", + "image/x-bzeps", + "image/x-gzeps", + "application/x-dvi", + "application/x-bzdvi", + "application/x-gzdvi", + "image/vnd.djvu", + "application/x-cbr", + "application/x-cbz", + "application/x-cb7", + "application/x-cbt", + NULL +}; + +G_GNUC_UNUSED static const gchar *video_mimetypes[] = { + "application/mxf", + "application/ogg", + "application/ram", + "application/sdp", + "application/vnd.ms-wpl", + "application/vnd.rn-realmedia", + "application/x-extension-m4a", + "application/x-extension-mp4", + "application/x-flash-video", + "application/x-matroska", + "application/x-netshow-channel", + "application/x-ogg", + "application/x-quicktimeplayer", + "application/x-shorten", + "image/vnd.rn-realpix", + "image/x-pict", + "misc/ultravox", + "text/x-google-video-pointer", + "video/3gpp", + "video/dv", + "video/fli", + "video/flv", + "video/mp2t", + "video/mp4", + "video/mp4v-es", + "video/mpeg", + "video/msvideo", + "video/ogg", + "video/quicktime", + "video/vivo", + "video/vnd.divx", + "video/vnd.rn-realvideo", + "video/vnd.vivo", + "video/webm", + "video/x-anim", + "video/x-avi", + "video/x-flc", + "video/x-fli", + "video/x-flic", + "video/x-flv", + "video/x-m4v", + "video/x-matroska", + "video/x-mpeg", + "video/x-ms-asf", + "video/x-ms-asx", + "video/x-msvideo", + "video/x-ms-wm", + "video/x-ms-wmv", + "video/x-ms-wmx", + "video/x-ms-wvx", + "video/x-nsv", + "video/x-ogm+ogg", + "video/x-theora+ogg", + "video/x-totem-stream", + NULL +}; + +G_GNUC_UNUSED static const gchar *audio_mimetypes[] = { + "audio/3gpp", + "audio/ac3", + "audio/AMR", + "audio/AMR-WB", + "audio/basic", + "audio/flac", + "audio/midi", + "audio/mp2", + "audio/mp4", + "audio/mpeg", + "audio/ogg", + "audio/prs.sid", + "audio/vnd.rn-realaudio", + "audio/x-aiff", + "audio/x-ape", + "audio/x-flac", + "audio/x-gsm", + "audio/x-it", + "audio/x-m4a", + "audio/x-matroska", + "audio/x-mod", + "audio/x-mp3", + "audio/x-mpeg", + "audio/x-ms-asf", + "audio/x-ms-asx", + "audio/x-ms-wax", + "audio/x-ms-wma", + "audio/x-musepack", + "audio/x-pn-aiff", + "audio/x-pn-au", + "audio/x-pn-wav", + "audio/x-pn-windows-acm", + "audio/x-realaudio", + "audio/x-real-audio", + "audio/x-sbc", + "audio/x-speex", + "audio/x-tta", + "audio/x-wav", + "audio/x-wavpack", + "audio/x-vorbis", + "audio/x-vorbis+ogg", + "audio/x-xm", + NULL +}; + +#endif /* __HOTPLUG_MIMETYPES_H__ */ diff --git a/src/hotplug-sniffer/hotplug-sniffer.c b/src/hotplug-sniffer/hotplug-sniffer.c new file mode 100644 index 0000000..fd087fd --- /dev/null +++ b/src/hotplug-sniffer/hotplug-sniffer.c @@ -0,0 +1,298 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Authors: David Zeuthen <davidz@redhat.com> + * Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#include "shell-mime-sniffer.h" +#include "hotplug-mimetypes.h" + +/* Set the environment variable HOTPLUG_SNIFFER_DEBUG to show debug */ +static void print_debug (const gchar *str, ...); + +#define BUS_NAME "org.gnome.Shell.HotplugSniffer" +#define AUTOQUIT_TIMEOUT 5 + +static const gchar introspection_xml[] = + "<node>" + " <interface name='org.gnome.Shell.HotplugSniffer'>" + " <method name='SniffURI'>" + " <arg type='s' name='uri' direction='in'/>" + " <arg type='as' name='content_types' direction='out'/>" + " </method>" + " </interface>" + "</node>"; + +static GDBusNodeInfo *introspection_data = NULL; +static GMainLoop *loop = NULL; +static guint autoquit_id = 0; + +static gboolean +autoquit_timeout_cb (gpointer _unused) +{ + print_debug ("Timeout reached, quitting..."); + + autoquit_id = 0; + g_main_loop_quit (loop); + + return FALSE; +} + +static void +ensure_autoquit_off (void) +{ + if (g_getenv ("HOTPLUG_SNIFFER_PERSIST") != NULL) + return; + + g_clear_handle_id (&autoquit_id, g_source_remove); +} + +static void +ensure_autoquit_on (void) +{ + if (g_getenv ("HOTPLUG_SNIFFER_PERSIST") != NULL) + return; + + autoquit_id = + g_timeout_add_seconds (AUTOQUIT_TIMEOUT, + autoquit_timeout_cb, NULL); + g_source_set_name_by_id (autoquit_id, "[gnome-shell] autoquit_timeout_cb"); +} + +typedef struct { + GVariant *parameters; + GDBusMethodInvocation *invocation; +} InvocationData; + +static InvocationData * +invocation_data_new (GVariant *params, + GDBusMethodInvocation *invocation) +{ + InvocationData *ret; + + ret = g_slice_new0 (InvocationData); + ret->parameters = g_variant_ref (params); + ret->invocation = g_object_ref (invocation); + + return ret; +} + +static void +invocation_data_free (InvocationData *data) +{ + g_variant_unref (data->parameters); + g_clear_object (&data->invocation); + + g_slice_free (InvocationData, data); +} + +static void +sniff_async_ready_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + InvocationData *data = user_data; + gchar **types; + GError *error = NULL; + + types = shell_mime_sniffer_sniff_finish (SHELL_MIME_SNIFFER (source), + res, &error); + + if (error != NULL) + { + g_dbus_method_invocation_return_gerror (data->invocation, error); + g_error_free (error); + goto out; + } + + g_dbus_method_invocation_return_value (data->invocation, + g_variant_new ("(^as)", types)); + g_strfreev (types); + + out: + invocation_data_free (data); + ensure_autoquit_on (); +} + +static void +handle_sniff_uri (InvocationData *data) +{ + ShellMimeSniffer *sniffer; + const gchar *uri; + GFile *file; + + ensure_autoquit_off (); + + g_variant_get (data->parameters, + "(&s)", &uri, + NULL); + file = g_file_new_for_uri (uri); + + print_debug ("Initiating sniff for uri %s", uri); + + sniffer = shell_mime_sniffer_new (file); + shell_mime_sniffer_sniff_async (sniffer, + sniff_async_ready_cb, + data); + + g_object_unref (sniffer); + g_object_unref (file); +} + +static void +handle_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + InvocationData *data; + + data = invocation_data_new (parameters, invocation); + + if (g_strcmp0 (method_name, "SniffURI") == 0) + handle_sniff_uri (data); + else + g_assert_not_reached (); +} + +static const GDBusInterfaceVTable interface_vtable = +{ + handle_method_call, + NULL, /* get_property */ + NULL, /* set_property */ +}; + +static void +on_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GError *error = NULL; + + print_debug ("Connected to the session bus: %s", name); + + g_dbus_connection_register_object (connection, + "/org/gnome/Shell/HotplugSniffer", + introspection_data->interfaces[0], + &interface_vtable, + NULL, + NULL, + &error); + + if (error != NULL) + { + g_printerr ("Error exporting object on the session bus: %s", + error->message); + g_error_free (error); + + _exit(1); + } + + print_debug ("Object exported on the session bus"); +} + +static void +on_name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + print_debug ("Lost bus name: %s, exiting", name); + + g_main_loop_quit (loop); +} + +static void +on_name_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + print_debug ("Acquired bus name: %s", name); +} + +int +main (int argc, + char **argv) +{ + guint name_owner_id; + + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (introspection_data != NULL); + + ensure_autoquit_on (); + loop = g_main_loop_new (NULL, FALSE); + + name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + BUS_NAME, 0, + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + g_main_loop_run (loop); + + if (name_owner_id != 0) + g_bus_unown_name (name_owner_id); + + if (loop != NULL) + g_main_loop_unref (loop); + + return 0; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void __attribute__((format(printf, 1, 0))) +print_debug (const gchar *format, ...) +{ + g_autofree char *s = NULL; + g_autofree char *timestamp = NULL; + va_list ap; + g_autoptr (GDateTime) now = NULL; + static volatile gsize once_init_value = 0; + static gboolean show_debug = FALSE; + static guint pid = 0; + + if (g_once_init_enter (&once_init_value)) + { + show_debug = (g_getenv ("HOTPLUG_SNIFFER_DEBUG") != NULL); + pid = getpid (); + g_once_init_leave (&once_init_value, 1); + } + + if (!show_debug) + goto out; + + now = g_date_time_new_now_local (); + timestamp = g_date_time_format (now, "%H:%M:%S"); + + va_start (ap, format); + s = g_strdup_vprintf (format, ap); + va_end (ap); + + g_print ("gnome-shell-hotplug-sniffer[%d]: %s.%03d: %s\n", + pid, timestamp, g_date_time_get_microsecond (now), s); + out: + ; +} + diff --git a/src/hotplug-sniffer/meson.build b/src/hotplug-sniffer/meson.build new file mode 100644 index 0000000..4a777e5 --- /dev/null +++ b/src/hotplug-sniffer/meson.build @@ -0,0 +1,22 @@ +hotplug_sources = [ + 'hotplug-mimetypes.h', + 'shell-mime-sniffer.h', + 'shell-mime-sniffer.c', + 'hotplug-sniffer.c' +] + +executable('gnome-shell-hotplug-sniffer', hotplug_sources, + dependencies: [gio_dep, gdk_pixbuf_dep], + include_directories: include_directories('../..'), + install_dir: libexecdir, + install: true +) + +service_file = 'org.gnome.Shell.HotplugSniffer.service' + +configure_file( + input: service_file + '.in', + output: service_file, + configuration: service_data, + install_dir: servicedir +) diff --git a/src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service.in b/src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service.in new file mode 100644 index 0000000..b14cea9 --- /dev/null +++ b/src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.gnome.Shell.HotplugSniffer +Exec=@libexecdir@/gnome-shell-hotplug-sniffer diff --git a/src/hotplug-sniffer/shell-mime-sniffer.c b/src/hotplug-sniffer/shell-mime-sniffer.c new file mode 100644 index 0000000..7a1c1fe --- /dev/null +++ b/src/hotplug-sniffer/shell-mime-sniffer.c @@ -0,0 +1,590 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 1999, 2000, 2001 Eazel, Inc. + * Copyright (C) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + * The code for crawling the directory hierarchy is based on + * nautilus/libnautilus-private/nautilus-directory-async.c, with + * the following copyright and author: + * + * Copyright (C) 1999, 2000, 2001 Eazel, Inc. + * Author: Darin Adler <darin@bentspoon.com> + * + */ + +#include "shell-mime-sniffer.h" +#include "hotplug-mimetypes.h" + +#include <glib/gi18n.h> + +#include <gdk-pixbuf/gdk-pixbuf.h> + +#define LOADER_ATTRS \ + G_FILE_ATTRIBUTE_STANDARD_TYPE "," \ + G_FILE_ATTRIBUTE_STANDARD_NAME "," \ + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," + +#define WATCHDOG_TIMEOUT 1500 +#define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100 +#define HIGH_SCORE_RATIO 0.10 + +enum { + PROP_FILE = 1, + NUM_PROPERTIES +}; + +static GHashTable *image_type_table = NULL; +static GHashTable *audio_type_table = NULL; +static GHashTable *video_type_table = NULL; +static GHashTable *docs_type_table = NULL; + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +typedef struct { + ShellMimeSniffer *self; + + GFile *file; + GFileEnumerator *enumerator; + GList *deep_count_subdirectories; + + gint audio_count; + gint image_count; + gint document_count; + gint video_count; + + gint total_items; +} DeepCountState; + +typedef struct _ShellMimeSnifferPrivate ShellMimeSnifferPrivate; + +struct _ShellMimeSniffer +{ + GObject parent_instance; + + ShellMimeSnifferPrivate *priv; +}; + +struct _ShellMimeSnifferPrivate { + GFile *file; + + GCancellable *cancellable; + guint watchdog_id; + + GTask *task; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (ShellMimeSniffer, shell_mime_sniffer, G_TYPE_OBJECT); + +static void deep_count_load (DeepCountState *state, + GFile *file); + +static void +init_mimetypes (void) +{ + static gsize once_init = 0; + + if (g_once_init_enter (&once_init)) + { + GSList *formats, *l; + GdkPixbufFormat *format; + gchar **types; + gint idx; + + image_type_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + video_type_table = g_hash_table_new (g_str_hash, g_str_equal); + audio_type_table = g_hash_table_new (g_str_hash, g_str_equal); + docs_type_table = g_hash_table_new (g_str_hash, g_str_equal); + + formats = gdk_pixbuf_get_formats (); + + for (l = formats; l != NULL; l = l->next) + { + format = l->data; + types = gdk_pixbuf_format_get_mime_types (format); + + for (idx = 0; types[idx] != NULL; idx++) + g_hash_table_insert (image_type_table, g_strdup (types[idx]), GINT_TO_POINTER (1)); + + g_strfreev (types); + } + + g_slist_free (formats); + + for (idx = 0; audio_mimetypes[idx] != NULL; idx++) + g_hash_table_insert (audio_type_table, (gpointer) audio_mimetypes[idx], GINT_TO_POINTER (1)); + + for (idx = 0; video_mimetypes[idx] != NULL; idx++) + g_hash_table_insert (video_type_table, (gpointer) video_mimetypes[idx], GINT_TO_POINTER (1)); + + for (idx = 0; docs_mimetypes[idx] != NULL; idx++) + g_hash_table_insert (docs_type_table, (gpointer) docs_mimetypes[idx], GINT_TO_POINTER (1)); + + g_once_init_leave (&once_init, 1); + } +} + +static void +add_content_type_to_cache (DeepCountState *state, + const gchar *content_type) +{ + gboolean matched = TRUE; + + if (g_hash_table_lookup (image_type_table, content_type)) + state->image_count++; + else if (g_hash_table_lookup (video_type_table, content_type)) + state->video_count++; + else if (g_hash_table_lookup (docs_type_table, content_type)) + state->document_count++; + else if (g_hash_table_lookup (audio_type_table, content_type)) + state->audio_count++; + else + matched = FALSE; + + if (matched) + state->total_items++; +} + +typedef struct { + const gchar *type; + gdouble ratio; +} SniffedResult; + +static gint +results_cmp_func (gconstpointer a, + gconstpointer b) +{ + const SniffedResult *sniffed_a = a; + const SniffedResult *sniffed_b = b; + + if (sniffed_a->ratio < sniffed_b->ratio) + return 1; + + if (sniffed_a->ratio > sniffed_b->ratio) + return -1; + + return 0; +} + +static void +prepare_async_result (DeepCountState *state) +{ + ShellMimeSniffer *self = state->self; + GArray *results; + GPtrArray *sniffed_mime; + SniffedResult result; + char **mimes; + + sniffed_mime = g_ptr_array_new (); + results = g_array_new (TRUE, TRUE, sizeof (SniffedResult)); + + if (state->total_items == 0) + goto out; + + result.type = "x-content/video"; + result.ratio = (gdouble) state->video_count / (gdouble) state->total_items; + g_array_append_val (results, result); + + result.type = "x-content/audio"; + result.ratio = (gdouble) state->audio_count / (gdouble) state->total_items; + g_array_append_val (results, result); + + result.type = "x-content/pictures"; + result.ratio = (gdouble) state->image_count / (gdouble) state->total_items; + g_array_append_val (results, result); + + result.type = "x-content/documents"; + result.ratio = (gdouble) state->document_count / (gdouble) state->total_items; + g_array_append_val (results, result); + + g_array_sort (results, results_cmp_func); + + result = g_array_index (results, SniffedResult, 0); + g_ptr_array_add (sniffed_mime, g_strdup (result.type)); + + /* if other types score high in ratio, add them, up to three */ + result = g_array_index (results, SniffedResult, 1); + if (result.ratio < HIGH_SCORE_RATIO) + goto out; + g_ptr_array_add (sniffed_mime, g_strdup (result.type)); + + result = g_array_index (results, SniffedResult, 2); + if (result.ratio < HIGH_SCORE_RATIO) + goto out; + g_ptr_array_add (sniffed_mime, g_strdup (result.type)); + + out: + g_ptr_array_add (sniffed_mime, NULL); + mimes = (gchar **) g_ptr_array_free (sniffed_mime, FALSE); + + g_array_free (results, TRUE); + g_task_return_pointer (self->priv->task, mimes, (GDestroyNotify)g_strfreev); +} + +/* adapted from nautilus/libnautilus-private/nautilus-directory-async.c */ +static void +deep_count_one (DeepCountState *state, + GFileInfo *info) +{ + GFile *subdir; + const char *content_type; + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) + { + /* record the fact that we have to descend into this directory */ + subdir = g_file_get_child (state->file, g_file_info_get_name (info)); + state->deep_count_subdirectories = + g_list_append (state->deep_count_subdirectories, subdir); + } + else + { + content_type = g_file_info_get_content_type (info); + + if (content_type) + add_content_type_to_cache (state, content_type); + } +} + +static void +deep_count_finish (DeepCountState *state) +{ + prepare_async_result (state); + + if (state->enumerator) + { + if (!g_file_enumerator_is_closed (state->enumerator)) + g_file_enumerator_close_async (state->enumerator, + 0, NULL, NULL, NULL); + + g_object_unref (state->enumerator); + } + + g_cancellable_reset (state->self->priv->cancellable); + g_clear_object (&state->file); + + g_list_free_full (state->deep_count_subdirectories, g_object_unref); + + g_free (state); +} + +static void +deep_count_next_dir (DeepCountState *state) +{ + GFile *new_file; + + g_clear_object (&state->file); + + if (state->deep_count_subdirectories != NULL) + { + /* Work on a new directory. */ + new_file = state->deep_count_subdirectories->data; + state->deep_count_subdirectories = + g_list_remove (state->deep_count_subdirectories, new_file); + + deep_count_load (state, new_file); + g_object_unref (new_file); + } + else + { + deep_count_finish (state); + } +} + +static void +deep_count_more_files_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + DeepCountState *state; + GList *files, *l; + GFileInfo *info; + + state = user_data; + + if (g_cancellable_is_cancelled (state->self->priv->cancellable)) + { + deep_count_finish (state); + return; + } + + files = g_file_enumerator_next_files_finish (state->enumerator, + res, NULL); + + for (l = files; l != NULL; l = l->next) + { + info = l->data; + deep_count_one (state, info); + g_object_unref (info); + } + + if (files == NULL) + { + g_file_enumerator_close_async (state->enumerator, 0, NULL, NULL, NULL); + g_object_unref (state->enumerator); + state->enumerator = NULL; + + deep_count_next_dir (state); + } + else + { + g_file_enumerator_next_files_async (state->enumerator, + DIRECTORY_LOAD_ITEMS_PER_CALLBACK, + G_PRIORITY_LOW, + state->self->priv->cancellable, + deep_count_more_files_callback, + state); + } + + g_list_free (files); +} + +static void +deep_count_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + DeepCountState *state; + GFileEnumerator *enumerator; + + state = user_data; + + if (g_cancellable_is_cancelled (state->self->priv->cancellable)) + { + deep_count_finish (state); + return; + } + + enumerator = g_file_enumerate_children_finish (G_FILE (source_object), + res, NULL); + + if (enumerator == NULL) + { + deep_count_next_dir (state); + } + else + { + state->enumerator = enumerator; + g_file_enumerator_next_files_async (state->enumerator, + DIRECTORY_LOAD_ITEMS_PER_CALLBACK, + G_PRIORITY_LOW, + state->self->priv->cancellable, + deep_count_more_files_callback, + state); + } +} + +static void +deep_count_load (DeepCountState *state, + GFile *file) +{ + state->file = g_object_ref (file); + + g_file_enumerate_children_async (state->file, + LOADER_ATTRS, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */ + G_PRIORITY_LOW, /* prio */ + state->self->priv->cancellable, + deep_count_callback, + state); +} + +static void +deep_count_start (ShellMimeSniffer *self) +{ + DeepCountState *state; + + state = g_new0 (DeepCountState, 1); + state->self = self; + + deep_count_load (state, self->priv->file); +} + +static void +query_info_async_ready_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GFileInfo *info; + GError *error = NULL; + ShellMimeSniffer *self = user_data; + + info = g_file_query_info_finish (G_FILE (source), + res, &error); + + if (error != NULL) + { + g_task_return_error (self->priv->task, error); + + return; + } + + if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY) + { + g_task_return_new_error (self->priv->task, + G_IO_ERROR, + G_IO_ERROR_NOT_DIRECTORY, + "Not a directory"); + + return; + } + + deep_count_start (self); +} + +static gboolean +watchdog_timeout_reached_cb (gpointer user_data) +{ + ShellMimeSniffer *self = user_data; + + self->priv->watchdog_id = 0; + g_cancellable_cancel (self->priv->cancellable); + + return FALSE; +} + +static void +start_loading_file (ShellMimeSniffer *self) +{ + g_file_query_info_async (self->priv->file, + LOADER_ATTRS, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + self->priv->cancellable, + query_info_async_ready_cb, + self); +} + +static void +shell_mime_sniffer_set_file (ShellMimeSniffer *self, + GFile *file) +{ + g_clear_object (&self->priv->file); + self->priv->file = g_object_ref (file); +} + +static void +shell_mime_sniffer_dispose (GObject *object) +{ + ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object); + + g_clear_object (&self->priv->file); + g_clear_object (&self->priv->cancellable); + g_clear_object (&self->priv->task); + + g_clear_handle_id (&self->priv->watchdog_id, g_source_remove); + + G_OBJECT_CLASS (shell_mime_sniffer_parent_class)->dispose (object); +} + +static void +shell_mime_sniffer_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object); + + switch (prop_id) { + case PROP_FILE: + g_value_set_object (value, self->priv->file); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +shell_mime_sniffer_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object); + + switch (prop_id) { + case PROP_FILE: + shell_mime_sniffer_set_file (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +shell_mime_sniffer_class_init (ShellMimeSnifferClass *klass) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (klass); + oclass->dispose = shell_mime_sniffer_dispose; + oclass->get_property = shell_mime_sniffer_get_property; + oclass->set_property = shell_mime_sniffer_set_property; + + properties[PROP_FILE] = + g_param_spec_object ("file", + "File", + "The loaded file", + G_TYPE_FILE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); +} + +static void +shell_mime_sniffer_init (ShellMimeSniffer *self) +{ + self->priv = shell_mime_sniffer_get_instance_private (self); + init_mimetypes (); +} + +ShellMimeSniffer * +shell_mime_sniffer_new (GFile *file) +{ + return g_object_new (SHELL_TYPE_MIME_SNIFFER, + "file", file, + NULL); +} + +void +shell_mime_sniffer_sniff_async (ShellMimeSniffer *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_assert (self->priv->watchdog_id == 0); + g_assert (self->priv->task == NULL); + + self->priv->cancellable = g_cancellable_new (); + self->priv->task = g_task_new (self, self->priv->cancellable, + callback, user_data); + + self->priv->watchdog_id = + g_timeout_add (WATCHDOG_TIMEOUT, + watchdog_timeout_reached_cb, self); + g_source_set_name_by_id (self->priv->watchdog_id, "[gnome-shell] watchdog_timeout_reached_cb"); + + start_loading_file (self); +} + +gchar ** +shell_mime_sniffer_sniff_finish (ShellMimeSniffer *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (self->priv->task, error); +} diff --git a/src/hotplug-sniffer/shell-mime-sniffer.h b/src/hotplug-sniffer/shell-mime-sniffer.h new file mode 100644 index 0000000..3936eef --- /dev/null +++ b/src/hotplug-sniffer/shell-mime-sniffer.h @@ -0,0 +1,46 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#ifndef __SHELL_MIME_SNIFFER_H__ +#define __SHELL_MIME_SNIFFER_H__ + +#include <glib-object.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define SHELL_TYPE_MIME_SNIFFER (shell_mime_sniffer_get_type ()) +G_DECLARE_FINAL_TYPE (ShellMimeSniffer, shell_mime_sniffer, + SHELL, MIME_SNIFFER, GObject) + +ShellMimeSniffer *shell_mime_sniffer_new (GFile *file); + +void shell_mime_sniffer_sniff_async (ShellMimeSniffer *self, + GAsyncReadyCallback callback, + gpointer user_data); + +gchar ** shell_mime_sniffer_sniff_finish (ShellMimeSniffer *self, + GAsyncResult *res, + GError **error); + +G_END_DECLS + +#endif /* __SHELL_MIME_SNIFFER_H__ */ |