2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include <glib/gi18n.h>
40 const gchar *(*name) (void);
41 const gchar *(*desc) (void);
42 const gchar *(*version) (void);
43 const gchar *(*type) (void);
44 const gchar *(*licence) (void);
49 * List of all loaded plugins
51 GSList *plugins = NULL;
52 GSList *plugin_types = NULL;
54 static gint list_find_by_string(gconstpointer data, gconstpointer str)
56 return strcmp((gchar *)data, (gchar *)str) ? TRUE : FALSE;
59 static gint list_find_by_plugin_filename(const Plugin *plugin, const gchar *filename)
61 g_return_val_if_fail(plugin, 1);
62 g_return_val_if_fail(plugin->filename, 1);
63 g_return_val_if_fail(filename, 1);
64 return strcmp(filename, plugin->filename);
67 void plugin_save_list(void)
69 gchar *rcpath, *block;
71 GSList *type_cur, *plugin_cur;
74 for (type_cur = plugin_types; type_cur != NULL; type_cur = g_slist_next(type_cur)) {
75 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
77 block = g_strconcat("PluginsWin32_", type_cur->data, NULL);
79 block = g_strconcat("Plugins_", type_cur->data, NULL);
81 if ((pfile = prefs_write_open(rcpath)) == NULL ||
82 (prefs_set_block_label(pfile, block) < 0)) {
83 g_warning("failed to write plugin list\n");
89 for (plugin_cur = plugins; plugin_cur != NULL; plugin_cur = g_slist_next(plugin_cur)) {
90 plugin = (Plugin *) plugin_cur->data;
92 if (!strcmp(plugin->type(), type_cur->data))
93 fprintf(pfile->fp, "%s\n", plugin->filename);
95 fprintf(pfile->fp, "\n");
97 if (prefs_file_close(pfile) < 0)
98 g_warning("failed to write plugin list\n");
104 static gboolean plugin_is_loaded(const gchar *filename)
106 return (g_slist_find_custom(plugins, filename,
107 (GCompareFunc)list_find_by_plugin_filename) != NULL);
110 static Plugin *plugin_get_by_filename(const gchar *filename)
112 GSList *cur = plugins;
113 for(; cur; cur = cur->next) {
114 Plugin *p = (Plugin *)cur->data;
115 if (!strcmp(p->filename, filename)) {
123 * Loads a plugin dependancies
125 * Plugin dependancies are, optionnaly, listed in a file in
126 * get_plugin_dir()/$pluginname.deps.
127 * \param filename The filename of the plugin for which we have to load deps
128 * \param error The location where an error string can be stored
129 * \return 0 on success, -1 otherwise
131 static gint plugin_load_deps(const gchar *filename, gchar **error)
134 gchar *deps_file = NULL;
138 tmp = g_strdup(filename);
139 *strrchr(tmp, '.') = '\0';
140 deps_file = g_strconcat(tmp, ".deps", NULL);
143 fp = g_fopen(deps_file, "rb");
149 while (fgets(buf, sizeof(buf), fp) != NULL) {
150 Plugin *dep_plugin = NULL;
152 buf[strlen(buf)-1]='\0'; /* chop off \n */
153 path = g_strconcat(get_plugin_dir(), buf,
154 ".", G_MODULE_SUFFIX, NULL);
155 if ((dep_plugin = plugin_get_by_filename(path)) == NULL) {
156 debug_print("trying to load %s\n", path);
157 dep_plugin = plugin_load(path, error);
158 if (dep_plugin == NULL) {
164 if (!g_slist_find_custom(dep_plugin->rdeps,
165 (gpointer) filename, list_find_by_string)) {
166 debug_print("adding %s to %s rdeps\n",
168 dep_plugin->filename);
170 g_slist_append(dep_plugin->rdeps,
178 static void plugin_unload_rdeps(Plugin *plugin)
180 GSList *cur = plugin->rdeps;
181 debug_print("removing %s rdeps\n", plugin->filename);
183 gchar *file = (gchar *)cur->data;
184 Plugin *rdep_plugin = file?plugin_get_by_filename(file):NULL;
185 debug_print(" rdep %s: %p\n", file, rdep_plugin);
187 plugin_unload(rdep_plugin);
192 g_slist_free(plugin->rdeps);
193 plugin->rdeps = NULL;
198 * \param filename The filename of the plugin to load
199 * \param error The location where an error string can be stored
200 * \return the plugin on success, NULL otherwise
202 Plugin *plugin_load(const gchar *filename, gchar **error)
205 gint (*plugin_init) (gchar **error);
206 gpointer plugin_name, plugin_desc, plugin_version;
207 const gchar *(*plugin_type)(void);
208 const gchar *(*plugin_licence)(void);
211 g_return_val_if_fail(filename != NULL, NULL);
212 g_return_val_if_fail(error != NULL, NULL);
214 /* check duplicate plugin path name */
215 if (plugin_is_loaded(filename)) {
216 *error = g_strdup(_("Plugin already loaded"));
220 if (plugin_load_deps(filename, error) < 0)
222 plugin = g_new0(Plugin, 1);
223 if (plugin == NULL) {
224 *error = g_strdup(_("Failed to allocate memory for Plugin"));
228 debug_print("trying to load `%s'\n", filename);
229 plugin->module = g_module_open(filename, 0);
230 if (plugin->module == NULL) {
231 *error = g_strdup(g_module_error());
236 if (!g_module_symbol(plugin->module, "plugin_name", &plugin_name) ||
237 !g_module_symbol(plugin->module, "plugin_desc", &plugin_desc) ||
238 !g_module_symbol(plugin->module, "plugin_version", &plugin_version) ||
239 !g_module_symbol(plugin->module, "plugin_type", (gpointer)&plugin_type) ||
240 !g_module_symbol(plugin->module, "plugin_licence", (gpointer)&plugin_licence) ||
241 !g_module_symbol(plugin->module, "plugin_init", (gpointer)&plugin_init)) {
242 *error = g_strdup(g_module_error());
243 g_module_close(plugin->module);
248 if (strcmp(plugin_licence(), "GPL")
249 && strncmp(plugin_licence(), "GPL-compatible", strlen("GPL-compatible"))) {
250 *error = g_strdup(_("This module is not licenced under a GPL compatible licence."));
251 g_module_close(plugin->module);
256 if (!strcmp(plugin_type(), "GTK")) {
257 *error = g_strdup(_("This module is for Sylpheed-Claws GTK1."));
258 g_module_close(plugin->module);
263 if ((ok = plugin_init(error)) < 0) {
264 g_module_close(plugin->module);
269 plugin->name = plugin_name;
270 plugin->desc = plugin_desc;
271 plugin->version = plugin_version;
272 plugin->type = plugin_type;
273 plugin->licence = plugin_licence;
274 plugin->filename = g_strdup(filename);
276 plugins = g_slist_append(plugins, plugin);
278 debug_print("Plugin %s (from file %s) loaded\n", plugin->name(), filename);
283 void plugin_unload(Plugin *plugin)
285 void (*plugin_done) (void);
287 plugin_unload_rdeps(plugin);
289 if (g_module_symbol(plugin->module, "plugin_done", (gpointer) &plugin_done)) {
293 g_module_close(plugin->module);
294 plugins = g_slist_remove(plugins, plugin);
295 g_free(plugin->filename);
299 void plugin_load_all(const gchar *type)
304 gchar *error, *block;
306 plugin_types = g_slist_append(plugin_types, g_strdup(type));
308 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
310 block = g_strconcat("PluginsWin32_", type, NULL);
312 block = g_strconcat("Plugins_", type, NULL);
314 if ((pfile = prefs_read_open(rcpath)) == NULL ||
315 (prefs_set_block_label(pfile, block) < 0)) {
321 while (fgets(buf, sizeof(buf), pfile->fp) != NULL) {
326 if ((buf[0] != '\0') && (plugin_load(buf, &error) == NULL)) {
327 g_warning("plugin loading error: %s\n", error);
331 prefs_file_close(pfile);
336 void plugin_unload_all(const gchar *type)
340 list = g_slist_copy(plugins);
341 list = g_slist_reverse(list);
343 for(cur = list; cur != NULL; cur = g_slist_next(cur)) {
344 Plugin *plugin = (Plugin *) cur->data;
346 if (!strcmp(type, plugin->type()))
347 plugin_unload(plugin);
351 cur = g_slist_find_custom(plugin_types, (gpointer) type, list_find_by_string);
354 g_slist_remove(plugin_types, cur);
358 GSList *plugin_get_list(void)
360 return g_slist_copy(plugins);
363 const gchar *plugin_get_name(Plugin *plugin)
365 return plugin->name();
368 const gchar *plugin_get_desc(Plugin *plugin)
370 return plugin->desc();
373 const gchar *plugin_get_version(Plugin *plugin)
375 return plugin->version();