1 /*
2  *  caja-module.h - Interface to caja extensions
3  *
4  *  Copyright (C) 2003 Novell, Inc.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public
17  *  License along with this library; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  *  Author: Dave Camp <dave@ximian.com>
21  *
22  */
23 
24 #include <config.h>
25 #include <gmodule.h>
26 
27 #include <eel/eel-gtk-macros.h>
28 #include <eel/eel-debug.h>
29 
30 #include "caja-module.h"
31 #include "caja-extensions.h"
32 
33 #define CAJA_TYPE_MODULE    	(caja_module_get_type ())
34 #define CAJA_MODULE(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), CAJA_TYPE_MODULE, CajaModule))
35 
36 typedef struct _CajaModule        CajaModule;
37 typedef struct _CajaModuleClass   CajaModuleClass;
38 
39 struct _CajaModule
40 {
41     GTypeModule parent;
42 
43     GModule *library;
44 
45     char *path;
46 
47     void (*initialize) (GTypeModule  *module);
48     void (*shutdown)   (void);
49 
50     void (*list_types) (const GType **types,
51                         int          *num_types);
52     void (*list_pyfiles) (GList     **pyfiles);
53 
54 };
55 
56 struct _CajaModuleClass
57 {
58     GTypeModuleClass parent;
59 };
60 
61 static GList *module_objects = NULL;
62 
63 static GType caja_module_get_type (void);
64 
65 G_DEFINE_TYPE (CajaModule, caja_module, G_TYPE_TYPE_MODULE);
66 #define parent_class caja_module_parent_class
67 
68 static gboolean
caja_module_load(GTypeModule * gmodule)69 caja_module_load (GTypeModule *gmodule)
70 {
71     CajaModule *module;
72 
73     module = CAJA_MODULE (gmodule);
74 
75     module->library = g_module_open (module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
76 
77     if (!module->library)
78     {
79         g_warning ("%s", g_module_error ());
80         return FALSE;
81     }
82 
83     if (!g_module_symbol (module->library,
84                           "caja_module_initialize",
85                           (gpointer *)&module->initialize) ||
86             !g_module_symbol (module->library,
87                               "caja_module_shutdown",
88                               (gpointer *)&module->shutdown) ||
89             !g_module_symbol (module->library,
90                               "caja_module_list_types",
91                               (gpointer *)&module->list_types))
92     {
93 
94         g_warning ("%s", g_module_error ());
95         g_module_close (module->library);
96 
97         return FALSE;
98     }
99 
100     g_module_symbol (module->library,
101                      "caja_module_list_pyfiles",
102                      (gpointer *)&module->list_pyfiles);
103 
104     module->initialize (gmodule);
105 
106     return TRUE;
107 }
108 
109 static void
caja_module_unload(GTypeModule * gmodule)110 caja_module_unload (GTypeModule *gmodule)
111 {
112     CajaModule *module;
113 
114     module = CAJA_MODULE (gmodule);
115 
116     module->shutdown ();
117 
118     g_module_close (module->library);
119 
120     module->initialize = NULL;
121     module->shutdown = NULL;
122     module->list_types = NULL;
123 }
124 
125 static void
caja_module_finalize(GObject * object)126 caja_module_finalize (GObject *object)
127 {
128     CajaModule *module;
129 
130     module = CAJA_MODULE (object);
131 
132     g_free (module->path);
133 
134     EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
135 }
136 
137 static void
caja_module_init(CajaModule * module)138 caja_module_init (CajaModule *module)
139 {
140 }
141 
142 static void
caja_module_class_init(CajaModuleClass * class)143 caja_module_class_init (CajaModuleClass *class)
144 {
145     G_OBJECT_CLASS (class)->finalize = caja_module_finalize;
146     G_TYPE_MODULE_CLASS (class)->load = caja_module_load;
147     G_TYPE_MODULE_CLASS (class)->unload = caja_module_unload;
148 }
149 
150 static void
module_object_weak_notify(gpointer user_data,GObject * object)151 module_object_weak_notify (gpointer user_data, GObject *object)
152 {
153     module_objects = g_list_remove (module_objects, object);
154 }
155 
156 static void
add_module_objects(CajaModule * module)157 add_module_objects (CajaModule *module)
158 {
159     GObject *object = NULL;
160     GList *pyfiles = NULL;
161     gchar *filename = NULL;
162     const GType *types = NULL;
163     int num_types = 0;
164     int i;
165 
166     module->list_types (&types, &num_types);
167     filename = g_path_get_basename (module->path);
168 
169     /* fetch extensions details loaded through python-caja module */
170     if (module->list_pyfiles)
171     {
172         module->list_pyfiles(&pyfiles);
173     }
174 
175     for (i = 0; i < num_types; i++)
176     {
177         if (types[i] == 0)   /* Work around broken extensions */
178         {
179             break;
180         }
181 
182         if (module->list_pyfiles)
183         {
184             filename = g_strconcat(g_list_nth_data(pyfiles, i), ".py", NULL);
185         }
186 
187         object = caja_module_add_type (types[i]);
188         caja_extension_register (filename, object);
189     }
190 }
191 
192 static CajaModule *
caja_module_load_file(const char * filename)193 caja_module_load_file (const char *filename)
194 {
195     CajaModule *module;
196 
197     module = g_object_new (CAJA_TYPE_MODULE, NULL);
198     module->path = g_strdup (filename);
199 
200     if (g_type_module_use (G_TYPE_MODULE (module)))
201     {
202         add_module_objects (module);
203         g_type_module_unuse (G_TYPE_MODULE (module));
204         return module;
205     }
206     else
207     {
208         g_object_unref (module);
209         return NULL;
210     }
211 }
212 
213 static void
load_module_dir(const char * dirname)214 load_module_dir (const char *dirname)
215 {
216     GDir *dir;
217 
218     dir = g_dir_open (dirname, 0, NULL);
219 
220     if (dir)
221     {
222         const char *name;
223 
224         while ((name = g_dir_read_name (dir)))
225         {
226             if (g_str_has_suffix (name, "." G_MODULE_SUFFIX))
227             {
228                 char *filename;
229 
230                 filename = g_build_filename (dirname,
231                                              name,
232                                              NULL);
233                 caja_module_load_file (filename);
234             }
235         }
236         g_dir_close (dir);
237     }
238 }
239 
240 static void
free_module_objects(void)241 free_module_objects (void)
242 {
243     GList *l, *next;
244 
245     for (l = module_objects; l != NULL; l = next)
246     {
247         next = l->next;
248         g_object_unref (l->data);
249     }
250 
251     g_list_free (module_objects);
252 }
253 
254 void
caja_module_setup(void)255 caja_module_setup (void)
256 {
257     static gboolean initialized = FALSE;
258 
259     if (!initialized)
260     {
261         const gchar *caja_extension_dirs = g_getenv ("CAJA_EXTENSION_DIRS");
262 
263         initialized = TRUE;
264 
265         if (caja_extension_dirs)
266         {
267             gchar **dir_vector = g_strsplit (caja_extension_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
268 
269             for (gchar **dir = dir_vector; *dir != NULL; ++ dir)
270                 load_module_dir (*dir);
271 
272             g_strfreev(dir_vector);
273         }
274 
275         load_module_dir (CAJA_EXTENSIONDIR);
276 
277         eel_debug_call_at_shutdown (free_module_objects);
278     }
279 }
280 
281 GList *
caja_module_get_extensions_for_type(GType type)282 caja_module_get_extensions_for_type (GType type)
283 {
284     GList *l;
285     GList *ret = NULL;
286 
287     for (l = module_objects; l != NULL; l = l->next)
288     {
289         if (G_TYPE_CHECK_INSTANCE_TYPE (G_OBJECT (l->data),
290                                         type))
291         {
292             g_object_ref (l->data);
293             ret = g_list_prepend (ret, l->data);
294         }
295     }
296 
297     return ret;
298 }
299 
300 void
caja_module_extension_list_free(GList * extensions)301 caja_module_extension_list_free (GList *extensions)
302 {
303     GList *l, *next;
304 
305     for (l = extensions; l != NULL; l = next)
306     {
307         next = l->next;
308         g_object_unref (l->data);
309     }
310     g_list_free (extensions);
311 }
312 
313 GObject *
caja_module_add_type(GType type)314 caja_module_add_type (GType type)
315 {
316     GObject *object;
317 
318     object = g_object_new (type, NULL);
319     g_object_weak_ref (object,
320                        (GWeakNotify)module_object_weak_notify,
321                        NULL);
322 
323     module_objects = g_list_prepend (module_objects, object);
324     return object;
325 }
326