mirror of
https://github.com/weechat/weechat.git
synced 2026-06-25 12:26:40 +02:00
Improved plugin API, most functions rewritten from scratch
This commit is contained in:
@@ -0,0 +1,601 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org>
|
||||
* See README for License detail, AUTHORS for developers list.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* plugin.c: manages WeeChat plugins (dynamic C libraries) */
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "../core/weechat.h"
|
||||
#include "../core/wee-command.h"
|
||||
#include "../core/wee-config.h"
|
||||
#include "../core/wee-hook.h"
|
||||
#include "../core/wee-log.h"
|
||||
#include "../core/wee-string.h"
|
||||
#include "../core/wee-util.h"
|
||||
#include "../gui/gui-chat.h"
|
||||
#include "plugin.h"
|
||||
#include "plugin-api.h"
|
||||
#include "plugin-config.h"
|
||||
#include "plugin-list.h"
|
||||
|
||||
|
||||
struct t_weechat_plugin *weechat_plugins = NULL;
|
||||
struct t_weechat_plugin *last_weechat_plugin = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* plugin_exec_on_files: find files in a directory and execute a
|
||||
* function on each file
|
||||
*/
|
||||
|
||||
void
|
||||
plugin_exec_on_files (struct t_weechat_plugin *plugin, char *directory,
|
||||
int (*callback)(struct t_weechat_plugin *, char *))
|
||||
{
|
||||
char complete_filename[1024];
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
struct stat statbuf;
|
||||
|
||||
dir = opendir (directory);
|
||||
if (dir)
|
||||
{
|
||||
while ((entry = readdir (dir)))
|
||||
{
|
||||
snprintf (complete_filename, sizeof (complete_filename) - 1,
|
||||
"%s/%s", directory, entry->d_name);
|
||||
lstat (complete_filename, &statbuf);
|
||||
if (!S_ISDIR(statbuf.st_mode))
|
||||
{
|
||||
(int) (*callback) (plugin, complete_filename);
|
||||
}
|
||||
}
|
||||
closedir (dir);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_search: search a plugin by name
|
||||
*/
|
||||
|
||||
struct t_weechat_plugin *
|
||||
plugin_search (char *name)
|
||||
{
|
||||
struct t_weechat_plugin *ptr_plugin;
|
||||
|
||||
for (ptr_plugin = weechat_plugins; ptr_plugin;
|
||||
ptr_plugin = ptr_plugin->next_plugin)
|
||||
{
|
||||
if (string_strcasecmp (ptr_plugin->name, name) == 0)
|
||||
return ptr_plugin;
|
||||
}
|
||||
|
||||
/* plugin not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_load: load a WeeChat plugin (a dynamic library)
|
||||
* return: pointer to new WeeChat plugin, NULL if error
|
||||
*/
|
||||
|
||||
struct t_weechat_plugin *
|
||||
plugin_load (char *filename)
|
||||
{
|
||||
char *full_name;
|
||||
void *handle;
|
||||
char *name, *description, *version, *charset;
|
||||
t_weechat_init_func *init_func;
|
||||
struct t_weechat_plugin *new_plugin;
|
||||
|
||||
if (!filename)
|
||||
return NULL;
|
||||
|
||||
full_name = util_search_full_lib_name (filename, "plugins");
|
||||
|
||||
if (!full_name)
|
||||
return NULL;
|
||||
|
||||
handle = dlopen (full_name, RTLD_GLOBAL | RTLD_NOW);
|
||||
if (!handle)
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%s%s unable to load plugin \"%s\": %s\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
WEECHAT_ERROR, full_name, dlerror());
|
||||
free (full_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look for plugin name */
|
||||
name = dlsym (handle, "plugin_name");
|
||||
if (!name)
|
||||
{
|
||||
dlclose (handle);
|
||||
gui_chat_printf (NULL,
|
||||
_("%s%s symbol \"%s\" not found in "
|
||||
"plugin \"%s\", failed to load\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
WEECHAT_ERROR, "plugin_name",
|
||||
full_name);
|
||||
free (full_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* check for plugin with same name */
|
||||
if (plugin_search (name))
|
||||
{
|
||||
dlclose (handle);
|
||||
gui_chat_printf (NULL,
|
||||
_("%s%s unable to load plugin \"%s\": a plugin "
|
||||
"with same name already exists\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
WEECHAT_ERROR, full_name);
|
||||
free (full_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look for plugin description */
|
||||
description = dlsym (handle, "plugin_description");
|
||||
if (!description)
|
||||
{
|
||||
dlclose (handle);
|
||||
gui_chat_printf (NULL,
|
||||
_("%s%s symbol \"%s\" not found "
|
||||
"in plugin \"%s\", failed to load\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
WEECHAT_ERROR, "plugin_description",
|
||||
full_name);
|
||||
free (full_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look for plugin version */
|
||||
version = dlsym (handle, "plugin_version");
|
||||
if (!version)
|
||||
{
|
||||
dlclose (handle);
|
||||
gui_chat_printf (NULL,
|
||||
_("%s%s symbol \"%s\" not found in "
|
||||
"plugin \"%s\", failed to load\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
WEECHAT_ERROR, "plugin_version",
|
||||
full_name);
|
||||
free (full_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look for plugin charset (optional) */
|
||||
charset = dlsym (handle, "plugin_charset");
|
||||
|
||||
/* look for plugin init function */
|
||||
init_func = dlsym (handle, "weechat_plugin_init");
|
||||
if (!init_func)
|
||||
{
|
||||
dlclose (handle);
|
||||
gui_chat_printf (NULL,
|
||||
_("%s%s function \"%s\" not "
|
||||
"found in plugin \"%s\", failed to load\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
WEECHAT_ERROR, "weechat_plugin_init",
|
||||
full_name);
|
||||
free (full_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create new plugin */
|
||||
new_plugin = (struct t_weechat_plugin *)malloc (sizeof (struct t_weechat_plugin));
|
||||
if (new_plugin)
|
||||
{
|
||||
/* variables */
|
||||
new_plugin->filename = strdup (full_name);
|
||||
new_plugin->handle = handle;
|
||||
new_plugin->name = strdup (name);
|
||||
new_plugin->description = strdup (description);
|
||||
new_plugin->version = strdup (version);
|
||||
new_plugin->charset = (charset) ? strdup (charset) : NULL;
|
||||
|
||||
/* functions */
|
||||
new_plugin->charset_set = &plugin_api_charset_set;
|
||||
new_plugin->iconv_to_internal = &plugin_api_iconv_to_internal;
|
||||
new_plugin->iconv_from_internal = &plugin_api_iconv_from_internal;
|
||||
new_plugin->gettext = &plugin_api_gettext;
|
||||
new_plugin->ngettext = &plugin_api_ngettext;
|
||||
new_plugin->strcasecmp = &plugin_api_strcasecmp;
|
||||
new_plugin->strncasecmp = &plugin_api_strncasecmp;
|
||||
new_plugin->explode_string = &plugin_api_explode_string;
|
||||
new_plugin->free_exploded_string = &plugin_api_free_exploded_string;
|
||||
|
||||
new_plugin->mkdir_home = &plugin_api_mkdir_home;
|
||||
new_plugin->exec_on_files = &plugin_api_exec_on_files;
|
||||
|
||||
new_plugin->printf = &plugin_api_printf;
|
||||
new_plugin->prefix = &plugin_api_prefix;
|
||||
new_plugin->color = &plugin_api_color;
|
||||
new_plugin->print_infobar = &plugin_api_print_infobar;
|
||||
new_plugin->infobar_remove = &plugin_api_infobar_remove;
|
||||
|
||||
new_plugin->hook_command = &plugin_api_hook_command;
|
||||
new_plugin->hook_message = &plugin_api_hook_message;
|
||||
new_plugin->hook_config = &plugin_api_hook_config;
|
||||
new_plugin->hook_timer = &plugin_api_hook_timer;
|
||||
new_plugin->hook_fd = &plugin_api_hook_fd;
|
||||
new_plugin->unhook = &plugin_api_unhook;
|
||||
new_plugin->unhook_all = &plugin_api_unhook_all;
|
||||
|
||||
new_plugin->buffer_new = &plugin_api_buffer_new;
|
||||
new_plugin->buffer_search = &plugin_api_buffer_search;
|
||||
new_plugin->buffer_close = &plugin_api_buffer_close;
|
||||
new_plugin->buffer_set = &plugin_api_buffer_set;
|
||||
new_plugin->buffer_nick_add = &plugin_api_buffer_nick_add;
|
||||
new_plugin->buffer_nick_remove = &plugin_api_buffer_nick_remove;
|
||||
|
||||
new_plugin->command = &plugin_api_command;
|
||||
|
||||
new_plugin->info_get = &plugin_api_info_get;
|
||||
|
||||
new_plugin->config_get = &plugin_api_config_get;
|
||||
new_plugin->config_set = &plugin_api_config_set;
|
||||
new_plugin->plugin_config_get = &plugin_api_plugin_config_get;
|
||||
new_plugin->plugin_config_set = &plugin_api_plugin_config_set;
|
||||
|
||||
new_plugin->log = &plugin_api_log;
|
||||
|
||||
/* add new plugin to list */
|
||||
new_plugin->prev_plugin = last_weechat_plugin;
|
||||
new_plugin->next_plugin = NULL;
|
||||
if (weechat_plugins)
|
||||
last_weechat_plugin->next_plugin = new_plugin;
|
||||
else
|
||||
weechat_plugins = new_plugin;
|
||||
last_weechat_plugin = new_plugin;
|
||||
|
||||
gui_chat_printf (NULL,
|
||||
_("%sInitializing plugin \"%s\" %s\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_INFO],
|
||||
new_plugin->name, new_plugin->version);
|
||||
|
||||
/* init plugin */
|
||||
if (((t_weechat_init_func *)init_func) (new_plugin) < 0)
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%s%s unable to initialize plugin "
|
||||
"\"%s\"\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
WEECHAT_ERROR, full_name);
|
||||
plugin_remove (new_plugin);
|
||||
free (full_name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%s%s unable to load plugin \"%s\" "
|
||||
"(not enough memory)\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
WEECHAT_ERROR, full_name);
|
||||
free (full_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gui_chat_printf (NULL,
|
||||
_("%sPlugin \"%s\" (%s) loaded.\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_INFO],
|
||||
name, full_name);
|
||||
|
||||
free (full_name);
|
||||
|
||||
return new_plugin;
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_auto_load_file: load a file found by plugin_auto_load,
|
||||
* but only it this is really a dynamic library
|
||||
*/
|
||||
|
||||
int
|
||||
plugin_auto_load_file (struct t_weechat_plugin *plugin, char *filename)
|
||||
{
|
||||
char *pos;
|
||||
|
||||
/* make C compiler happy */
|
||||
(void) plugin;
|
||||
|
||||
if (cfg_plugins_extension && cfg_plugins_extension[0])
|
||||
{
|
||||
pos = strstr (filename, cfg_plugins_extension);
|
||||
if (pos)
|
||||
{
|
||||
if (string_strcasecmp (pos, cfg_plugins_extension) == 0)
|
||||
plugin_load (filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
plugin_load (filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_auto_load: auto-load WeeChat plugins
|
||||
*/
|
||||
|
||||
void
|
||||
plugin_auto_load ()
|
||||
{
|
||||
char *ptr_home, *dir_name, *plugins_path, *plugins_path2;
|
||||
char *list_plugins, *pos, *pos2;
|
||||
|
||||
if (cfg_plugins_autoload && cfg_plugins_autoload[0])
|
||||
{
|
||||
if (string_strcasecmp (cfg_plugins_autoload, "*") == 0)
|
||||
{
|
||||
/* auto-load plugins in WeeChat home dir */
|
||||
if (cfg_plugins_path && cfg_plugins_path[0])
|
||||
{
|
||||
ptr_home = getenv ("HOME");
|
||||
plugins_path = string_replace (cfg_plugins_path, "~", ptr_home);
|
||||
plugins_path2 = string_replace ((plugins_path) ?
|
||||
plugins_path : cfg_plugins_path,
|
||||
"%h", weechat_home);
|
||||
plugin_exec_on_files (NULL,
|
||||
(plugins_path2) ?
|
||||
plugins_path2 : ((plugins_path) ?
|
||||
plugins_path : cfg_plugins_path),
|
||||
&plugin_auto_load_file);
|
||||
if (plugins_path)
|
||||
free (plugins_path);
|
||||
if (plugins_path2)
|
||||
free (plugins_path2);
|
||||
}
|
||||
|
||||
/* auto-load plugins in WeeChat global lib dir */
|
||||
dir_name = (char *)malloc (strlen (WEECHAT_LIBDIR) + 16);
|
||||
if (dir_name)
|
||||
{
|
||||
snprintf (dir_name, strlen (WEECHAT_LIBDIR) + 16,
|
||||
"%s/plugins", WEECHAT_LIBDIR);
|
||||
plugin_exec_on_files (NULL, dir_name, &plugin_auto_load_file);
|
||||
free (dir_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
list_plugins = strdup (cfg_plugins_autoload);
|
||||
if (list_plugins)
|
||||
{
|
||||
pos = list_plugins;
|
||||
while (pos && pos[0])
|
||||
{
|
||||
pos2 = strchr (pos, ',');
|
||||
if (pos2)
|
||||
pos2[0] = '\0';
|
||||
plugin_load (pos);
|
||||
if (pos2)
|
||||
pos = pos2 + 1;
|
||||
else
|
||||
pos = NULL;
|
||||
}
|
||||
free (list_plugins);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_remove: remove a WeeChat plugin
|
||||
*/
|
||||
|
||||
void
|
||||
plugin_remove (struct t_weechat_plugin *plugin)
|
||||
{
|
||||
struct t_weechat_plugin *new_weechat_plugins;
|
||||
|
||||
/* remove plugin from list */
|
||||
if (last_weechat_plugin == plugin)
|
||||
last_weechat_plugin = plugin->prev_plugin;
|
||||
if (plugin->prev_plugin)
|
||||
{
|
||||
(plugin->prev_plugin)->next_plugin = plugin->next_plugin;
|
||||
new_weechat_plugins = weechat_plugins;
|
||||
}
|
||||
else
|
||||
new_weechat_plugins = plugin->next_plugin;
|
||||
|
||||
if (plugin->next_plugin)
|
||||
(plugin->next_plugin)->prev_plugin = plugin->prev_plugin;
|
||||
|
||||
/* remove all hooks */
|
||||
unhook_all_plugin (plugin);
|
||||
|
||||
/* free data */
|
||||
if (plugin->filename)
|
||||
free (plugin->filename);
|
||||
dlclose (plugin->handle);
|
||||
if (plugin->name)
|
||||
free (plugin->name);
|
||||
if (plugin->description)
|
||||
free (plugin->description);
|
||||
if (plugin->version)
|
||||
free (plugin->version);
|
||||
if (plugin->charset)
|
||||
free (plugin->charset);
|
||||
free (plugin);
|
||||
|
||||
weechat_plugins = new_weechat_plugins;
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_unload: unload a WeeChat plugin
|
||||
*/
|
||||
|
||||
void
|
||||
plugin_unload (struct t_weechat_plugin *plugin)
|
||||
{
|
||||
t_weechat_end_func *end_func;
|
||||
char *name;
|
||||
|
||||
name = (plugin->name) ? strdup (plugin->name) : strdup ("???");
|
||||
|
||||
end_func = dlsym (plugin->handle, "weechat_plugin_end");
|
||||
if (end_func)
|
||||
(void) (end_func) (plugin);
|
||||
plugin_remove (plugin);
|
||||
|
||||
gui_chat_printf (NULL,
|
||||
_("%sPlugin \"%s\" unloaded.\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_INFO],
|
||||
(name) ? name : "???");
|
||||
if (name)
|
||||
free (name);
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_unload_name: unload a WeeChat plugin by name
|
||||
*/
|
||||
|
||||
void
|
||||
plugin_unload_name (char *name)
|
||||
{
|
||||
struct t_weechat_plugin *ptr_plugin;
|
||||
|
||||
ptr_plugin = plugin_search (name);
|
||||
if (ptr_plugin)
|
||||
plugin_unload (ptr_plugin);
|
||||
else
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%s%s plugin \"%s\" not found\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
WEECHAT_ERROR, name);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_unload_all: unload all WeeChat plugins
|
||||
*/
|
||||
|
||||
void
|
||||
plugin_unload_all ()
|
||||
{
|
||||
while (weechat_plugins)
|
||||
plugin_unload (last_weechat_plugin);
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_reload_name: reload a WeeChat plugin by name
|
||||
*/
|
||||
|
||||
void
|
||||
plugin_reload_name (char *name)
|
||||
{
|
||||
struct t_weechat_plugin *ptr_plugin;
|
||||
char *filename;
|
||||
|
||||
ptr_plugin = plugin_search (name);
|
||||
if (ptr_plugin)
|
||||
{
|
||||
filename = strdup (ptr_plugin->filename);
|
||||
if (filename)
|
||||
{
|
||||
plugin_unload (ptr_plugin);
|
||||
gui_chat_printf (NULL,
|
||||
_("%sPlugin \"%s\" unloaded.\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_INFO],
|
||||
name);
|
||||
plugin_load (filename);
|
||||
free (filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%s%s plugin \"%s\" not found\n"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
WEECHAT_ERROR, name);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_init: init plugin support
|
||||
*/
|
||||
|
||||
void
|
||||
plugin_init (int auto_load)
|
||||
{
|
||||
/* read plugins options on disk */
|
||||
plugin_config_read ();
|
||||
|
||||
/* auto-load plugins if asked */
|
||||
if (auto_load)
|
||||
plugin_auto_load ();
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_end: end plugin support
|
||||
*/
|
||||
|
||||
void
|
||||
plugin_end ()
|
||||
{
|
||||
/* write plugins config options */
|
||||
plugin_config_write ();
|
||||
|
||||
/* unload all plugins */
|
||||
plugin_unload_all ();
|
||||
}
|
||||
|
||||
/*
|
||||
* plugin_print_log: print plugin infos in log (usually for crash dump)
|
||||
*/
|
||||
|
||||
void
|
||||
plugin_print_log ()
|
||||
{
|
||||
struct t_weechat_plugin *ptr_plugin;
|
||||
|
||||
for (ptr_plugin = weechat_plugins; ptr_plugin;
|
||||
ptr_plugin = ptr_plugin->next_plugin)
|
||||
{
|
||||
weechat_log_printf ("\n");
|
||||
weechat_log_printf ("[plugin (addr:0x%X)]\n", ptr_plugin);
|
||||
weechat_log_printf (" filename . . . . . . . : '%s'\n", ptr_plugin->filename);
|
||||
weechat_log_printf (" handle . . . . . . . . : 0x%X\n", ptr_plugin->handle);
|
||||
weechat_log_printf (" name . . . . . . . . . : '%s'\n", ptr_plugin->name);
|
||||
weechat_log_printf (" description. . . . . . : '%s'\n", ptr_plugin->description);
|
||||
weechat_log_printf (" version. . . . . . . . : '%s'\n", ptr_plugin->version);
|
||||
weechat_log_printf (" charset. . . . . . . . : '%s'\n", ptr_plugin->charset);
|
||||
weechat_log_printf (" prev_plugin. . . . . . : 0x%X\n", ptr_plugin->prev_plugin);
|
||||
weechat_log_printf (" next_plugin. . . . . . : 0x%X\n", ptr_plugin->next_plugin);
|
||||
}
|
||||
|
||||
plugin_list_print_log ();
|
||||
}
|
||||
Reference in New Issue
Block a user