/* Copyright (C) 2000 - 2006 Christian Kreibich . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies of the Software and its documentation and acknowledgment shall be given in the documentation and software packages that this Software was used. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include static GList *proto_plugins = NULL; /* list */ static LND_ProtoPlugin *proto_plugin_new(char *filename); static void proto_plugin_scan(const char *dirname); static gboolean proto_plugin_hook_up(LND_ProtoPlugin *p); static void proto_plugin_load(void); static int proto_plugin_cmp(LND_ProtoPlugin *p1, char *filename); static const char * proto_plugin_dummy_name(void) { return "Unnamed Plugin."; } static const char * proto_plugin_dummy_description(void) { return "No description available."; } static const char * proto_plugin_dummy_author(void) { return "No author(s) specified."; } static const char * proto_plugin_dummy_version(void) { return "No version specified."; } static int proto_plugin_dummy_op(void) { return 0; } static LND_Protocol * proto_plugin_dummy_init(void) { return NULL; } static LND_ProtoPlugin * proto_plugin_new(char *filename) { LND_ProtoPlugin *plugin; plugin = (LND_ProtoPlugin*) g_new0(LND_ProtoPlugin, 1); D_ASSERT_PTR(plugin); if (!plugin) return NULL; if (filename) plugin->filename = g_strdup(filename); plugin->name = proto_plugin_dummy_name; plugin->description = proto_plugin_dummy_description; plugin->author = proto_plugin_dummy_author; plugin->version = proto_plugin_dummy_version; plugin->init = proto_plugin_dummy_init; plugin->cleanup = proto_plugin_dummy_op; return (plugin); } static void proto_plugin_free(LND_ProtoPlugin *plugin) { if (!plugin) return; lt_dlclose(plugin->lt); g_free(plugin->filename); g_free(plugin); } static int proto_plugin_cmp(LND_ProtoPlugin *p1, char *filename) { if (!p1 || !filename) return (-1); return (strcmp(p1->filename, filename)); } static void proto_plugin_scan(const char *dir) { DIR *dirp; struct dirent *de; char *dot; struct stat st; char path[MAXPATHLEN]; D(("Scanning '%s' for protocols\n", dir)); if (! (dirp = opendir(dir))) return; for (de = readdir(dirp); de; de = readdir(dirp)) { g_snprintf(path, MAXPATHLEN, "%s/%s", dir, de->d_name); if (stat(path, &st) != 0) { D(("Could not stat %s\n", path)); continue; } if (! S_ISREG(st.st_mode)) { D(("Skipping %s, not a regular file.\n", path)); continue; } /* Chop off any file extension, if found. libltdl takes * care of that. */ if ( (dot = strrchr(path, '.'))) *dot = '\0'; if (!g_list_find_custom(proto_plugins, path, (GCompareFunc)proto_plugin_cmp)) { proto_plugins = g_list_append(proto_plugins, proto_plugin_new(path)); } } closedir(dirp); } static gboolean proto_plugin_hook_up(LND_ProtoPlugin *plugin) { LND_Protocol *proto; lt_ptr_t sym; if ((sym = lt_dlsym(plugin->lt, "name")) != NULL) plugin->name = sym; if ((sym = lt_dlsym(plugin->lt, "description")) != NULL) plugin->description = sym; if ((sym = lt_dlsym(plugin->lt, "author")) != NULL) plugin->author = sym; if ((sym = lt_dlsym(plugin->lt, "version")) != NULL) plugin->version = sym; if ((sym = lt_dlsym(plugin->lt, "init")) != NULL) plugin->init = sym; if ((sym = lt_dlsym(plugin->lt, "cleanup")) != NULL) plugin->cleanup = sym; /* Call the init routine the plugin provides. If this * routine does not return a protocol, the plugin is broken * and we abort. */ if (! (proto = plugin->init())) return FALSE; /* register the plugin: */ proto->plugin = plugin; libnd_proto_registry_register(proto); return TRUE; } static gint proto_plugin_strcmp(gconstpointer a, gconstpointer b) { LND_ProtoPlugin *p1 = (LND_ProtoPlugin *) a; LND_ProtoPlugin *p2 = (LND_ProtoPlugin *) b; return strcmp(p1->name(), p2->name()); } static void proto_plugin_load(void) { GList *l, *l2; LND_ProtoPlugin *plugin; for (l = g_list_first(proto_plugins); l; l = g_list_next(l)) { plugin = (LND_ProtoPlugin *) l->data; if (! (plugin->lt = lt_dlopenext(plugin->filename)) || ! proto_plugin_hook_up(plugin)) { l->data = NULL; proto_plugin_free(plugin); } } /* We may have deleted some plugins above that failed to initialize. * Clear out those list items in a separate iteration. */ for (l = g_list_first(proto_plugins); l; ) { if (! l->data) { l2 = l; l = g_list_next(l); proto_plugins = g_list_remove_link(proto_plugins, l2); g_list_free_1(l2); } else { l = g_list_next(l); } } /* Now sort the list of plugins alphanumerically, * so that we automatically get sorted menus etc. */ proto_plugins = g_list_sort(proto_plugins, proto_plugin_strcmp); } void libnd_proto_plugin_init(void) { if (lt_dlinit() != 0) { fprintf(stderr, "Could not initialize libltdl -- exiting.\n"); exit(-1); } proto_plugin_scan(libnd_prefs_get_proto_dir_user()); proto_plugin_scan(libnd_prefs_get_proto_dir_global()); proto_plugin_load(); } const char * libnd_proto_plugin_get_description(LND_ProtoPlugin *plugin) { if (!plugin) return NULL; return plugin->description(); } const char * libnd_proto_plugin_get_author(LND_ProtoPlugin *plugin) { if (!plugin) return NULL; return plugin->author(); } const char * libnd_proto_plugin_get_version(LND_ProtoPlugin *plugin) { if (!plugin) return NULL; return plugin->version(); } const char * libnd_proto_plugin_get_name(LND_ProtoPlugin *plugin) { if (!plugin) return NULL; return plugin->name(); } void libnd_proto_plugin_foreach(LND_ProtoPluginFunc callback, void *user_data) { GList *l; if (!callback) return; for (l = g_list_first(proto_plugins); l; l = g_list_next(l)) callback((LND_ProtoPlugin *)l->data, user_data); }