1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2 
3 /*
4  * GImageView
5  * Copyright (C) 2001 Takuro Ashie
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  * $Id: gimv_plugin.c,v 1.6 2004/09/21 08:44:32 makeinu Exp $
22  */
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include <gmodule.h>
27 
28 #include "gimageview.h"
29 
30 #include "fileutil.h"
31 #include "gfileutil.h"
32 #include "gimv_plugin.h"
33 #include "prefs.h"
34 
35 #define PLUGIN_RC_DIR "pluginrc"
36 
37 typedef struct PluginTypeEntry_Tag
38 {
39    const gchar        *type;
40    GimvPluginRegistFn  regist_fn;
41    GList              *plugin_list;
42    const gchar        *prefsrc_filename;
43    GHashTable         *prefs_table;
44 } PluginTypeEntry;
45 
46 #define PLUGIN_REGIST_FUNC(type) \
47 extern gboolean type##_plugin_regist (const gchar *plugin_name, \
48                                       const gchar *module_name, \
49                                       gpointer     impl, \
50                                       gint         size)
51 
52 PLUGIN_REGIST_FUNC(gimv_image_loader);
53 PLUGIN_REGIST_FUNC(gimv_image_saver);
54 PLUGIN_REGIST_FUNC(gimv_io);
55 PLUGIN_REGIST_FUNC(fr_archive);
56 PLUGIN_REGIST_FUNC(gimv_thumb_cache);
57 PLUGIN_REGIST_FUNC(gimv_image_view);
58 PLUGIN_REGIST_FUNC(gimv_thumb_view);
59 
60 static PluginTypeEntry plugin_types[] = {
61    {GIMV_PLUGIN_IMAGE_LOADER,      gimv_image_loader_plugin_regist, NULL, "image_loader",   NULL},
62    {GIMV_PLUGIN_IMAGE_SAVER,       gimv_image_saver_plugin_regist,  NULL, "image_saver",    NULL},
63    {GIMV_PLUGIN_IO_STREAMER,       gimv_io_plugin_regist,           NULL, "io_stream",      NULL},
64    {GIMV_PLUGIN_EXT_ARCHIVER,      fr_archive_plugin_regist,        NULL, "ext_archiver",   NULL},
65    {GIMV_PLUGIN_THUMB_CACHE,       gimv_thumb_cache_plugin_regist,  NULL, "thumbnail",      NULL},
66    {GIMV_PLUGIN_IMAGEVIEW_EMBEDER, gimv_image_view_plugin_regist,   NULL, "image_view",     NULL},
67    {GIMV_PLUGIN_THUMBVIEW_EMBEDER, gimv_thumb_view_plugin_regist,   NULL, "thumbnail_view", NULL},
68 };
69 static guint plugin_types_num = sizeof (plugin_types) / sizeof (PluginTypeEntry);
70 
71 static GList      *search_dir_list    = NULL;
72 static GHashTable *plugin_types_table = NULL;
73 static GList      *all_plugin_list    = NULL;
74 
75 
76 /******************************************************************************
77  *
78  *   Private functions.
79  *
80  ******************************************************************************/
81 static void
plugin_clear_search_dir_list(void)82 plugin_clear_search_dir_list (void)
83 {
84    g_list_foreach (search_dir_list, (GFunc) g_free, NULL);
85    g_list_free (search_dir_list);
86    search_dir_list = NULL;
87 }
88 
89 
90 static const gchar *
get_plugin_prefs_filename(const gchar * type)91 get_plugin_prefs_filename (const gchar *type)
92 {
93    PluginTypeEntry *entry = g_hash_table_lookup (plugin_types_table, type);
94 
95    g_return_val_if_fail(entry, NULL);
96 
97    return entry->prefsrc_filename;
98 }
99 
100 
101 static gchar *
get_plugin_prefs_path(const gchar * type)102 get_plugin_prefs_path (const gchar *type)
103 {
104    const gchar *filename;
105    gchar *path;
106 
107    filename = get_plugin_prefs_filename (type);
108    g_return_val_if_fail (filename, NULL);
109 
110    path = g_strconcat (g_getenv("HOME"), "/", GIMV_RC_DIR, "/",
111                        PLUGIN_RC_DIR, "/", filename, NULL);
112 
113    return path;
114 }
115 
116 
117 static GHashTable *
get_prefs_table(const gchar * type)118 get_prefs_table (const gchar *type)
119 {
120    PluginTypeEntry *entry = g_hash_table_lookup (plugin_types_table, type);
121 
122    g_return_val_if_fail(entry, NULL);
123 
124    if (!entry->prefs_table)
125       entry->prefs_table = g_hash_table_new (g_str_hash, g_str_equal);
126    return entry->prefs_table;
127 }
128 
129 
130 static void
plugin_prefs_read_file(const gchar * type)131 plugin_prefs_read_file (const gchar *type)
132 {
133    FILE *pluginrc;
134    gchar *path;
135    gchar buf[BUF_SIZE], section[256], **pair = NULL;
136    gint len;
137 
138    if (!plugin_types_table) {
139       guint i;
140 
141       plugin_types_table = g_hash_table_new (g_str_hash, g_str_equal);
142 
143       for (i = 0; i < plugin_types_num; i++) {
144          PluginTypeEntry *entry = &plugin_types[i];
145          g_hash_table_insert (plugin_types_table, (gpointer) entry->type, entry);
146       }
147    }
148 
149    path = get_plugin_prefs_path (type);
150    if (!path || !*path) return;
151 
152    pluginrc = fopen(path, "r");
153    if (!pluginrc) {
154       /* g_warning (_("Can't open plugin preference file for read.")); */
155       goto ERROR;
156    }
157 
158    section[0] = '\0';
159    while (fgets (buf, sizeof(buf), pluginrc)) {
160       g_strstrip (buf);
161       if (buf[0] == '\0') continue;
162       if (buf[0] == '\n') continue;
163 
164       len = strlen(buf);
165 
166       if (len > 2 && len < 256 && buf[0] == '[' && buf[len - 1] == ']') {
167          strncpy (section, buf + 1, len - 2);
168          section[len - 2] = '\0';
169          continue;
170       }
171 
172       if (!*section) continue;
173 
174       pair = g_strsplit (buf, "=", 2);
175       if (!pair) continue;
176 
177       if (pair[0]) g_strstrip(pair[0]);
178       if (pair[1]) g_strstrip(pair[1]);
179 
180       if (pair[0] && *pair[0]) {
181          gimv_plugin_prefs_save_value (section, type,
182                                        pair[0],
183                                        pair[1] ? pair[1] : NULL);
184       }
185 
186       g_strfreev (pair);
187       pair = NULL;
188    }
189 
190    fclose (pluginrc);
191 ERROR:
192    g_free (path);
193 }
194 
195 
196 static void
write_value(gpointer key,gpointer value,gpointer user_data)197 write_value (gpointer key, gpointer value, gpointer user_data)
198 {
199    FILE *pluginrc = user_data;
200    gchar *key_string = key;
201    gchar *value_string = value;
202 
203    g_return_if_fail (key_string && *key_string);
204    g_return_if_fail (value_string);
205    g_return_if_fail (pluginrc);
206 
207    fprintf (pluginrc, "%s=%s\n", key_string, value_string);
208 }
209 
210 
211 static void
write_section(gpointer key,gpointer value,gpointer user_data)212 write_section (gpointer key, gpointer value, gpointer user_data)
213 {
214    GHashTable *htable = value;
215    FILE *pluginrc = user_data;
216    gchar *string = key;
217 
218    g_return_if_fail (string && *string);
219    g_return_if_fail (pluginrc);
220 
221    if (!htable) return;
222    if (g_hash_table_size (htable) < 1) return;
223 
224    fprintf (pluginrc, "[%s]\n", string);
225    g_hash_table_foreach (htable, (GHFunc) write_value, pluginrc);
226    fprintf (pluginrc, "\n");
227 }
228 
229 
230 static void
plugin_prefs_write_file(const gchar * type)231 plugin_prefs_write_file (const gchar *type)
232 {
233    GHashTable *prefs_table;
234    gchar *path;
235    FILE *pluginrc;
236 
237    prefs_table = get_prefs_table (type);
238    if (!prefs_table) return;
239    if (g_hash_table_size (prefs_table) < 1) return;
240 
241    path = get_plugin_prefs_path (type);
242    if (!path) return;
243 
244    mkdirs (path);
245 
246    pluginrc = fopen (path, "w");
247    if (!pluginrc) {
248       g_warning (_("Can't open plugin preference file for write."));
249       goto ERROR;
250    }
251 
252    g_hash_table_foreach (prefs_table, (GHFunc) write_section, pluginrc);
253 
254    fclose (pluginrc);
255 ERROR:
256    g_free (path);
257 }
258 
259 
260 /******************************************************************************
261  *
262  *   Public functions.
263  *
264  ******************************************************************************/
265 const gchar *
gimv_plugin_type_get(guint idx)266 gimv_plugin_type_get (guint idx)
267 {
268    if (idx >= plugin_types_num) return NULL;
269    return plugin_types[idx].type;
270 }
271 
272 
273 gboolean
gimv_plugin_load_module(const gchar * filename,gint * error)274 gimv_plugin_load_module (const gchar *filename, gint *error)
275 {
276 #ifndef G_MODULE_SUFFIX
277 #  define G_MODULE_SUFFIX "so"
278 #endif /* G_MODULE_SUFFIX */
279 #define SO_EXT ("." G_MODULE_SUFFIX)
280 #define SO_EXT_LEN (strlen(SO_EXT))
281    GModule *module;
282    GimvPluginInfo *info;
283    const gchar *type;
284    gpointer impl;
285    GimvPrefsWinPage *prefs_ui;
286    GimvMimeTypeEntry *mime_type;
287    guint size;
288    GHashTable *hash = NULL;
289    gboolean success, regist;
290    gint i;
291 
292    success = FALSE;
293    regist = FALSE;
294 
295    /* load module */
296    module =  g_module_open (filename, G_MODULE_BIND_LAZY);
297    if (!module) {
298       /* FIXME: show message (return GError?) */
299       return FALSE;
300    }
301    success = g_module_symbol (module, "gimv_plugin_info", (gpointer) &info);
302    if (!success) {
303       /* FIXME: show message (return GError?) */
304       return FALSE;
305    }
306 
307    if (info->if_version != GIMV_PLUGIN_IF_VERSION) {
308       g_warning ("GimvPlugin: plugin interface version is invalid!: %s",
309                  filename);
310       g_module_close (module);
311       return FALSE;
312    }
313 
314    if (!info->get_implement) {
315       g_warning ("GimvPlugin: get_implement() method is not found!");
316       g_module_close (module);
317       return FALSE;
318    }
319 
320    /* get plugin implement */
321    hash = g_hash_table_new (g_str_hash, g_str_equal);
322 
323    for (i = 0; (type = info->get_implement(i, &impl, &size)); i++) {
324       PluginTypeEntry *type_entry;
325 
326       if (!impl || size <= 0) continue;
327 
328       /* regist each method to memory */
329       type_entry = g_hash_table_lookup (plugin_types_table, type);
330       if (!type_entry || !type_entry->regist_fn) continue;
331 
332       success =  type_entry->regist_fn (info->name,
333                                         g_module_name (module),
334                                         impl, size);
335       regist = regist || success;
336 
337       if (success && !g_hash_table_lookup (hash, type)) {
338          type_entry->plugin_list
339             = g_list_append (type_entry->plugin_list, module);
340          g_hash_table_insert (hash, (gpointer) type, (gpointer) type);
341       }
342    }
343 
344    g_hash_table_destroy(hash);
345    hash = NULL;
346 
347    /* get mime types */
348    for (i = 0;
349         regist
350            && info->get_mime_type
351            && info->get_mime_type (i, &mime_type, &size);
352         i++)
353    {
354       if (!mime_type || size <= 0) continue;
355       gimv_mime_types_regist (mime_type, module);
356    }
357 
358    /* get preference UI */
359    for (i = 0;
360         regist
361            && info->get_prefs_ui
362            && info->get_prefs_ui (i, &prefs_ui, &size);
363         i++)
364    {
365       if (!prefs_ui || size <= 0) continue;
366       gimv_prefs_win_add_page_entry (prefs_ui);
367    }
368 
369    /* append to list */
370    if (regist) {
371       all_plugin_list = g_list_append (all_plugin_list, module);
372    } else {
373       g_module_close (module);
374    }
375 
376    return regist;
377 }
378 
379 
380 gboolean
gimv_plugin_unload_module(const gchar * filename,gint * error)381 gimv_plugin_unload_module (const gchar *filename, gint *error)
382 {
383    g_warning ("GimvPlugin: not imlemented yet!");
384    return FALSE;
385 }
386 
387 
388 void
gimv_plugin_search_directory(const gchar * dirname)389 gimv_plugin_search_directory (const gchar *dirname)
390 {
391    GList *filelist = NULL, *node;
392 
393    g_return_if_fail (dirname);
394    if (!isdir (dirname)) return;
395 
396    get_dir (dirname, GETDIR_FOLLOW_SYMLINK, &filelist, NULL);
397    if (!filelist) return;
398 
399    for (node = filelist; node; node = g_list_next (node)) {
400       gchar *filename = node->data;
401       size_t len;
402 
403       len = strlen (filename);
404       if (len < SO_EXT_LEN || strcmp (filename + len - SO_EXT_LEN, SO_EXT))
405          continue;
406 
407       gimv_plugin_load_module (filename, NULL);
408    }
409 
410    g_list_foreach (filelist, (GFunc) g_free, NULL);
411    g_list_free (filelist);
412 }
413 
414 
415 void
gimv_plugin_create_search_dir_list(void)416 gimv_plugin_create_search_dir_list (void)
417 {
418    const gchar *dirlist = NULL;
419    gchar **dirs;
420    gint i;
421 
422    plugin_clear_search_dir_list ();
423 
424    if (conf.plugin_use_default_search_dir_list)
425       dirlist = PLUGIN_DEFAULT_SEARCH_DIR_LIST;
426    else
427       dirlist = conf.plugin_search_dir_list;
428 
429    if (!dirlist || !*dirlist) return;
430 
431    dirs = g_strsplit (dirlist, ",", -1);
432 
433    for (i = 0; dirs && dirs[i]; i++) {
434       if (*dirs[i])
435          search_dir_list = g_list_append (search_dir_list, g_strdup (dirs[i]));
436    }
437 
438    g_strfreev (dirs);
439 }
440 
441 
442 GList *
gimv_plugin_get_search_dir_list(void)443 gimv_plugin_get_search_dir_list (void)
444 {
445    return search_dir_list;
446 }
447 
448 
449 const gchar *
gimv_plugin_get_name(GModule * module)450 gimv_plugin_get_name (GModule *module)
451 {
452    gboolean success;
453    GimvPluginInfo *info;
454 
455    g_return_val_if_fail (module, NULL);
456 
457    success = g_module_symbol (module, "gimv_plugin_info", (gpointer) &info);
458    g_return_val_if_fail (success, NULL);
459 
460    return info->name;
461 }
462 
463 
464 const gchar *
gimv_plugin_get_version_string(GModule * module)465 gimv_plugin_get_version_string (GModule *module)
466 {
467    gboolean success;
468    GimvPluginInfo *info;
469 
470    g_return_val_if_fail (module, NULL);
471 
472    success = g_module_symbol (module, "gimv_plugin_info", (gpointer) &info);
473    g_return_val_if_fail (success, NULL);
474 
475    return info->version;
476 }
477 
478 
479 const gchar *
gimv_plugin_get_author(GModule * module)480 gimv_plugin_get_author (GModule *module)
481 {
482    gboolean success;
483    GimvPluginInfo *info;
484 
485    g_return_val_if_fail (module, NULL);
486 
487    success = g_module_symbol (module, "gimv_plugin_info", (gpointer) &info);
488    g_return_val_if_fail (success, NULL);
489 
490    return info->author;
491 }
492 
493 
494 const gchar *
gimv_plugin_get_module_name(GModule * module)495 gimv_plugin_get_module_name (GModule *module)
496 {
497    gboolean success;
498    GimvPluginInfo *info;
499 
500    g_return_val_if_fail (module, NULL);
501 
502    success = g_module_symbol (module, "gimv_plugin_info", (gpointer) &info);
503    g_return_val_if_fail (success, NULL);
504 
505    return g_module_name (module);
506 }
507 
508 
509 GList *
gimv_plugin_get_list(const gchar * type)510 gimv_plugin_get_list (const gchar *type)
511 {
512    PluginTypeEntry *entry;
513 
514    if (!type || !g_strcasecmp(type, "all")) {
515       return all_plugin_list;
516    } else {
517       entry = g_hash_table_lookup(plugin_types_table, type);
518       g_return_val_if_fail (entry, NULL);
519       return entry->plugin_list;
520    }
521 }
522 
523 
524 gboolean
gimv_plugin_prefs_save_value(const gchar * pname,const gchar * ptype,const gchar * key,const gchar * value)525 gimv_plugin_prefs_save_value (const gchar *pname,
526                               const gchar *ptype,
527                               const gchar *key,
528                               const gchar *value)
529 {
530    GHashTable *prefs_table, *htable;
531    gchar *old_value, *orig_key;
532    gboolean success;
533    gchar *new_key, *new_value;
534 
535    g_return_val_if_fail (pname && *pname, FALSE);
536    g_return_val_if_fail (key && *key, FALSE);
537 
538    prefs_table = get_prefs_table (ptype);
539    g_return_val_if_fail (prefs_table, FALSE);
540 
541    new_key   = g_strdup(key);
542    new_value = value ? g_strdup(value) : g_strdup("");
543 
544    htable = g_hash_table_lookup (prefs_table, pname);
545    if (!htable) {
546       htable = g_hash_table_new (g_str_hash, g_str_equal);
547       g_hash_table_insert (prefs_table, g_strdup (pname), htable);
548    }
549 
550    success = g_hash_table_lookup_extended (htable, key,
551                                            (gpointer) &orig_key,
552                                            (gpointer) &old_value);
553    if (success) {
554       g_hash_table_remove (htable, key);
555       g_free (orig_key);
556       g_free (old_value);
557    }
558 
559    g_hash_table_insert (htable, new_key, new_value);
560 
561    return TRUE;
562 }
563 
564 
565 gboolean
gimv_plugin_prefs_load_value(const gchar * pname,const gchar * ptype,const gchar * key,GimvPluginPrefsType type,gpointer data)566 gimv_plugin_prefs_load_value (const gchar *pname,
567                               const gchar *ptype,
568                               const gchar *key,
569                               GimvPluginPrefsType type,
570                               gpointer data)
571 {
572    GHashTable *prefs_table, *htable;
573    gchar *value;
574 
575    g_return_val_if_fail (pname && *pname, FALSE);
576    g_return_val_if_fail (key && *key, FALSE);
577    g_return_val_if_fail (data, FALSE);
578 
579    prefs_table = get_prefs_table (ptype);
580    g_return_val_if_fail (prefs_table, FALSE);
581 
582    htable = g_hash_table_lookup (prefs_table, pname);
583    if (!htable) return FALSE;
584 
585    value = g_hash_table_lookup (htable, key);
586    if (!value) return FALSE;
587    /* g_return_val_if_fail (!*value, FALSE); */
588 
589    switch (type) {
590    case GIMV_PLUGIN_PREFS_STRING:
591       *((gchar **) data) = value;
592       break;
593    case GIMV_PLUGIN_PREFS_INT:
594       *((gint *) data) = atoi (value);
595       break;
596    case GIMV_PLUGIN_PREFS_FLOAT:
597       *((gfloat *) data) = atof (value);
598       break;
599    case GIMV_PLUGIN_PREFS_BOOL:
600       if (!g_strcasecmp (value, "TRUE"))
601          *((gboolean *) data) = TRUE;
602       else
603          *((gboolean *) data) = FALSE;
604       break;
605    default:
606       return FALSE;
607    }
608 
609    return TRUE;
610 }
611 
612 
613 void
gimv_plugin_prefs_read_files(void)614 gimv_plugin_prefs_read_files (void)
615 {
616    guint i;
617    for (i = 0; i < plugin_types_num; i ++)
618       plugin_prefs_read_file (plugin_types[i].type);
619 }
620 
621 
622 void
gimv_plugin_prefs_write_files(void)623 gimv_plugin_prefs_write_files (void)
624 {
625    guint i;
626    for (i = 0; i < plugin_types_num; i ++)
627       plugin_prefs_write_file (plugin_types[i].type);
628 }
629 
630 
631 void
gimv_plugin_init(void)632 gimv_plugin_init (void)
633 {
634    GList *list;
635 
636    gimv_plugin_create_search_dir_list ();
637 
638    /* check whether platform supports dynamic loading or not */
639    if (!g_module_supported ()) return;
640 
641    if (!plugin_types_table) {
642       guint i;
643 
644       plugin_types_table = g_hash_table_new (g_str_hash, g_str_equal);
645 
646       for (i = 0; i < plugin_types_num; i++) {
647          PluginTypeEntry *entry = &plugin_types[i];
648          g_hash_table_insert (plugin_types_table, (gpointer) entry->type, entry);
649       }
650    }
651 
652    /* load module from disk */
653    list = g_list_last (search_dir_list);
654    for (; list; list = g_list_previous (list)) {
655       gchar *dir = list->data;
656 
657       if (dir && *dir)
658          gimv_plugin_search_directory (dir);
659    }
660 }
661