1 /*
2  * Copyright (C) 2003-2012 Edscott Wilson Garcia
3  * EMail: edscott@users.sf.net
4  *
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program;
18  */
19 
20 #define __MODULES_C__
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24 
25 //#define DEBUG
26 
27 #include "rfm.h"
28 #include "rfm_modules.h"
29 
30 static GHashTable *module_hash = NULL;
31 
32 
33 static RfmProgramOptions lite_plugin_options[]={
34     {"fstab",N_("Mount local disks and devices"),TRUE,NULL},
35     {"ps",N_("View current processes and monitor system state"),TRUE,NULL},
36     {"dotdesktop",N_("Next-generation application launcher."),TRUE,NULL},
37 
38     {"submodule-label",N_("Windows networks (SMB)"),TRUE,NULL},
39     {"smb",N_("SMB Browser"),TRUE,NULL},
40     {"submodule-indent",NULL,TRUE,NULL},
41 	{"workgroup",N_("Windows workgroup"),TRUE,NULL},
42 	{"shares",N_("Windows Shares"),TRUE,NULL},
43     {"submodule-unindent",NULL,TRUE,NULL},
44 
45     {"submodule-label",N_("FUSE Volume"),TRUE,NULL},
46     {"fuse",N_("Mount user-space filesystems (FUSE)"),TRUE,NULL},
47     {"submodule-indent",NULL,TRUE,NULL},
48 	{"nfs",N_("NFS Network Volume"),TRUE,NULL},
49 	{"sftp",N_("Secure FTP (SSH)"),TRUE,NULL},
50 #ifdef THIS_IS_LINUX
51 	{"ecryptfs",N_("Encrypted filesystem"),TRUE,NULL},
52 	{"obex",N_("Bluetooth Transfer"),TRUE,NULL},
53 	{"ftp",N_("FTP Client"),TRUE,NULL},
54 #endif
55 	{"cifs",N_("CIFS Volume"),TRUE,NULL},
56     {"submodule-unindent",NULL,TRUE,NULL},
57     {NULL,NULL,FALSE,NULL}
58 };
59 
60 static RfmProgramOptions lite_module_options[]={
61     {"callbacks",N_("Callbacks"),FALSE,NULL},
62     {"bcrypt",N_("Blowfish"),FALSE,NULL},
63     {"settings",N_("Settings"),FALSE,NULL},
64     {"run",N_("Run program and return its output"),FALSE,NULL},
65     {"properties",N_("Properties"),TRUE,NULL},
66     {"completion",N_("Text completion"),TRUE,NULL},
67     {"combobox",N_("History of combo url."),TRUE,NULL},
68     {"mime",N_("Mime Type"),FALSE,NULL},
69     {"mimemagic",N_("Use MIME type magic"),TRUE,NULL},
70     {"mimezip",N_("ZIP archive plugin"),TRUE,NULL},
71     {"icons",N_("Icon Themes"),TRUE,NULL},
72      {NULL,NULL,FALSE,NULL}
73 };
74 
rfm_get_lite_module_options(void)75 RfmProgramOptions *rfm_get_lite_module_options(void){return lite_module_options;}
rfm_get_lite_plugin_options(void)76 RfmProgramOptions *rfm_get_lite_plugin_options(void){return lite_plugin_options;}
77 
78 // This is to set downstream modules/plugins to be used...
79 static gchar *plugin_dir=NULL;
80 
rfm_set_module_dir(const gchar * libdir,const gchar * dir)81 const gchar *rfm_set_module_dir(const gchar *libdir, const gchar *dir){
82     DBG("rfm_set_module_dir() is no longer used\n");
83     return NULL;
84 }
85 
rfm_set_plugin_dir(const gchar * libdir,const gchar * dir)86 const gchar *rfm_set_plugin_dir(const gchar *libdir, const gchar *dir){
87     g_free(plugin_dir);
88     plugin_dir = g_build_filename (libdir, dir, NULL);
89     return (const gchar *) plugin_dir;
90 }
91 
rfm_plugin_dir(void)92 const gchar *rfm_plugin_dir(void){
93     if (!plugin_dir){
94         plugin_dir = g_build_filename (LIBDIR, "rfm", "plugins", NULL);
95 
96         DBG("rfm_set_plugin_dir() not set. Using default %s\n",plugin_dir);
97     }
98     return (const gchar *) plugin_dir;
99 }
100 
101 static pthread_mutex_t *
get_module_hash_mutex(void)102 get_module_hash_mutex(void){
103     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
104     return &mutex;
105 }
106 
107 
108 const gchar *
rfm_get_module_popup_id(void)109 rfm_get_module_popup_id(void){
110     return "module_popup_widget";
111 }
112 
113 gboolean
rfm_is_root_plugin(const gchar * module_name)114 rfm_is_root_plugin(const gchar *module_name){
115     if (!module_name) return FALSE;
116     return (GPOINTER_TO_INT(rfm_void(PLUGIN_DIR, module_name, "is_root_module")));
117 }
118 const gchar *
rfm_get_plugin_icon(const gchar * module_name)119 rfm_get_plugin_icon(const gchar *module_name){
120     if (!module_name) return NULL;
121     return (rfm_void(PLUGIN_DIR, module_name, "module_icon_id"));
122 }
123 gchar *
rfm_get_plugin_label(const gchar * module_name)124 rfm_get_plugin_label(const gchar *module_name){
125     if (!module_name) return NULL;
126     return (rfm_void(PLUGIN_DIR, module_name, "module_label"));
127 }
128 
129 void
rfm_sanity_check(int argc,char ** argv,int librfm_serial)130 rfm_sanity_check (int argc, char **argv, int librfm_serial) {
131     if(librfm_serial != LIBRFM_SERIAL) {
132         gchar *p = g_strdup_printf ("%s needs to be recompiled \n(has obsolete library headers)", argv[0]);
133 	rfm_confirm(NULL,GTK_MESSAGE_ERROR,p,NULL,NULL);
134 	g_assert_not_reached();
135 	//g_error("%s\n", p);
136     }
137     return;
138 }
139 
140 static gint
compare_strings(gconstpointer a,gconstpointer b)141 compare_strings (gconstpointer a, gconstpointer b) {
142     return (strcmp ((char *)a, (char *)b));
143 }
144 
145 GSList *
rfm_find_plugins(void)146 rfm_find_plugins (void) {
147     static GSList *plugin_list = NULL;
148     static gsize initialized = 0;
149 
150     // Only once...
151     if (g_once_init_enter(&initialized)){
152 	GError *error = NULL;
153 
154 	GDir *dir;
155 	if((dir = g_dir_open (PLUGIN_DIR, 0, &error)) != NULL) {
156 	    const gchar *name;
157 	    while((name = g_dir_read_name (dir)) != NULL) {
158 		gchar *plugin;
159 		if(strncmp (name, "lib", strlen ("lib"))){
160 		    plugin = g_strdup (name);
161 		} else {
162 		    plugin = g_strdup (name + strlen ("lib"));
163 		}
164 		if(strchr (plugin, '.')) plugin = strtok (plugin, ".");
165 
166 		if(g_slist_find_custom (plugin_list, plugin, compare_strings)) {
167 		    g_free (plugin);
168 		} else {
169 		    NOOP ("modules: g_slist_append %s\n", plugin);
170 		    plugin_list = g_slist_prepend (plugin_list, plugin);
171 		}
172 	    }
173 	    g_dir_close (dir);
174 	}
175 	g_once_init_leave(&initialized, 1);
176     }
177     return plugin_list;
178 }
179 
180 
181 /*************************************************************/
182 
183 //void unload_module (const gchar * module_name);
184 
185 static void *
module_error(const gchar * module_name,gchar * module,gchar * warning)186 module_error(const gchar * module_name, gchar *module, gchar *warning){
187     pthread_mutex_t *module_hash_mutex = get_module_hash_mutex();
188     pthread_mutex_lock(module_hash_mutex);
189     g_hash_table_insert (module_hash, (gpointer) module_name, GINT_TO_POINTER(-1));
190     pthread_mutex_unlock(module_hash_mutex);
191     DBG ("Module %s (%s): %s\n", module, module_name, warning);
192     g_free (warning);
193     g_free (module);
194     return NULL;
195 }
196 
197 
198 GModule *
get_module_info(const gchar * librarydir,const gchar * module_name)199 get_module_info (const gchar * librarydir, const gchar * module_name) {
200     if (!module_name || !librarydir || !strlen(module_name) ) {
201 	DBG("Failed to get module info on !module_name (%p) || !librarydir (%p) || !strlen(module_name) \"%s\"\n",
202                 module_name, librarydir, module_name);
203 	return NULL;
204     }
205 
206     //Quick hack to avoid loading icon module when icontheme is not selected as an option...
207     if (strcmp(module_name, "icons")==0){
208         gboolean gtk_icontheme = (getenv("RFM_USE_GTK_ICON_THEME") && strlen(getenv("RFM_USE_GTK_ICON_THEME")));
209         if (!gtk_icontheme){
210             NOOP(stderr, "quick hack to avoid loading icon module...\n");
211             return NULL;
212         }
213     }
214 
215 
216 
217     rfm_global_t *rfm_global_p = rfm_global();
218     if (rfm_global_p && rfm_global_p->status == STATUS_EXIT) {
219 	TRACE("Refusing to get module info on STATUS_EXIT for %s\n",module_name);
220 	return NULL;
221     }
222     GModule *module_cm = NULL;
223     void *(*module_sanity) (void);
224     int init_version;
225 
226     gchar *module = g_module_build_path (librarydir, module_name);
227 
228     if(!rfm_g_file_test (module, G_FILE_TEST_EXISTS)) {
229 	DBG("Module \"%s\" does not exist!\n", module_name);
230 	g_free (module);
231 	return NULL;
232     }
233 
234     if (strstr(module_name,"gridview"))DBG("getting module info for %s (librarydir=%s)\n", module, librarydir);
235 
236     if(!module_hash) {
237         TRACE("get_module_info: creating module hash\n");
238         module_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
239         if(!module_hash) g_assert_not_reached ();
240     }
241     NOOP ("looking for module in hash: %s\n", module_name);
242     pthread_mutex_t *module_hash_mutex = get_module_hash_mutex();
243     pthread_mutex_lock(module_hash_mutex);
244     module_cm = g_hash_table_lookup (module_hash, module_name);
245     pthread_mutex_unlock(module_hash_mutex);
246     if (GPOINTER_TO_INT(module_cm) == -1){
247 	DBG("Module %s cannot be loaded.\n", module);
248 	g_free(module);
249 	return NULL;
250     }
251     if(module_cm != NULL) {
252         NOOP("module info for %s found (already loaded) 0x%x\n",
253 		module,
254 		GPOINTER_TO_INT(module_cm));
255 	g_free(module);
256         return module_cm;
257     }
258 
259     NOOP ("get_module_info(): RSS changes with load of %s\n", module);
260     // Here's the rub: lazy mode willfetch a symbol with the same name
261     // from another module than that which was specified.
262     // Only use lazy if different modules do not export symbols with the
263     // same name (or just do not use lazy).
264 #ifdef DEBUG
265     module_cm = g_module_open (module, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
266 #else
267     module_cm = g_module_open (module, G_MODULE_BIND_LOCAL);
268 #endif
269 
270     if(!module_cm) {
271 	return module_error(module_name, module,
272 		g_strdup("g_module_open() == NULL\nmodule cannot be opened: Check if correctly installed or unresolved symbols within...\n****\n"));
273     }
274 
275     // Check for module sanity
276     if(!g_module_symbol (module_cm, "module_sanity", (gpointer) & (module_sanity))) {
277 	return module_error(module_name, module,
278 		g_strdup ("Module is not sane!\nDoes module specify: \"LIBRFM_MODULE\"?"));
279     }
280 
281     // Check for construction with correct structure serial (types.h)
282     init_version = GPOINTER_TO_INT ((*module_sanity) ());
283     if(init_version != LIBRFM_SERIAL) {
284 	return module_error(module_name, module,
285 		g_strdup ("Module is compiled with obsolete headers (not loaded)"));
286     }
287 
288 
289 
290     NOOP ("adding module to module hash: %s (0x%lx)\n", module_name, (unsigned long)module_cm);
291     pthread_mutex_lock(module_hash_mutex);
292     g_hash_table_insert (module_hash, g_strdup(module_name), module_cm);
293     pthread_mutex_unlock(module_hash_mutex);
294     TRACE("get_module_info: module %s successfully loaded\n", module);
295     g_free (module);
296     return module_cm;
297 
298 }
299 
300 
rfm_destroy_module_hash(void)301 void rfm_destroy_module_hash(void){
302     pthread_mutex_t *module_hash_mutex = get_module_hash_mutex();
303     pthread_mutex_lock(module_hash_mutex);
304     if (module_hash) g_hash_table_destroy(module_hash);
305     pthread_mutex_unlock(module_hash_mutex);
306 }
307 
308 
309 void *
rfm_void(const gchar * librarydir,const gchar * module_name,const gchar * function_id)310 rfm_void (const gchar * librarydir, const gchar * module_name, const gchar * function_id) {
311     if (!module_name || !strlen(module_name)) return NULL;
312     gchar *(*function) (void);
313     if(!librarydir || !module_name || !function_id) return NULL;
314     TRACE("attempting module function: %s\n", function_id);
315     GModule *module_cm = get_module_info (librarydir, module_name);
316     if(!module_cm){
317 	DBG("get_module_info failed: %s\n", module_name);
318 	return NULL;
319     }
320 
321     if(!g_module_symbol (module_cm, function_id, (gpointer) & (function))) {
322         /*if (strcmp(function_id,"submodule_name")!=0 && strcmp(function_id,"submodule_dir")!=0) */
323         {
324             TRACE("g_module_symbol(%s) != FALSE in module %s\n", function_id, module_name);
325         }
326         /*unload_module(module_name); */
327         return NULL;
328     }
329     return (*function) ();
330 }
331 
332 void *
rfm_natural(const gchar * librarydir,const gchar * module_name,void * n,const gchar * function_id)333 rfm_natural (const gchar * librarydir, const gchar * module_name, void *n, const gchar * function_id) {
334     gchar *(*function) (void *n);
335     if(!librarydir || !module_name || !function_id){
336 	DBG("!librarydir (%s)|| !module_name (%s)|| !function_id(%s)\n",
337 		librarydir, module_name, function_id);
338         return NULL;
339     }
340     GModule *module_cm = get_module_info (librarydir, module_name);
341     if(!module_cm) return NULL;
342     if(!g_module_symbol (module_cm, function_id, (gpointer) & (function))) {
343         if(strcmp (function_id, "submodule_name") != 0 && strcmp (function_id, "submodule_dir") != 0) {
344             DBG ("g_module_symbol(%s) != FALSE in module %s\n", function_id, module_name);
345         }
346         /*unload_module(module_name); */
347         return NULL;
348     }
349     return (*function) (n);
350 }
351 
352 void *
rfm_rational(const gchar * librarydir,const gchar * module_name,void * p,void * q,const gchar * function_id)353 rfm_rational (const gchar * librarydir, const gchar * module_name, void *p, void *q, const gchar * function_id) {
354     gchar *(*function) (void *p, void *q);
355     if(!librarydir || !module_name || !function_id) return NULL;
356     GModule *module_cm = get_module_info (librarydir, module_name);
357     if(!module_cm) return NULL;
358     if(!g_module_symbol (module_cm, function_id, (gpointer) & (function))) {
359         if(strcmp (function_id, "submodule_name") != 0 && strcmp (function_id, "submodule_dir") != 0) {
360             DBG ("g_module_symbol(%s) != FALSE in module %s\n", function_id, module_name);
361         }
362         /*unload_module(module_name); */
363         return NULL;
364     }
365     return (*function) (p, q);
366 }
367 
368 void *
rfm_complex(const gchar * librarydir,const gchar * module_name,void * p,void * q,void * r,const gchar * function_id)369 rfm_complex (const gchar * librarydir, const gchar * module_name, void *p, void *q, void *r, const gchar * function_id) {
370     gchar *(*function) (void *p, void *q, void *r);
371     if(!librarydir || !module_name || !function_id) return NULL;
372     GModule *module_cm = get_module_info (librarydir, module_name);
373     if(!module_cm) return NULL;
374     if(!g_module_symbol (module_cm, function_id, (gpointer) & (function))) {
375         if(strcmp (function_id, "submodule_name") != 0 && strcmp (function_id, "submodule_dir") != 0) {
376             DBG ("g_module_symbol(%s) != FALSE in module %s\n", function_id, module_name);
377         }
378         /*unload_module(module_name); */
379         return NULL;
380     }
381     return (*function) (p, q, r);
382 }
383 
384 static void **
argument_container_new(gint size,const void ** in_vector)385 argument_container_new(gint size, const void **in_vector){
386     void **argument_container = (void **)malloc(size * sizeof(void *));
387     if (!argument_container) g_error("malloc %s\n", strerror(errno));
388     memset(argument_container, 0, size * sizeof(void *));
389     if (in_vector){
390 	gint i;
391 	for (i=0; i < size; i++) argument_container[i] = (void *)in_vector[i];
392     }
393     return argument_container;
394 }
395 
396 
397 G_MODULE_EXPORT
398 void *
rfm_vector_run(const gchar * librarydir,const gchar * module_name,void * vector_size_p,const void ** vector,const gchar * function_id)399 rfm_vector_run(const gchar * librarydir, const gchar * module_name,
400 	void *vector_size_p, const void **vector, const gchar *function_id){
401 
402     void **arg = argument_container_new(GPOINTER_TO_INT(vector_size_p), vector);
403     TRACE("vector run %d: %s\n", GPOINTER_TO_INT(vector_size_p), function_id);
404     return rfm_natural(librarydir, module_name, arg, function_id);
405 }
406 
407 
408 #if 0
409 void *
410 load_module (const gchar * librarydir, const gchar * module_name) {
411     GModule *module_cm = get_module_info (librarydir, module_name);
412     NOOP ("finished load attempt of module %s (%s)\n", module_name, librarydir);
413     if(!module_cm) {
414         DBG ("Show stopper: cannot get module info for %s\n", module_name);
415         g_assert_not_reached ();
416     }
417     return GINT_TO_POINTER(1);
418 }
419 #endif
420 
421 void
rfm_unload_module(const gchar * module_name)422 rfm_unload_module (const gchar * module_name) {
423 
424     if(!module_hash) return;
425     pthread_mutex_t *module_hash_mutex = get_module_hash_mutex();
426     pthread_mutex_lock(module_hash_mutex);
427     GModule *module_cm = g_hash_table_lookup (module_hash, module_name);
428     pthread_mutex_unlock(module_hash_mutex);
429     if(!module_cm) {
430         DBG ("module %s is not loaded\n", module_name);
431         return;
432     }
433         TRACE ("unloading module %s\n", module_name);
434 
435     if(!g_module_close (module_cm)) {
436         DBG ("g_module_close (%s) failed\n", module_name);
437     } else {
438     pthread_mutex_lock(module_hash_mutex);
439         if(!g_hash_table_remove (module_hash, module_name)) {
440             DBG ("could not remove %s from module hash\n", module_name);
441         }
442     pthread_mutex_unlock(module_hash_mutex);
443         TRACE("module %s unloaded\n", module_name);
444     }
445 }
446 
447 
448 /*********************************************************************************************/
449 
450 
451