summaryrefslogtreecommitdiffstats
path: root/src/nautilus-module.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nautilus-module.c')
-rw-r--r--src/nautilus-module.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/src/nautilus-module.c b/src/nautilus-module.c
new file mode 100644
index 0000000..ced810e
--- /dev/null
+++ b/src/nautilus-module.c
@@ -0,0 +1,332 @@
+/*
+ * nautilus-module.h - Interface to nautilus extensions
+ *
+ * Copyright (C) 2003 Novell, Inc.
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Dave Camp <dave@ximian.com>
+ *
+ */
+
+#include <config.h>
+#include "nautilus-module.h"
+
+#include <eel/eel-debug.h>
+#include <gmodule.h>
+
+#define NAUTILUS_TYPE_MODULE (nautilus_module_get_type ())
+#define NAUTILUS_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_MODULE, NautilusModule))
+#define NAUTILUS_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_MODULE, NautilusModule))
+#define NAUTILUS_IS_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_MODULE))
+#define NAUTILUS_IS_MODULE_CLASS(klass) (G_TYPE_CLASS_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_MODULE))
+
+typedef struct _NautilusModule NautilusModule;
+typedef struct _NautilusModuleClass NautilusModuleClass;
+
+struct _NautilusModule
+{
+ GTypeModule parent;
+
+ GModule *library;
+
+ char *path;
+
+ void (*initialize) (GTypeModule *module);
+ void (*shutdown) (void);
+
+ void (*list_types) (const GType **types,
+ int *num_types);
+};
+
+struct _NautilusModuleClass
+{
+ GTypeModuleClass parent;
+};
+
+static GList *module_objects = NULL;
+static GStrv installed_module_names = NULL;
+
+static GType nautilus_module_get_type (void);
+
+G_DEFINE_TYPE (NautilusModule, nautilus_module, G_TYPE_TYPE_MODULE);
+
+static gboolean
+module_pulls_in_orbit (GModule *module)
+{
+ gpointer symbol;
+ gboolean res;
+
+ res = g_module_symbol (module, "ORBit_realloc_tcval", &symbol);
+
+ return res;
+}
+
+static gboolean
+nautilus_module_load (GTypeModule *gmodule)
+{
+ NautilusModule *module;
+
+ module = NAUTILUS_MODULE (gmodule);
+
+ module->library = g_module_open (module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+
+ if (!module->library)
+ {
+ g_warning ("%s", g_module_error ());
+ return FALSE;
+ }
+
+ /* ORBit installs atexit() handlers, which would get unloaded together
+ * with the module now that the main process doesn't depend on GConf anymore,
+ * causing nautilus to sefgault at exit.
+ * If we detect that an extension would pull in ORBit, we make the
+ * module resident to prevent that.
+ */
+ if (module_pulls_in_orbit (module->library))
+ {
+ g_module_make_resident (module->library);
+ }
+
+ if (!g_module_symbol (module->library,
+ "nautilus_module_initialize",
+ (gpointer *) &module->initialize) ||
+ !g_module_symbol (module->library,
+ "nautilus_module_shutdown",
+ (gpointer *) &module->shutdown) ||
+ !g_module_symbol (module->library,
+ "nautilus_module_list_types",
+ (gpointer *) &module->list_types))
+ {
+ g_warning ("%s", g_module_error ());
+ g_module_close (module->library);
+
+ return FALSE;
+ }
+
+ module->initialize (gmodule);
+
+ return TRUE;
+}
+
+static void
+nautilus_module_unload (GTypeModule *gmodule)
+{
+ NautilusModule *module;
+
+ module = NAUTILUS_MODULE (gmodule);
+
+ module->shutdown ();
+
+ g_module_close (module->library);
+
+ module->initialize = NULL;
+ module->shutdown = NULL;
+ module->list_types = NULL;
+}
+
+static void
+nautilus_module_finalize (GObject *object)
+{
+ NautilusModule *module;
+
+ module = NAUTILUS_MODULE (object);
+
+ g_free (module->path);
+
+ G_OBJECT_CLASS (nautilus_module_parent_class)->finalize (object);
+}
+
+static void
+nautilus_module_init (NautilusModule *module)
+{
+}
+
+static void
+nautilus_module_class_init (NautilusModuleClass *class)
+{
+ G_OBJECT_CLASS (class)->finalize = nautilus_module_finalize;
+ G_TYPE_MODULE_CLASS (class)->load = nautilus_module_load;
+ G_TYPE_MODULE_CLASS (class)->unload = nautilus_module_unload;
+}
+
+static void
+module_object_weak_notify (gpointer user_data,
+ GObject *object)
+{
+ module_objects = g_list_remove (module_objects, object);
+}
+
+static void
+add_module_objects (NautilusModule *module)
+{
+ const GType *types;
+ int num_types;
+ int i;
+
+ module->list_types (&types, &num_types);
+
+ for (i = 0; i < num_types; i++)
+ {
+ if (types[i] == 0) /* Work around broken extensions */
+ {
+ break;
+ }
+ nautilus_module_add_type (types[i]);
+ }
+}
+
+static NautilusModule *
+nautilus_module_load_file (const char *filename,
+ GStrvBuilder *installed_module_name_builder)
+{
+ NautilusModule *module;
+
+ module = g_object_new (NAUTILUS_TYPE_MODULE, NULL);
+ module->path = g_strdup (filename);
+
+ if (g_type_module_use (G_TYPE_MODULE (module)))
+ {
+ add_module_objects (module);
+ g_type_module_unuse (G_TYPE_MODULE (module));
+ g_strv_builder_add (installed_module_name_builder, filename);
+ return module;
+ }
+ else
+ {
+ g_object_unref (module);
+ return NULL;
+ }
+}
+
+char *
+nautilus_module_get_installed_module_names (void)
+{
+ return g_strjoinv ("\n", installed_module_names);
+}
+
+static void
+load_module_dir (const char *dirname)
+{
+ GDir *dir;
+
+ g_autoptr (GStrvBuilder) installed_module_name_builder = g_strv_builder_new ();
+ dir = g_dir_open (dirname, 0, NULL);
+
+ if (dir)
+ {
+ const char *name;
+
+ while ((name = g_dir_read_name (dir)))
+ {
+ if (g_str_has_suffix (name, "." G_MODULE_SUFFIX))
+ {
+ char *filename;
+
+ filename = g_build_filename (dirname,
+ name,
+ NULL);
+ nautilus_module_load_file (filename, installed_module_name_builder);
+ g_free (filename);
+ }
+ }
+
+ g_dir_close (dir);
+ }
+
+ installed_module_names = g_strv_builder_end (installed_module_name_builder);
+}
+
+static void
+free_module_objects (void)
+{
+ GList *l, *next;
+
+ for (l = module_objects; l != NULL; l = next)
+ {
+ next = l->next;
+ g_object_unref (l->data);
+ }
+
+ g_list_free (module_objects);
+ g_strfreev (installed_module_names);
+}
+
+void
+nautilus_module_setup (void)
+{
+ static gboolean initialized = FALSE;
+ const gchar *disable_plugins;
+
+ disable_plugins = g_getenv ("NAUTILUS_DISABLE_PLUGINS");
+ if (g_strcmp0 (disable_plugins, "TRUE") == 0)
+ {
+ /* Troublingshooting envvar is set to disable extensions */
+ return;
+ }
+
+ if (!initialized)
+ {
+ initialized = TRUE;
+
+ load_module_dir (NAUTILUS_EXTENSIONDIR);
+
+ eel_debug_call_at_shutdown (free_module_objects);
+ }
+}
+
+GList *
+nautilus_module_get_extensions_for_type (GType type)
+{
+ GList *l;
+ GList *ret = NULL;
+
+ for (l = module_objects; l != NULL; l = l->next)
+ {
+ if (G_TYPE_CHECK_INSTANCE_TYPE (G_OBJECT (l->data),
+ type))
+ {
+ g_object_ref (l->data);
+ ret = g_list_prepend (ret, l->data);
+ }
+ }
+
+ return ret;
+}
+
+void
+nautilus_module_extension_list_free (GList *extensions)
+{
+ GList *l, *next;
+
+ for (l = extensions; l != NULL; l = next)
+ {
+ next = l->next;
+ g_object_unref (l->data);
+ }
+ g_list_free (extensions);
+}
+
+void
+nautilus_module_add_type (GType type)
+{
+ GObject *object;
+
+ object = g_object_new (type, NULL);
+ g_object_weak_ref (object,
+ (GWeakNotify) module_object_weak_notify,
+ NULL);
+
+ module_objects = g_list_prepend (module_objects, object);
+}