diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2018-08-14 07:15:39 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2018-08-14 07:15:39 +0200 |
commit | 49c3e6210666f271d1c87115c091b54e4f35ae53 (patch) | |
tree | b8adea435b71bdcc1d2d46aec967590c5cf419c8 /src/core | |
parent | 42be1a74a036bb0318201a40d91eadc7d9d6454f (diff) | |
download | weechat-49c3e6210666f271d1c87115c091b54e4f35ae53.zip |
core: split wee-hook.c into multiple sources
Diffstat (limited to 'src/core')
40 files changed, 6942 insertions, 4902 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8d84160d5..8574dcf78 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -46,6 +46,24 @@ set(LIB_CORE_SRC wee-utf8.c wee-utf8.h wee-util.c wee-util.h wee-version.c wee-version.h + hook/wee-hook-command-run.c hook/wee-hook-command-run.h + hook/wee-hook-command.c hook/wee-hook-command.h + hook/wee-hook-completion.c hook/wee-hook-completion.h + hook/wee-hook-config.c hook/wee-hook-config.h + hook/wee-hook-connect.c hook/wee-hook-connect.h + hook/wee-hook-fd.c hook/wee-hook-fd.h + hook/wee-hook-focus.c hook/wee-hook-focus.h + hook/wee-hook-hdata.c hook/wee-hook-hdata.h + hook/wee-hook-hsignal.c hook/wee-hook-hsignal.h + hook/wee-hook-info-hashtable.c hook/wee-hook-info-hashtable.h + hook/wee-hook-info.c hook/wee-hook-info.h + hook/wee-hook-infolist.c hook/wee-hook-infolist.h + hook/wee-hook-line.c hook/wee-hook-line.h + hook/wee-hook-modifier.c hook/wee-hook-modifier.h + hook/wee-hook-print.c hook/wee-hook-print.h + hook/wee-hook-process.c hook/wee-hook-process.h + hook/wee-hook-signal.c hook/wee-hook-signal.h + hook/wee-hook-timer.c hook/wee-hook-timer.h ) # Check for flock support diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 6d79fad75..009f0c2cb 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -72,6 +72,42 @@ lib_weechat_core_a_SOURCES = weechat.c \ wee-util.c \ wee-util.h \ wee-version.c \ - wee-version.h + wee-version.h \ + hook/wee-hook-command-run.c \ + hook/wee-hook-command-run.h \ + hook/wee-hook-command.c \ + hook/wee-hook-command.h \ + hook/wee-hook-completion.c \ + hook/wee-hook-completion.h \ + hook/wee-hook-config.c \ + hook/wee-hook-config.h \ + hook/wee-hook-connect.c \ + hook/wee-hook-connect.h \ + hook/wee-hook-fd.c \ + hook/wee-hook-fd.h \ + hook/wee-hook-focus.c \ + hook/wee-hook-focus.h \ + hook/wee-hook-hdata.c \ + hook/wee-hook-hdata.h \ + hook/wee-hook-hsignal.c \ + hook/wee-hook-hsignal.h \ + hook/wee-hook-info-hashtable.c \ + hook/wee-hook-info-hashtable.h \ + hook/wee-hook-info.c \ + hook/wee-hook-info.h \ + hook/wee-hook-infolist.c \ + hook/wee-hook-infolist.h \ + hook/wee-hook-line.c \ + hook/wee-hook-line.h \ + hook/wee-hook-modifier.c \ + hook/wee-hook-modifier.h \ + hook/wee-hook-print.c \ + hook/wee-hook-print.h \ + hook/wee-hook-process.c \ + hook/wee-hook-process.h \ + hook/wee-hook-signal.c \ + hook/wee-hook-signal.h \ + hook/wee-hook-timer.c \ + hook/wee-hook-timer.h EXTRA_DIST = CMakeLists.txt diff --git a/src/core/hook/wee-hook-command-run.c b/src/core/hook/wee-hook-command-run.c new file mode 100644 index 000000000..ad07954a7 --- /dev/null +++ b/src/core/hook/wee-hook-command-run.c @@ -0,0 +1,219 @@ +/* + * wee-hook-command-run.c - WeeChat command_run hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" +#include "../wee-utf8.h" +#include "../../plugins/plugin.h" + + +/* + * Hooks a command when it's run by WeeChat. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_command_run (struct t_weechat_plugin *plugin, + const char *command, + t_hook_callback_command_run *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_command_run *new_hook_command_run; + int priority; + const char *ptr_command; + + if (!callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_command_run = malloc (sizeof (*new_hook_command_run)); + if (!new_hook_command_run) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (command, &priority, &ptr_command); + hook_init_data (new_hook, plugin, HOOK_TYPE_COMMAND_RUN, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_command_run; + new_hook_command_run->callback = callback; + new_hook_command_run->command = strdup ((ptr_command) ? ptr_command : + ((command) ? command : "")); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Executes a command_run hook. + */ + +int +hook_command_run_exec (struct t_gui_buffer *buffer, const char *command) +{ + struct t_hook *ptr_hook, *next_hook; + int rc, hook_matching, length; + char *command2; + const char *ptr_command; + + if (!weechat_hooks[HOOK_TYPE_COMMAND_RUN]) + return WEECHAT_RC_OK; + + ptr_command = command; + command2 = NULL; + + if (command[0] != '/') + { + length = strlen (command) + 1; + command2 = malloc (length); + if (command2) + { + snprintf (command2, length, "/%s", command + 1); + ptr_command = command2; + } + } + + ptr_hook = weechat_hooks[HOOK_TYPE_COMMAND_RUN]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && HOOK_COMMAND_RUN(ptr_hook, command)) + { + hook_matching = string_match (ptr_command, + HOOK_COMMAND_RUN(ptr_hook, command), + 0); + + if (!hook_matching + && !strchr (HOOK_COMMAND_RUN(ptr_hook, command), ' ')) + { + length = strlen (HOOK_COMMAND_RUN(ptr_hook, command)); + hook_matching = ((string_strncasecmp (ptr_command, + HOOK_COMMAND_RUN(ptr_hook, command), + utf8_strlen (HOOK_COMMAND_RUN(ptr_hook, command))) == 0) + && ((ptr_command[length] == ' ') + || (ptr_command[length] == '\0'))); + } + + if (hook_matching) + { + ptr_hook->running = 1; + rc = (HOOK_COMMAND_RUN(ptr_hook, callback)) ( + ptr_hook->callback_pointer, + ptr_hook->callback_data, + buffer, + ptr_command); + ptr_hook->running = 0; + if (rc == WEECHAT_RC_OK_EAT) + { + if (command2) + free (command2); + return rc; + } + } + } + + ptr_hook = next_hook; + } + + if (command2) + free (command2); + + return WEECHAT_RC_OK; +} + +/* + * Frees data in a command_run hook. + */ + +void +hook_command_run_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_COMMAND_RUN(hook, command)) + { + free (HOOK_COMMAND_RUN(hook, command)); + HOOK_COMMAND_RUN(hook, command) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds command_run hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_command_run_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_COMMAND_RUN(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "command", HOOK_COMMAND_RUN(hook, command))) + return 0; + + return 1; +} + +/* + * Prints command_run hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_command_run_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" command_run data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_COMMAND_RUN(hook, callback)); + log_printf (" command . . . . . . . : '%s'", HOOK_COMMAND_RUN(hook, command)); +} diff --git a/src/core/hook/wee-hook-command-run.h b/src/core/hook/wee-hook-command-run.h new file mode 100644 index 000000000..95355547c --- /dev/null +++ b/src/core/hook/wee-hook-command-run.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_COMMAND_RUN_H +#define WEECHAT_HOOK_COMMAND_RUN_H + +struct t_weechat_plugin; +struct t_infolist_item; +struct t_gui_buffer; + +#define HOOK_COMMAND_RUN(hook, var) (((struct t_hook_command_run *)hook->hook_data)->var) + +typedef int (t_hook_callback_command_run)(const void *pointer, void *data, + struct t_gui_buffer *buffer, + const char *command); + +struct t_hook_command_run +{ + t_hook_callback_command_run *callback; /* command_run callback */ + char *command; /* name of command (without '/') */ +}; + +extern struct t_hook *hook_command_run (struct t_weechat_plugin *plugin, + const char *command, + t_hook_callback_command_run *callback, + const void *callback_pointer, + void *callback_data); +extern int hook_command_run_exec (struct t_gui_buffer *buffer, + const char *command); +extern void hook_command_run_free_data (struct t_hook *hook); +extern int hook_command_run_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_command_run_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_COMMAND_RUN_H */ diff --git a/src/core/hook/wee-hook-command.c b/src/core/hook/wee-hook-command.c new file mode 100644 index 000000000..a90f6c985 --- /dev/null +++ b/src/core/hook/wee-hook-command.c @@ -0,0 +1,651 @@ +/* + * wee-hook-command.c - WeeChat command hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-config.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-list.h" +#include "../wee-log.h" +#include "../wee-string.h" +#include "../wee-utf8.h" +#include "../../gui/gui-chat.h" +#include "../../plugins/plugin.h" + + +/* + * Searches for a command hook in list. + * + * Returns pointer to hook found, NULL if not found. + */ + +struct t_hook * +hook_command_search (struct t_weechat_plugin *plugin, const char *command) +{ + struct t_hook *ptr_hook; + + for (ptr_hook = weechat_hooks[HOOK_TYPE_COMMAND]; ptr_hook; + ptr_hook = ptr_hook->next_hook) + { + if (!ptr_hook->deleted + && (ptr_hook->plugin == plugin) + && (string_strcasecmp (HOOK_COMMAND(ptr_hook, command), command) == 0)) + return ptr_hook; + } + + /* command hook not found for plugin */ + return NULL; +} + +/* + * Builds variables/arrays that will be used for completion of commands + * arguments. + */ + +void +hook_command_build_completion (struct t_hook_command *hook_command) +{ + int i, j, k, length, num_items; + struct t_weelist *list; + char *pos_completion, *pos_double_pipe, *pos_start, *pos_end; + char **items; + const char *last_space, *ptr_template; + + /* split templates using "||" as separator */ + hook_command->cplt_num_templates = 1; + pos_completion = hook_command->completion; + while ((pos_double_pipe = strstr (pos_completion, "||")) != NULL) + { + hook_command->cplt_num_templates++; + pos_completion = pos_double_pipe + 2; + } + hook_command->cplt_templates = malloc (hook_command->cplt_num_templates * + sizeof (*hook_command->cplt_templates)); + for (i = 0; i < hook_command->cplt_num_templates; i++) + { + hook_command->cplt_templates[i] = NULL; + } + pos_completion = hook_command->completion; + i = 0; + while (pos_completion) + { + pos_double_pipe = strstr (pos_completion, "||"); + if (!pos_double_pipe) + pos_double_pipe = pos_completion + strlen (pos_completion); + pos_start = pos_completion; + pos_end = pos_double_pipe - 1; + if (pos_end < pos_start) + { + hook_command->cplt_templates[i] = strdup (""); + } + else + { + while (pos_start[0] == ' ') + { + pos_start++; + } + pos_end = pos_double_pipe - 1; + while ((pos_end > pos_start) && (pos_end[0] == ' ')) + { + pos_end--; + } + hook_command->cplt_templates[i] = string_strndup (pos_start, + pos_end - pos_start + 1); + } + i++; + if (!pos_double_pipe[0]) + pos_completion = NULL; + else + pos_completion = pos_double_pipe + 2; + } + + /* for each template, split/count args */ + hook_command->cplt_templates_static = malloc (hook_command->cplt_num_templates * + sizeof (*hook_command->cplt_templates_static)); + hook_command->cplt_template_num_args = malloc (hook_command->cplt_num_templates * + sizeof (*hook_command->cplt_template_num_args)); + hook_command->cplt_template_args = malloc (hook_command->cplt_num_templates * + sizeof (*hook_command->cplt_template_args)); + hook_command->cplt_template_num_args_concat = 0; + for (i = 0; i < hook_command->cplt_num_templates; i++) + { + /* + * build static part of template: it's first argument(s) which does not + * contain "%" + */ + last_space = NULL; + ptr_template = hook_command->cplt_templates[i]; + while (ptr_template && ptr_template[0]) + { + if (ptr_template[0] == ' ') + { + last_space = ptr_template; + break; + } + else if (ptr_template[0] == '%') + break; + ptr_template = utf8_next_char (ptr_template); + } + if (last_space) + { + last_space--; + while (last_space > hook_command->cplt_templates[i]) + { + if (last_space[0] != ' ') + break; + } + if (last_space < hook_command->cplt_templates[i]) + last_space = NULL; + else + last_space++; + } + if (last_space) + hook_command->cplt_templates_static[i] = string_strndup (hook_command->cplt_templates[i], + last_space - hook_command->cplt_templates[i]); + else + hook_command->cplt_templates_static[i] = strdup (hook_command->cplt_templates[i]); + + /* build arguments for each template */ + hook_command->cplt_template_args[i] = string_split (hook_command->cplt_templates[i], + " ", 0, 0, + &(hook_command->cplt_template_num_args[i])); + if (hook_command->cplt_template_num_args[i] > hook_command->cplt_template_num_args_concat) + hook_command->cplt_template_num_args_concat = hook_command->cplt_template_num_args[i]; + } + + /* + * build strings with concatenation of items from different templates + * for each argument: these strings will be used when completing argument + * if we can't find which template to use (for example for first argument) + */ + if (hook_command->cplt_template_num_args_concat == 0) + hook_command->cplt_template_args_concat = NULL; + else + { + hook_command->cplt_template_args_concat = malloc (hook_command->cplt_template_num_args_concat * + sizeof (*hook_command->cplt_template_args_concat)); + list = weelist_new (); + for (i = 0; i < hook_command->cplt_template_num_args_concat; i++) + { + /* first compute length */ + length = 1; + for (j = 0; j < hook_command->cplt_num_templates; j++) + { + if (i < hook_command->cplt_template_num_args[j]) + length += strlen (hook_command->cplt_template_args[j][i]) + 1; + } + /* alloc memory */ + hook_command->cplt_template_args_concat[i] = malloc (length); + if (hook_command->cplt_template_args_concat[i]) + { + /* concatenate items with "|" as separator */ + weelist_remove_all (list); + hook_command->cplt_template_args_concat[i][0] = '\0'; + for (j = 0; j < hook_command->cplt_num_templates; j++) + { + if (i < hook_command->cplt_template_num_args[j]) + { + items = string_split (hook_command->cplt_template_args[j][i], + "|", 0, 0, &num_items); + for (k = 0; k < num_items; k++) + { + if (!weelist_search (list, items[k])) + { + if (hook_command->cplt_template_args_concat[i][0]) + strcat (hook_command->cplt_template_args_concat[i], "|"); + strcat (hook_command->cplt_template_args_concat[i], + items[k]); + weelist_add (list, items[k], WEECHAT_LIST_POS_END, + NULL); + } + } + string_free_split (items); + } + } + } + } + weelist_free (list); + } +} + +/* + * Hooks a command. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_command (struct t_weechat_plugin *plugin, const char *command, + const char *description, + const char *args, const char *args_description, + const char *completion, + t_hook_callback_command *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_command *new_hook_command; + int priority; + const char *ptr_command; + + if (!callback) + return NULL; + + if (hook_command_search (plugin, command)) + { + gui_chat_printf (NULL, + _("%sError: another command \"%s\" already exists " + "for plugin \"%s\""), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + command, + plugin_get_name (plugin)); + return NULL; + } + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_command = malloc (sizeof (*new_hook_command)); + if (!new_hook_command) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (command, &priority, &ptr_command); + hook_init_data (new_hook, plugin, HOOK_TYPE_COMMAND, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_command; + new_hook_command->callback = callback; + new_hook_command->command = strdup ((ptr_command) ? ptr_command : + ((command) ? command : "")); + new_hook_command->description = strdup ((description) ? description : ""); + new_hook_command->args = strdup ((args) ? args : ""); + new_hook_command->args_description = strdup ((args_description) ? + args_description : ""); + new_hook_command->completion = strdup ((completion) ? completion : ""); + + /* build completion variables for command */ + new_hook_command->cplt_num_templates = 0; + new_hook_command->cplt_templates = NULL; + new_hook_command->cplt_templates_static = NULL; + new_hook_command->cplt_template_num_args = NULL; + new_hook_command->cplt_template_args = NULL; + new_hook_command->cplt_template_num_args_concat = 0; + new_hook_command->cplt_template_args_concat = NULL; + hook_command_build_completion (new_hook_command); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Executes a command hook. + * + * Returns: + * HOOK_COMMAND_EXEC_OK: command executed successfully + * HOOK_COMMAND_EXEC_ERROR: command executed and failed + * HOOK_COMMAND_EXEC_NOT_FOUND: command not found + * HOOK_COMMAND_EXEC_AMBIGUOUS_PLUGINS: command is ambiguous (same command + * exists for another plugin, and we don't know which one to run) + * HOOK_COMMAND_EXEC_AMBIGUOUS_INCOMPLETE: command is ambiguous (incomplete + * command and multiple commands start with this name) + * HOOK_COMMAND_EXEC_RUNNING: command is already running + */ + +int +hook_command_exec (struct t_gui_buffer *buffer, int any_plugin, + struct t_weechat_plugin *plugin, const char *string) +{ + struct t_hook *ptr_hook, *next_hook; + struct t_hook *hook_plugin, *hook_other_plugin, *hook_other_plugin2; + struct t_hook *hook_incomplete_command; + char **argv, **argv_eol; + const char *ptr_command_name; + int argc, rc, length_command_name, allow_incomplete_commands; + int count_other_plugin, count_incomplete_commands; + + if (!buffer || !string || !string[0]) + return HOOK_COMMAND_EXEC_NOT_FOUND; + + if (hook_command_run_exec (buffer, string) == WEECHAT_RC_OK_EAT) + return HOOK_COMMAND_EXEC_OK; + + argv = string_split (string, " ", 0, 0, &argc); + if (argc == 0) + { + string_free_split (argv); + return HOOK_COMMAND_EXEC_NOT_FOUND; + } + argv_eol = string_split (string, " ", 1, 0, NULL); + + ptr_command_name = utf8_next_char (argv[0]); + length_command_name = strlen (ptr_command_name); + + hook_exec_start (); + + hook_plugin = NULL; + hook_other_plugin = NULL; + hook_other_plugin2 = NULL; + hook_incomplete_command = NULL; + count_other_plugin = 0; + allow_incomplete_commands = CONFIG_BOOLEAN(config_look_command_incomplete); + count_incomplete_commands = 0; + ptr_hook = weechat_hooks[HOOK_TYPE_COMMAND]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted) + { + if (string_strcasecmp (ptr_command_name, + HOOK_COMMAND(ptr_hook, command)) == 0) + { + if (ptr_hook->plugin == plugin) + { + if (!hook_plugin) + hook_plugin = ptr_hook; + } + else + { + if (any_plugin) + { + if (!hook_other_plugin) + hook_other_plugin = ptr_hook; + else if (!hook_other_plugin2) + hook_other_plugin2 = ptr_hook; + count_other_plugin++; + } + } + } + else if (allow_incomplete_commands + && (string_strncasecmp (ptr_command_name, + HOOK_COMMAND(ptr_hook, command), + length_command_name) == 0)) + { + hook_incomplete_command = ptr_hook; + count_incomplete_commands++; + } + } + + ptr_hook = next_hook; + } + + rc = HOOK_COMMAND_EXEC_NOT_FOUND; + ptr_hook = NULL; + + if (hook_plugin || hook_other_plugin) + { + if (!hook_plugin && (count_other_plugin > 1) + && (hook_other_plugin->priority == hook_other_plugin2->priority)) + { + /* + * ambiguous: no command for current plugin, but more than one + * command was found for other plugins with the same priority + * => we don't know which one to run! + */ + rc = HOOK_COMMAND_EXEC_AMBIGUOUS_PLUGINS; + } + else + { + if (hook_plugin && hook_other_plugin) + { + /* + * if we have a command in current plugin and another plugin, + * choose the command with the higher priority (if priority + * is the same, always choose the command for the current + * plugin) + */ + ptr_hook = (hook_other_plugin->priority > hook_plugin->priority) ? + hook_other_plugin : hook_plugin; + } + else + { + /* + * choose the command for current plugin, if found, otherwise + * use command found in another plugin + */ + ptr_hook = (hook_plugin) ? hook_plugin : hook_other_plugin; + } + } + } + else if (hook_incomplete_command) + { + if (count_incomplete_commands == 1) + ptr_hook = hook_incomplete_command; + else + rc = HOOK_COMMAND_EXEC_AMBIGUOUS_INCOMPLETE; + } + + /* execute the command for the hook found */ + if (ptr_hook) + { + if (ptr_hook->running >= HOOK_COMMAND_MAX_CALLS) + { + /* loop in execution of command => do NOT execute again */ + rc = HOOK_COMMAND_EXEC_RUNNING; + } + else + { + /* execute the command! */ + ptr_hook->running++; + rc = (int) (HOOK_COMMAND(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + buffer, + argc, + argv, + argv_eol); + ptr_hook->running--; + if (rc == WEECHAT_RC_ERROR) + rc = HOOK_COMMAND_EXEC_ERROR; + else + rc = HOOK_COMMAND_EXEC_OK; + } + } + + string_free_split (argv); + string_free_split (argv_eol); + + hook_exec_end (); + + return rc; +} + +/* + * Frees data in a command hook. + */ + +void +hook_command_free_data (struct t_hook *hook) +{ + int i; + + if (!hook || !hook->hook_data) + return; + + if (HOOK_COMMAND(hook, command)) + { + free (HOOK_COMMAND(hook, command)); + HOOK_COMMAND(hook, command) = NULL; + } + if (HOOK_COMMAND(hook, description)) + { + free (HOOK_COMMAND(hook, description)); + HOOK_COMMAND(hook, description) = NULL; + } + if (HOOK_COMMAND(hook, args)) + { + free (HOOK_COMMAND(hook, args)); + HOOK_COMMAND(hook, args) = NULL; + } + if (HOOK_COMMAND(hook, args_description)) + { + free (HOOK_COMMAND(hook, args_description)); + HOOK_COMMAND(hook, args_description) = NULL; + } + if (HOOK_COMMAND(hook, completion)) + { + free (HOOK_COMMAND(hook, completion)); + HOOK_COMMAND(hook, completion) = NULL; + } + if (HOOK_COMMAND(hook, cplt_templates)) + { + for (i = 0; i < HOOK_COMMAND(hook, cplt_num_templates); i++) + { + if (HOOK_COMMAND(hook, cplt_templates)[i]) + free (HOOK_COMMAND(hook, cplt_templates)[i]); + if (HOOK_COMMAND(hook, cplt_templates_static)[i]) + free (HOOK_COMMAND(hook, cplt_templates_static)[i]); + string_free_split (HOOK_COMMAND(hook, cplt_template_args)[i]); + } + free (HOOK_COMMAND(hook, cplt_templates)); + } + if (HOOK_COMMAND(hook, cplt_templates_static)) + { + free (HOOK_COMMAND(hook, cplt_templates_static)); + HOOK_COMMAND(hook, cplt_templates_static) = NULL; + } + if (HOOK_COMMAND(hook, cplt_template_num_args)) + { + free (HOOK_COMMAND(hook, cplt_template_num_args)); + HOOK_COMMAND(hook, cplt_template_num_args) = NULL; + } + if (HOOK_COMMAND(hook, cplt_template_args)) + { + free (HOOK_COMMAND(hook, cplt_template_args)); + HOOK_COMMAND(hook, cplt_template_args) = NULL; + } + if (HOOK_COMMAND(hook, cplt_template_args_concat)) + { + for (i = 0; + i < HOOK_COMMAND(hook, cplt_template_num_args_concat); + i++) + { + free (HOOK_COMMAND(hook, cplt_template_args_concat[i])); + } + free (HOOK_COMMAND(hook, cplt_template_args_concat)); + HOOK_COMMAND(hook, cplt_template_args_concat) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds command hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_command_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_COMMAND(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "command", HOOK_COMMAND(hook, command))) + return 0; + if (!infolist_new_var_string (item, "description", + HOOK_COMMAND(hook, description))) + return 0; + if (!infolist_new_var_string (item, "description_nls", + (HOOK_COMMAND(hook, description) + && HOOK_COMMAND(hook, description)[0]) ? + _(HOOK_COMMAND(hook, description)) : "")) + return 0; + if (!infolist_new_var_string (item, "args", + HOOK_COMMAND(hook, args))) + return 0; + if (!infolist_new_var_string (item, "args_nls", + (HOOK_COMMAND(hook, args) + && HOOK_COMMAND(hook, args)[0]) ? + _(HOOK_COMMAND(hook, args)) : "")) + return 0; + if (!infolist_new_var_string (item, "args_description", + HOOK_COMMAND(hook, args_description))) + return 0; + if (!infolist_new_var_string (item, "args_description_nls", + (HOOK_COMMAND(hook, args_description) + && HOOK_COMMAND(hook, args_description)[0]) ? + _(HOOK_COMMAND(hook, args_description)) : "")) + return 0; + if (!infolist_new_var_string (item, "completion", HOOK_COMMAND(hook, completion))) + return 0; + + return 1; +} + +/* + * Prints command hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_command_print_log (struct t_hook *hook) +{ + int i, j; + + if (!hook || !hook->hook_data) + return; + + log_printf (" command data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_COMMAND(hook, callback)); + log_printf (" command . . . . . . . : '%s'", HOOK_COMMAND(hook, command)); + log_printf (" description . . . . . : '%s'", HOOK_COMMAND(hook, description)); + log_printf (" args. . . . . . . . . : '%s'", HOOK_COMMAND(hook, args)); + log_printf (" args_description. . . : '%s'", HOOK_COMMAND(hook, args_description)); + log_printf (" completion. . . . . . : '%s'", HOOK_COMMAND(hook, completion)); + log_printf (" cplt_num_templates. . : %d", HOOK_COMMAND(hook, cplt_num_templates)); + for (i = 0; i < HOOK_COMMAND(hook, cplt_num_templates); i++) + { + log_printf (" cplt_templates[%04d] . . . : '%s'", + i, HOOK_COMMAND(hook, cplt_templates)[i]); + log_printf (" cplt_templates_static[%04d]: '%s'", + i, HOOK_COMMAND(hook, cplt_templates_static)[i]); + log_printf (" num_args. . . . . . : %d", + HOOK_COMMAND(hook, cplt_template_num_args)[i]); + for (j = 0; j < HOOK_COMMAND(hook, cplt_template_num_args)[i]; j++) + { + log_printf (" args[%04d]. . . . . : '%s'", + j, HOOK_COMMAND(hook, cplt_template_args)[i][j]); + } + } + log_printf (" num_args_concat . . . : %d", HOOK_COMMAND(hook, cplt_template_num_args_concat)); + for (i = 0; i < HOOK_COMMAND(hook, cplt_template_num_args_concat); i++) + { + log_printf (" args_concat[%04d] . . : '%s'", + i, HOOK_COMMAND(hook, cplt_template_args_concat)[i]); + } +} diff --git a/src/core/hook/wee-hook-command.h b/src/core/hook/wee-hook-command.h new file mode 100644 index 000000000..537e986a1 --- /dev/null +++ b/src/core/hook/wee-hook-command.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_COMMAND_H +#define WEECHAT_HOOK_COMMAND_H + +struct t_weechat_plugin; +struct t_infolist_item; +struct t_gui_buffer; + +#define HOOK_COMMAND(hook, var) (((struct t_hook_command *)hook->hook_data)->var) + +/* max calls that can be done for a command (recursive calls) */ +#define HOOK_COMMAND_MAX_CALLS 5 + +/* return code when a command is executed */ +#define HOOK_COMMAND_EXEC_OK 1 +#define HOOK_COMMAND_EXEC_ERROR 0 +#define HOOK_COMMAND_EXEC_NOT_FOUND -1 +#define HOOK_COMMAND_EXEC_AMBIGUOUS_PLUGINS -2 +#define HOOK_COMMAND_EXEC_AMBIGUOUS_INCOMPLETE -3 +#define HOOK_COMMAND_EXEC_RUNNING -4 + +typedef int (t_hook_callback_command)(const void *pointer, void *data, + struct t_gui_buffer *buffer, + int argc, char **argv, char **argv_eol); + +struct t_hook_command +{ + t_hook_callback_command *callback; /* command callback */ + char *command; /* name of command (without '/') */ + char *description; /* (for /help) short cmd description*/ + char *args; /* (for /help) command arguments */ + char *args_description; /* (for /help) args long description*/ + char *completion; /* template for completion */ + + /* templates */ + int cplt_num_templates; /* number of templates for compl. */ + char **cplt_templates; /* completion templates */ + char **cplt_templates_static; /* static part of template (at */ + /* beginning */ + + /* arguments for each template */ + int *cplt_template_num_args; /* number of arguments for template */ + char ***cplt_template_args; /* arguments for each template */ + + /* concatenation of arg N for each template */ + int cplt_template_num_args_concat; /* number of concatenated arguments */ + char **cplt_template_args_concat; /* concatenated arguments */ +}; + +extern struct t_hook *hook_command (struct t_weechat_plugin *plugin, + const char *command, + const char *description, + const char *args, + const char *args_description, + const char *completion, + t_hook_callback_command *callback, + const void *callback_pointer, + void *callback_data); +extern int hook_command_exec (struct t_gui_buffer *buffer, int any_plugin, + struct t_weechat_plugin *plugin, + const char *string); +extern void hook_command_free_data (struct t_hook *hook); +extern int hook_command_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_command_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_COMMAND_H */ diff --git a/src/core/hook/wee-hook-completion.c b/src/core/hook/wee-hook-completion.c new file mode 100644 index 000000000..e10c9eb3e --- /dev/null +++ b/src/core/hook/wee-hook-completion.c @@ -0,0 +1,234 @@ +/* + * wee-hook-completion.c - WeeChat completion hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" +#include "../../gui/gui-completion.h" + + +/* + * Hooks a completion. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_completion (struct t_weechat_plugin *plugin, const char *completion_item, + const char *description, + t_hook_callback_completion *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_completion *new_hook_completion; + int priority; + const char *ptr_completion_item; + + if (!completion_item || !completion_item[0] + || strchr (completion_item, ' ') || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_completion = malloc (sizeof (*new_hook_completion)); + if (!new_hook_completion) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (completion_item, &priority, &ptr_completion_item); + hook_init_data (new_hook, plugin, HOOK_TYPE_COMPLETION, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_completion; + new_hook_completion->callback = callback; + new_hook_completion->completion_item = strdup ((ptr_completion_item) ? + ptr_completion_item : completion_item); + new_hook_completion->description = strdup ((description) ? + description : ""); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Gets a completion property as string. + */ + +const char * +hook_completion_get_string (struct t_gui_completion *completion, + const char *property) +{ + return gui_completion_get_string (completion, property); +} + +/* + * Adds a word for a completion. + */ + +void +hook_completion_list_add (struct t_gui_completion *completion, + const char *word, int nick_completion, + const char *where) +{ + gui_completion_list_add (completion, word, nick_completion, where); +} + +/* + * Executes a completion hook. + */ + +void +hook_completion_exec (struct t_weechat_plugin *plugin, + const char *completion_item, + struct t_gui_buffer *buffer, + struct t_gui_completion *completion) +{ + struct t_hook *ptr_hook, *next_hook; + const char *pos; + char *item; + + /* make C compiler happy */ + (void) plugin; + + if (!weechat_hooks[HOOK_TYPE_COMPLETION]) + return; + + hook_exec_start (); + + pos = strchr (completion_item, ':'); + item = (pos) ? + string_strndup (completion_item, pos - completion_item) : + strdup (completion_item); + if (!item) + return; + + ptr_hook = weechat_hooks[HOOK_TYPE_COMPLETION]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (string_strcasecmp (HOOK_COMPLETION(ptr_hook, completion_item), + item) == 0)) + { + ptr_hook->running = 1; + (void) (HOOK_COMPLETION(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + completion_item, + buffer, + completion); + ptr_hook->running = 0; + } + + ptr_hook = next_hook; + } + + free (item); + + hook_exec_end (); +} + +/* + * Frees data in a completion hook. + */ + +void +hook_completion_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_COMPLETION(hook, completion_item)) + { + free (HOOK_COMPLETION(hook, completion_item)); + HOOK_COMPLETION(hook, completion_item) = NULL; + } + if (HOOK_COMPLETION(hook, description)) + { + free (HOOK_COMPLETION(hook, description)); + HOOK_COMPLETION(hook, description) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds completion hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_completion_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_COMPLETION(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "completion_item", HOOK_COMPLETION(hook, completion_item))) + return 0; + if (!infolist_new_var_string (item, "description", HOOK_COMPLETION(hook, description))) + return 0; + if (!infolist_new_var_string (item, "description_nls", + (HOOK_COMPLETION(hook, description) + && HOOK_COMPLETION(hook, description)[0]) ? + _(HOOK_COMPLETION(hook, description)) : "")) + return 0; + + return 1; +} + +/* + * Prints completion hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_completion_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" completion data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_COMPLETION(hook, callback)); + log_printf (" completion_item . . . : '%s'", HOOK_COMPLETION(hook, completion_item)); + log_printf (" description . . . . . : '%s'", HOOK_COMPLETION(hook, description)); +} diff --git a/src/core/hook/wee-hook-completion.h b/src/core/hook/wee-hook-completion.h new file mode 100644 index 000000000..97f58e26c --- /dev/null +++ b/src/core/hook/wee-hook-completion.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_COMPLETION_H +#define WEECHAT_HOOK_COMPLETION_H + +struct t_weechat_plugin; +struct t_infolist_item; +struct t_gui_buffer; +struct t_gui_completion; + +#define HOOK_COMPLETION(hook, var) (((struct t_hook_completion *)hook->hook_data)->var) + +typedef int (t_hook_callback_completion)(const void *pointer, void *data, + const char *completion_item, + struct t_gui_buffer *buffer, + struct t_gui_completion *completion); + +struct t_hook_completion +{ + t_hook_callback_completion *callback; /* completion callback */ + char *completion_item; /* name of completion */ + char *description; /* description */ +}; + +extern struct t_hook *hook_completion (struct t_weechat_plugin *plugin, + const char *completion_item, + const char *description, + t_hook_callback_completion *callback, + const void *callback_pointer, + void *callback_data); +extern const char *hook_completion_get_string (struct t_gui_completion *completion, + const char *property); +extern void hook_completion_list_add (struct t_gui_completion *completion, + const char *word, int nick_completion, + const char *where); +extern void hook_completion_exec (struct t_weechat_plugin *plugin, + const char *completion_item, + struct t_gui_buffer *buffer, + struct t_gui_completion *completion); +extern void hook_completion_free_data (struct t_hook *hook); +extern int hook_completion_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_completion_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_COMPLETION_H */ diff --git a/src/core/hook/wee-hook-config.c b/src/core/hook/wee-hook-config.c new file mode 100644 index 000000000..0888268a7 --- /dev/null +++ b/src/core/hook/wee-hook-config.c @@ -0,0 +1,172 @@ +/* + * wee-hook-config.c - WeeChat config hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" + + +/* + * Hooks a configuration option. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_config (struct t_weechat_plugin *plugin, const char *option, + t_hook_callback_config *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_config *new_hook_config; + int priority; + const char *ptr_option; + + if (!callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_config = malloc (sizeof (*new_hook_config)); + if (!new_hook_config) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (option, &priority, &ptr_option); + hook_init_data (new_hook, plugin, HOOK_TYPE_CONFIG, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_config; + new_hook_config->callback = callback; + new_hook_config->option = strdup ((ptr_option) ? ptr_option : + ((option) ? option : "")); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Executes a config hook. + */ + +void +hook_config_exec (const char *option, const char *value) +{ + struct t_hook *ptr_hook, *next_hook; + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_CONFIG]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (!HOOK_CONFIG(ptr_hook, option) + || (string_match (option, HOOK_CONFIG(ptr_hook, option), 0)))) + { + ptr_hook->running = 1; + (void) (HOOK_CONFIG(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + option, + value); + ptr_hook->running = 0; + } + + ptr_hook = next_hook; + } + + hook_exec_end (); +} + +/* + * Frees data in a config hook. + */ + +void +hook_config_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_CONFIG(hook, option)) + { + free (HOOK_CONFIG(hook, option)); + HOOK_CONFIG(hook, option) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds config hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_config_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_CONFIG(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "option", HOOK_CONFIG(hook, option))) + return 0; + + return 1; +} + +/* + * Prints config hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_config_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" config data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_CONFIG(hook, callback)); + log_printf (" option. . . . . . . . : '%s'", HOOK_CONFIG(hook, option)); +} diff --git a/src/core/hook/wee-hook-config.h b/src/core/hook/wee-hook-config.h new file mode 100644 index 000000000..145189f56 --- /dev/null +++ b/src/core/hook/wee-hook-config.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_CONFIG_H +#define WEECHAT_HOOK_CONFIG_H + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_CONFIG(hook, var) (((struct t_hook_config *)hook->hook_data)->var) + +typedef int (t_hook_callback_config)(const void *pointer, void *data, + const char *option, const char *value); + +struct t_hook_config +{ + t_hook_callback_config *callback; /* config callback */ + char *option; /* config option for hook */ + /* (NULL = hook for all options) */ +}; + +extern struct t_hook *hook_config (struct t_weechat_plugin *plugin, + const char *option, + t_hook_callback_config *callback, + const void *callback_pointer, + void *callback_data); +extern void hook_config_exec (const char *option, const char *value); +extern void hook_config_free_data (struct t_hook *hook); +extern int hook_config_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_config_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_CONFIG_H */ diff --git a/src/core/hook/wee-hook-connect.c b/src/core/hook/wee-hook-connect.c new file mode 100644 index 000000000..5282d6490 --- /dev/null +++ b/src/core/hook/wee-hook-connect.c @@ -0,0 +1,420 @@ +/* + * wee-hook-connect.c - WeeChat connect hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * Copyright (C) 2012 Simon Arlott + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-network.h" +#include "../../plugins/plugin.h" + + +/* + * Hooks a connection to a peer (using fork). + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_connect (struct t_weechat_plugin *plugin, const char *proxy, + const char *address, int port, int ipv6, int retry, + void *gnutls_sess, void *gnutls_cb, int gnutls_dhkey_size, + const char *gnutls_priorities, const char *local_hostname, + t_hook_callback_connect *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_connect *new_hook_connect; + int i; + +#ifndef HAVE_GNUTLS + /* make C compiler happy */ + (void) gnutls_sess; + (void) gnutls_cb; + (void) gnutls_dhkey_size; + (void) gnutls_priorities; +#endif /* HAVE_GNUTLS */ + + if (!address || (port <= 0) || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_connect = malloc (sizeof (*new_hook_connect)); + if (!new_hook_connect) + { + free (new_hook); + return NULL; + } + + hook_init_data (new_hook, plugin, HOOK_TYPE_CONNECT, HOOK_PRIORITY_DEFAULT, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_connect; + new_hook_connect->callback = callback; + new_hook_connect->proxy = (proxy) ? strdup (proxy) : NULL; + new_hook_connect->address = strdup (address); + new_hook_connect->port = port; + new_hook_connect->sock = -1; + new_hook_connect->ipv6 = ipv6; + new_hook_connect->retry = retry; +#ifdef HAVE_GNUTLS + new_hook_connect->gnutls_sess = gnutls_sess; + new_hook_connect->gnutls_cb = gnutls_cb; + new_hook_connect->gnutls_dhkey_size = gnutls_dhkey_size; + new_hook_connect->gnutls_priorities = (gnutls_priorities) ? + strdup (gnutls_priorities) : NULL; +#endif /* HAVE_GNUTLS */ + new_hook_connect->local_hostname = (local_hostname) ? + strdup (local_hostname) : NULL; + new_hook_connect->child_read = -1; + new_hook_connect->child_write = -1; + new_hook_connect->child_recv = -1; + new_hook_connect->child_send = -1; + new_hook_connect->child_pid = 0; + new_hook_connect->hook_child_timer = NULL; + new_hook_connect->hook_fd = NULL; + new_hook_connect->handshake_hook_fd = NULL; + new_hook_connect->handshake_hook_timer = NULL; + new_hook_connect->handshake_fd_flags = 0; + new_hook_connect->handshake_ip_address = NULL; + if (!hook_socketpair_ok) + { + for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + { + new_hook_connect->sock_v4[i] = -1; + new_hook_connect->sock_v6[i] = -1; + } + } + + hook_add_to_list (new_hook); + + network_connect_with_fork (new_hook); + + return new_hook; +} + +/* + * Verifies certificates. + */ + +#ifdef HAVE_GNUTLS +int +hook_connect_gnutls_verify_certificates (gnutls_session_t tls_session) +{ + struct t_hook *ptr_hook; + int rc; + + rc = -1; + ptr_hook = weechat_hooks[HOOK_TYPE_CONNECT]; + while (ptr_hook) + { + /* looking for the right hook using to the gnutls session pointer */ + if (!ptr_hook->deleted + && HOOK_CONNECT(ptr_hook, gnutls_sess) + && (*(HOOK_CONNECT(ptr_hook, gnutls_sess)) == tls_session)) + { + rc = (int) (HOOK_CONNECT(ptr_hook, gnutls_cb)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + tls_session, NULL, 0, + NULL, 0, NULL, + WEECHAT_HOOK_CONNECT_GNUTLS_CB_VERIFY_CERT); + break; + } + ptr_hook = ptr_hook->next_hook; + } + + return rc; +} +#endif /* HAVE_GNUTLS */ + +/* + * Sets certificates. + */ + +#ifdef HAVE_GNUTLS +int +hook_connect_gnutls_set_certificates (gnutls_session_t tls_session, + const gnutls_datum_t *req_ca, int nreq, + const gnutls_pk_algorithm_t *pk_algos, + int pk_algos_len, +#if LIBGNUTLS_VERSION_NUMBER >= 0x020b00 /* 2.11.0 */ + gnutls_retr2_st *answer) +#else + gnutls_retr_st *answer) +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020b00 */ +{ + struct t_hook *ptr_hook; + int rc; + + rc = -1; + ptr_hook = weechat_hooks[HOOK_TYPE_CONNECT]; + while (ptr_hook) + { + /* looking for the right hook using to the gnutls session pointer */ + if (!ptr_hook->deleted + && HOOK_CONNECT(ptr_hook, gnutls_sess) + && (*(HOOK_CONNECT(ptr_hook, gnutls_sess)) == tls_session)) + { + rc = (int) (HOOK_CONNECT(ptr_hook, gnutls_cb)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + tls_session, req_ca, nreq, + pk_algos, pk_algos_len, answer, + WEECHAT_HOOK_CONNECT_GNUTLS_CB_SET_CERT); + break; + } + ptr_hook = ptr_hook->next_hook; + } + + return rc; +} +#endif /* HAVE_GNUTLS */ + +/* + * Frees data in a connect hook. + */ + +void +hook_connect_free_data (struct t_hook *hook) +{ + int i; + + if (!hook || !hook->hook_data) + return; + + if (HOOK_CONNECT(hook, proxy)) + { + free (HOOK_CONNECT(hook, proxy)); + HOOK_CONNECT(hook, proxy) = NULL; + } + if (HOOK_CONNECT(hook, address)) + { + free (HOOK_CONNECT(hook, address)); + HOOK_CONNECT(hook, address) = NULL; + } +#ifdef HAVE_GNUTLS + if (HOOK_CONNECT(hook, gnutls_priorities)) + { + free (HOOK_CONNECT(hook, gnutls_priorities)); + HOOK_CONNECT(hook, gnutls_priorities) = NULL; + } +#endif /* HAVE_GNUTLS */ + if (HOOK_CONNECT(hook, local_hostname)) + { + free (HOOK_CONNECT(hook, local_hostname)); + HOOK_CONNECT(hook, local_hostname) = NULL; + } + if (HOOK_CONNECT(hook, hook_child_timer)) + { + unhook (HOOK_CONNECT(hook, hook_child_timer)); + HOOK_CONNECT(hook, hook_child_timer) = NULL; + } + if (HOOK_CONNECT(hook, hook_fd)) + { + unhook (HOOK_CONNECT(hook, hook_fd)); + HOOK_CONNECT(hook, hook_fd) = NULL; + } + if (HOOK_CONNECT(hook, handshake_hook_fd)) + { + unhook (HOOK_CONNECT(hook, handshake_hook_fd)); + HOOK_CONNECT(hook, handshake_hook_fd) = NULL; + } + if (HOOK_CONNECT(hook, handshake_hook_timer)) + { + unhook (HOOK_CONNECT(hook, handshake_hook_timer)); + HOOK_CONNECT(hook, handshake_hook_timer) = NULL; + } + if (HOOK_CONNECT(hook, handshake_ip_address)) + { + free (HOOK_CONNECT(hook, handshake_ip_address)); + HOOK_CONNECT(hook, handshake_ip_address) = NULL; + } + if (HOOK_CONNECT(hook, child_pid) > 0) + { + kill (HOOK_CONNECT(hook, child_pid), SIGKILL); + waitpid (HOOK_CONNECT(hook, child_pid), NULL, 0); + HOOK_CONNECT(hook, child_pid) = 0; + } + if (HOOK_CONNECT(hook, child_read) != -1) + { + close (HOOK_CONNECT(hook, child_read)); + HOOK_CONNECT(hook, child_read) = -1; + } + if (HOOK_CONNECT(hook, child_write) != -1) + { + close (HOOK_CONNECT(hook, child_write)); + HOOK_CONNECT(hook, child_write) = -1; + } + if (HOOK_CONNECT(hook, child_recv) != -1) + { + close (HOOK_CONNECT(hook, child_recv)); + HOOK_CONNECT(hook, child_recv) = -1; + } + if (HOOK_CONNECT(hook, child_send) != -1) + { + close (HOOK_CONNECT(hook, child_send)); + HOOK_CONNECT(hook, child_send) = -1; + } + if (!hook_socketpair_ok) + { + for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + { + if (HOOK_CONNECT(hook, sock_v4[i]) != -1) + { + close (HOOK_CONNECT(hook, sock_v4[i])); + HOOK_CONNECT(hook, sock_v4[i]) = -1; + } + if (HOOK_CONNECT(hook, sock_v6[i]) != -1) + { + close (HOOK_CONNECT(hook, sock_v6[i])); + HOOK_CONNECT(hook, sock_v6[i]) = -1; + } + } + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds connect hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_connect_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_CONNECT(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "address", HOOK_CONNECT(hook, address))) + return 0; + if (!infolist_new_var_integer (item, "port", HOOK_CONNECT(hook, port))) + return 0; + if (!infolist_new_var_integer (item, "sock", HOOK_CONNECT(hook, sock))) + return 0; + if (!infolist_new_var_integer (item, "ipv6", HOOK_CONNECT(hook, ipv6))) + return 0; + if (!infolist_new_var_integer (item, "retry", HOOK_CONNECT(hook, retry))) + return 0; +#ifdef HAVE_GNUTLS + if (!infolist_new_var_pointer (item, "gnutls_sess", HOOK_CONNECT(hook, gnutls_sess))) + return 0; + if (!infolist_new_var_pointer (item, "gnutls_cb", HOOK_CONNECT(hook, gnutls_cb))) + return 0; + if (!infolist_new_var_integer (item, "gnutls_dhkey_size", HOOK_CONNECT(hook, gnutls_dhkey_size))) + return 0; +#endif /* HAVE_GNUTLS */ + if (!infolist_new_var_string (item, "local_hostname", HOOK_CONNECT(hook, local_hostname))) + return 0; + if (!infolist_new_var_integer (item, "child_read", HOOK_CONNECT(hook, child_read))) + return 0; + if (!infolist_new_var_integer (item, "child_write", HOOK_CONNECT(hook, child_write))) + return 0; + if (!infolist_new_var_integer (item, "child_recv", HOOK_CONNECT(hook, child_recv))) + return 0; + if (!infolist_new_var_integer (item, "child_send", HOOK_CONNECT(hook, child_send))) + return 0; + if (!infolist_new_var_integer (item, "child_pid", HOOK_CONNECT(hook, child_pid))) + return 0; + if (!infolist_new_var_pointer (item, "hook_child_timer", HOOK_CONNECT(hook, hook_child_timer))) + return 0; + if (!infolist_new_var_pointer (item, "hook_fd", HOOK_CONNECT(hook, hook_fd))) + return 0; + if (!infolist_new_var_pointer (item, "handshake_hook_fd", HOOK_CONNECT(hook, handshake_hook_fd))) + return 0; + if (!infolist_new_var_pointer (item, "handshake_hook_timer", HOOK_CONNECT(hook, handshake_hook_timer))) + return 0; + if (!infolist_new_var_integer (item, "handshake_fd_flags", HOOK_CONNECT(hook, handshake_fd_flags))) + return 0; + if (!infolist_new_var_string (item, "handshake_ip_address", HOOK_CONNECT(hook, handshake_ip_address))) + return 0; + + return 1; +} + +/* + * Prints connect hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_connect_print_log (struct t_hook *hook) +{ + int i; + + if (!hook || !hook->hook_data) + return; + + log_printf (" connect data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_CONNECT(hook, callback)); + log_printf (" address . . . . . . . : '%s'", HOOK_CONNECT(hook, address)); + log_printf (" port. . . . . . . . . : %d", HOOK_CONNECT(hook, port)); + log_printf (" sock. . . . . . . . . : %d", HOOK_CONNECT(hook, sock)); + log_printf (" ipv6. . . . . . . . . : %d", HOOK_CONNECT(hook, ipv6)); + log_printf (" retry . . . . . . . . : %d", HOOK_CONNECT(hook, retry)); +#ifdef HAVE_GNUTLS + log_printf (" gnutls_sess . . . . . : 0x%lx", HOOK_CONNECT(hook, gnutls_sess)); + log_printf (" gnutls_cb . . . . . . : 0x%lx", HOOK_CONNECT(hook, gnutls_cb)); + log_printf (" gnutls_dhkey_size . . : %d", HOOK_CONNECT(hook, gnutls_dhkey_size)); + log_printf (" gnutls_priorities . . : '%s'", HOOK_CONNECT(hook, gnutls_priorities)); +#endif /* HAVE_GNUTLS */ + log_printf (" local_hostname. . . . : '%s'", HOOK_CONNECT(hook, local_hostname)); + log_printf (" child_read. . . . . . : %d", HOOK_CONNECT(hook, child_read)); + log_printf (" child_write . . . . . : %d", HOOK_CONNECT(hook, child_write)); + log_printf (" child_recv. . . . . . : %d", HOOK_CONNECT(hook, child_recv)); + log_printf (" child_send. . . . . . : %d", HOOK_CONNECT(hook, child_send)); + log_printf (" child_pid . . . . . . : %d", HOOK_CONNECT(hook, child_pid)); + log_printf (" hook_child_timer. . . : 0x%lx", HOOK_CONNECT(hook, hook_child_timer)); + log_printf (" hook_fd . . . . . . . : 0x%lx", HOOK_CONNECT(hook, hook_fd)); + log_printf (" handshake_hook_fd . . : 0x%lx", HOOK_CONNECT(hook, handshake_hook_fd)); + log_printf (" handshake_hook_timer. : 0x%lx", HOOK_CONNECT(hook, handshake_hook_timer)); + log_printf (" handshake_fd_flags. . : %d", HOOK_CONNECT(hook, handshake_fd_flags)); + log_printf (" handshake_ip_address. : '%s'", HOOK_CONNECT(hook, handshake_ip_address)); + if (!hook_socketpair_ok) + { + for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + { + log_printf (" sock_v4[%d]. . . . . . : '%d'", HOOK_CONNECT(hook, sock_v4[i])); + log_printf (" sock_v6[%d]. . . . . . : '%d'", HOOK_CONNECT(hook, sock_v6[i])); + } + } +} diff --git a/src/core/hook/wee-hook-connect.h b/src/core/hook/wee-hook-connect.h new file mode 100644 index 000000000..b179bf064 --- /dev/null +++ b/src/core/hook/wee-hook-connect.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * Copyright (C) 2012 Simon Arlott + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_CONNECT_H +#define WEECHAT_HOOK_CONNECT_H + +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_CONNECT(hook, var) (((struct t_hook_connect *)hook->hook_data)->var) + +/* used if socketpair() function is NOT available */ +#define HOOK_CONNECT_MAX_SOCKETS 4 + +typedef int (t_hook_callback_connect)(const void *pointer, void *data, + int status, int gnutls_rc, int sock, + const char *error, + const char *ip_address); + +#ifdef HAVE_GNUTLS +typedef int (gnutls_callback_t)(const void *pointer, void *data, + gnutls_session_t tls_session, + const gnutls_datum_t *req_ca, int nreq, + const gnutls_pk_algorithm_t *pk_algos, + int pk_algos_len, +#if LIBGNUTLS_VERSION_NUMBER >= 0x020b00 /* 2.11.0 */ + gnutls_retr2_st *answer, +#else + gnutls_retr_st *answer, +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020b00 */ + int action); +#endif /* HAVE_GNUTLS */ + +struct t_hook_connect +{ + t_hook_callback_connect *callback; /* connect callback */ + char *proxy; /* proxy (optional) */ + char *address; /* peer address */ + int port; /* peer port */ + int ipv6; /* use IPv6 */ + int sock; /* socket (set when connected) */ + int retry; /* retry count */ +#ifdef HAVE_GNUTLS + gnutls_session_t *gnutls_sess; /* GnuTLS session (SSL connection) */ + gnutls_callback_t *gnutls_cb; /* GnuTLS callback during handshake */ + int gnutls_dhkey_size; /* Diffie Hellman Key Exchange size */ + char *gnutls_priorities; /* GnuTLS priorities */ +#endif /* HAVE_GNUTLS */ + char *local_hostname; /* force local hostname (optional) */ + int child_read; /* to read data in pipe from child */ + int child_write; /* to write data in pipe for child */ + int child_recv; /* to read data from child socket */ + int child_send; /* to write data to child socket */ + pid_t child_pid; /* pid of child process (connecting) */ + struct t_hook *hook_child_timer; /* timer for child process timeout */ + struct t_hook *hook_fd; /* pointer to fd hook */ + struct t_hook *handshake_hook_fd; /* fd hook for handshake */ + struct t_hook *handshake_hook_timer; /* timer for handshake timeout */ + int handshake_fd_flags; /* socket flags saved for handshake */ + char *handshake_ip_address; /* ip address (used for handshake) */ + /* sockets used if socketpair() is NOT available */ + int sock_v4[HOOK_CONNECT_MAX_SOCKETS]; /* IPv4 sockets for connecting */ + int sock_v6[HOOK_CONNECT_MAX_SOCKETS]; /* IPv6 sockets for connecting */ +}; + +extern struct t_hook *hook_connect (struct t_weechat_plugin *plugin, + const char *proxy, const char *address, + int port, int ipv6, int retry, + void *gnutls_session, void *gnutls_cb, + int gnutls_dhkey_size, + const char *gnutls_priorities, + const char *local_hostname, + t_hook_callback_connect *callback, + const void *callback_pointer, + void *callback_data); +#ifdef HAVE_GNUTLS +extern int hook_connect_gnutls_verify_certificates (gnutls_session_t tls_session); +extern int hook_connect_gnutls_set_certificates (gnutls_session_t tls_session, + const gnutls_datum_t *req_ca, int nreq, + const gnutls_pk_algorithm_t *pk_algos, + int pk_algos_len, +#if LIBGNUTLS_VERSION_NUMBER >= 0x020b00 /* 2.11.0 */ + gnutls_retr2_st *answer); +#else + gnutls_retr_st *answer); +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020b00 */ +#endif /* HAVE_GNUTLS */ +extern void hook_connect_free_data (struct t_hook *hook); +extern int hook_connect_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_connect_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_CONNECT_H */ diff --git a/src/core/hook/wee-hook-fd.c b/src/core/hook/wee-hook-fd.c new file mode 100644 index 000000000..bc69301a2 --- /dev/null +++ b/src/core/hook/wee-hook-fd.c @@ -0,0 +1,331 @@ +/* + * wee-hook-fd.c - WeeChat fd hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <poll.h> +#include <fcntl.h> +#include <errno.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-util.h" +#include "../../gui/gui-chat.h" + + +struct pollfd *hook_fd_pollfd = NULL; /* file descriptors for poll() */ +int hook_fd_pollfd_count = 0; /* number of file descriptors */ + + +/* + * Searches for a fd hook in list. + * + * Returns pointer to hook found, NULL if not found. + */ + +struct t_hook * +hook_fd_search (int fd) +{ + struct t_hook *ptr_hook; + + for (ptr_hook = weechat_hooks[HOOK_TYPE_FD]; ptr_hook; + ptr_hook = ptr_hook->next_hook) + { + if (!ptr_hook->deleted && (HOOK_FD(ptr_hook, fd) == fd)) + return ptr_hook; + } + + /* fd hook not found */ + return NULL; +} + +/* + * Reallocates the "struct pollfd" array for poll(). + */ + +void +hook_fd_realloc_pollfd () +{ + struct pollfd *ptr_pollfd; + int count; + + if (hooks_count[HOOK_TYPE_FD] == hook_fd_pollfd_count) + return; + + count = hooks_count[HOOK_TYPE_FD]; + + if (count == 0) + { + if (hook_fd_pollfd) + { + free (hook_fd_pollfd); + hook_fd_pollfd = NULL; + } + } + else + { + ptr_pollfd = realloc (hook_fd_pollfd, + count * sizeof (struct pollfd)); + if (!ptr_pollfd) + return; + hook_fd_pollfd = ptr_pollfd; + } + + hook_fd_pollfd_count = count; +} + +/* + * Callback called when a fd hook is added in the list of hooks. + */ + +void +hook_fd_add_cb (struct t_hook *hook) +{ + /* make C compiler happy */ + (void) hook; + + hook_fd_realloc_pollfd (); +} + +/* + * Callback called when a fd hook is removed from the list of hooks. + */ + +void +hook_fd_remove_cb (struct t_hook *hook) +{ + /* make C compiler happy */ + (void) hook; + + hook_fd_realloc_pollfd (); +} + +/* + * Hooks a fd event. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_fd (struct t_weechat_plugin *plugin, int fd, int flag_read, + int flag_write, int flag_exception, + t_hook_callback_fd *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_fd *new_hook_fd; + + if ((fd < 0) || hook_fd_search (fd) || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_fd = malloc (sizeof (*new_hook_fd)); + if (!new_hook_fd) + { + free (new_hook); + return NULL; + } + + hook_init_data (new_hook, plugin, HOOK_TYPE_FD, HOOK_PRIORITY_DEFAULT, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_fd; + new_hook_fd->callback = callback; + new_hook_fd->fd = fd; + new_hook_fd->flags = 0; + new_hook_fd->error = 0; + if (flag_read) + new_hook_fd->flags |= HOOK_FD_FLAG_READ; + if (flag_write) + new_hook_fd->flags |= HOOK_FD_FLAG_WRITE; + if (flag_exception) + new_hook_fd->flags |= HOOK_FD_FLAG_EXCEPTION; + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Executes fd hooks: + * - poll() on fie descriptors + * - call of hook fd callbacks if needed. + */ + +void +hook_fd_exec () +{ + int i, num_fd, timeout, ready, found; + struct t_hook *ptr_hook, *next_hook; + + if (!weechat_hooks[HOOK_TYPE_FD]) + return; + + /* build an array of "struct pollfd" for poll() */ + num_fd = 0; + for (ptr_hook = weechat_hooks[HOOK_TYPE_FD]; ptr_hook; + ptr_hook = ptr_hook->next_hook) + { + if (!ptr_hook->deleted) + { + /* skip invalid file descriptors */ + if ((fcntl (HOOK_FD(ptr_hook,fd), F_GETFD) == -1) + && (errno == EBADF)) + { + if (HOOK_FD(ptr_hook, error) == 0) + { + HOOK_FD(ptr_hook, error) = errno; + gui_chat_printf (NULL, + _("%sError: bad file descriptor (%d) " + "used in hook_fd"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + HOOK_FD(ptr_hook, fd)); + } + } + else + { + if (num_fd > hook_fd_pollfd_count) + break; + + hook_fd_pollfd[num_fd].fd = HOOK_FD(ptr_hook, fd); + hook_fd_pollfd[num_fd].events = 0; + hook_fd_pollfd[num_fd].revents = 0; + if (HOOK_FD(ptr_hook, flags) & HOOK_FD_FLAG_READ) + hook_fd_pollfd[num_fd].events |= POLLIN; + if (HOOK_FD(ptr_hook, flags) & HOOK_FD_FLAG_WRITE) + hook_fd_pollfd[num_fd].events |= POLLOUT; + + num_fd++; + } + } + } + + /* perform the poll() */ + timeout = hook_timer_get_time_to_next (); + if (hook_process_pending) + timeout = 0; + ready = poll (hook_fd_pollfd, num_fd, timeout); + if (ready <= 0) + return; + + /* execute callbacks for file descriptors with activity */ + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_FD]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running) + { + found = 0; + for (i = 0; i < num_fd; i++) + { + if (hook_fd_pollfd[i].fd == HOOK_FD(ptr_hook, fd) + && hook_fd_pollfd[i].revents) + { + found = 1; + break; + } + } + if (found) + { + ptr_hook->running = 1; + (void) (HOOK_FD(ptr_hook, callback)) ( + ptr_hook->callback_pointer, + ptr_hook->callback_data, + HOOK_FD(ptr_hook, fd)); + ptr_hook->running = 0; + } + } + + ptr_hook = next_hook; + } + + hook_exec_end (); +} + +/* + * Frees data in a fd hook. + */ + +void +hook_fd_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds fd hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_fd_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_FD(hook, callback))) + return 0; + if (!infolist_new_var_integer (item, "fd", HOOK_FD(hook, fd))) + return 0; + if (!infolist_new_var_integer (item, "flags", HOOK_FD(hook, flags))) + return 0; + if (!infolist_new_var_integer (item, "error", HOOK_FD(hook, error))) + return 0; + + return 1; +} + +/* + * Prints fd hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_fd_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" fd data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_FD(hook, callback)); + log_printf (" fd. . . . . . . . . . : %d", HOOK_FD(hook, fd)); + log_printf (" flags . . . . . . . . : %d", HOOK_FD(hook, flags)); + log_printf (" error . . . . . . . . : %d", HOOK_FD(hook, error)); +} diff --git a/src/core/hook/wee-hook-fd.h b/src/core/hook/wee-hook-fd.h new file mode 100644 index 000000000..502dff95b --- /dev/null +++ b/src/core/hook/wee-hook-fd.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_FD_H +#define WEECHAT_HOOK_FD_H + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_FD(hook, var) (((struct t_hook_fd *)hook->hook_data)->var) + +/* flags for fd hooks */ +#define HOOK_FD_FLAG_READ 1 +#define HOOK_FD_FLAG_WRITE 2 +#define HOOK_FD_FLAG_EXCEPTION 4 + +typedef int (t_hook_callback_fd)(const void *pointer, void *data, int fd); + +struct t_hook_fd +{ + t_hook_callback_fd *callback; /* fd callback */ + int fd; /* socket or file descriptor */ + int flags; /* fd flags (read,write,..) */ + int error; /* contains errno if error occurred */ + /* with fd */ +}; + +extern void hook_fd_add_cb (struct t_hook *hook); +extern void hook_fd_remove_cb (struct t_hook *hook); +extern struct t_hook *hook_fd (struct t_weechat_plugin *plugin, int fd, + int flag_read, int flag_write, + int flag_exception, + t_hook_callback_fd *callback, + const void *callback_pointer, + void *callback_data); +extern void hook_fd_exec (); +extern void hook_fd_free_data (struct t_hook *hook); +extern int hook_fd_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_fd_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_FD_H */ diff --git a/src/core/hook/wee-hook-focus.c b/src/core/hook/wee-hook-focus.c new file mode 100644 index 000000000..effb60aee --- /dev/null +++ b/src/core/hook/wee-hook-focus.c @@ -0,0 +1,317 @@ +/* + * wee-hook-focus.c - WeeChat focus hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hashtable.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" + + +/* + * Hooks a focus. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_focus (struct t_weechat_plugin *plugin, + const char *area, + t_hook_callback_focus *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_focus *new_hook_focus; + int priority; + const char *ptr_area; + + if (!area || !area[0] || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_focus = malloc (sizeof (*new_hook_focus)); + if (!new_hook_focus) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (area, &priority, &ptr_area); + hook_init_data (new_hook, plugin, HOOK_TYPE_FOCUS, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_focus; + new_hook_focus->callback = callback; + new_hook_focus->area = strdup ((ptr_area) ? ptr_area : area); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Adds keys of a hashtable into another. + */ + +void +hook_focus_hashtable_map_cb (void *data, + struct t_hashtable *hashtable, + const void *key, const void *value) +{ + struct t_hashtable *hashtable1; + + /* make C compiler happy */ + (void) hashtable; + + hashtable1 = (struct t_hashtable *)data; + + if (hashtable1 && key && value) + hashtable_set (hashtable1, (const char *)key, (const char *)value); +} + +/* + * Adds keys of a hashtable into another (adding suffix "2" to keys). + */ + +void +hook_focus_hashtable_map2_cb (void *data, + struct t_hashtable *hashtable, + const void *key, const void *value) +{ + struct t_hashtable *hashtable1; + int length; + char *key2; + + /* make C compiler happy */ + (void) hashtable; + + hashtable1 = (struct t_hashtable *)data; + + length = strlen ((const char *)key) + 1 + 1; + key2 = malloc (length); + if (key2) + { + snprintf (key2, length, "%s2", (const char *)key); + if (hashtable1 && key && value) + hashtable_set (hashtable1, key2, (const char *)value); + free (key2); + } +} + +/* + * Gets data for focus on (x,y) on screen. + * + * Argument hashtable_focus2 is not NULL only for a mouse gesture (it's for + * point where mouse button has been released). + */ + +struct t_hashtable * +hook_focus_get_data (struct t_hashtable *hashtable_focus1, + struct t_hashtable *hashtable_focus2) +{ + struct t_hook *ptr_hook, *next_hook; + struct t_hashtable *hashtable1, *hashtable2, *hashtable_ret; + const char *focus1_chat, *focus1_bar_item_name, *keys; + char **list_keys, *new_key; + int num_keys, i, length, focus1_is_chat; + + if (!hashtable_focus1) + return NULL; + + focus1_chat = hashtable_get (hashtable_focus1, "_chat"); + focus1_is_chat = (focus1_chat && (strcmp (focus1_chat, "1") == 0)); + focus1_bar_item_name = hashtable_get (hashtable_focus1, "_bar_item_name"); + + hashtable1 = hashtable_dup (hashtable_focus1); + if (!hashtable1) + return NULL; + hashtable2 = (hashtable_focus2) ? hashtable_dup (hashtable_focus2) : NULL; + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_FOCUS]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && ((focus1_is_chat + && (strcmp (HOOK_FOCUS(ptr_hook, area), "chat") == 0)) + || (focus1_bar_item_name && focus1_bar_item_name[0] + && (strcmp (HOOK_FOCUS(ptr_hook, area), focus1_bar_item_name) == 0)))) + { + /* run callback for focus #1 */ + ptr_hook->running = 1; + hashtable_ret = (HOOK_FOCUS(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + hashtable1); + ptr_hook->running = 0; + if (hashtable_ret) + { + if (hashtable_ret != hashtable1) + { + /* + * add keys of hashtable_ret into hashtable1 + * and destroy it + */ + hashtable_map (hashtable_ret, + &hook_focus_hashtable_map_cb, + hashtable1); + hashtable_free (hashtable_ret); + } + } + + /* run callback for focus #2 */ + if (hashtable2) + { + ptr_hook->running = 1; + hashtable_ret = (HOOK_FOCUS(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + hashtable2); + ptr_hook->running = 0; + if (hashtable_ret) + { + if (hashtable_ret != hashtable2) + { + /* + * add keys of hashtable_ret into hashtable2 + * and destroy it + */ + hashtable_map (hashtable_ret, + &hook_focus_hashtable_map_cb, + hashtable2); + hashtable_free (hashtable_ret); + } + } + } + } + + ptr_hook = next_hook; + } + + if (hashtable2) + { + hashtable_map (hashtable2, + &hook_focus_hashtable_map2_cb, hashtable1); + hashtable_free (hashtable2); + } + else + { + keys = hashtable_get_string (hashtable1, "keys"); + if (keys) + { + list_keys = string_split (keys, ",", 0, 0, &num_keys); + if (list_keys) + { + for (i = 0; i < num_keys; i++) + { + length = strlen (list_keys[i]) + 1 + 1; + new_key = malloc (length); + if (new_key) + { + snprintf (new_key, length, "%s2", list_keys[i]); + hashtable_set (hashtable1, new_key, + hashtable_get (hashtable1, + list_keys[i])); + free (new_key); + } + } + string_free_split (list_keys); + } + } + } + + hook_exec_end (); + + return hashtable1; +} + +/* + * Frees data in a focus hook. + */ + +void +hook_focus_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_FOCUS(hook, area)) + { + free (HOOK_FOCUS(hook, area)); + HOOK_FOCUS(hook, area) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds focus hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_focus_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_FOCUS(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "area", HOOK_FOCUS(hook, area))) + return 0; + + return 1; +} + +/* + * Prints focus hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_focus_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" focus data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_FOCUS(hook, callback)); + log_printf (" area. . . . . . . . . : '%s'", HOOK_FOCUS(hook, area)); +} diff --git a/src/core/hook/wee-hook-focus.h b/src/core/hook/wee-hook-focus.h new file mode 100644 index 000000000..e4fc0bb9f --- /dev/null +++ b/src/core/hook/wee-hook-focus.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_FOCUS_H +#define WEECHAT_HOOK_FOCUS_H + +struct t_weechat_plugin; +struct t_hashtable; +struct t_infolist_item; + +#define HOOK_FOCUS(hook, var) (((struct t_hook_focus *)hook->hook_data)->var) + +typedef struct t_hashtable *(t_hook_callback_focus)(const void *pointer, + void *data, + struct t_hashtable *info); + +struct t_hook_focus +{ + t_hook_callback_focus *callback; /* focus callback */ + char *area; /* "chat" or bar item name */ +}; + +extern struct t_hook *hook_focus (struct t_weechat_plugin *plugin, + const char *area, + t_hook_callback_focus *callback, + const void *callback_pointer, + void *callback_data); +extern struct t_hashtable *hook_focus_get_data (struct t_hashtable *hashtable_focus1, + struct t_hashtable *hashtable_focus2); +extern void hook_focus_free_data (struct t_hook *hook); +extern int hook_focus_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_focus_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_FOCUS_H */ diff --git a/src/core/hook/wee-hook-hdata.c b/src/core/hook/wee-hook-hdata.c new file mode 100644 index 000000000..bb674df9b --- /dev/null +++ b/src/core/hook/wee-hook-hdata.c @@ -0,0 +1,206 @@ +/* + * wee-hook-hdata.c - WeeChat hdata hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hashtable.h" +#include "../wee-hdata.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" + + +/* + * Hooks a hdata. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_hdata (struct t_weechat_plugin *plugin, const char *hdata_name, + const char *description, + t_hook_callback_hdata *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_hdata *new_hook_hdata; + int priority; + const char *ptr_hdata_name; + + if (!hdata_name || !hdata_name[0] || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_hdata = malloc (sizeof (*new_hook_hdata)); + if (!new_hook_hdata) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (hdata_name, &priority, &ptr_hdata_name); + hook_init_data (new_hook, plugin, HOOK_TYPE_HDATA, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_hdata; + new_hook_hdata->callback = callback; + new_hook_hdata->hdata_name = strdup ((ptr_hdata_name) ? + ptr_hdata_name : hdata_name); + new_hook_hdata->description = strdup ((description) ? description : ""); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Gets hdata via hdata hook. + */ + +struct t_hdata * +hook_hdata_get (struct t_weechat_plugin *plugin, const char *hdata_name) +{ + struct t_hook *ptr_hook, *next_hook; + struct t_hdata *value; + + /* make C compiler happy */ + (void) plugin; + + if (!hdata_name || !hdata_name[0]) + return NULL; + + if (weechat_hdata) + { + value = hashtable_get (weechat_hdata, hdata_name); + if (value) + return value; + } + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_HDATA]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (strcmp (HOOK_HDATA(ptr_hook, hdata_name), hdata_name) == 0)) + { + ptr_hook->running = 1; + value = (HOOK_HDATA(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + HOOK_HDATA(ptr_hook, hdata_name)); + ptr_hook->running = 0; + + hook_exec_end (); + return value; + } + + ptr_hook = next_hook; + } + + hook_exec_end (); + + /* hdata not found */ + return NULL; +} + +/* + * Frees data in a hdata hook. + */ + +void +hook_hdata_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_HDATA(hook, hdata_name)) + { + free (HOOK_HDATA(hook, hdata_name)); + HOOK_HDATA(hook, hdata_name) = NULL; + } + if (HOOK_HDATA(hook, description)) + { + free (HOOK_HDATA(hook, description)); + HOOK_HDATA(hook, description) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds hdata hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_hdata_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_HDATA(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "hdata_name", HOOK_HDATA(hook, hdata_name))) + return 0; + if (!infolist_new_var_string (item, "description", HOOK_HDATA(hook, description))) + return 0; + if (!infolist_new_var_string (item, "description_nls", + (HOOK_HDATA(hook, description) + && HOOK_HDATA(hook, description)[0]) ? + _(HOOK_HDATA(hook, description)) : "")) + return 0; + + return 1; +} + +/* + * Prints hdata hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_hdata_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" hdata data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_HDATA(hook, callback)); + log_printf (" hdata_name. . . . . . : '%s'", HOOK_HDATA(hook, hdata_name)); + log_printf (" description . . . . . : '%s'", HOOK_HDATA(hook, description)); +} diff --git a/src/core/hook/wee-hook-hdata.h b/src/core/hook/wee-hook-hdata.h new file mode 100644 index 000000000..dfd4583cb --- /dev/null +++ b/src/core/hook/wee-hook-hdata.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_HDATA_H +#define WEECHAT_HOOK_HDATA_H + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_HDATA(hook, var) (((struct t_hook_hdata *)hook->hook_data)->var) + +typedef struct t_hdata *(t_hook_callback_hdata)(const void *pointer, + void *data, + const char *hdata_name); + +struct t_hook_hdata +{ + t_hook_callback_hdata *callback; /* hdata callback */ + char *hdata_name; /* hdata name */ + char *description; /* description */ +}; + +extern struct t_hook *hook_hdata (struct t_weechat_plugin *plugin, + const char *hdata_name, + const char *description, + t_hook_callback_hdata *callback, + const void *callback_pointer, + void *callback_data); +extern struct t_hdata *hook_hdata_get (struct t_weechat_plugin *plugin, + const char *hdata_name); +extern void hook_hdata_free_data (struct t_hook *hook); +extern int hook_hdata_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_hdata_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_HDATA_H */ diff --git a/src/core/hook/wee-hook-hsignal.c b/src/core/hook/wee-hook-hsignal.c new file mode 100644 index 000000000..6f2d1490c --- /dev/null +++ b/src/core/hook/wee-hook-hsignal.c @@ -0,0 +1,179 @@ +/* + * wee-hook-hsignal.c - WeeChat hsignal hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" +#include "../../plugins/plugin.h" + + +/* + * Hooks a hsignal (signal with hashtable). + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_hsignal (struct t_weechat_plugin *plugin, const char *signal, + t_hook_callback_hsignal *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_hsignal *new_hook_hsignal; + int priority; + const char *ptr_signal; + + if (!signal || !signal[0] || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_hsignal = malloc (sizeof (*new_hook_hsignal)); + if (!new_hook_hsignal) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (signal, &priority, &ptr_signal); + hook_init_data (new_hook, plugin, HOOK_TYPE_HSIGNAL, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_hsignal; + new_hook_hsignal->callback = callback; + new_hook_hsignal->signal = strdup ((ptr_signal) ? ptr_signal : signal); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Sends a hsignal (signal with hashtable). + */ + +int +hook_hsignal_send (const char *signal, struct t_hashtable *hashtable) +{ + struct t_hook *ptr_hook, *next_hook; + int rc; + + rc = WEECHAT_RC_OK; + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_HSIGNAL]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (string_match (signal, HOOK_HSIGNAL(ptr_hook, signal), 0))) + { + ptr_hook->running = 1; + rc = (HOOK_HSIGNAL(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + signal, + hashtable); + ptr_hook->running = 0; + + if (rc == WEECHAT_RC_OK_EAT) + break; + } + + ptr_hook = next_hook; + } + + hook_exec_end (); + + return rc; +} + +/* + * Frees data in a hsignal hook. + */ + +void +hook_hsignal_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_HSIGNAL(hook, signal)) + { + free (HOOK_HSIGNAL(hook, signal)); + HOOK_HSIGNAL(hook, signal) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds hsignal hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_hsignal_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_HSIGNAL(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "signal", HOOK_HSIGNAL(hook, signal))) + return 0; + + return 1; +} + +/* + * Prints hsignal hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_hsignal_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" signal data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_HSIGNAL(hook, callback)); + log_printf (" signal. . . . . . . . : '%s'", HOOK_HSIGNAL(hook, signal)); +} diff --git a/src/core/hook/wee-hook-hsignal.h b/src/core/hook/wee-hook-hsignal.h new file mode 100644 index 000000000..c1375f5bd --- /dev/null +++ b/src/core/hook/wee-hook-hsignal.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_HSIGNAL_H +#define WEECHAT_HOOK_HSIGNAL_H + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_HSIGNAL(hook, var) (((struct t_hook_hsignal *)hook->hook_data)->var) + +typedef int (t_hook_callback_hsignal)(const void *pointer, void *data, + const char *signal, + struct t_hashtable *hashtable); + +struct t_hook_hsignal +{ + t_hook_callback_hsignal *callback; /* signal callback */ + char *signal; /* signal selected (may begin or end */ + /* with "*", "*" == any signal) */ +}; + +extern struct t_hook *hook_hsignal (struct t_weechat_plugin *plugin, + const char *signal, + t_hook_callback_hsignal *callback, + const void *callback_pointer, + void *callback_data); +extern int hook_hsignal_send (const char *signal, + struct t_hashtable *hashtable); +extern void hook_hsignal_free_data (struct t_hook *hook); +extern int hook_hsignal_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_hsignal_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_HSIGNAL_H */ diff --git a/src/core/hook/wee-hook-info-hashtable.c b/src/core/hook/wee-hook-info-hashtable.c new file mode 100644 index 000000000..89767eeda --- /dev/null +++ b/src/core/hook/wee-hook-info-hashtable.c @@ -0,0 +1,232 @@ +/* + * wee-hook-info-hashtable.c - WeeChat info_hashtable hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" + + +/* + * Hooks an info using hashtable. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_info_hashtable (struct t_weechat_plugin *plugin, const char *info_name, + const char *description, const char *args_description, + const char *output_description, + t_hook_callback_info_hashtable *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_info_hashtable *new_hook_info_hashtable; + int priority; + const char *ptr_info_name; + + if (!info_name || !info_name[0] || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_info_hashtable = malloc (sizeof (*new_hook_info_hashtable)); + if (!new_hook_info_hashtable) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (info_name, &priority, &ptr_info_name); + hook_init_data (new_hook, plugin, HOOK_TYPE_INFO_HASHTABLE, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_info_hashtable; + new_hook_info_hashtable->callback = callback; + new_hook_info_hashtable->info_name = strdup ((ptr_info_name) ? + ptr_info_name : info_name); + new_hook_info_hashtable->description = strdup ((description) ? description : ""); + new_hook_info_hashtable->args_description = strdup ((args_description) ? + args_description : ""); + new_hook_info_hashtable->output_description = strdup ((output_description) ? + output_description : ""); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Gets info (as hashtable) via info hook. + */ + +struct t_hashtable * +hook_info_get_hashtable (struct t_weechat_plugin *plugin, const char *info_name, + struct t_hashtable *hashtable) +{ + struct t_hook *ptr_hook, *next_hook; + struct t_hashtable *value; + + /* make C compiler happy */ + (void) plugin; + + if (!info_name || !info_name[0]) + return NULL; + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_INFO_HASHTABLE]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (string_strcasecmp (HOOK_INFO_HASHTABLE(ptr_hook, info_name), + info_name) == 0)) + { + ptr_hook->running = 1; + value = (HOOK_INFO_HASHTABLE(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + info_name, + hashtable); + ptr_hook->running = 0; + + hook_exec_end (); + return value; + } + + ptr_hook = next_hook; + } + + hook_exec_end (); + + /* info not found */ + return NULL; +} + +/* + * Frees data in an info_hashtable hook. + */ + +void +hook_info_hashtable_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_INFO_HASHTABLE(hook, info_name)) + { + free (HOOK_INFO_HASHTABLE(hook, info_name)); + HOOK_INFO_HASHTABLE(hook, info_name) = NULL; + } + if (HOOK_INFO_HASHTABLE(hook, description)) + { + free (HOOK_INFO_HASHTABLE(hook, description)); + HOOK_INFO_HASHTABLE(hook, description) = NULL; + } + if (HOOK_INFO_HASHTABLE(hook, args_description)) + { + free (HOOK_INFO_HASHTABLE(hook, args_description)); + HOOK_INFO_HASHTABLE(hook, args_description) = NULL; + } + if (HOOK_INFO_HASHTABLE(hook, output_description)) + { + free (HOOK_INFO_HASHTABLE(hook, output_description)); + HOOK_INFO_HASHTABLE(hook, output_description) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds info_hashtable hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_info_hashtable_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_INFO_HASHTABLE(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "info_name", HOOK_INFO_HASHTABLE(hook, info_name))) + return 0; + if (!infolist_new_var_string (item, "description", HOOK_INFO_HASHTABLE(hook, description))) + return 0; + if (!infolist_new_var_string (item, "description_nls", + (HOOK_INFO_HASHTABLE(hook, description) + && HOOK_INFO_HASHTABLE(hook, description)[0]) ? + _(HOOK_INFO_HASHTABLE(hook, description)) : "")) + return 0; + if (!infolist_new_var_string (item, "args_description", HOOK_INFO_HASHTABLE(hook, args_description))) + return 0; + if (!infolist_new_var_string (item, "args_description_nls", + (HOOK_INFO_HASHTABLE(hook, args_description) + && HOOK_INFO_HASHTABLE(hook, args_description)[0]) ? + _(HOOK_INFO_HASHTABLE(hook, args_description)) : "")) + return 0; + if (!infolist_new_var_string (item, "output_description", HOOK_INFO_HASHTABLE(hook, output_description))) + return 0; + if (!infolist_new_var_string (item, "output_description_nls", + (HOOK_INFO_HASHTABLE(hook, output_description) + && HOOK_INFO_HASHTABLE(hook, output_description)[0]) ? + _(HOOK_INFO_HASHTABLE(hook, output_description)) : "")) + return 0; + + return 1; +} + +/* + * Prints info_hashtable hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_info_hashtable_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" info_hashtable data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_INFO_HASHTABLE(hook, callback)); + log_printf (" info_name . . . . . . : '%s'", HOOK_INFO_HASHTABLE(hook, info_name)); + log_printf (" description . . . . . : '%s'", HOOK_INFO_HASHTABLE(hook, description)); + log_printf (" args_description. . . : '%s'", HOOK_INFO_HASHTABLE(hook, args_description)); + log_printf (" output_description. . : '%s'", HOOK_INFO_HASHTABLE(hook, output_description)); +} diff --git a/src/core/hook/wee-hook-info-hashtable.h b/src/core/hook/wee-hook-info-hashtable.h new file mode 100644 index 000000000..40a152b2a --- /dev/null +++ b/src/core/hook/wee-hook-info-hashtable.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_INFO_HASHTABLE_H +#define WEECHAT_HOOK_INFO_HASHTABLE_H + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_INFO_HASHTABLE(hook, var) (((struct t_hook_info_hashtable *)hook->hook_data)->var) + +typedef struct t_hashtable *(t_hook_callback_info_hashtable)(const void *pointer, + void *data, + const char *info_name, + struct t_hashtable *hashtable); + +struct t_hook_info_hashtable +{ + t_hook_callback_info_hashtable *callback; /* info_hashtable callback */ + char *info_name; /* name of info returned */ + char *description; /* description */ + char *args_description; /* description of arguments */ + char *output_description; /* description of output (hashtable) */ +}; + +extern struct t_hook *hook_info_hashtable (struct t_weechat_plugin *plugin, + const char *info_name, + const char *description, + const char *args_description, + const char *output_description, + t_hook_callback_info_hashtable *callback, + const void *callback_pointer, + void *callback_data); +extern struct t_hashtable *hook_info_get_hashtable (struct t_weechat_plugin *plugin, + const char *info_name, + struct t_hashtable *hashtable); +extern void hook_info_hashtable_free_data (struct t_hook *hook); +extern int hook_info_hashtable_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_info_hashtable_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_INFO_HASHTABLE_H */ diff --git a/src/core/hook/wee-hook-info.c b/src/core/hook/wee-hook-info.c new file mode 100644 index 000000000..aeafca640 --- /dev/null +++ b/src/core/hook/wee-hook-info.c @@ -0,0 +1,213 @@ +/* + * wee-hook-info.c - WeeChat info hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" + + +/* + * Hooks an info. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_info (struct t_weechat_plugin *plugin, const char *info_name, + const char *description, const char *args_description, + t_hook_callback_info *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_info *new_hook_info; + int priority; + const char *ptr_info_name; + + if (!info_name || !info_name[0] || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_info = malloc (sizeof (*new_hook_info)); + if (!new_hook_info) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (info_name, &priority, &ptr_info_name); + hook_init_data (new_hook, plugin, HOOK_TYPE_INFO, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_info; + new_hook_info->callback = callback; + new_hook_info->info_name = strdup ((ptr_info_name) ? + ptr_info_name : info_name); + new_hook_info->description = strdup ((description) ? description : ""); + new_hook_info->args_description = strdup ((args_description) ? + args_description : ""); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Gets info (as string) via info hook. + */ + +const char * +hook_info_get (struct t_weechat_plugin *plugin, const char *info_name, + const char *arguments) +{ + struct t_hook *ptr_hook, *next_hook; + const char *value; + + /* make C compiler happy */ + (void) plugin; + + if (!info_name || !info_name[0]) + return NULL; + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_INFO]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (string_strcasecmp (HOOK_INFO(ptr_hook, info_name), + info_name) == 0)) + { + ptr_hook->running = 1; + value = (HOOK_INFO(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + info_name, + arguments); + ptr_hook->running = 0; + + hook_exec_end (); + return value; + } + + ptr_hook = next_hook; + } + + hook_exec_end (); + + /* info not found */ + return NULL; +} + +/* + * Frees data in an info hook. + */ + +void +hook_info_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_INFO(hook, info_name)) + { + free (HOOK_INFO(hook, info_name)); + HOOK_INFO(hook, info_name) = NULL; + } + if (HOOK_INFO(hook, description)) + { + free (HOOK_INFO(hook, description)); + HOOK_INFO(hook, description) = NULL; + } + if (HOOK_INFO(hook, args_description)) + { + free (HOOK_INFO(hook, args_description)); + HOOK_INFO(hook, args_description) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds info hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_info_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_INFO(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "info_name", HOOK_INFO(hook, info_name))) + return 0; + if (!infolist_new_var_string (item, "description", HOOK_INFO(hook, description))) + return 0; + if (!infolist_new_var_string (item, "description_nls", + (HOOK_INFO(hook, description) + && HOOK_INFO(hook, description)[0]) ? + _(HOOK_INFO(hook, description)) : "")) + return 0; + if (!infolist_new_var_string (item, "args_description", HOOK_INFO(hook, args_description))) + return 0; + if (!infolist_new_var_string (item, "args_description_nls", + (HOOK_INFO(hook, args_description) + && HOOK_INFO(hook, args_description)[0]) ? + _(HOOK_INFO(hook, args_description)) : "")) + return 0; + + return 1; +} + +/* + * Prints info hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_info_print_log (struct t_hook *hook) +{ + log_printf (" info data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_INFO(hook, callback)); + log_printf (" info_name . . . . . . : '%s'", HOOK_INFO(hook, info_name)); + log_printf (" description . . . . . : '%s'", HOOK_INFO(hook, description)); + log_printf (" args_description. . . : '%s'", HOOK_INFO(hook, args_description)); +} diff --git a/src/core/hook/wee-hook-info.h b/src/core/hook/wee-hook-info.h new file mode 100644 index 000000000..273933fa4 --- /dev/null +++ b/src/core/hook/wee-hook-info.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_INFO_H +#define WEECHAT_HOOK_INFO_H + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_INFO(hook, var) (((struct t_hook_info *)hook->hook_data)->var) + +typedef const char *(t_hook_callback_info)(const void *pointer, void *data, + const char *info_name, + const char *arguments); + +struct t_hook_info +{ + t_hook_callback_info *callback; /* info callback */ + char *info_name; /* name of info returned */ + char *description; /* description */ + char *args_description; /* description of arguments */ +}; + +extern struct t_hook *hook_info (struct t_weechat_plugin *plugin, + const char *info_name, + const char *description, + const char *args_description, + t_hook_callback_info *callback, + const void *callback_pointer, + void *callback_data); +extern const char *hook_info_get (struct t_weechat_plugin *plugin, + const char *info_name, + const char *arguments); +extern void hook_info_free_data (struct t_hook *hook); +extern int hook_info_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_info_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_INFO_H */ diff --git a/src/core/hook/wee-hook-infolist.c b/src/core/hook/wee-hook-infolist.c new file mode 100644 index 000000000..f2e049142 --- /dev/null +++ b/src/core/hook/wee-hook-infolist.c @@ -0,0 +1,233 @@ +/* + * wee-hook-infolist.c - WeeChat infolist hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" + + +/* + * Hooks an infolist. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_infolist (struct t_weechat_plugin *plugin, const char *infolist_name, + const char *description, const char *pointer_description, + const char *args_description, + t_hook_callback_infolist *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_infolist *new_hook_infolist; + int priority; + const char *ptr_infolist_name; + + if (!infolist_name || !infolist_name[0] || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_infolist = malloc (sizeof (*new_hook_infolist)); + if (!new_hook_infolist) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (infolist_name, &priority, &ptr_infolist_name); + hook_init_data (new_hook, plugin, HOOK_TYPE_INFOLIST, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_infolist; + new_hook_infolist->callback = callback; + new_hook_infolist->infolist_name = strdup ((ptr_infolist_name) ? + ptr_infolist_name : infolist_name); + new_hook_infolist->description = strdup ((description) ? description : ""); + new_hook_infolist->pointer_description = strdup ((pointer_description) ? + pointer_description : ""); + new_hook_infolist->args_description = strdup ((args_description) ? + args_description : ""); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Gets an infolist via infolist hook. + */ + +struct t_infolist * +hook_infolist_get (struct t_weechat_plugin *plugin, const char *infolist_name, + void *pointer, const char *arguments) +{ + struct t_hook *ptr_hook, *next_hook; + struct t_infolist *value; + + /* make C compiler happy */ + (void) plugin; + + if (!infolist_name || !infolist_name[0]) + return NULL; + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_INFOLIST]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (string_strcasecmp (HOOK_INFOLIST(ptr_hook, infolist_name), + infolist_name) == 0)) + { + ptr_hook->running = 1; + value = (HOOK_INFOLIST(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + infolist_name, + pointer, + arguments); + ptr_hook->running = 0; + + hook_exec_end (); + return value; + } + + ptr_hook = next_hook; + } + + hook_exec_end (); + + /* infolist not found */ + return NULL; +} + +/* + * Frees data in an infolist hook. + */ + +void +hook_infolist_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_INFOLIST(hook, infolist_name)) + { + free (HOOK_INFOLIST(hook, infolist_name)); + HOOK_INFOLIST(hook, infolist_name) = NULL; + } + if (HOOK_INFOLIST(hook, description)) + { + free (HOOK_INFOLIST(hook, description)); + HOOK_INFOLIST(hook, description) = NULL; + } + if (HOOK_INFOLIST(hook, pointer_description)) + { + free (HOOK_INFOLIST(hook, pointer_description)); + HOOK_INFOLIST(hook, pointer_description) = NULL; + } + if (HOOK_INFOLIST(hook, args_description)) + { + free (HOOK_INFOLIST(hook, args_description)); + HOOK_INFOLIST(hook, args_description) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds infolist hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_infolist_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_INFOLIST(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "infolist_name", HOOK_INFOLIST(hook, infolist_name))) + return 0; + if (!infolist_new_var_string (item, "description", HOOK_INFOLIST(hook, description))) + return 0; + if (!infolist_new_var_string (item, "description_nls", + (HOOK_INFOLIST(hook, description) + && HOOK_INFOLIST(hook, description)[0]) ? + _(HOOK_INFOLIST(hook, description)) : "")) + return 0; + if (!infolist_new_var_string (item, "pointer_description", HOOK_INFOLIST(hook, pointer_description))) + return 0; + if (!infolist_new_var_string (item, "pointer_description_nls", + (HOOK_INFOLIST(hook, pointer_description) + && HOOK_INFOLIST(hook, pointer_description)[0]) ? + _(HOOK_INFOLIST(hook, pointer_description)) : "")) + return 0; + if (!infolist_new_var_string (item, "args_description", HOOK_INFOLIST(hook, args_description))) + return 0; + if (!infolist_new_var_string (item, "args_description_nls", + (HOOK_INFOLIST(hook, args_description) + && HOOK_INFOLIST(hook, args_description)[0]) ? + _(HOOK_INFOLIST(hook, args_description)) : "")) + return 0; + + return 1; +} + +/* + * Prints infolist hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_infolist_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" infolist data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_INFOLIST(hook, callback)); + log_printf (" infolist_name . . . . : '%s'", HOOK_INFOLIST(hook, infolist_name)); + log_printf (" description . . . . . : '%s'", HOOK_INFOLIST(hook, description)); + log_printf (" pointer_description . : '%s'", HOOK_INFOLIST(hook, pointer_description)); + log_printf (" args_description. . . : '%s'", HOOK_INFOLIST(hook, args_description)); +} diff --git a/src/core/hook/wee-hook-infolist.h b/src/core/hook/wee-hook-infolist.h new file mode 100644 index 000000000..933382a28 --- /dev/null +++ b/src/core/hook/wee-hook-infolist.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_INFOLIST_H +#define WEECHAT_HOOK_INFOLIST_H + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_INFOLIST(hook, var) (((struct t_hook_infolist *)hook->hook_data)->var) + +typedef struct t_infolist *(t_hook_callback_infolist)(const void *pointer, + void *data, + const char *infolist_name, + void *obj_pointer, + const char *arguments); + +struct t_hook_infolist +{ + t_hook_callback_infolist *callback; /* infolist callback */ + char *infolist_name; /* name of infolist returned */ + char *description; /* description */ + char *pointer_description; /* description of pointer */ + char *args_description; /* description of arguments */ +}; + +extern struct t_hook *hook_infolist (struct t_weechat_plugin *plugin, + const char *infolist_name, + const char *description, + const char *pointer_description, + const char *args_description, + t_hook_callback_infolist *callback, + const void *callback_pointer, + void *callback_data); +extern struct t_infolist *hook_infolist_get (struct t_weechat_plugin *plugin, + const char *infolist_name, + void *pointer, + const char *arguments); +extern void hook_infolist_free_data (struct t_hook *hook); +extern int hook_infolist_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_infolist_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_INFOLIST_H */ diff --git a/src/core/hook/wee-hook-line.c b/src/core/hook/wee-hook-line.c new file mode 100644 index 000000000..f08813cac --- /dev/null +++ b/src/core/hook/wee-hook-line.c @@ -0,0 +1,268 @@ +/* + * wee-hook-line.c - WeeChat line hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hashtable.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" +#include "../../gui/gui-buffer.h" +#include "../../gui/gui-line.h" +#include "../../plugins/plugin.h" + + +/* + * Hooks a line added in a buffer. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_line (struct t_weechat_plugin *plugin, const char *buffer_type, + const char *buffer_name, const char *tags, + t_hook_callback_line *callback, const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_line *new_hook_line; + + if (!callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_line = malloc (sizeof (*new_hook_line)); + if (!new_hook_line) + { + free (new_hook); + return NULL; + } + + hook_init_data (new_hook, plugin, HOOK_TYPE_LINE, HOOK_PRIORITY_DEFAULT, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_line; + new_hook_line->callback = callback; + if (!buffer_type || !buffer_type[0]) + new_hook_line->buffer_type = GUI_BUFFER_TYPE_FORMATTED; + else if (strcmp (buffer_type, "*") == 0) + new_hook_line->buffer_type = -1; + else + new_hook_line->buffer_type = gui_buffer_search_type (buffer_type); + new_hook_line->buffers = string_split ( + (buffer_name && buffer_name[0]) ? buffer_name : "*", + ",", 0, 0, &new_hook_line->num_buffers); + new_hook_line->tags_array = string_split_tags (tags, + &new_hook_line->tags_count); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Executes a line hook and updates the line data. + */ + +void +hook_line_exec (struct t_gui_line *line) +{ + struct t_hook *ptr_hook, *next_hook; + struct t_hashtable *hashtable, *hashtable2; + char str_value[128], *str_tags; + + if (!weechat_hooks[HOOK_TYPE_LINE]) + return; + + hashtable = NULL; + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_LINE]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted && !ptr_hook->running + && ((HOOK_LINE(ptr_hook, buffer_type) == -1) + || ((int)(line->data->buffer->type) == (HOOK_LINE(ptr_hook, buffer_type)))) + && gui_buffer_match_list_split (line->data->buffer, + HOOK_LINE(ptr_hook, num_buffers), + HOOK_LINE(ptr_hook, buffers)) + && (!HOOK_LINE(ptr_hook, tags_array) + || gui_line_match_tags (line->data, + HOOK_LINE(ptr_hook, tags_count), + HOOK_LINE(ptr_hook, tags_array)))) + { + /* create the hashtable that will be sent to callback */ + if (!hashtable) + { + hashtable = hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + if (!hashtable) + break; + } + HASHTABLE_SET_PTR("buffer", line->data->buffer); + HASHTABLE_SET_PTR("buffer_name", line->data->buffer->full_name); + HASHTABLE_SET_PTR("buffer_type", + gui_buffer_type_string[line->data->buffer->type]); + HASHTABLE_SET_INT("y", line->data->y); + HASHTABLE_SET_TIME("date", line->data->date); + HASHTABLE_SET_TIME("date_printed", line->data->date_printed); + HASHTABLE_SET_STR_NOT_NULL("str_time", line->data->str_time); + HASHTABLE_SET_INT("tags_count", line->data->tags_count); + str_tags = string_build_with_split_string ( + (const char **)line->data->tags_array, ","); + HASHTABLE_SET_STR_NOT_NULL("tags", str_tags); + if (str_tags) + free (str_tags); + HASHTABLE_SET_INT("displayed", line->data->displayed); + HASHTABLE_SET_INT("notify_level", line->data->notify_level); + HASHTABLE_SET_INT("highlight", line->data->highlight); + HASHTABLE_SET_STR_NOT_NULL("prefix", line->data->prefix); + HASHTABLE_SET_STR_NOT_NULL("message", line->data->message); + + /* run callback */ + ptr_hook->running = 1; + hashtable2 = (HOOK_LINE(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + hashtable); + ptr_hook->running = 0; + + if (hashtable2) + { + gui_line_hook_update (line, hashtable, hashtable2); + hashtable_free (hashtable2); + if (!line->data->buffer) + break; + } + } + + ptr_hook = next_hook; + } + + hook_exec_end (); + + if (hashtable) + hashtable_free (hashtable); +} + +/* + * Frees data in a line hook. + */ + +void +hook_line_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_LINE(hook, tags_array)) + { + string_free_split_tags (HOOK_LINE(hook, tags_array)); + HOOK_LINE(hook, tags_array) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds line hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_line_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_LINE(hook, callback))) + return 0; + if (!infolist_new_var_integer (item, "buffer_type", HOOK_LINE(hook, buffer_type))) + return 0; + if (!infolist_new_var_pointer (item, "buffers", HOOK_LINE(hook, buffers))) + return 0; + if (!infolist_new_var_integer (item, "num_buffers", HOOK_LINE(hook, num_buffers))) + return 0; + if (!infolist_new_var_integer (item, "tags_count", HOOK_LINE(hook, tags_count))) + return 0; + if (!infolist_new_var_pointer (item, "tags_array", HOOK_LINE(hook, tags_array))) + return 0; + + return 1; +} + +/* + * Prints line hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_line_print_log (struct t_hook *hook) +{ + int i, j; + + if (!hook || !hook->hook_data) + return; + + log_printf (" line data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_LINE(hook, callback)); + log_printf (" buffer_type . . . . . : %d", HOOK_LINE(hook, buffer_type)); + log_printf (" buffers . . . . . . . : 0x%lx", HOOK_LINE(hook, buffers)); + log_printf (" num_buffers . . . . . : %d", HOOK_LINE(hook, num_buffers)); + for (i = 0; i < HOOK_LINE(hook, num_buffers); i++) + { + log_printf (" buffers[%03d]. . . : '%s'", + i, HOOK_LINE(hook, buffers)[i]); + } + log_printf (" tags_count. . . . . . : %d", HOOK_LINE(hook, tags_count)); + log_printf (" tags_array. . . . . . : 0x%lx", HOOK_LINE(hook, tags_array)); + if (HOOK_LINE(hook, tags_array)) + { + for (i = 0; i < HOOK_LINE(hook, tags_count); i++) + { + for (j = 0; HOOK_LINE(hook, tags_array)[i][j]; j++) + { + log_printf (" tags_array[%03d][%03d]: '%s'", + i, + j, + HOOK_LINE(hook, tags_array)[i][j]); + } + } + } +} diff --git a/src/core/hook/wee-hook-line.h b/src/core/hook/wee-hook-line.h new file mode 100644 index 000000000..e385189e4 --- /dev/null +++ b/src/core/hook/wee-hook-line.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_LINE_H +#define WEECHAT_HOOK_LINE_H + +struct t_weechat_plugin; +struct t_infolist_item; +struct t_hashtable; +struct t_gui_line; + +#define HOOK_LINE(hook, var) (((struct t_hook_line *)hook->hook_data)->var) + +typedef struct t_hashtable *(t_hook_callback_line)(const void *pointer, + void *data, + struct t_hashtable *line); + +struct t_hook_line +{ + t_hook_callback_line *callback; /* line callback */ + int buffer_type; /* -1 = any type, ≥ 0: only this type*/ + char **buffers; /* list of buffer masks where the */ + /* hook is executed (see the */ + /* function "buffer_match_list") */ + int num_buffers; /* number of buffers in list */ + int tags_count; /* number of tags selected */ + char ***tags_array; /* tags selected (NULL = any) */ +}; + +extern struct t_hook *hook_line (struct t_weechat_plugin *plugin, + const char *buffer_type, + const char *buffer_name, + const char *tags, + t_hook_callback_line *callback, + const void *callback_pointer, + void *callback_data); +extern void hook_line_exec (struct t_gui_line *line); +extern void hook_line_free_data (struct t_hook *hook); +extern int hook_line_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_line_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_LINE_H */ diff --git a/src/core/hook/wee-hook-modifier.c b/src/core/hook/wee-hook-modifier.c new file mode 100644 index 000000000..654439e14 --- /dev/null +++ b/src/core/hook/wee-hook-modifier.c @@ -0,0 +1,204 @@ +/* + * wee-hook-modifier.c - WeeChat modifier hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" + + +/* + * Hooks a modifier. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_modifier (struct t_weechat_plugin *plugin, const char *modifier, + t_hook_callback_modifier *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_modifier *new_hook_modifier; + int priority; + const char *ptr_modifier; + + if (!modifier || !modifier[0] || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_modifier = malloc (sizeof (*new_hook_modifier)); + if (!new_hook_modifier) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (modifier, &priority, &ptr_modifier); + hook_init_data (new_hook, plugin, HOOK_TYPE_MODIFIER, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_modifier; + new_hook_modifier->callback = callback; + new_hook_modifier->modifier = strdup ((ptr_modifier) ? ptr_modifier : modifier); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Executes a modifier hook. + * + * Note: result must be freed after use. + */ + +char * +hook_modifier_exec (struct t_weechat_plugin *plugin, const char *modifier, + const char *modifier_data, const char *string) +{ + struct t_hook *ptr_hook, *next_hook; + char *new_msg, *message_modified; + + /* make C compiler happy */ + (void) plugin; + + if (!modifier || !modifier[0]) + return NULL; + + new_msg = NULL; + message_modified = strdup (string); + if (!message_modified) + return NULL; + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_MODIFIER]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (string_strcasecmp (HOOK_MODIFIER(ptr_hook, modifier), + modifier) == 0)) + { + ptr_hook->running = 1; + new_msg = (HOOK_MODIFIER(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + modifier, + modifier_data, + message_modified); + ptr_hook->running = 0; + + /* empty string returned => message dropped */ + if (new_msg && !new_msg[0]) + { + free (message_modified); + hook_exec_end (); + return new_msg; + } + + /* new message => keep it as base for next modifier */ + if (new_msg) + { + free (message_modified); + message_modified = new_msg; + } + } + + ptr_hook = next_hook; + } + + hook_exec_end (); + + return message_modified; +} + +/* + * Frees data in a modifier hook. + */ + +void +hook_modifier_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_MODIFIER(hook, modifier)) + { + free (HOOK_MODIFIER(hook, modifier)); + HOOK_MODIFIER(hook, modifier) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds modifier hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_modifier_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_MODIFIER(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "modifier", HOOK_MODIFIER(hook, modifier))) + return 0; + + return 1; +} + +/* + * Prints modifier hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_modifier_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" modifier data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_MODIFIER(hook, callback)); + log_printf (" modifier. . . . . . . : '%s'", HOOK_MODIFIER(hook, modifier)); +} diff --git a/src/core/hook/wee-hook-modifier.h b/src/core/hook/wee-hook-modifier.h new file mode 100644 index 000000000..09845c881 --- /dev/null +++ b/src/core/hook/wee-hook-modifier.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_MODIFIER_H +#define WEECHAT_HOOK_MODIFIER_H + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_MODIFIER(hook, var) (((struct t_hook_modifier *)hook->hook_data)->var) + +typedef char *(t_hook_callback_modifier)(const void *pointer, void *data, + const char *modifier, + const char *modifier_data, + const char *string); + +struct t_hook_modifier +{ + t_hook_callback_modifier *callback; /* modifier callback */ + char *modifier; /* name of modifier */ +}; + +extern struct t_hook *hook_modifier (struct t_weechat_plugin *plugin, + const char *modifier, + t_hook_callback_modifier *callback, + const void *callback_pointer, + void *callback_data); +extern char *hook_modifier_exec (struct t_weechat_plugin *plugin, + const char *modifier, + const char *modifier_data, + const char *string); +extern void hook_modifier_free_data (struct t_hook *hook); +extern int hook_modifier_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_modifier_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_MODIFIER_H */ diff --git a/src/core/hook/wee-hook-print.c b/src/core/hook/wee-hook-print.c new file mode 100644 index 000000000..9c359c466 --- /dev/null +++ b/src/core/hook/wee-hook-print.c @@ -0,0 +1,244 @@ +/* + * wee-hook-print.c - WeeChat print hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" +#include "../../gui/gui-color.h" +#include "../../gui/gui-line.h" + + +/* + * Hooks a message printed by WeeChat. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_print (struct t_weechat_plugin *plugin, struct t_gui_buffer *buffer, + const char *tags, const char *message, int strip_colors, + t_hook_callback_print *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_print *new_hook_print; + + if (!callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_print = malloc (sizeof (*new_hook_print)); + if (!new_hook_print) + { + free (new_hook); + return NULL; + } + + hook_init_data (new_hook, plugin, HOOK_TYPE_PRINT, HOOK_PRIORITY_DEFAULT, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_print; + new_hook_print->callback = callback; + new_hook_print->buffer = buffer; + new_hook_print->tags_array = string_split_tags (tags, + &new_hook_print->tags_count); + new_hook_print->message = (message) ? strdup (message) : NULL; + new_hook_print->strip_colors = strip_colors; + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Executes a print hook. + */ + +void +hook_print_exec (struct t_gui_buffer *buffer, struct t_gui_line *line) +{ + struct t_hook *ptr_hook, *next_hook; + char *prefix_no_color, *message_no_color; + + if (!weechat_hooks[HOOK_TYPE_PRINT]) + return; + + if (!line->data->message || !line->data->message[0]) + return; + + prefix_no_color = (line->data->prefix) ? + gui_color_decode (line->data->prefix, NULL) : NULL; + + message_no_color = gui_color_decode (line->data->message, NULL); + if (!message_no_color) + { + if (prefix_no_color) + free (prefix_no_color); + return; + } + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_PRINT]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (!HOOK_PRINT(ptr_hook, buffer) + || (buffer == HOOK_PRINT(ptr_hook, buffer))) + && (!HOOK_PRINT(ptr_hook, message) + || !HOOK_PRINT(ptr_hook, message)[0] + || string_strcasestr (prefix_no_color, HOOK_PRINT(ptr_hook, message)) + || string_strcasestr (message_no_color, HOOK_PRINT(ptr_hook, message))) + && (!HOOK_PRINT(ptr_hook, tags_array) + || gui_line_match_tags (line->data, + HOOK_PRINT(ptr_hook, tags_count), + HOOK_PRINT(ptr_hook, tags_array)))) + { + /* run callback */ + ptr_hook->running = 1; + (void) (HOOK_PRINT(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + buffer, + line->data->date, + line->data->tags_count, + (const char **)line->data->tags_array, + (int)line->data->displayed, (int)line->data->highlight, + (HOOK_PRINT(ptr_hook, strip_colors)) ? prefix_no_color : line->data->prefix, + (HOOK_PRINT(ptr_hook, strip_colors)) ? message_no_color : line->data->message); + ptr_hook->running = 0; + } + + ptr_hook = next_hook; + } + + if (prefix_no_color) + free (prefix_no_color); + if (message_no_color) + free (message_no_color); + + hook_exec_end (); +} + +/* + * Frees data in a print hook. + */ + +void +hook_print_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_PRINT(hook, tags_array)) + { + string_free_split_tags (HOOK_PRINT(hook, tags_array)); + HOOK_PRINT(hook, tags_array) = NULL; + } + if (HOOK_PRINT(hook, message)) + { + free (HOOK_PRINT(hook, message)); + HOOK_PRINT(hook, message) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds print hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_print_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_PRINT(hook, callback))) + return 0; + if (!infolist_new_var_pointer (item, "buffer", HOOK_PRINT(hook, buffer))) + return 0; + if (!infolist_new_var_integer (item, "tags_count", HOOK_PRINT(hook, tags_count))) + return 0; + if (!infolist_new_var_pointer (item, "tags_array", HOOK_PRINT(hook, tags_array))) + return 0; + if (!infolist_new_var_string (item, "message", HOOK_PRINT(hook, message))) + return 0; + if (!infolist_new_var_integer (item, "strip_colors", HOOK_PRINT(hook, strip_colors))) + return 0; + + return 1; +} + +/* + * Prints print hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_print_print_log (struct t_hook *hook) +{ + int i, j; + + if (!hook || !hook->hook_data) + return; + + log_printf (" print data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_PRINT(hook, callback)); + log_printf (" buffer. . . . . . . . : 0x%lx", HOOK_PRINT(hook, buffer)); + log_printf (" tags_count. . . . . . : %d", HOOK_PRINT(hook, tags_count)); + log_printf (" tags_array. . . . . . : 0x%lx", HOOK_PRINT(hook, tags_array)); + if (HOOK_PRINT(hook, tags_array)) + { + for (i = 0; i < HOOK_PRINT(hook, tags_count); i++) + { + for (j = 0; HOOK_PRINT(hook, tags_array)[i][j]; j++) + { + log_printf (" tags_array[%03d][%03d]: '%s'", + i, + j, + HOOK_PRINT(hook, tags_array)[i][j]); + } + } + } + log_printf (" message . . . . . . . : '%s'", HOOK_PRINT(hook, message)); + log_printf (" strip_colors. . . . . : %d", HOOK_PRINT(hook, strip_colors)); +} diff --git a/src/core/hook/wee-hook-print.h b/src/core/hook/wee-hook-print.h new file mode 100644 index 000000000..933675131 --- /dev/null +++ b/src/core/hook/wee-hook-print.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_PRINT_H +#define WEECHAT_HOOK_PRINT_H + +#include <time.h> + +struct t_weechat_plugin; +struct t_infolist_item; +struct t_gui_buffer; +struct t_gui_line; + +#define HOOK_PRINT(hook, var) (((struct t_hook_print *)hook->hook_data)->var) + +typedef int (t_hook_callback_print)(const void *pointer, void *data, + struct t_gui_buffer *buffer, + time_t date, int tags_count, + const char **tags, int displayed, + int highlight, const char *prefix, + const char *message); + +struct t_hook_print +{ + t_hook_callback_print *callback; /* print callback */ + struct t_gui_buffer *buffer; /* buffer selected (NULL = all) */ + int tags_count; /* number of tags selected */ + char ***tags_array; /* tags selected (NULL = any) */ + char *message; /* part of message (NULL/empty = all)*/ + int strip_colors; /* strip colors in msg for callback? */ +}; + +extern struct t_hook *hook_print (struct t_weechat_plugin *plugin, + struct t_gui_buffer *buffer, + const char *tags, const char *message, + int strip_colors, + t_hook_callback_print *callback, + const void *callback_pointer, + void *callback_data); +extern void hook_print_exec (struct t_gui_buffer *buffer, + struct t_gui_line *line); +extern void hook_print_free_data (struct t_hook *hook); +extern int hook_print_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_print_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_PRINT_H */ diff --git a/src/core/hook/wee-hook-process.c b/src/core/hook/wee-hook-process.c new file mode 100644 index 000000000..c3ac87ce4 --- /dev/null +++ b/src/core/hook/wee-hook-process.c @@ -0,0 +1,974 @@ +/* + * wee-hook-process.c - WeeChat process hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/wait.h> +#include <poll.h> +#include <fcntl.h> +#include <errno.h> + +#include "../weechat.h" +#include "../wee-hashtable.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" +#include "../wee-url.h" +#include "../../gui/gui-chat.h" +#include "../../plugins/plugin.h" + + +int hook_process_pending = 0; /* 1 if there are some process to */ + /* run (via fork) */ + + +void hook_process_run (struct t_hook *hook_process); + + +/* + * Hooks a process (using fork) with options in hashtable. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_process_hashtable (struct t_weechat_plugin *plugin, + const char *command, + struct t_hashtable *options, + int timeout, + t_hook_callback_process *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_process *new_hook_process; + char *stdout_buffer, *stderr_buffer, *error; + const char *ptr_value; + long number; + + stdout_buffer = NULL; + stderr_buffer = NULL; + new_hook = NULL; + new_hook_process = NULL; + + if (!command || !command[0] || !callback) + goto error; + + stdout_buffer = malloc (HOOK_PROCESS_BUFFER_SIZE + 1); + if (!stdout_buffer) + goto error; + + stderr_buffer = malloc (HOOK_PROCESS_BUFFER_SIZE + 1); + if (!stderr_buffer) + goto error; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + goto error; + + new_hook_process = malloc (sizeof (*new_hook_process)); + if (!new_hook_process) + goto error; + + hook_init_data (new_hook, plugin, HOOK_TYPE_PROCESS, HOOK_PRIORITY_DEFAULT, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_process; + new_hook_process->callback = callback; + new_hook_process->command = strdup (command); + new_hook_process->options = (options) ? hashtable_dup (options) : NULL; + new_hook_process->detached = (options && hashtable_has_key (options, + "detached")); + new_hook_process->timeout = timeout; + new_hook_process->child_read[HOOK_PROCESS_STDIN] = -1; + new_hook_process->child_read[HOOK_PROCESS_STDOUT] = -1; + new_hook_process->child_read[HOOK_PROCESS_STDERR] = -1; + new_hook_process->child_write[HOOK_PROCESS_STDIN] = -1; + new_hook_process->child_write[HOOK_PROCESS_STDOUT] = -1; + new_hook_process->child_write[HOOK_PROCESS_STDERR] = -1; + new_hook_process->child_pid = 0; + new_hook_process->hook_fd[HOOK_PROCESS_STDIN] = NULL; + new_hook_process->hook_fd[HOOK_PROCESS_STDOUT] = NULL; + new_hook_process->hook_fd[HOOK_PROCESS_STDERR] = NULL; + new_hook_process->hook_timer = NULL; + new_hook_process->buffer[HOOK_PROCESS_STDIN] = NULL; + new_hook_process->buffer[HOOK_PROCESS_STDOUT] = stdout_buffer; + new_hook_process->buffer[HOOK_PROCESS_STDERR] = stderr_buffer; + new_hook_process->buffer_size[HOOK_PROCESS_STDIN] = 0; + new_hook_process->buffer_size[HOOK_PROCESS_STDOUT] = 0; + new_hook_process->buffer_size[HOOK_PROCESS_STDERR] = 0; + new_hook_process->buffer_flush = HOOK_PROCESS_BUFFER_SIZE; + if (options) + { + ptr_value = hashtable_get (options, "buffer_flush"); + if (ptr_value && ptr_value[0]) + { + number = strtol (ptr_value, &error, 10); + if (error && !error[0] + && (number >= 1) && (number <= HOOK_PROCESS_BUFFER_SIZE)) + { + new_hook_process->buffer_flush = (int)number; + } + } + } + + hook_add_to_list (new_hook); + + if (weechat_debug_core >= 1) + { + gui_chat_printf (NULL, + "debug: hook_process: command=\"%s\", " + "options=\"%s\", timeout=%d", + new_hook_process->command, + hashtable_get_string (new_hook_process->options, + "keys_values"), + new_hook_process->timeout); + } + + if (strncmp (new_hook_process->command, "func:", 5) == 0) + hook_process_pending = 1; + else + hook_process_run (new_hook); + + return new_hook; + +error: + if (stdout_buffer) + free (stdout_buffer); + if (stderr_buffer) + free (stderr_buffer); + if (new_hook) + free (new_hook); + if (new_hook_process) + free (new_hook_process); + return NULL; +} + +/* + * Hooks a process (using fork). + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_process (struct t_weechat_plugin *plugin, + const char *command, + int timeout, + t_hook_callback_process *callback, + const void *callback_pointer, + void *callback_data) +{ + return hook_process_hashtable (plugin, command, NULL, timeout, + callback, callback_pointer, callback_data); +} + +/* + * Child process for hook process: executes command and returns string result + * into pipe for WeeChat process. + */ + +void +hook_process_child (struct t_hook *hook_process) +{ + char **exec_args, *arg0, str_arg[64]; + const char *ptr_url, *ptr_arg; + int rc, i, num_args; + FILE *f; + + /* read stdin from parent, if a pipe was defined */ + if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]) >= 0) + { + if (dup2 (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]), + STDIN_FILENO) < 0) + { + _exit (EXIT_FAILURE); + } + } + else + { + /* no stdin pipe from parent, use "/dev/null" for stdin stream */ + f = freopen ("/dev/null", "r", stdin); + (void) f; + } + if (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDIN]) >= 0) + close (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDIN])); + + /* redirect stdout/stderr to pipe (so that parent process can read them) */ + if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]) >= 0) + { + close (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT])); + if (dup2 (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDOUT]), + STDOUT_FILENO) < 0) + { + _exit (EXIT_FAILURE); + } + } + else + { + /* detached mode: write stdout in /dev/null */ + f = freopen ("/dev/null", "w", stdout); + (void) f; + } + if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]) >= 0) + { + close (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR])); + if (dup2 (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDERR]), + STDERR_FILENO) < 0) + { + _exit (EXIT_FAILURE); + } + } + else + { + /* detached mode: write stderr in /dev/null */ + f = freopen ("/dev/null", "w", stderr); + (void) f; + } + + rc = EXIT_FAILURE; + + if (strncmp (HOOK_PROCESS(hook_process, command), "url:", 4) == 0) + { + /* get URL output (on stdout or file, depending on options) */ + ptr_url = HOOK_PROCESS(hook_process, command) + 4; + while (ptr_url[0] == ' ') + { + ptr_url++; + } + rc = weeurl_download (ptr_url, HOOK_PROCESS(hook_process, options)); + } + else if (strncmp (HOOK_PROCESS(hook_process, command), "func:", 5) == 0) + { + /* run a function (via the hook callback) */ + rc = (int) (HOOK_PROCESS(hook_process, callback)) + (hook_process->callback_pointer, + hook_process->callback_data, + HOOK_PROCESS(hook_process, command), + WEECHAT_HOOK_PROCESS_CHILD, + NULL, NULL); + } + else + { + /* launch command */ + num_args = 0; + if (HOOK_PROCESS(hook_process, options)) + { + /* + * count number of arguments given in the hashtable options, + * keys are: "arg1", "arg2", ... + */ + while (1) + { + snprintf (str_arg, sizeof (str_arg), "arg%d", num_args + 1); + ptr_arg = hashtable_get (HOOK_PROCESS(hook_process, options), + str_arg); + if (!ptr_arg) + break; + num_args++; + } + } + if (num_args > 0) + { + /* + * if at least one argument was found in hashtable option, the + * "command" contains only path to binary (without arguments), and + * the arguments are in hashtable + */ + exec_args = malloc ((num_args + 2) * sizeof (exec_args[0])); + if (exec_args) + { + exec_args[0] = strdup (HOOK_PROCESS(hook_process, command)); + for (i = 1; i <= num_args; i++) + { + snprintf (str_arg, sizeof (str_arg), "arg%d", i); + ptr_arg = hashtable_get (HOOK_PROCESS(hook_process, options), + str_arg); + exec_args[i] = (ptr_arg) ? strdup (ptr_arg) : NULL; + } + exec_args[num_args + 1] = NULL; + } + } + else + { + /* + * if no arguments were found in hashtable, make an automatic split + * of command, like the shell does + */ + exec_args = string_split_shell (HOOK_PROCESS(hook_process, command), + NULL); + } + + if (exec_args) + { + arg0 = string_expand_home (exec_args[0]); + if (arg0) + { + free (exec_args[0]); + exec_args[0] = arg0; + } + if (weechat_debug_core >= 1) + { + log_printf ("hook_process, command='%s'", + HOOK_PROCESS(hook_process, command)); + for (i = 0; exec_args[i]; i++) + { + log_printf (" args[%02d] == '%s'", i, exec_args[i]); + } + } + execvp (exec_args[0], exec_args); + } + + /* should not be executed if execvp was OK */ + if (exec_args) + string_free_split (exec_args); + fprintf (stderr, "Error with command '%s'\n", + HOOK_PROCESS(hook_process, command)); + } + + fflush (stdout); + fflush (stderr); + + _exit (rc); +} + +/* + * Sends buffers (stdout/stderr) to callback. + */ + +void +hook_process_send_buffers (struct t_hook *hook_process, int callback_rc) +{ + int size; + + /* add '\0' at end of stdout and stderr */ + size = HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDOUT]); + if (size > 0) + HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDOUT])[size] = '\0'; + size = HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDERR]); + if (size > 0) + HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDERR])[size] = '\0'; + + /* send buffers to callback */ + (void) (HOOK_PROCESS(hook_process, callback)) + (hook_process->callback_pointer, + hook_process->callback_data, + HOOK_PROCESS(hook_process, command), + callback_rc, + (HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDOUT]) > 0) ? + HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDOUT]) : NULL, + (HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDERR]) > 0) ? + HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDERR]) : NULL); + + /* reset size for stdout and stderr */ + HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDOUT]) = 0; + HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDERR]) = 0; +} + +/* + * Adds some data to buffer (stdout or stderr). + */ + +void +hook_process_add_to_buffer (struct t_hook *hook_process, int index_buffer, + const char *buffer, int size) +{ + if (HOOK_PROCESS(hook_process, buffer_size[index_buffer]) + size > HOOK_PROCESS_BUFFER_SIZE) + hook_process_send_buffers (hook_process, WEECHAT_HOOK_PROCESS_RUNNING); + + memcpy (HOOK_PROCESS(hook_process, buffer[index_buffer]) + + HOOK_PROCESS(hook_process, buffer_size[index_buffer]), + buffer, size); + HOOK_PROCESS(hook_process, buffer_size[index_buffer]) += size; +} + +/* + * Reads process output (stdout or stderr) from child process. + */ + +void +hook_process_child_read (struct t_hook *hook_process, int fd, + int index_buffer, struct t_hook **hook_fd) +{ + char buffer[HOOK_PROCESS_BUFFER_SIZE / 8]; + int num_read; + + if (hook_process->deleted) + return; + + num_read = read (fd, buffer, sizeof (buffer) - 1); + if (num_read > 0) + { + hook_process_add_to_buffer (hook_process, index_buffer, + buffer, num_read); + if (HOOK_PROCESS(hook_process, buffer_size[index_buffer]) >= + HOOK_PROCESS(hook_process, buffer_flush)) + { + hook_process_send_buffers (hook_process, + WEECHAT_HOOK_PROCESS_RUNNING); + } + } + else if (num_read == 0) + { + unhook (*hook_fd); + *hook_fd = NULL; + } +} + +/* + * Reads process output (stdout) from child process. + */ + +int +hook_process_child_read_stdout_cb (const void *pointer, void *data, int fd) +{ + struct t_hook *hook_process; + + /* make C compiler happy */ + (void) data; + + hook_process = (struct t_hook *)pointer; + + hook_process_child_read (hook_process, fd, HOOK_PROCESS_STDOUT, + &(HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDOUT]))); + return WEECHAT_RC_OK; +} + +/* + * Reads process output (stderr) from child process. + */ + +int +hook_process_child_read_stderr_cb (const void *pointer, void *data, int fd) +{ + struct t_hook *hook_process; + + /* make C compiler happy */ + (void) data; + + hook_process = (struct t_hook *)pointer; + + hook_process_child_read (hook_process, fd, HOOK_PROCESS_STDERR, + &(HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDERR]))); + return WEECHAT_RC_OK; +} + +/* + * Reads process output from child process until EOF + * (called when the child process has ended). + */ + +void +hook_process_child_read_until_eof (struct t_hook *hook_process) +{ + struct pollfd poll_fd[2]; + int count, fd_stdout, fd_stderr, num_fd, ready, i; + + fd_stdout = HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]); + fd_stderr = HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]); + + /* + * use a counter to prevent any infinite loop + * (if child's output is very very long...) + */ + count = 0; + while (count < 1024) + { + num_fd = 0; + + if (HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDOUT]) + && ((fcntl (fd_stdout, F_GETFD) != -1) || (errno != EBADF))) + { + poll_fd[num_fd].fd = fd_stdout; + poll_fd[num_fd].events = POLLIN; + poll_fd[num_fd].revents = 0; + num_fd++; + } + + if (HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDERR]) + && ((fcntl (fd_stderr, F_GETFD) != -1) || (errno != EBADF))) + { + poll_fd[num_fd].fd = fd_stderr; + poll_fd[num_fd].events = POLLIN; + poll_fd[num_fd].revents = 0; + num_fd++; + } + + if (num_fd == 0) + break; + + ready = poll (poll_fd, num_fd, 0); + + if (ready <= 0) + break; + + for (i = 0; i < num_fd; i++) + { + if (poll_fd[i].revents & POLLIN) + { + if (poll_fd[i].fd == fd_stdout) + { + (void) hook_process_child_read_stdout_cb ( + hook_process, + NULL, + HOOK_PROCESS(hook_process, + child_read[HOOK_PROCESS_STDOUT])); + } + else + { + (void) hook_process_child_read_stderr_cb ( + hook_process, + NULL, + HOOK_PROCESS(hook_process, + child_read[HOOK_PROCESS_STDERR])); + } + } + } + + count++; + } +} + +/* + * Checks if child process is still alive. + */ + +int +hook_process_timer_cb (const void *pointer, void *data, int remaining_calls) +{ + struct t_hook *hook_process; + int status, rc; + + /* make C compiler happy */ + (void) data; + (void) remaining_calls; + + hook_process = (struct t_hook *)pointer; + + if (hook_process->deleted) + return WEECHAT_RC_OK; + + if (remaining_calls == 0) + { + hook_process_send_buffers (hook_process, WEECHAT_HOOK_PROCESS_ERROR); + if (weechat_debug_core >= 1) + { + gui_chat_printf (NULL, + _("End of command '%s', timeout reached (%.1fs)"), + HOOK_PROCESS(hook_process, command), + ((float)HOOK_PROCESS(hook_process, timeout)) / 1000); + } + kill (HOOK_PROCESS(hook_process, child_pid), SIGKILL); + usleep (1000); + unhook (hook_process); + } + else + { + if (waitpid (HOOK_PROCESS(hook_process, child_pid), + &status, WNOHANG) > 0) + { + if (WIFEXITED(status)) + { + /* child terminated normally */ + rc = WEXITSTATUS(status); + hook_process_child_read_until_eof (hook_process); + hook_process_send_buffers (hook_process, rc); + unhook (hook_process); + } + else if (WIFSIGNALED(status)) + { + /* child terminated by a signal */ + hook_process_child_read_until_eof (hook_process); + hook_process_send_buffers (hook_process, + WEECHAT_HOOK_PROCESS_ERROR); + unhook (hook_process); + } + } + } + + return WEECHAT_RC_OK; +} + +/* + * Executes process command in child, and read data in current process, + * with fd hook. + */ + +void +hook_process_run (struct t_hook *hook_process) +{ + int pipes[3][2], timeout, max_calls, rc, i; + char str_error[1024]; + long interval; + pid_t pid; + + for (i = 0; i < 3; i++) + { + pipes[i][0] = -1; + pipes[i][1] = -1; + } + + /* create pipe for stdin (only if stdin was given in options) */ + if (HOOK_PROCESS(hook_process, options) + && hashtable_has_key (HOOK_PROCESS(hook_process, options), "stdin")) + { + if (pipe (pipes[HOOK_PROCESS_STDIN]) < 0) + goto error; + } + + /* create pipes for stdout/err (if not running in detached mode) */ + if (!HOOK_PROCESS(hook_process, detached)) + { + if (pipe (pipes[HOOK_PROCESS_STDOUT]) < 0) + goto error; + if (pipe (pipes[HOOK_PROCESS_STDERR]) < 0) + goto error; + } + + /* assign pipes to variables in hook */ + for (i = 0; i < 3; i++) + { + HOOK_PROCESS(hook_process, child_read[i]) = pipes[i][0]; + HOOK_PROCESS(hook_process, child_write[i]) = pipes[i][1]; + } + + /* fork */ + switch (pid = fork ()) + { + /* fork failed */ + case -1: + snprintf (str_error, sizeof (str_error), + "fork error: %s", + strerror (errno)); + (void) (HOOK_PROCESS(hook_process, callback)) + (hook_process->callback_pointer, + hook_process->callback_data, + HOOK_PROCESS(hook_process, command), + WEECHAT_HOOK_PROCESS_ERROR, + NULL, str_error); + unhook (hook_process); + return; + /* child process */ + case 0: + rc = setuid (getuid ()); + (void) rc; + hook_process_child (hook_process); + /* never executed */ + _exit (EXIT_SUCCESS); + break; + } + + /* parent process */ + HOOK_PROCESS(hook_process, child_pid) = pid; + if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]) >= 0) + { + close (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN])); + HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]) = -1; + } + if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]) >= 0) + { + close (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDOUT])); + HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDOUT]) = -1; + } + if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]) >= 0) + { + close (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDERR])); + HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDERR]) = -1; + } + + if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]) >= 0) + { + HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDOUT]) = + hook_fd (hook_process->plugin, + HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]), + 1, 0, 0, + &hook_process_child_read_stdout_cb, + hook_process, NULL); + } + + if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]) >= 0) + { + HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDERR]) = + hook_fd (hook_process->plugin, + HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]), + 1, 0, 0, + &hook_process_child_read_stderr_cb, + hook_process, NULL); + } + + timeout = HOOK_PROCESS(hook_process, timeout); + interval = 100; + max_calls = 0; + if (timeout > 0) + { + if (timeout <= 100) + { + interval = timeout; + max_calls = 1; + } + else + { + interval = 100; + max_calls = timeout / 100; + if (timeout % 100 == 0) + max_calls++; + } + } + HOOK_PROCESS(hook_process, hook_timer) = hook_timer (hook_process->plugin, + interval, 0, max_calls, + &hook_process_timer_cb, + hook_process, + NULL); + return; + +error: + for (i = 0; i < 3; i++) + { + if (pipes[i][0] >= 0) + close (pipes[i][0]); + if (pipes[i][1] >= 0) + close (pipes[i][1]); + } + (void) (HOOK_PROCESS(hook_process, callback)) + (hook_process->callback_pointer, + hook_process->callback_data, + HOOK_PROCESS(hook_process, command), + WEECHAT_HOOK_PROCESS_ERROR, + NULL, NULL); + unhook (hook_process); +} + +/* + * Executes all process commands pending. + */ + +void +hook_process_exec () +{ + struct t_hook *ptr_hook, *next_hook; + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_PROCESS]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (HOOK_PROCESS(ptr_hook, child_pid) == 0)) + { + ptr_hook->running = 1; + hook_process_run (ptr_hook); + ptr_hook->running = 0; + } + + ptr_hook = next_hook; + } + + hook_exec_end (); + + hook_process_pending = 0; +} + +/* + * Frees data in a process hook. + */ + +void +hook_process_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_PROCESS(hook, command)) + { + free (HOOK_PROCESS(hook, command)); + HOOK_PROCESS(hook, command) = NULL; + } + if (HOOK_PROCESS(hook, options)) + { + hashtable_free (HOOK_PROCESS(hook, options)); + HOOK_PROCESS(hook, options) = NULL; + } + if (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN])) + { + unhook (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN])); + HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN]) = NULL; + } + if (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT])) + { + unhook (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT])); + HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT]) = NULL; + } + if (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR])) + { + unhook (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR])); + HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR]) = NULL; + } + if (HOOK_PROCESS(hook, hook_timer)) + { + unhook (HOOK_PROCESS(hook, hook_timer)); + HOOK_PROCESS(hook, hook_timer) = NULL; + } + if (HOOK_PROCESS(hook, child_pid) > 0) + { + kill (HOOK_PROCESS(hook, child_pid), SIGKILL); + waitpid (HOOK_PROCESS(hook, child_pid), NULL, 0); + HOOK_PROCESS(hook, child_pid) = 0; + } + if (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN]) != -1) + { + close (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN])); + HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN]) = -1; + } + if (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]) != -1) + { + close (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN])); + HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]) = -1; + } + if (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT]) != -1) + { + close (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT])); + HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT]) = -1; + } + if (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT]) != -1) + { + close (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT])); + HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT]) = -1; + } + if (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR]) != -1) + { + close (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR])); + HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR]) = -1; + } + if (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR]) != -1) + { + close (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR])); + HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR]) = -1; + } + if (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDIN])) + { + free (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDIN])); + HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDIN]) = NULL; + } + if (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDOUT])) + { + free (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDOUT])); + HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDOUT]) = NULL; + } + if (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDERR])) + { + free (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDERR])); + HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDERR]) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds process hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_process_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_PROCESS(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "command", HOOK_PROCESS(hook, command))) + return 0; + if (!infolist_new_var_string (item, "options", hashtable_get_string (HOOK_PROCESS(hook, options), "keys_values"))) + return 0; + if (!infolist_new_var_integer (item, "detached", HOOK_PROCESS(hook, detached))) + return 0; + if (!infolist_new_var_integer (item, "timeout", (int)(HOOK_PROCESS(hook, timeout)))) + return 0; + if (!infolist_new_var_integer (item, "child_read_stdin", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN]))) + return 0; + if (!infolist_new_var_integer (item, "child_write_stdin", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]))) + return 0; + if (!infolist_new_var_integer (item, "child_read_stdout", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT]))) + return 0; + if (!infolist_new_var_integer (item, "child_write_stdout", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT]))) + return 0; + if (!infolist_new_var_integer (item, "child_read_stderr", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR]))) + return 0; + if (!infolist_new_var_integer (item, "child_write_stderr", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR]))) + return 0; + if (!infolist_new_var_integer (item, "child_pid", HOOK_PROCESS(hook, child_pid))) + return 0; + if (!infolist_new_var_pointer (item, "hook_fd_stdin", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN]))) + return 0; + if (!infolist_new_var_pointer (item, "hook_fd_stdout", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT]))) + return 0; + if (!infolist_new_var_pointer (item, "hook_fd_stderr", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR]))) + return 0; + if (!infolist_new_var_pointer (item, "hook_timer", HOOK_PROCESS(hook, hook_timer))) + return 0; + + return 1; +} + +/* + * Prints process hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_process_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" process data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_PROCESS(hook, callback)); + log_printf (" command . . . . . . . : '%s'", HOOK_PROCESS(hook, command)); + log_printf (" options . . . . . . . : 0x%lx (hashtable: '%s')", + HOOK_PROCESS(hook, options), + hashtable_get_string (HOOK_PROCESS(hook, options), + "keys_values")); + log_printf (" detached. . . . . . . : %d", HOOK_PROCESS(hook, detached)); + log_printf (" timeout . . . . . . . : %ld", HOOK_PROCESS(hook, timeout)); + log_printf (" child_read[stdin] . . : %d", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN])); + log_printf (" child_write[stdin]. . : %d", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN])); + log_printf (" child_read[stdout]. . : %d", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT])); + log_printf (" child_write[stdout] . : %d", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT])); + log_printf (" child_read[stderr]. . : %d", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR])); + log_printf (" child_write[stderr] . : %d", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR])); + log_printf (" child_pid . . . . . . : %d", HOOK_PROCESS(hook, child_pid)); + log_printf (" hook_fd[stdin]. . . . : 0x%lx", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN])); + log_printf (" hook_fd[stdout] . . . : 0x%lx", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT])); + log_printf (" hook_fd[stderr] . . . : 0x%lx", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR])); + log_printf (" hook_timer. . . . . . : 0x%lx", HOOK_PROCESS(hook, hook_timer)); +} diff --git a/src/core/hook/wee-hook-process.h b/src/core/hook/wee-hook-process.h new file mode 100644 index 000000000..9eb069f2e --- /dev/null +++ b/src/core/hook/wee-hook-process.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_PROCESS_H +#define WEECHAT_HOOK_PROCESS_H + +struct t_weechat_plugin; +struct t_infolist_item; +struct t_hashtable; + +#define HOOK_PROCESS(hook, var) (((struct t_hook_process *)hook->hook_data)->var) + +/* constants for hook process */ +#define HOOK_PROCESS_STDIN 0 +#define HOOK_PROCESS_STDOUT 1 +#define HOOK_PROCESS_STDERR 2 +#define HOOK_PROCESS_BUFFER_SIZE 65536 + +typedef int (t_hook_callback_process)(const void *pointer, void *data, + const char *command, + int return_code, + const char *out, const char *err); + +struct t_hook_process +{ + t_hook_callback_process *callback; /* process callback (after child end)*/ + char *command; /* command executed by child */ + struct t_hashtable *options; /* options for process (see doc) */ + int detached; /* detached mode (background) */ + long timeout; /* timeout (ms) (0 = no timeout) */ + int child_read[3]; /* read stdin/out/err data from child*/ + int child_write[3]; /* write stdin/out/err data for child*/ + pid_t child_pid; /* pid of child process */ + struct t_hook *hook_fd[3]; /* hook fd for stdin/out/err */ + struct t_hook *hook_timer; /* timer to check if child has died */ + char *buffer[3]; /* buffers for child stdin/out/err */ + int buffer_size[3]; /* size of child stdin/out/err */ + int buffer_flush; /* bytes to flush output buffers */ +}; + +extern int hook_process_pending; + +extern struct t_hook *hook_process (struct t_weechat_plugin *plugin, + const char *command, + int timeout, + t_hook_callback_process *callback, + const void *callback_pointer, + void *callback_data); +extern struct t_hook *hook_process_hashtable (struct t_weechat_plugin *plugin, + const char *command, + struct t_hashtable *options, + int timeout, + t_hook_callback_process *callback, + const void *callback_pointer, + void *callback_data); +extern void hook_process_exec (); +extern void hook_process_free_data (struct t_hook *hook); +extern int hook_process_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_process_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_PROCESS_H */ diff --git a/src/core/hook/wee-hook-signal.c b/src/core/hook/wee-hook-signal.c new file mode 100644 index 000000000..ac99cc3f3 --- /dev/null +++ b/src/core/hook/wee-hook-signal.c @@ -0,0 +1,180 @@ +/* + * wee-hook-signal.c - WeeChat signal hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-string.h" +#include "../../plugins/plugin.h" + + +/* + * Hooks a signal. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_signal (struct t_weechat_plugin *plugin, const char *signal, + t_hook_callback_signal *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_signal *new_hook_signal; + int priority; + const char *ptr_signal; + + if (!signal || !signal[0] || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_signal = malloc (sizeof (*new_hook_signal)); + if (!new_hook_signal) + { + free (new_hook); + return NULL; + } + + hook_get_priority_and_name (signal, &priority, &ptr_signal); + hook_init_data (new_hook, plugin, HOOK_TYPE_SIGNAL, priority, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_signal; + new_hook_signal->callback = callback; + new_hook_signal->signal = strdup ((ptr_signal) ? ptr_signal : signal); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Sends a signal. + */ + +int +hook_signal_send (const char *signal, const char *type_data, void *signal_data) +{ + struct t_hook *ptr_hook, *next_hook; + int rc; + + rc = WEECHAT_RC_OK; + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_SIGNAL]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (string_match (signal, HOOK_SIGNAL(ptr_hook, signal), 0))) + { + ptr_hook->running = 1; + rc = (HOOK_SIGNAL(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + signal, + type_data, + signal_data); + ptr_hook->running = 0; + + if (rc == WEECHAT_RC_OK_EAT) + break; + } + + ptr_hook = next_hook; + } + + hook_exec_end (); + + return rc; +} + +/* + * Frees data in a signal hook. + */ + +void +hook_signal_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + if (HOOK_SIGNAL(hook, signal)) + { + free (HOOK_SIGNAL(hook, signal)); + HOOK_SIGNAL(hook, signal) = NULL; + } + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds signal hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_signal_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_SIGNAL(hook, callback))) + return 0; + if (!infolist_new_var_string (item, "signal", HOOK_SIGNAL(hook, signal))) + return 0; + + return 1; +} + +/* + * Prints signal hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_signal_print_log (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + log_printf (" signal data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_SIGNAL(hook, callback)); + log_printf (" signal. . . . . . . . : '%s'", HOOK_SIGNAL(hook, signal)); +} diff --git a/src/core/hook/wee-hook-signal.h b/src/core/hook/wee-hook-signal.h new file mode 100644 index 000000000..22510f4fb --- /dev/null +++ b/src/core/hook/wee-hook-signal.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_SIGNAL_H +#define WEECHAT_HOOK_SIGNAL_H + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_SIGNAL(hook, var) (((struct t_hook_signal *)hook->hook_data)->var) + +typedef int (t_hook_callback_signal)(const void *pointer, void *data, + const char *signal, const char *type_data, + void *signal_data); + +struct t_hook_signal +{ + t_hook_callback_signal *callback; /* signal callback */ + char *signal; /* signal selected (may begin or end */ + /* with "*", "*" == any signal) */ +}; + +extern struct t_hook *hook_signal (struct t_weechat_plugin *plugin, + const char *signal, + t_hook_callback_signal *callback, + const void *callback_pointer, + void *callback_data); +extern int hook_signal_send (const char *signal, const char *type_data, + void *signal_data); +extern void hook_signal_free_data (struct t_hook *hook); +extern int hook_signal_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_signal_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_SIGNAL_H */ diff --git a/src/core/hook/wee-hook-timer.c b/src/core/hook/wee-hook-timer.c new file mode 100644 index 000000000..880b99bad --- /dev/null +++ b/src/core/hook/wee-hook-timer.c @@ -0,0 +1,417 @@ +/* + * wee-hook-timer.c - WeeChat timer hook + * + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <time.h> + +#include "../weechat.h" +#include "../wee-hook.h" +#include "../wee-infolist.h" +#include "../wee-log.h" +#include "../wee-util.h" +#include "../../gui/gui-chat.h" + + +time_t hook_last_system_time = 0; /* used to detect system clock skew */ + + +/* + * Initializes a timer hook. + */ + +void +hook_timer_init (struct t_hook *hook) +{ + time_t time_now; + struct tm *local_time, *gm_time; + int local_hour, gm_hour, diff_hour; + + gettimeofday (&HOOK_TIMER(hook, last_exec), NULL); + time_now = time (NULL); + local_time = localtime (&time_now); + local_hour = local_time->tm_hour; + gm_time = gmtime (&time_now); + gm_hour = gm_time->tm_hour; + if ((local_time->tm_year > gm_time->tm_year) + || (local_time->tm_mon > gm_time->tm_mon) + || (local_time->tm_mday > gm_time->tm_mday)) + { + diff_hour = (24 - gm_hour) + local_hour; + } + else if ((gm_time->tm_year > local_time->tm_year) + || (gm_time->tm_mon > local_time->tm_mon) + || (gm_time->tm_mday > local_time->tm_mday)) + { + diff_hour = (-1) * ((24 - local_hour) + gm_hour); + } + else + diff_hour = local_hour - gm_hour; + + if ((HOOK_TIMER(hook, interval) >= 1000) + && (HOOK_TIMER(hook, align_second) > 0)) + { + /* + * here we should use 0, but with this value timer is sometimes called + * before second has changed, so for displaying time, it may display + * 2 times the same second, that's why we use 10000 micro seconds + */ + HOOK_TIMER(hook, last_exec).tv_usec = 10000; + HOOK_TIMER(hook, last_exec).tv_sec = + HOOK_TIMER(hook, last_exec).tv_sec - + ((HOOK_TIMER(hook, last_exec).tv_sec + (diff_hour * 3600)) % + HOOK_TIMER(hook, align_second)); + } + + /* init next call with date of last call */ + HOOK_TIMER(hook, next_exec).tv_sec = HOOK_TIMER(hook, last_exec).tv_sec; + HOOK_TIMER(hook, next_exec).tv_usec = HOOK_TIMER(hook, last_exec).tv_usec; + + /* add interval to next call date */ + util_timeval_add (&HOOK_TIMER(hook, next_exec), + ((long long)HOOK_TIMER(hook, interval)) * 1000); +} + +/* + * Hooks a timer. + * + * Returns pointer to new hook, NULL if error. + */ + +struct t_hook * +hook_timer (struct t_weechat_plugin *plugin, long interval, int align_second, + int max_calls, t_hook_callback_timer *callback, + const void *callback_pointer, + void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_timer *new_hook_timer; + + if ((interval <= 0) || !callback) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_timer = malloc (sizeof (*new_hook_timer)); + if (!new_hook_timer) + { + free (new_hook); + return NULL; + } + + hook_init_data (new_hook, plugin, HOOK_TYPE_TIMER, HOOK_PRIORITY_DEFAULT, + callback_pointer, callback_data); + + new_hook->hook_data = new_hook_timer; + new_hook_timer->callback = callback; + new_hook_timer->interval = interval; + new_hook_timer->align_second = align_second; + new_hook_timer->remaining_calls = max_calls; + + hook_timer_init (new_hook); + + hook_add_to_list (new_hook); + + return new_hook; +} + +/* + * Checks if system clock is older than previous call to this function (that + * means new time is lower than in past). If yes, adjusts all timers to current + * time. + */ + +void +hook_timer_check_system_clock () +{ + time_t now; + long diff_time; + struct t_hook *ptr_hook; + + now = time (NULL); + + /* + * check if difference with previous time is more than 10 seconds: + * if it is, then consider it's clock skew and reinitialize all timers + */ + diff_time = now - hook_last_system_time; + if ((diff_time <= -10) || (diff_time >= 10)) + { + if (weechat_debug_core >= 1) + { + gui_chat_printf (NULL, + _("System clock skew detected (%+ld seconds), " + "reinitializing all timers"), + diff_time); + } + + /* reinitialize all timers */ + for (ptr_hook = weechat_hooks[HOOK_TYPE_TIMER]; ptr_hook; + ptr_hook = ptr_hook->next_hook) + { + if (!ptr_hook->deleted) + hook_timer_init (ptr_hook); + } + } + + hook_last_system_time = now; +} + +/* + * Returns time until next timeout (in milliseconds). + */ + +int +hook_timer_get_time_to_next () +{ + struct t_hook *ptr_hook; + int found, timeout; + struct timeval tv_now, tv_timeout; + long diff_usec; + + hook_timer_check_system_clock (); + + found = 0; + tv_timeout.tv_sec = 0; + tv_timeout.tv_usec = 0; + + for (ptr_hook = weechat_hooks[HOOK_TYPE_TIMER]; ptr_hook; + ptr_hook = ptr_hook->next_hook) + { + if (!ptr_hook->deleted + && (!found + || (util_timeval_cmp (&HOOK_TIMER(ptr_hook, next_exec), + &tv_timeout) < 0))) + { + found = 1; + tv_timeout.tv_sec = HOOK_TIMER(ptr_hook, next_exec).tv_sec; + tv_timeout.tv_usec = HOOK_TIMER(ptr_hook, next_exec).tv_usec; + } + } + + /* no timeout found, return 2 seconds by default */ + if (!found) + { + tv_timeout.tv_sec = 2; + tv_timeout.tv_usec = 0; + goto end; + } + + gettimeofday (&tv_now, NULL); + + /* next timeout is past date! */ + if (util_timeval_cmp (&tv_timeout, &tv_now) < 0) + { + tv_timeout.tv_sec = 0; + tv_timeout.tv_usec = 0; + goto end; + } + + tv_timeout.tv_sec = tv_timeout.tv_sec - tv_now.tv_sec; + diff_usec = tv_timeout.tv_usec - tv_now.tv_usec; + if (diff_usec >= 0) + { + tv_timeout.tv_usec = diff_usec; + } + else + { + tv_timeout.tv_sec--; + tv_timeout.tv_usec = 1000000 + diff_usec; + } + + /* + * to detect clock skew, we ensure there's a call to timers every + * 2 seconds max + */ + if (tv_timeout.tv_sec >= 2) + { + tv_timeout.tv_sec = 2; + tv_timeout.tv_usec = 0; + } + +end: + /* return a number of milliseconds */ + timeout = (tv_timeout.tv_sec * 1000) + (tv_timeout.tv_usec / 1000); + return (timeout < 1) ? 1 : timeout; +} + +/* + * Executes timer hooks. + */ + +void +hook_timer_exec () +{ + struct timeval tv_time; + struct t_hook *ptr_hook, *next_hook; + + if (!weechat_hooks[HOOK_TYPE_TIMER]) + return; + + hook_timer_check_system_clock (); + + gettimeofday (&tv_time, NULL); + + hook_exec_start (); + + ptr_hook = weechat_hooks[HOOK_TYPE_TIMER]; + while (ptr_hook) + { + next_hook = ptr_hook->next_hook; + + if (!ptr_hook->deleted + && !ptr_hook->running + && (util_timeval_cmp (&HOOK_TIMER(ptr_hook, next_exec), + &tv_time) <= 0)) + { + ptr_hook->running = 1; + (void) (HOOK_TIMER(ptr_hook, callback)) + (ptr_hook->callback_pointer, + ptr_hook->callback_data, + (HOOK_TIMER(ptr_hook, remaining_calls) > 0) ? + HOOK_TIMER(ptr_hook, remaining_calls) - 1 : -1); + ptr_hook->running = 0; + if (!ptr_hook->deleted) + { + HOOK_TIMER(ptr_hook, last_exec).tv_sec = tv_time.tv_sec; + HOOK_TIMER(ptr_hook, last_exec).tv_usec = tv_time.tv_usec; + + util_timeval_add ( + &HOOK_TIMER(ptr_hook, next_exec), + ((long long)HOOK_TIMER(ptr_hook, interval)) * 1000); + + if (HOOK_TIMER(ptr_hook, remaining_calls) > 0) + { + HOOK_TIMER(ptr_hook, remaining_calls)--; + if (HOOK_TIMER(ptr_hook, remaining_calls) == 0) + unhook (ptr_hook); + } + } + } + + ptr_hook = next_hook; + } + + hook_exec_end (); +} + +/* + * Frees data in a timer hook. + */ + +void +hook_timer_free_data (struct t_hook *hook) +{ + if (!hook || !hook->hook_data) + return; + + free (hook->hook_data); + hook->hook_data = NULL; +} + +/* + * Adds timer hook data in the infolist item. + * + * Returns: + * 1: OK + * 0: error + */ + +int +hook_timer_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook) +{ + char value[64]; + + if (!item || !hook || !hook->hook_data) + return 0; + + if (!infolist_new_var_pointer (item, "callback", HOOK_TIMER(hook, callback))) + return 0; + snprintf (value, sizeof (value), "%ld", HOOK_TIMER(hook, interval)); + if (!infolist_new_var_string (item, "interval", value)) + return 0; + if (!infolist_new_var_integer (item, "align_second", HOOK_TIMER(hook, align_second))) + return 0; + if (!infolist_new_var_integer (item, "remaining_calls", HOOK_TIMER(hook, remaining_calls))) + return 0; + if (!infolist_new_var_buffer (item, "last_exec", + &(HOOK_TIMER(hook, last_exec)), + sizeof (HOOK_TIMER(hook, last_exec)))) + return 0; + if (!infolist_new_var_buffer (item, "next_exec", + &(HOOK_TIMER(hook, next_exec)), + sizeof (HOOK_TIMER(hook, next_exec)))) + return 0; + + return 1; +} + +/* + * Prints timer hook data in WeeChat log file (usually for crash dump). + */ + +void +hook_timer_print_log (struct t_hook *hook) +{ + struct tm *local_time; + time_t seconds; + char text_time[1024]; + + if (!hook || !hook->hook_data) + return; + + log_printf (" timer data:"); + log_printf (" callback. . . . . . . : 0x%lx", HOOK_TIMER(hook, callback)); + log_printf (" interval. . . . . . . : %ld", HOOK_TIMER(hook, interval)); + log_printf (" align_second. . . . . : %d", HOOK_TIMER(hook, align_second)); + log_printf (" remaining_calls . . . : %d", HOOK_TIMER(hook, remaining_calls)); + text_time[0] = '\0'; + seconds = HOOK_TIMER(hook, last_exec).tv_sec; + local_time = localtime (&seconds); + if (local_time) + { + if (strftime (text_time, sizeof (text_time), + "%d/%m/%Y %H:%M:%S", local_time) == 0) + text_time[0] = '\0'; + } + log_printf (" last_exec.tv_sec. . . : %lld (%s)", + (long long)(HOOK_TIMER(hook, last_exec.tv_sec)), + text_time); + log_printf (" last_exec.tv_usec . . : %ld", HOOK_TIMER(hook, last_exec.tv_usec)); + text_time[0] = '\0'; + seconds = HOOK_TIMER(hook, next_exec).tv_sec; + local_time = localtime (&seconds); + if (local_time) + { + if (strftime (text_time, sizeof (text_time), + "%d/%m/%Y %H:%M:%S", local_time) == 0) + text_time[0] = '\0'; + } + log_printf (" next_exec.tv_sec. . . : %lld (%s)", + (long long)(HOOK_TIMER(hook, next_exec.tv_sec)), + text_time); + log_printf (" next_exec.tv_usec . . : %ld", HOOK_TIMER(hook, next_exec.tv_usec)); +} diff --git a/src/core/hook/wee-hook-timer.h b/src/core/hook/wee-hook-timer.h new file mode 100644 index 000000000..052bd750c --- /dev/null +++ b/src/core/hook/wee-hook-timer.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_HOOK_TIMER_H +#define WEECHAT_HOOK_TIMER_H + +#include <time.h> + +struct t_weechat_plugin; +struct t_infolist_item; + +#define HOOK_TIMER(hook, var) (((struct t_hook_timer *)hook->hook_data)->var) + +typedef int (t_hook_callback_timer)(const void *pointer, void *data, + int remaining_calls); + +struct t_hook_timer +{ + t_hook_callback_timer *callback; /* timer callback */ + long interval; /* timer interval (milliseconds) */ + int align_second; /* alignment on a second */ + /* for ex.: 60 = each min. at 0 sec */ + int remaining_calls; /* calls remaining (0 = unlimited) */ + struct timeval last_exec; /* last time hook was executed */ + struct timeval next_exec; /* next scheduled execution */ +}; + +extern time_t hook_last_system_time; + +extern struct t_hook *hook_timer (struct t_weechat_plugin *plugin, + long interval, int align_second, + int max_calls, + t_hook_callback_timer *callback, + const void *callback_pointer, + void *callback_data); +extern int hook_timer_get_time_to_next (); +extern void hook_timer_exec (); +extern void hook_timer_free_data (struct t_hook *hook); +extern int hook_timer_add_to_infolist (struct t_infolist_item *item, + struct t_hook *hook); +extern void hook_timer_print_log (struct t_hook *hook); + +#endif /* WEECHAT_HOOK_TIMER_H */ diff --git a/src/core/wee-hook.c b/src/core/wee-hook.c index e112c8549..771e29d8c 100644 --- a/src/core/wee-hook.c +++ b/src/core/wee-hook.c @@ -2,7 +2,6 @@ * wee-hook.c - WeeChat hooks management * * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> - * Copyright (C) 2012 Simon Arlott * * This file is part of WeeChat, the extensible chat client. * @@ -25,42 +24,20 @@ #endif #include <stdlib.h> -#include <stdio.h> -#include <stddef.h> #include <unistd.h> #include <string.h> -#include <sys/time.h> #include <time.h> -#include <sys/types.h> #include <sys/socket.h> -#include <sys/wait.h> -#include <poll.h> -#include <signal.h> -#include <fcntl.h> #include <errno.h> #include "weechat.h" #include "wee-hook.h" -#include "wee-config.h" #include "wee-hashtable.h" -#include "wee-hdata.h" #include "wee-infolist.h" -#include "wee-list.h" #include "wee-log.h" -#include "wee-network.h" #include "wee-string.h" -#include "wee-url.h" -#include "wee-utf8.h" #include "wee-util.h" #include "../gui/gui-chat.h" -#include "../gui/gui-bar.h" -#include "../gui/gui-bar-window.h" -#include "../gui/gui-buffer.h" -#include "../gui/gui-color.h" -#include "../gui/gui-completion.h" -#include "../gui/gui-focus.h" -#include "../gui/gui-line.h" -#include "../gui/gui-window.h" #include "../plugins/plugin.h" @@ -73,17 +50,47 @@ struct t_hook *last_weechat_hook[HOOK_NUM_TYPES]; /* last hook */ int hooks_count[HOOK_NUM_TYPES]; /* number of hooks */ int hooks_count_total = 0; /* total number of hooks */ int hook_exec_recursion = 0; /* 1 when a hook is executed */ -time_t hook_last_system_time = 0; /* used to detect system clock skew */ int real_delete_pending = 0; /* 1 if some hooks must be deleted */ -struct pollfd *hook_fd_pollfd = NULL; /* file descriptors for poll() */ -int hook_fd_pollfd_count = 0; /* number of file descriptors */ -int hook_process_pending = 0; /* 1 if there are some process to */ - /* run (via fork) */ int hook_socketpair_ok = 0; /* 1 if socketpair() is OK */ - -void hook_process_run (struct t_hook *hook_process); +/* hook callbacks */ +t_callback_hook *hook_callback_add[HOOK_NUM_TYPES] = +{ NULL, NULL, NULL, &hook_fd_add_cb, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; +t_callback_hook *hook_callback_remove[HOOK_NUM_TYPES] = +{ NULL, NULL, NULL, &hook_fd_remove_cb, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; +t_callback_hook *hook_callback_free_data[HOOK_NUM_TYPES] = +{ &hook_command_free_data, &hook_command_run_free_data, + &hook_timer_free_data, &hook_fd_free_data, + &hook_process_free_data, &hook_connect_free_data, + &hook_line_free_data, &hook_print_free_data, + &hook_signal_free_data, &hook_hsignal_free_data, + &hook_config_free_data, &hook_completion_free_data, + &hook_modifier_free_data, &hook_info_free_data, + &hook_info_hashtable_free_data, &hook_infolist_free_data, + &hook_hdata_free_data, &hook_focus_free_data }; +t_callback_hook_infolist *hook_callback_add_to_infolist[HOOK_NUM_TYPES] = +{ &hook_command_add_to_infolist, &hook_command_run_add_to_infolist, + &hook_timer_add_to_infolist, &hook_fd_add_to_infolist, + &hook_process_add_to_infolist, &hook_connect_add_to_infolist, + &hook_line_add_to_infolist, &hook_print_add_to_infolist, + &hook_signal_add_to_infolist, &hook_hsignal_add_to_infolist, + &hook_config_add_to_infolist, &hook_completion_add_to_infolist, + &hook_modifier_add_to_infolist, &hook_info_add_to_infolist, + &hook_info_hashtable_add_to_infolist, &hook_infolist_add_to_infolist, + &hook_hdata_add_to_infolist, &hook_focus_add_to_infolist }; +t_callback_hook *hook_callback_print_log[HOOK_NUM_TYPES] = +{ &hook_command_print_log, &hook_command_run_print_log, + &hook_timer_print_log, &hook_fd_print_log, + &hook_process_print_log, &hook_connect_print_log, + &hook_line_print_log, &hook_print_print_log, + &hook_signal_print_log, &hook_hsignal_print_log, + &hook_config_print_log, &hook_completion_print_log, + &hook_modifier_print_log, &hook_info_print_log, + &hook_info_hashtable_print_log, &hook_infolist_print_log, + &hook_hdata_print_log, &hook_focus_print_log }; /* @@ -95,7 +102,7 @@ hook_init () { int type, sock[2], rc; - /* initialize list of hooks */ + /* initialize list of hooks and callbacks */ for (type = 0; type < HOOK_NUM_TYPES; type++) { weechat_hooks[type] = NULL; @@ -167,41 +174,6 @@ hook_search_type (const char *type) } /* - * Reallocates the "struct pollfd" array for poll(). - */ - -void -hook_fd_realloc_pollfd () -{ - struct pollfd *ptr_pollfd; - int count; - - if (hooks_count[HOOK_TYPE_FD] == hook_fd_pollfd_count) - return; - - count = hooks_count[HOOK_TYPE_FD]; - - if (count == 0) - { - if (hook_fd_pollfd) - { - free (hook_fd_pollfd); - hook_fd_pollfd = NULL; - } - } - else - { - ptr_pollfd = realloc (hook_fd_pollfd, - count * sizeof (struct pollfd)); - if (!ptr_pollfd) - return; - hook_fd_pollfd = ptr_pollfd; - } - - hook_fd_pollfd_count = count; -} - -/* * Searches for position of hook in list (to keep hooks sorted). * * Hooks are sorted by priority, except commands which are sorted by command @@ -289,8 +261,8 @@ hook_add_to_list (struct t_hook *new_hook) hooks_count[new_hook->type]++; hooks_count_total++; - if (new_hook->type == HOOK_TYPE_FD) - hook_fd_realloc_pollfd (); + if (hook_callback_add[new_hook->type]) + (hook_callback_add[new_hook->type]) (new_hook); } /* @@ -318,15 +290,15 @@ hook_remove_from_list (struct t_hook *hook) if (hook->next_hook) (hook->next_hook)->prev_hook = hook->prev_hook; - free (hook); - weechat_hooks[type] = new_hooks; hooks_count[type]--; hooks_count_total--; - if (type == HOOK_TYPE_FD) - hook_fd_realloc_pollfd (); + if (hook_callback_remove[hook->type]) + (hook_callback_remove[hook->type]) (hook); + + free (hook); } /* @@ -487,3309 +459,6 @@ hook_exec_end () } /* - * Searches for a command hook in list. - * - * Returns pointer to hook found, NULL if not found. - */ - -struct t_hook * -hook_search_command (struct t_weechat_plugin *plugin, const char *command) -{ - struct t_hook *ptr_hook; - - for (ptr_hook = weechat_hooks[HOOK_TYPE_COMMAND]; ptr_hook; - ptr_hook = ptr_hook->next_hook) - { - if (!ptr_hook->deleted - && (ptr_hook->plugin == plugin) - && (string_strcasecmp (HOOK_COMMAND(ptr_hook, command), command) == 0)) - return ptr_hook; - } - - /* command hook not found for plugin */ - return NULL; -} - -/* - * Builds variables/arrays that will be used for completion of commands - * arguments. - */ - -void -hook_command_build_completion (struct t_hook_command *hook_command) -{ - int i, j, k, length, num_items; - struct t_weelist *list; - char *pos_completion, *pos_double_pipe, *pos_start, *pos_end; - char **items; - const char *last_space, *ptr_template; - - /* split templates using "||" as separator */ - hook_command->cplt_num_templates = 1; - pos_completion = hook_command->completion; - while ((pos_double_pipe = strstr (pos_completion, "||")) != NULL) - { - hook_command->cplt_num_templates++; - pos_completion = pos_double_pipe + 2; - } - hook_command->cplt_templates = malloc (hook_command->cplt_num_templates * - sizeof (*hook_command->cplt_templates)); - for (i = 0; i < hook_command->cplt_num_templates; i++) - { - hook_command->cplt_templates[i] = NULL; - } - pos_completion = hook_command->completion; - i = 0; - while (pos_completion) - { - pos_double_pipe = strstr (pos_completion, "||"); - if (!pos_double_pipe) - pos_double_pipe = pos_completion + strlen (pos_completion); - pos_start = pos_completion; - pos_end = pos_double_pipe - 1; - if (pos_end < pos_start) - { - hook_command->cplt_templates[i] = strdup (""); - } - else - { - while (pos_start[0] == ' ') - { - pos_start++; - } - pos_end = pos_double_pipe - 1; - while ((pos_end > pos_start) && (pos_end[0] == ' ')) - { - pos_end--; - } - hook_command->cplt_templates[i] = string_strndup (pos_start, - pos_end - pos_start + 1); - } - i++; - if (!pos_double_pipe[0]) - pos_completion = NULL; - else - pos_completion = pos_double_pipe + 2; - } - - /* for each template, split/count args */ - hook_command->cplt_templates_static = malloc (hook_command->cplt_num_templates * - sizeof (*hook_command->cplt_templates_static)); - hook_command->cplt_template_num_args = malloc (hook_command->cplt_num_templates * - sizeof (*hook_command->cplt_template_num_args)); - hook_command->cplt_template_args = malloc (hook_command->cplt_num_templates * - sizeof (*hook_command->cplt_template_args)); - hook_command->cplt_template_num_args_concat = 0; - for (i = 0; i < hook_command->cplt_num_templates; i++) - { - /* - * build static part of template: it's first argument(s) which does not - * contain "%" - */ - last_space = NULL; - ptr_template = hook_command->cplt_templates[i]; - while (ptr_template && ptr_template[0]) - { - if (ptr_template[0] == ' ') - { - last_space = ptr_template; - break; - } - else if (ptr_template[0] == '%') - break; - ptr_template = utf8_next_char (ptr_template); - } - if (last_space) - { - last_space--; - while (last_space > hook_command->cplt_templates[i]) - { - if (last_space[0] != ' ') - break; - } - if (last_space < hook_command->cplt_templates[i]) - last_space = NULL; - else - last_space++; - } - if (last_space) - hook_command->cplt_templates_static[i] = string_strndup (hook_command->cplt_templates[i], - last_space - hook_command->cplt_templates[i]); - else - hook_command->cplt_templates_static[i] = strdup (hook_command->cplt_templates[i]); - - /* build arguments for each template */ - hook_command->cplt_template_args[i] = string_split (hook_command->cplt_templates[i], - " ", 0, 0, - &(hook_command->cplt_template_num_args[i])); - if (hook_command->cplt_template_num_args[i] > hook_command->cplt_template_num_args_concat) - hook_command->cplt_template_num_args_concat = hook_command->cplt_template_num_args[i]; - } - - /* - * build strings with concatenation of items from different templates - * for each argument: these strings will be used when completing argument - * if we can't find which template to use (for example for first argument) - */ - if (hook_command->cplt_template_num_args_concat == 0) - hook_command->cplt_template_args_concat = NULL; - else - { - hook_command->cplt_template_args_concat = malloc (hook_command->cplt_template_num_args_concat * - sizeof (*hook_command->cplt_template_args_concat)); - list = weelist_new (); - for (i = 0; i < hook_command->cplt_template_num_args_concat; i++) - { - /* first compute length */ - length = 1; - for (j = 0; j < hook_command->cplt_num_templates; j++) - { - if (i < hook_command->cplt_template_num_args[j]) - length += strlen (hook_command->cplt_template_args[j][i]) + 1; - } - /* alloc memory */ - hook_command->cplt_template_args_concat[i] = malloc (length); - if (hook_command->cplt_template_args_concat[i]) - { - /* concatenate items with "|" as separator */ - weelist_remove_all (list); - hook_command->cplt_template_args_concat[i][0] = '\0'; - for (j = 0; j < hook_command->cplt_num_templates; j++) - { - if (i < hook_command->cplt_template_num_args[j]) - { - items = string_split (hook_command->cplt_template_args[j][i], - "|", 0, 0, &num_items); - for (k = 0; k < num_items; k++) - { - if (!weelist_search (list, items[k])) - { - if (hook_command->cplt_template_args_concat[i][0]) - strcat (hook_command->cplt_template_args_concat[i], "|"); - strcat (hook_command->cplt_template_args_concat[i], - items[k]); - weelist_add (list, items[k], WEECHAT_LIST_POS_END, - NULL); - } - } - string_free_split (items); - } - } - } - } - weelist_free (list); - } -} - -/* - * Hooks a command. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_command (struct t_weechat_plugin *plugin, const char *command, - const char *description, - const char *args, const char *args_description, - const char *completion, - t_hook_callback_command *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_command *new_hook_command; - int priority; - const char *ptr_command; - - if (!callback) - return NULL; - - if (hook_search_command (plugin, command)) - { - gui_chat_printf (NULL, - _("%sError: another command \"%s\" already exists " - "for plugin \"%s\""), - gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], - command, - plugin_get_name (plugin)); - return NULL; - } - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_command = malloc (sizeof (*new_hook_command)); - if (!new_hook_command) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (command, &priority, &ptr_command); - hook_init_data (new_hook, plugin, HOOK_TYPE_COMMAND, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_command; - new_hook_command->callback = callback; - new_hook_command->command = strdup ((ptr_command) ? ptr_command : - ((command) ? command : "")); - new_hook_command->description = strdup ((description) ? description : ""); - new_hook_command->args = strdup ((args) ? args : ""); - new_hook_command->args_description = strdup ((args_description) ? - args_description : ""); - new_hook_command->completion = strdup ((completion) ? completion : ""); - - /* build completion variables for command */ - new_hook_command->cplt_num_templates = 0; - new_hook_command->cplt_templates = NULL; - new_hook_command->cplt_templates_static = NULL; - new_hook_command->cplt_template_num_args = NULL; - new_hook_command->cplt_template_args = NULL; - new_hook_command->cplt_template_num_args_concat = 0; - new_hook_command->cplt_template_args_concat = NULL; - hook_command_build_completion (new_hook_command); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Executes a command hook. - * - * Returns: - * HOOK_COMMAND_EXEC_OK: command executed successfully - * HOOK_COMMAND_EXEC_ERROR: command executed and failed - * HOOK_COMMAND_EXEC_NOT_FOUND: command not found - * HOOK_COMMAND_EXEC_AMBIGUOUS_PLUGINS: command is ambiguous (same command - * exists for another plugin, and we don't know which one to run) - * HOOK_COMMAND_EXEC_AMBIGUOUS_INCOMPLETE: command is ambiguous (incomplete - * command and multiple commands start with this name) - * HOOK_COMMAND_EXEC_RUNNING: command is already running - */ - -int -hook_command_exec (struct t_gui_buffer *buffer, int any_plugin, - struct t_weechat_plugin *plugin, const char *string) -{ - struct t_hook *ptr_hook, *next_hook; - struct t_hook *hook_plugin, *hook_other_plugin, *hook_other_plugin2; - struct t_hook *hook_incomplete_command; - char **argv, **argv_eol; - const char *ptr_command_name; - int argc, rc, length_command_name, allow_incomplete_commands; - int count_other_plugin, count_incomplete_commands; - - if (!buffer || !string || !string[0]) - return HOOK_COMMAND_EXEC_NOT_FOUND; - - if (hook_command_run_exec (buffer, string) == WEECHAT_RC_OK_EAT) - return HOOK_COMMAND_EXEC_OK; - - argv = string_split (string, " ", 0, 0, &argc); - if (argc == 0) - { - string_free_split (argv); - return HOOK_COMMAND_EXEC_NOT_FOUND; - } - argv_eol = string_split (string, " ", 1, 0, NULL); - - ptr_command_name = utf8_next_char (argv[0]); - length_command_name = strlen (ptr_command_name); - - hook_exec_start (); - - hook_plugin = NULL; - hook_other_plugin = NULL; - hook_other_plugin2 = NULL; - hook_incomplete_command = NULL; - count_other_plugin = 0; - allow_incomplete_commands = CONFIG_BOOLEAN(config_look_command_incomplete); - count_incomplete_commands = 0; - ptr_hook = weechat_hooks[HOOK_TYPE_COMMAND]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted) - { - if (string_strcasecmp (ptr_command_name, - HOOK_COMMAND(ptr_hook, command)) == 0) - { - if (ptr_hook->plugin == plugin) - { - if (!hook_plugin) - hook_plugin = ptr_hook; - } - else - { - if (any_plugin) - { - if (!hook_other_plugin) - hook_other_plugin = ptr_hook; - else if (!hook_other_plugin2) - hook_other_plugin2 = ptr_hook; - count_other_plugin++; - } - } - } - else if (allow_incomplete_commands - && (string_strncasecmp (ptr_command_name, - HOOK_COMMAND(ptr_hook, command), - length_command_name) == 0)) - { - hook_incomplete_command = ptr_hook; - count_incomplete_commands++; - } - } - - ptr_hook = next_hook; - } - - rc = HOOK_COMMAND_EXEC_NOT_FOUND; - ptr_hook = NULL; - - if (hook_plugin || hook_other_plugin) - { - if (!hook_plugin && (count_other_plugin > 1) - && (hook_other_plugin->priority == hook_other_plugin2->priority)) - { - /* - * ambiguous: no command for current plugin, but more than one - * command was found for other plugins with the same priority - * => we don't know which one to run! - */ - rc = HOOK_COMMAND_EXEC_AMBIGUOUS_PLUGINS; - } - else - { - if (hook_plugin && hook_other_plugin) - { - /* - * if we have a command in current plugin and another plugin, - * choose the command with the higher priority (if priority - * is the same, always choose the command for the current - * plugin) - */ - ptr_hook = (hook_other_plugin->priority > hook_plugin->priority) ? - hook_other_plugin : hook_plugin; - } - else - { - /* - * choose the command for current plugin, if found, otherwise - * use command found in another plugin - */ - ptr_hook = (hook_plugin) ? hook_plugin : hook_other_plugin; - } - } - } - else if (hook_incomplete_command) - { - if (count_incomplete_commands == 1) - ptr_hook = hook_incomplete_command; - else - rc = HOOK_COMMAND_EXEC_AMBIGUOUS_INCOMPLETE; - } - - /* execute the command for the hook found */ - if (ptr_hook) - { - if (ptr_hook->running >= HOOK_COMMAND_MAX_CALLS) - { - /* loop in execution of command => do NOT execute again */ - rc = HOOK_COMMAND_EXEC_RUNNING; - } - else - { - /* execute the command! */ - ptr_hook->running++; - rc = (int) (HOOK_COMMAND(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - buffer, - argc, - argv, - argv_eol); - ptr_hook->running--; - if (rc == WEECHAT_RC_ERROR) - rc = HOOK_COMMAND_EXEC_ERROR; - else - rc = HOOK_COMMAND_EXEC_OK; - } - } - - string_free_split (argv); - string_free_split (argv_eol); - - hook_exec_end (); - - return rc; -} - -/* - * Hooks a command when it's run by WeeChat. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_command_run (struct t_weechat_plugin *plugin, - const char *command, - t_hook_callback_command_run *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_command_run *new_hook_command_run; - int priority; - const char *ptr_command; - - if (!callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_command_run = malloc (sizeof (*new_hook_command_run)); - if (!new_hook_command_run) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (command, &priority, &ptr_command); - hook_init_data (new_hook, plugin, HOOK_TYPE_COMMAND_RUN, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_command_run; - new_hook_command_run->callback = callback; - new_hook_command_run->command = strdup ((ptr_command) ? ptr_command : - ((command) ? command : "")); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Executes a command_run hook. - */ - -int -hook_command_run_exec (struct t_gui_buffer *buffer, const char *command) -{ - struct t_hook *ptr_hook, *next_hook; - int rc, hook_matching, length; - char *command2; - const char *ptr_command; - - if (!weechat_hooks[HOOK_TYPE_COMMAND_RUN]) - return WEECHAT_RC_OK; - - ptr_command = command; - command2 = NULL; - - if (command[0] != '/') - { - length = strlen (command) + 1; - command2 = malloc (length); - if (command2) - { - snprintf (command2, length, "/%s", command + 1); - ptr_command = command2; - } - } - - ptr_hook = weechat_hooks[HOOK_TYPE_COMMAND_RUN]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && HOOK_COMMAND_RUN(ptr_hook, command)) - { - hook_matching = string_match (ptr_command, - HOOK_COMMAND_RUN(ptr_hook, command), - 0); - - if (!hook_matching - && !strchr (HOOK_COMMAND_RUN(ptr_hook, command), ' ')) - { - length = strlen (HOOK_COMMAND_RUN(ptr_hook, command)); - hook_matching = ((string_strncasecmp (ptr_command, - HOOK_COMMAND_RUN(ptr_hook, command), - utf8_strlen (HOOK_COMMAND_RUN(ptr_hook, command))) == 0) - && ((ptr_command[length] == ' ') - || (ptr_command[length] == '\0'))); - } - - if (hook_matching) - { - ptr_hook->running = 1; - rc = (HOOK_COMMAND_RUN(ptr_hook, callback)) ( - ptr_hook->callback_pointer, - ptr_hook->callback_data, - buffer, - ptr_command); - ptr_hook->running = 0; - if (rc == WEECHAT_RC_OK_EAT) - { - if (command2) - free (command2); - return rc; - } - } - } - - ptr_hook = next_hook; - } - - if (command2) - free (command2); - - return WEECHAT_RC_OK; -} - -/* - * Initializes a timer hook. - */ - -void -hook_timer_init (struct t_hook *hook) -{ - time_t time_now; - struct tm *local_time, *gm_time; - int local_hour, gm_hour, diff_hour; - - gettimeofday (&HOOK_TIMER(hook, last_exec), NULL); - time_now = time (NULL); - local_time = localtime (&time_now); - local_hour = local_time->tm_hour; - gm_time = gmtime (&time_now); - gm_hour = gm_time->tm_hour; - if ((local_time->tm_year > gm_time->tm_year) - || (local_time->tm_mon > gm_time->tm_mon) - || (local_time->tm_mday > gm_time->tm_mday)) - { - diff_hour = (24 - gm_hour) + local_hour; - } - else if ((gm_time->tm_year > local_time->tm_year) - || (gm_time->tm_mon > local_time->tm_mon) - || (gm_time->tm_mday > local_time->tm_mday)) - { - diff_hour = (-1) * ((24 - local_hour) + gm_hour); - } - else - diff_hour = local_hour - gm_hour; - - if ((HOOK_TIMER(hook, interval) >= 1000) - && (HOOK_TIMER(hook, align_second) > 0)) - { - /* - * here we should use 0, but with this value timer is sometimes called - * before second has changed, so for displaying time, it may display - * 2 times the same second, that's why we use 10000 micro seconds - */ - HOOK_TIMER(hook, last_exec).tv_usec = 10000; - HOOK_TIMER(hook, last_exec).tv_sec = - HOOK_TIMER(hook, last_exec).tv_sec - - ((HOOK_TIMER(hook, last_exec).tv_sec + (diff_hour * 3600)) % - HOOK_TIMER(hook, align_second)); - } - - /* init next call with date of last call */ - HOOK_TIMER(hook, next_exec).tv_sec = HOOK_TIMER(hook, last_exec).tv_sec; - HOOK_TIMER(hook, next_exec).tv_usec = HOOK_TIMER(hook, last_exec).tv_usec; - - /* add interval to next call date */ - util_timeval_add (&HOOK_TIMER(hook, next_exec), - ((long long)HOOK_TIMER(hook, interval)) * 1000); -} - -/* - * Hooks a timer. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_timer (struct t_weechat_plugin *plugin, long interval, int align_second, - int max_calls, t_hook_callback_timer *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_timer *new_hook_timer; - - if ((interval <= 0) || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_timer = malloc (sizeof (*new_hook_timer)); - if (!new_hook_timer) - { - free (new_hook); - return NULL; - } - - hook_init_data (new_hook, plugin, HOOK_TYPE_TIMER, HOOK_PRIORITY_DEFAULT, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_timer; - new_hook_timer->callback = callback; - new_hook_timer->interval = interval; - new_hook_timer->align_second = align_second; - new_hook_timer->remaining_calls = max_calls; - - hook_timer_init (new_hook); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Checks if system clock is older than previous call to this function (that - * means new time is lower than in past). If yes, adjusts all timers to current - * time. - */ - -void -hook_timer_check_system_clock () -{ - time_t now; - long diff_time; - struct t_hook *ptr_hook; - - now = time (NULL); - - /* - * check if difference with previous time is more than 10 seconds: - * if it is, then consider it's clock skew and reinitialize all timers - */ - diff_time = now - hook_last_system_time; - if ((diff_time <= -10) || (diff_time >= 10)) - { - if (weechat_debug_core >= 1) - { - gui_chat_printf (NULL, - _("System clock skew detected (%+ld seconds), " - "reinitializing all timers"), - diff_time); - } - - /* reinitialize all timers */ - for (ptr_hook = weechat_hooks[HOOK_TYPE_TIMER]; ptr_hook; - ptr_hook = ptr_hook->next_hook) - { - if (!ptr_hook->deleted) - hook_timer_init (ptr_hook); - } - } - - hook_last_system_time = now; -} - -/* - * Returns time until next timeout (in milliseconds). - */ - -int -hook_timer_get_time_to_next () -{ - struct t_hook *ptr_hook; - int found, timeout; - struct timeval tv_now, tv_timeout; - long diff_usec; - - hook_timer_check_system_clock (); - - found = 0; - tv_timeout.tv_sec = 0; - tv_timeout.tv_usec = 0; - - for (ptr_hook = weechat_hooks[HOOK_TYPE_TIMER]; ptr_hook; - ptr_hook = ptr_hook->next_hook) - { - if (!ptr_hook->deleted - && (!found - || (util_timeval_cmp (&HOOK_TIMER(ptr_hook, next_exec), - &tv_timeout) < 0))) - { - found = 1; - tv_timeout.tv_sec = HOOK_TIMER(ptr_hook, next_exec).tv_sec; - tv_timeout.tv_usec = HOOK_TIMER(ptr_hook, next_exec).tv_usec; - } - } - - /* no timeout found, return 2 seconds by default */ - if (!found) - { - tv_timeout.tv_sec = 2; - tv_timeout.tv_usec = 0; - goto end; - } - - gettimeofday (&tv_now, NULL); - - /* next timeout is past date! */ - if (util_timeval_cmp (&tv_timeout, &tv_now) < 0) - { - tv_timeout.tv_sec = 0; - tv_timeout.tv_usec = 0; - goto end; - } - - tv_timeout.tv_sec = tv_timeout.tv_sec - tv_now.tv_sec; - diff_usec = tv_timeout.tv_usec - tv_now.tv_usec; - if (diff_usec >= 0) - { - tv_timeout.tv_usec = diff_usec; - } - else - { - tv_timeout.tv_sec--; - tv_timeout.tv_usec = 1000000 + diff_usec; - } - - /* - * to detect clock skew, we ensure there's a call to timers every - * 2 seconds max - */ - if (tv_timeout.tv_sec >= 2) - { - tv_timeout.tv_sec = 2; - tv_timeout.tv_usec = 0; - } - -end: - /* return a number of milliseconds */ - timeout = (tv_timeout.tv_sec * 1000) + (tv_timeout.tv_usec / 1000); - return (timeout < 1) ? 1 : timeout; -} - -/* - * Executes timer hooks. - */ - -void -hook_timer_exec () -{ - struct timeval tv_time; - struct t_hook *ptr_hook, *next_hook; - - if (!weechat_hooks[HOOK_TYPE_TIMER]) - return; - - hook_timer_check_system_clock (); - - gettimeofday (&tv_time, NULL); - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_TIMER]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (util_timeval_cmp (&HOOK_TIMER(ptr_hook, next_exec), - &tv_time) <= 0)) - { - ptr_hook->running = 1; - (void) (HOOK_TIMER(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - (HOOK_TIMER(ptr_hook, remaining_calls) > 0) ? - HOOK_TIMER(ptr_hook, remaining_calls) - 1 : -1); - ptr_hook->running = 0; - if (!ptr_hook->deleted) - { - HOOK_TIMER(ptr_hook, last_exec).tv_sec = tv_time.tv_sec; - HOOK_TIMER(ptr_hook, last_exec).tv_usec = tv_time.tv_usec; - - util_timeval_add ( - &HOOK_TIMER(ptr_hook, next_exec), - ((long long)HOOK_TIMER(ptr_hook, interval)) * 1000); - - if (HOOK_TIMER(ptr_hook, remaining_calls) > 0) - { - HOOK_TIMER(ptr_hook, remaining_calls)--; - if (HOOK_TIMER(ptr_hook, remaining_calls) == 0) - unhook (ptr_hook); - } - } - } - - ptr_hook = next_hook; - } - - hook_exec_end (); -} - -/* - * Searches for a fd hook in list. - * - * Returns pointer to hook found, NULL if not found. - */ - -struct t_hook * -hook_search_fd (int fd) -{ - struct t_hook *ptr_hook; - - for (ptr_hook = weechat_hooks[HOOK_TYPE_FD]; ptr_hook; - ptr_hook = ptr_hook->next_hook) - { - if (!ptr_hook->deleted && (HOOK_FD(ptr_hook, fd) == fd)) - return ptr_hook; - } - - /* fd hook not found */ - return NULL; -} - -/* - * Hooks a fd event. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_fd (struct t_weechat_plugin *plugin, int fd, int flag_read, - int flag_write, int flag_exception, - t_hook_callback_fd *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_fd *new_hook_fd; - - if ((fd < 0) || hook_search_fd (fd) || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_fd = malloc (sizeof (*new_hook_fd)); - if (!new_hook_fd) - { - free (new_hook); - return NULL; - } - - hook_init_data (new_hook, plugin, HOOK_TYPE_FD, HOOK_PRIORITY_DEFAULT, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_fd; - new_hook_fd->callback = callback; - new_hook_fd->fd = fd; - new_hook_fd->flags = 0; - new_hook_fd->error = 0; - if (flag_read) - new_hook_fd->flags |= HOOK_FD_FLAG_READ; - if (flag_write) - new_hook_fd->flags |= HOOK_FD_FLAG_WRITE; - if (flag_exception) - new_hook_fd->flags |= HOOK_FD_FLAG_EXCEPTION; - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Executes fd hooks: - * - poll() on fie descriptors - * - call of hook fd callbacks if needed. - */ - -void -hook_fd_exec () -{ - int i, num_fd, timeout, ready, found; - struct t_hook *ptr_hook, *next_hook; - - if (!weechat_hooks[HOOK_TYPE_FD]) - return; - - /* build an array of "struct pollfd" for poll() */ - num_fd = 0; - for (ptr_hook = weechat_hooks[HOOK_TYPE_FD]; ptr_hook; - ptr_hook = ptr_hook->next_hook) - { - if (!ptr_hook->deleted) - { - /* skip invalid file descriptors */ - if ((fcntl (HOOK_FD(ptr_hook,fd), F_GETFD) == -1) - && (errno == EBADF)) - { - if (HOOK_FD(ptr_hook, error) == 0) - { - HOOK_FD(ptr_hook, error) = errno; - gui_chat_printf (NULL, - _("%sError: bad file descriptor (%d) " - "used in hook_fd"), - gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], - HOOK_FD(ptr_hook, fd)); - } - } - else - { - if (num_fd > hook_fd_pollfd_count) - break; - - hook_fd_pollfd[num_fd].fd = HOOK_FD(ptr_hook, fd); - hook_fd_pollfd[num_fd].events = 0; - hook_fd_pollfd[num_fd].revents = 0; - if (HOOK_FD(ptr_hook, flags) & HOOK_FD_FLAG_READ) - hook_fd_pollfd[num_fd].events |= POLLIN; - if (HOOK_FD(ptr_hook, flags) & HOOK_FD_FLAG_WRITE) - hook_fd_pollfd[num_fd].events |= POLLOUT; - - num_fd++; - } - } - } - - /* perform the poll() */ - timeout = hook_timer_get_time_to_next (); - if (hook_process_pending) - timeout = 0; - ready = poll (hook_fd_pollfd, num_fd, timeout); - if (ready <= 0) - return; - - /* execute callbacks for file descriptors with activity */ - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_FD]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running) - { - found = 0; - for (i = 0; i < num_fd; i++) - { - if (hook_fd_pollfd[i].fd == HOOK_FD(ptr_hook, fd) - && hook_fd_pollfd[i].revents) - { - found = 1; - break; - } - } - if (found) - { - ptr_hook->running = 1; - (void) (HOOK_FD(ptr_hook, callback)) ( - ptr_hook->callback_pointer, - ptr_hook->callback_data, - HOOK_FD(ptr_hook, fd)); - ptr_hook->running = 0; - } - } - - ptr_hook = next_hook; - } - - hook_exec_end (); -} - -/* - * Hooks a process (using fork) with options in hashtable. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_process_hashtable (struct t_weechat_plugin *plugin, - const char *command, - struct t_hashtable *options, - int timeout, - t_hook_callback_process *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_process *new_hook_process; - char *stdout_buffer, *stderr_buffer, *error; - const char *ptr_value; - long number; - - stdout_buffer = NULL; - stderr_buffer = NULL; - new_hook = NULL; - new_hook_process = NULL; - - if (!command || !command[0] || !callback) - goto error; - - stdout_buffer = malloc (HOOK_PROCESS_BUFFER_SIZE + 1); - if (!stdout_buffer) - goto error; - - stderr_buffer = malloc (HOOK_PROCESS_BUFFER_SIZE + 1); - if (!stderr_buffer) - goto error; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - goto error; - - new_hook_process = malloc (sizeof (*new_hook_process)); - if (!new_hook_process) - goto error; - - hook_init_data (new_hook, plugin, HOOK_TYPE_PROCESS, HOOK_PRIORITY_DEFAULT, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_process; - new_hook_process->callback = callback; - new_hook_process->command = strdup (command); - new_hook_process->options = (options) ? hashtable_dup (options) : NULL; - new_hook_process->detached = (options && hashtable_has_key (options, - "detached")); - new_hook_process->timeout = timeout; - new_hook_process->child_read[HOOK_PROCESS_STDIN] = -1; - new_hook_process->child_read[HOOK_PROCESS_STDOUT] = -1; - new_hook_process->child_read[HOOK_PROCESS_STDERR] = -1; - new_hook_process->child_write[HOOK_PROCESS_STDIN] = -1; - new_hook_process->child_write[HOOK_PROCESS_STDOUT] = -1; - new_hook_process->child_write[HOOK_PROCESS_STDERR] = -1; - new_hook_process->child_pid = 0; - new_hook_process->hook_fd[HOOK_PROCESS_STDIN] = NULL; - new_hook_process->hook_fd[HOOK_PROCESS_STDOUT] = NULL; - new_hook_process->hook_fd[HOOK_PROCESS_STDERR] = NULL; - new_hook_process->hook_timer = NULL; - new_hook_process->buffer[HOOK_PROCESS_STDIN] = NULL; - new_hook_process->buffer[HOOK_PROCESS_STDOUT] = stdout_buffer; - new_hook_process->buffer[HOOK_PROCESS_STDERR] = stderr_buffer; - new_hook_process->buffer_size[HOOK_PROCESS_STDIN] = 0; - new_hook_process->buffer_size[HOOK_PROCESS_STDOUT] = 0; - new_hook_process->buffer_size[HOOK_PROCESS_STDERR] = 0; - new_hook_process->buffer_flush = HOOK_PROCESS_BUFFER_SIZE; - if (options) - { - ptr_value = hashtable_get (options, "buffer_flush"); - if (ptr_value && ptr_value[0]) - { - number = strtol (ptr_value, &error, 10); - if (error && !error[0] - && (number >= 1) && (number <= HOOK_PROCESS_BUFFER_SIZE)) - { - new_hook_process->buffer_flush = (int)number; - } - } - } - - hook_add_to_list (new_hook); - - if (weechat_debug_core >= 1) - { - gui_chat_printf (NULL, - "debug: hook_process: command=\"%s\", " - "options=\"%s\", timeout=%d", - new_hook_process->command, - hashtable_get_string (new_hook_process->options, - "keys_values"), - new_hook_process->timeout); - } - - if (strncmp (new_hook_process->command, "func:", 5) == 0) - hook_process_pending = 1; - else - hook_process_run (new_hook); - - return new_hook; - -error: - if (stdout_buffer) - free (stdout_buffer); - if (stderr_buffer) - free (stderr_buffer); - if (new_hook) - free (new_hook); - if (new_hook_process) - free (new_hook_process); - return NULL; -} - -/* - * Hooks a process (using fork). - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_process (struct t_weechat_plugin *plugin, - const char *command, - int timeout, - t_hook_callback_process *callback, - const void *callback_pointer, - void *callback_data) -{ - return hook_process_hashtable (plugin, command, NULL, timeout, - callback, callback_pointer, callback_data); -} - -/* - * Child process for hook process: executes command and returns string result - * into pipe for WeeChat process. - */ - -void -hook_process_child (struct t_hook *hook_process) -{ - char **exec_args, *arg0, str_arg[64]; - const char *ptr_url, *ptr_arg; - int rc, i, num_args; - FILE *f; - - /* read stdin from parent, if a pipe was defined */ - if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]) >= 0) - { - if (dup2 (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]), - STDIN_FILENO) < 0) - { - _exit (EXIT_FAILURE); - } - } - else - { - /* no stdin pipe from parent, use "/dev/null" for stdin stream */ - f = freopen ("/dev/null", "r", stdin); - (void) f; - } - if (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDIN]) >= 0) - close (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDIN])); - - /* redirect stdout/stderr to pipe (so that parent process can read them) */ - if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]) >= 0) - { - close (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT])); - if (dup2 (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDOUT]), - STDOUT_FILENO) < 0) - { - _exit (EXIT_FAILURE); - } - } - else - { - /* detached mode: write stdout in /dev/null */ - f = freopen ("/dev/null", "w", stdout); - (void) f; - } - if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]) >= 0) - { - close (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR])); - if (dup2 (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDERR]), - STDERR_FILENO) < 0) - { - _exit (EXIT_FAILURE); - } - } - else - { - /* detached mode: write stderr in /dev/null */ - f = freopen ("/dev/null", "w", stderr); - (void) f; - } - - rc = EXIT_FAILURE; - - if (strncmp (HOOK_PROCESS(hook_process, command), "url:", 4) == 0) - { - /* get URL output (on stdout or file, depending on options) */ - ptr_url = HOOK_PROCESS(hook_process, command) + 4; - while (ptr_url[0] == ' ') - { - ptr_url++; - } - rc = weeurl_download (ptr_url, HOOK_PROCESS(hook_process, options)); - } - else if (strncmp (HOOK_PROCESS(hook_process, command), "func:", 5) == 0) - { - /* run a function (via the hook callback) */ - rc = (int) (HOOK_PROCESS(hook_process, callback)) - (hook_process->callback_pointer, - hook_process->callback_data, - HOOK_PROCESS(hook_process, command), - WEECHAT_HOOK_PROCESS_CHILD, - NULL, NULL); - } - else - { - /* launch command */ - num_args = 0; - if (HOOK_PROCESS(hook_process, options)) - { - /* - * count number of arguments given in the hashtable options, - * keys are: "arg1", "arg2", ... - */ - while (1) - { - snprintf (str_arg, sizeof (str_arg), "arg%d", num_args + 1); - ptr_arg = hashtable_get (HOOK_PROCESS(hook_process, options), - str_arg); - if (!ptr_arg) - break; - num_args++; - } - } - if (num_args > 0) - { - /* - * if at least one argument was found in hashtable option, the - * "command" contains only path to binary (without arguments), and - * the arguments are in hashtable - */ - exec_args = malloc ((num_args + 2) * sizeof (exec_args[0])); - if (exec_args) - { - exec_args[0] = strdup (HOOK_PROCESS(hook_process, command)); - for (i = 1; i <= num_args; i++) - { - snprintf (str_arg, sizeof (str_arg), "arg%d", i); - ptr_arg = hashtable_get (HOOK_PROCESS(hook_process, options), - str_arg); - exec_args[i] = (ptr_arg) ? strdup (ptr_arg) : NULL; - } - exec_args[num_args + 1] = NULL; - } - } - else - { - /* - * if no arguments were found in hashtable, make an automatic split - * of command, like the shell does - */ - exec_args = string_split_shell (HOOK_PROCESS(hook_process, command), - NULL); - } - - if (exec_args) - { - arg0 = string_expand_home (exec_args[0]); - if (arg0) - { - free (exec_args[0]); - exec_args[0] = arg0; - } - if (weechat_debug_core >= 1) - { - log_printf ("hook_process, command='%s'", - HOOK_PROCESS(hook_process, command)); - for (i = 0; exec_args[i]; i++) - { - log_printf (" args[%02d] == '%s'", i, exec_args[i]); - } - } - execvp (exec_args[0], exec_args); - } - - /* should not be executed if execvp was OK */ - if (exec_args) - string_free_split (exec_args); - fprintf (stderr, "Error with command '%s'\n", - HOOK_PROCESS(hook_process, command)); - } - - fflush (stdout); - fflush (stderr); - - _exit (rc); -} - -/* - * Sends buffers (stdout/stderr) to callback. - */ - -void -hook_process_send_buffers (struct t_hook *hook_process, int callback_rc) -{ - int size; - - /* add '\0' at end of stdout and stderr */ - size = HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDOUT]); - if (size > 0) - HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDOUT])[size] = '\0'; - size = HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDERR]); - if (size > 0) - HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDERR])[size] = '\0'; - - /* send buffers to callback */ - (void) (HOOK_PROCESS(hook_process, callback)) - (hook_process->callback_pointer, - hook_process->callback_data, - HOOK_PROCESS(hook_process, command), - callback_rc, - (HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDOUT]) > 0) ? - HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDOUT]) : NULL, - (HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDERR]) > 0) ? - HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDERR]) : NULL); - - /* reset size for stdout and stderr */ - HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDOUT]) = 0; - HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDERR]) = 0; -} - -/* - * Adds some data to buffer (stdout or stderr). - */ - -void -hook_process_add_to_buffer (struct t_hook *hook_process, int index_buffer, - const char *buffer, int size) -{ - if (HOOK_PROCESS(hook_process, buffer_size[index_buffer]) + size > HOOK_PROCESS_BUFFER_SIZE) - hook_process_send_buffers (hook_process, WEECHAT_HOOK_PROCESS_RUNNING); - - memcpy (HOOK_PROCESS(hook_process, buffer[index_buffer]) + - HOOK_PROCESS(hook_process, buffer_size[index_buffer]), - buffer, size); - HOOK_PROCESS(hook_process, buffer_size[index_buffer]) += size; -} - -/* - * Reads process output (stdout or stderr) from child process. - */ - -void -hook_process_child_read (struct t_hook *hook_process, int fd, - int index_buffer, struct t_hook **hook_fd) -{ - char buffer[HOOK_PROCESS_BUFFER_SIZE / 8]; - int num_read; - - if (hook_process->deleted) - return; - - num_read = read (fd, buffer, sizeof (buffer) - 1); - if (num_read > 0) - { - hook_process_add_to_buffer (hook_process, index_buffer, - buffer, num_read); - if (HOOK_PROCESS(hook_process, buffer_size[index_buffer]) >= - HOOK_PROCESS(hook_process, buffer_flush)) - { - hook_process_send_buffers (hook_process, - WEECHAT_HOOK_PROCESS_RUNNING); - } - } - else if (num_read == 0) - { - unhook (*hook_fd); - *hook_fd = NULL; - } -} - -/* - * Reads process output (stdout) from child process. - */ - -int -hook_process_child_read_stdout_cb (const void *pointer, void *data, int fd) -{ - struct t_hook *hook_process; - - /* make C compiler happy */ - (void) data; - - hook_process = (struct t_hook *)pointer; - - hook_process_child_read (hook_process, fd, HOOK_PROCESS_STDOUT, - &(HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDOUT]))); - return WEECHAT_RC_OK; -} - -/* - * Reads process output (stderr) from child process. - */ - -int -hook_process_child_read_stderr_cb (const void *pointer, void *data, int fd) -{ - struct t_hook *hook_process; - - /* make C compiler happy */ - (void) data; - - hook_process = (struct t_hook *)pointer; - - hook_process_child_read (hook_process, fd, HOOK_PROCESS_STDERR, - &(HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDERR]))); - return WEECHAT_RC_OK; -} - -/* - * Reads process output from child process until EOF - * (called when the child process has ended). - */ - -void -hook_process_child_read_until_eof (struct t_hook *hook_process) -{ - struct pollfd poll_fd[2]; - int count, fd_stdout, fd_stderr, num_fd, ready, i; - - fd_stdout = HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]); - fd_stderr = HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]); - - /* - * use a counter to prevent any infinite loop - * (if child's output is very very long...) - */ - count = 0; - while (count < 1024) - { - num_fd = 0; - - if (HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDOUT]) - && ((fcntl (fd_stdout, F_GETFD) != -1) || (errno != EBADF))) - { - poll_fd[num_fd].fd = fd_stdout; - poll_fd[num_fd].events = POLLIN; - poll_fd[num_fd].revents = 0; - num_fd++; - } - - if (HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDERR]) - && ((fcntl (fd_stderr, F_GETFD) != -1) || (errno != EBADF))) - { - poll_fd[num_fd].fd = fd_stderr; - poll_fd[num_fd].events = POLLIN; - poll_fd[num_fd].revents = 0; - num_fd++; - } - - if (num_fd == 0) - break; - - ready = poll (poll_fd, num_fd, 0); - - if (ready <= 0) - break; - - for (i = 0; i < num_fd; i++) - { - if (poll_fd[i].revents & POLLIN) - { - if (poll_fd[i].fd == fd_stdout) - { - (void) hook_process_child_read_stdout_cb ( - hook_process, - NULL, - HOOK_PROCESS(hook_process, - child_read[HOOK_PROCESS_STDOUT])); - } - else - { - (void) hook_process_child_read_stderr_cb ( - hook_process, - NULL, - HOOK_PROCESS(hook_process, - child_read[HOOK_PROCESS_STDERR])); - } - } - } - - count++; - } -} - -/* - * Checks if child process is still alive. - */ - -int -hook_process_timer_cb (const void *pointer, void *data, int remaining_calls) -{ - struct t_hook *hook_process; - int status, rc; - - /* make C compiler happy */ - (void) data; - (void) remaining_calls; - - hook_process = (struct t_hook *)pointer; - - if (hook_process->deleted) - return WEECHAT_RC_OK; - - if (remaining_calls == 0) - { - hook_process_send_buffers (hook_process, WEECHAT_HOOK_PROCESS_ERROR); - if (weechat_debug_core >= 1) - { - gui_chat_printf (NULL, - _("End of command '%s', timeout reached (%.1fs)"), - HOOK_PROCESS(hook_process, command), - ((float)HOOK_PROCESS(hook_process, timeout)) / 1000); - } - kill (HOOK_PROCESS(hook_process, child_pid), SIGKILL); - usleep (1000); - unhook (hook_process); - } - else - { - if (waitpid (HOOK_PROCESS(hook_process, child_pid), - &status, WNOHANG) > 0) - { - if (WIFEXITED(status)) - { - /* child terminated normally */ - rc = WEXITSTATUS(status); - hook_process_child_read_until_eof (hook_process); - hook_process_send_buffers (hook_process, rc); - unhook (hook_process); - } - else if (WIFSIGNALED(status)) - { - /* child terminated by a signal */ - hook_process_child_read_until_eof (hook_process); - hook_process_send_buffers (hook_process, - WEECHAT_HOOK_PROCESS_ERROR); - unhook (hook_process); - } - } - } - - return WEECHAT_RC_OK; -} - -/* - * Executes process command in child, and read data in current process, - * with fd hook. - */ - -void -hook_process_run (struct t_hook *hook_process) -{ - int pipes[3][2], timeout, max_calls, rc, i; - char str_error[1024]; - long interval; - pid_t pid; - - for (i = 0; i < 3; i++) - { - pipes[i][0] = -1; - pipes[i][1] = -1; - } - - /* create pipe for stdin (only if stdin was given in options) */ - if (HOOK_PROCESS(hook_process, options) - && hashtable_has_key (HOOK_PROCESS(hook_process, options), "stdin")) - { - if (pipe (pipes[HOOK_PROCESS_STDIN]) < 0) - goto error; - } - - /* create pipes for stdout/err (if not running in detached mode) */ - if (!HOOK_PROCESS(hook_process, detached)) - { - if (pipe (pipes[HOOK_PROCESS_STDOUT]) < 0) - goto error; - if (pipe (pipes[HOOK_PROCESS_STDERR]) < 0) - goto error; - } - - /* assign pipes to variables in hook */ - for (i = 0; i < 3; i++) - { - HOOK_PROCESS(hook_process, child_read[i]) = pipes[i][0]; - HOOK_PROCESS(hook_process, child_write[i]) = pipes[i][1]; - } - - /* fork */ - switch (pid = fork ()) - { - /* fork failed */ - case -1: - snprintf (str_error, sizeof (str_error), - "fork error: %s", - strerror (errno)); - (void) (HOOK_PROCESS(hook_process, callback)) - (hook_process->callback_pointer, - hook_process->callback_data, - HOOK_PROCESS(hook_process, command), - WEECHAT_HOOK_PROCESS_ERROR, - NULL, str_error); - unhook (hook_process); - return; - /* child process */ - case 0: - rc = setuid (getuid ()); - (void) rc; - hook_process_child (hook_process); - /* never executed */ - _exit (EXIT_SUCCESS); - break; - } - - /* parent process */ - HOOK_PROCESS(hook_process, child_pid) = pid; - if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]) >= 0) - { - close (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN])); - HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]) = -1; - } - if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]) >= 0) - { - close (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDOUT])); - HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDOUT]) = -1; - } - if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]) >= 0) - { - close (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDERR])); - HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDERR]) = -1; - } - - if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]) >= 0) - { - HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDOUT]) = - hook_fd (hook_process->plugin, - HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]), - 1, 0, 0, - &hook_process_child_read_stdout_cb, - hook_process, NULL); - } - - if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]) >= 0) - { - HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDERR]) = - hook_fd (hook_process->plugin, - HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]), - 1, 0, 0, - &hook_process_child_read_stderr_cb, - hook_process, NULL); - } - - timeout = HOOK_PROCESS(hook_process, timeout); - interval = 100; - max_calls = 0; - if (timeout > 0) - { - if (timeout <= 100) - { - interval = timeout; - max_calls = 1; - } - else - { - interval = 100; - max_calls = timeout / 100; - if (timeout % 100 == 0) - max_calls++; - } - } - HOOK_PROCESS(hook_process, hook_timer) = hook_timer (hook_process->plugin, - interval, 0, max_calls, - &hook_process_timer_cb, - hook_process, - NULL); - return; - -error: - for (i = 0; i < 3; i++) - { - if (pipes[i][0] >= 0) - close (pipes[i][0]); - if (pipes[i][1] >= 0) - close (pipes[i][1]); - } - (void) (HOOK_PROCESS(hook_process, callback)) - (hook_process->callback_pointer, - hook_process->callback_data, - HOOK_PROCESS(hook_process, command), - WEECHAT_HOOK_PROCESS_ERROR, - NULL, NULL); - unhook (hook_process); -} - -/* - * Executes all process commands pending. - */ - -void -hook_process_exec () -{ - struct t_hook *ptr_hook, *next_hook; - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_PROCESS]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (HOOK_PROCESS(ptr_hook, child_pid) == 0)) - { - ptr_hook->running = 1; - hook_process_run (ptr_hook); - ptr_hook->running = 0; - } - - ptr_hook = next_hook; - } - - hook_exec_end (); - - hook_process_pending = 0; -} - -/* - * Hooks a connection to a peer (using fork). - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_connect (struct t_weechat_plugin *plugin, const char *proxy, - const char *address, int port, int ipv6, int retry, - void *gnutls_sess, void *gnutls_cb, int gnutls_dhkey_size, - const char *gnutls_priorities, const char *local_hostname, - t_hook_callback_connect *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_connect *new_hook_connect; - int i; - -#ifndef HAVE_GNUTLS - /* make C compiler happy */ - (void) gnutls_sess; - (void) gnutls_cb; - (void) gnutls_dhkey_size; - (void) gnutls_priorities; -#endif /* HAVE_GNUTLS */ - - if (!address || (port <= 0) || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_connect = malloc (sizeof (*new_hook_connect)); - if (!new_hook_connect) - { - free (new_hook); - return NULL; - } - - hook_init_data (new_hook, plugin, HOOK_TYPE_CONNECT, HOOK_PRIORITY_DEFAULT, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_connect; - new_hook_connect->callback = callback; - new_hook_connect->proxy = (proxy) ? strdup (proxy) : NULL; - new_hook_connect->address = strdup (address); - new_hook_connect->port = port; - new_hook_connect->sock = -1; - new_hook_connect->ipv6 = ipv6; - new_hook_connect->retry = retry; -#ifdef HAVE_GNUTLS - new_hook_connect->gnutls_sess = gnutls_sess; - new_hook_connect->gnutls_cb = gnutls_cb; - new_hook_connect->gnutls_dhkey_size = gnutls_dhkey_size; - new_hook_connect->gnutls_priorities = (gnutls_priorities) ? - strdup (gnutls_priorities) : NULL; -#endif /* HAVE_GNUTLS */ - new_hook_connect->local_hostname = (local_hostname) ? - strdup (local_hostname) : NULL; - new_hook_connect->child_read = -1; - new_hook_connect->child_write = -1; - new_hook_connect->child_recv = -1; - new_hook_connect->child_send = -1; - new_hook_connect->child_pid = 0; - new_hook_connect->hook_child_timer = NULL; - new_hook_connect->hook_fd = NULL; - new_hook_connect->handshake_hook_fd = NULL; - new_hook_connect->handshake_hook_timer = NULL; - new_hook_connect->handshake_fd_flags = 0; - new_hook_connect->handshake_ip_address = NULL; - if (!hook_socketpair_ok) - { - for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) - { - new_hook_connect->sock_v4[i] = -1; - new_hook_connect->sock_v6[i] = -1; - } - } - - hook_add_to_list (new_hook); - - network_connect_with_fork (new_hook); - - return new_hook; -} - -/* - * Verifies certificates. - */ - -#ifdef HAVE_GNUTLS -int -hook_connect_gnutls_verify_certificates (gnutls_session_t tls_session) -{ - struct t_hook *ptr_hook; - int rc; - - rc = -1; - ptr_hook = weechat_hooks[HOOK_TYPE_CONNECT]; - while (ptr_hook) - { - /* looking for the right hook using to the gnutls session pointer */ - if (!ptr_hook->deleted - && HOOK_CONNECT(ptr_hook, gnutls_sess) - && (*(HOOK_CONNECT(ptr_hook, gnutls_sess)) == tls_session)) - { - rc = (int) (HOOK_CONNECT(ptr_hook, gnutls_cb)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - tls_session, NULL, 0, - NULL, 0, NULL, - WEECHAT_HOOK_CONNECT_GNUTLS_CB_VERIFY_CERT); - break; - } - ptr_hook = ptr_hook->next_hook; - } - - return rc; -} -#endif /* HAVE_GNUTLS */ - -/* - * Sets certificates. - */ - -#ifdef HAVE_GNUTLS -int -hook_connect_gnutls_set_certificates (gnutls_session_t tls_session, - const gnutls_datum_t *req_ca, int nreq, - const gnutls_pk_algorithm_t *pk_algos, - int pk_algos_len, -#if LIBGNUTLS_VERSION_NUMBER >= 0x020b00 /* 2.11.0 */ - gnutls_retr2_st *answer) -#else - gnutls_retr_st *answer) -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020b00 */ -{ - struct t_hook *ptr_hook; - int rc; - - rc = -1; - ptr_hook = weechat_hooks[HOOK_TYPE_CONNECT]; - while (ptr_hook) - { - /* looking for the right hook using to the gnutls session pointer */ - if (!ptr_hook->deleted - && HOOK_CONNECT(ptr_hook, gnutls_sess) - && (*(HOOK_CONNECT(ptr_hook, gnutls_sess)) == tls_session)) - { - rc = (int) (HOOK_CONNECT(ptr_hook, gnutls_cb)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - tls_session, req_ca, nreq, - pk_algos, pk_algos_len, answer, - WEECHAT_HOOK_CONNECT_GNUTLS_CB_SET_CERT); - break; - } - ptr_hook = ptr_hook->next_hook; - } - - return rc; -} -#endif /* HAVE_GNUTLS */ - -/* - * Hooks a line added in a buffer. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_line (struct t_weechat_plugin *plugin, const char *buffer_type, - const char *buffer_name, const char *tags, - t_hook_callback_line *callback, const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_line *new_hook_line; - - if (!callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_line = malloc (sizeof (*new_hook_line)); - if (!new_hook_line) - { - free (new_hook); - return NULL; - } - - hook_init_data (new_hook, plugin, HOOK_TYPE_LINE, HOOK_PRIORITY_DEFAULT, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_line; - new_hook_line->callback = callback; - if (!buffer_type || !buffer_type[0]) - new_hook_line->buffer_type = GUI_BUFFER_TYPE_FORMATTED; - else if (strcmp (buffer_type, "*") == 0) - new_hook_line->buffer_type = -1; - else - new_hook_line->buffer_type = gui_buffer_search_type (buffer_type); - new_hook_line->buffers = string_split ( - (buffer_name && buffer_name[0]) ? buffer_name : "*", - ",", 0, 0, &new_hook_line->num_buffers); - new_hook_line->tags_array = string_split_tags (tags, - &new_hook_line->tags_count); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Executes a line hook and updates the line data. - */ - -void -hook_line_exec (struct t_gui_line *line) -{ - struct t_hook *ptr_hook, *next_hook; - struct t_hashtable *hashtable, *hashtable2; - char str_value[128], *str_tags; - - if (!weechat_hooks[HOOK_TYPE_LINE]) - return; - - hashtable = NULL; - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_LINE]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted && !ptr_hook->running - && ((HOOK_LINE(ptr_hook, buffer_type) == -1) - || ((int)(line->data->buffer->type) == (HOOK_LINE(ptr_hook, buffer_type)))) - && gui_buffer_match_list_split (line->data->buffer, - HOOK_LINE(ptr_hook, num_buffers), - HOOK_LINE(ptr_hook, buffers)) - && (!HOOK_LINE(ptr_hook, tags_array) - || gui_line_match_tags (line->data, - HOOK_LINE(ptr_hook, tags_count), - HOOK_LINE(ptr_hook, tags_array)))) - { - /* create the hashtable that will be sent to callback */ - if (!hashtable) - { - hashtable = hashtable_new (32, - WEECHAT_HASHTABLE_STRING, - WEECHAT_HASHTABLE_STRING, - NULL, NULL); - if (!hashtable) - break; - } - HASHTABLE_SET_PTR("buffer", line->data->buffer); - HASHTABLE_SET_PTR("buffer_name", line->data->buffer->full_name); - HASHTABLE_SET_PTR("buffer_type", - gui_buffer_type_string[line->data->buffer->type]); - HASHTABLE_SET_INT("y", line->data->y); - HASHTABLE_SET_TIME("date", line->data->date); - HASHTABLE_SET_TIME("date_printed", line->data->date_printed); - HASHTABLE_SET_STR_NOT_NULL("str_time", line->data->str_time); - HASHTABLE_SET_INT("tags_count", line->data->tags_count); - str_tags = string_build_with_split_string ( - (const char **)line->data->tags_array, ","); - HASHTABLE_SET_STR_NOT_NULL("tags", str_tags); - if (str_tags) - free (str_tags); - HASHTABLE_SET_INT("displayed", line->data->displayed); - HASHTABLE_SET_INT("notify_level", line->data->notify_level); - HASHTABLE_SET_INT("highlight", line->data->highlight); - HASHTABLE_SET_STR_NOT_NULL("prefix", line->data->prefix); - HASHTABLE_SET_STR_NOT_NULL("message", line->data->message); - - /* run callback */ - ptr_hook->running = 1; - hashtable2 = (HOOK_LINE(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - hashtable); - ptr_hook->running = 0; - - if (hashtable2) - { - gui_line_hook_update (line, hashtable, hashtable2); - hashtable_free (hashtable2); - if (!line->data->buffer) - break; - } - } - - ptr_hook = next_hook; - } - - hook_exec_end (); - - if (hashtable) - hashtable_free (hashtable); -} - -/* - * Hooks a message printed by WeeChat. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_print (struct t_weechat_plugin *plugin, struct t_gui_buffer *buffer, - const char *tags, const char *message, int strip_colors, - t_hook_callback_print *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_print *new_hook_print; - - if (!callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_print = malloc (sizeof (*new_hook_print)); - if (!new_hook_print) - { - free (new_hook); - return NULL; - } - - hook_init_data (new_hook, plugin, HOOK_TYPE_PRINT, HOOK_PRIORITY_DEFAULT, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_print; - new_hook_print->callback = callback; - new_hook_print->buffer = buffer; - new_hook_print->tags_array = string_split_tags (tags, - &new_hook_print->tags_count); - new_hook_print->message = (message) ? strdup (message) : NULL; - new_hook_print->strip_colors = strip_colors; - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Executes a print hook. - */ - -void -hook_print_exec (struct t_gui_buffer *buffer, struct t_gui_line *line) -{ - struct t_hook *ptr_hook, *next_hook; - char *prefix_no_color, *message_no_color; - - if (!weechat_hooks[HOOK_TYPE_PRINT]) - return; - - if (!line->data->message || !line->data->message[0]) - return; - - prefix_no_color = (line->data->prefix) ? - gui_color_decode (line->data->prefix, NULL) : NULL; - - message_no_color = gui_color_decode (line->data->message, NULL); - if (!message_no_color) - { - if (prefix_no_color) - free (prefix_no_color); - return; - } - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_PRINT]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (!HOOK_PRINT(ptr_hook, buffer) - || (buffer == HOOK_PRINT(ptr_hook, buffer))) - && (!HOOK_PRINT(ptr_hook, message) - || !HOOK_PRINT(ptr_hook, message)[0] - || string_strcasestr (prefix_no_color, HOOK_PRINT(ptr_hook, message)) - || string_strcasestr (message_no_color, HOOK_PRINT(ptr_hook, message))) - && (!HOOK_PRINT(ptr_hook, tags_array) - || gui_line_match_tags (line->data, - HOOK_PRINT(ptr_hook, tags_count), - HOOK_PRINT(ptr_hook, tags_array)))) - { - /* run callback */ - ptr_hook->running = 1; - (void) (HOOK_PRINT(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - buffer, - line->data->date, - line->data->tags_count, - (const char **)line->data->tags_array, - (int)line->data->displayed, (int)line->data->highlight, - (HOOK_PRINT(ptr_hook, strip_colors)) ? prefix_no_color : line->data->prefix, - (HOOK_PRINT(ptr_hook, strip_colors)) ? message_no_color : line->data->message); - ptr_hook->running = 0; - } - - ptr_hook = next_hook; - } - - if (prefix_no_color) - free (prefix_no_color); - if (message_no_color) - free (message_no_color); - - hook_exec_end (); -} - -/* - * Hooks a signal. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_signal (struct t_weechat_plugin *plugin, const char *signal, - t_hook_callback_signal *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_signal *new_hook_signal; - int priority; - const char *ptr_signal; - - if (!signal || !signal[0] || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_signal = malloc (sizeof (*new_hook_signal)); - if (!new_hook_signal) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (signal, &priority, &ptr_signal); - hook_init_data (new_hook, plugin, HOOK_TYPE_SIGNAL, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_signal; - new_hook_signal->callback = callback; - new_hook_signal->signal = strdup ((ptr_signal) ? ptr_signal : signal); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Sends a signal. - */ - -int -hook_signal_send (const char *signal, const char *type_data, void *signal_data) -{ - struct t_hook *ptr_hook, *next_hook; - int rc; - - rc = WEECHAT_RC_OK; - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_SIGNAL]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (string_match (signal, HOOK_SIGNAL(ptr_hook, signal), 0))) - { - ptr_hook->running = 1; - rc = (HOOK_SIGNAL(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - signal, - type_data, - signal_data); - ptr_hook->running = 0; - - if (rc == WEECHAT_RC_OK_EAT) - break; - } - - ptr_hook = next_hook; - } - - hook_exec_end (); - - return rc; -} - -/* - * Hooks a hsignal (signal with hashtable). - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_hsignal (struct t_weechat_plugin *plugin, const char *signal, - t_hook_callback_hsignal *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_hsignal *new_hook_hsignal; - int priority; - const char *ptr_signal; - - if (!signal || !signal[0] || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_hsignal = malloc (sizeof (*new_hook_hsignal)); - if (!new_hook_hsignal) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (signal, &priority, &ptr_signal); - hook_init_data (new_hook, plugin, HOOK_TYPE_HSIGNAL, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_hsignal; - new_hook_hsignal->callback = callback; - new_hook_hsignal->signal = strdup ((ptr_signal) ? ptr_signal : signal); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Sends a hsignal (signal with hashtable). - */ - -int -hook_hsignal_send (const char *signal, struct t_hashtable *hashtable) -{ - struct t_hook *ptr_hook, *next_hook; - int rc; - - rc = WEECHAT_RC_OK; - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_HSIGNAL]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (string_match (signal, HOOK_HSIGNAL(ptr_hook, signal), 0))) - { - ptr_hook->running = 1; - rc = (HOOK_HSIGNAL(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - signal, - hashtable); - ptr_hook->running = 0; - - if (rc == WEECHAT_RC_OK_EAT) - break; - } - - ptr_hook = next_hook; - } - - hook_exec_end (); - - return rc; -} - -/* - * Hooks a configuration option. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_config (struct t_weechat_plugin *plugin, const char *option, - t_hook_callback_config *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_config *new_hook_config; - int priority; - const char *ptr_option; - - if (!callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_config = malloc (sizeof (*new_hook_config)); - if (!new_hook_config) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (option, &priority, &ptr_option); - hook_init_data (new_hook, plugin, HOOK_TYPE_CONFIG, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_config; - new_hook_config->callback = callback; - new_hook_config->option = strdup ((ptr_option) ? ptr_option : - ((option) ? option : "")); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Executes a config hook. - */ - -void -hook_config_exec (const char *option, const char *value) -{ - struct t_hook *ptr_hook, *next_hook; - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_CONFIG]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (!HOOK_CONFIG(ptr_hook, option) - || (string_match (option, HOOK_CONFIG(ptr_hook, option), 0)))) - { - ptr_hook->running = 1; - (void) (HOOK_CONFIG(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - option, - value); - ptr_hook->running = 0; - } - - ptr_hook = next_hook; - } - - hook_exec_end (); -} - -/* - * Hooks a completion. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_completion (struct t_weechat_plugin *plugin, const char *completion_item, - const char *description, - t_hook_callback_completion *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_completion *new_hook_completion; - int priority; - const char *ptr_completion_item; - - if (!completion_item || !completion_item[0] - || strchr (completion_item, ' ') || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_completion = malloc (sizeof (*new_hook_completion)); - if (!new_hook_completion) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (completion_item, &priority, &ptr_completion_item); - hook_init_data (new_hook, plugin, HOOK_TYPE_COMPLETION, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_completion; - new_hook_completion->callback = callback; - new_hook_completion->completion_item = strdup ((ptr_completion_item) ? - ptr_completion_item : completion_item); - new_hook_completion->description = strdup ((description) ? - description : ""); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Gets a completion property as string. - */ - -const char * -hook_completion_get_string (struct t_gui_completion *completion, - const char *property) -{ - return gui_completion_get_string (completion, property); -} - -/* - * Adds a word for a completion. - */ - -void -hook_completion_list_add (struct t_gui_completion *completion, - const char *word, int nick_completion, - const char *where) -{ - gui_completion_list_add (completion, word, nick_completion, where); -} - -/* - * Executes a completion hook. - */ - -void -hook_completion_exec (struct t_weechat_plugin *plugin, - const char *completion_item, - struct t_gui_buffer *buffer, - struct t_gui_completion *completion) -{ - struct t_hook *ptr_hook, *next_hook; - const char *pos; - char *item; - - /* make C compiler happy */ - (void) plugin; - - if (!weechat_hooks[HOOK_TYPE_COMPLETION]) - return; - - hook_exec_start (); - - pos = strchr (completion_item, ':'); - item = (pos) ? - string_strndup (completion_item, pos - completion_item) : - strdup (completion_item); - if (!item) - return; - - ptr_hook = weechat_hooks[HOOK_TYPE_COMPLETION]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (string_strcasecmp (HOOK_COMPLETION(ptr_hook, completion_item), - item) == 0)) - { - ptr_hook->running = 1; - (void) (HOOK_COMPLETION(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - completion_item, - buffer, - completion); - ptr_hook->running = 0; - } - - ptr_hook = next_hook; - } - - free (item); - - hook_exec_end (); -} - -/* - * Hooks a modifier. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_modifier (struct t_weechat_plugin *plugin, const char *modifier, - t_hook_callback_modifier *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_modifier *new_hook_modifier; - int priority; - const char *ptr_modifier; - - if (!modifier || !modifier[0] || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_modifier = malloc (sizeof (*new_hook_modifier)); - if (!new_hook_modifier) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (modifier, &priority, &ptr_modifier); - hook_init_data (new_hook, plugin, HOOK_TYPE_MODIFIER, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_modifier; - new_hook_modifier->callback = callback; - new_hook_modifier->modifier = strdup ((ptr_modifier) ? ptr_modifier : modifier); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Executes a modifier hook. - * - * Note: result must be freed after use. - */ - -char * -hook_modifier_exec (struct t_weechat_plugin *plugin, const char *modifier, - const char *modifier_data, const char *string) -{ - struct t_hook *ptr_hook, *next_hook; - char *new_msg, *message_modified; - - /* make C compiler happy */ - (void) plugin; - - if (!modifier || !modifier[0]) - return NULL; - - new_msg = NULL; - message_modified = strdup (string); - if (!message_modified) - return NULL; - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_MODIFIER]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (string_strcasecmp (HOOK_MODIFIER(ptr_hook, modifier), - modifier) == 0)) - { - ptr_hook->running = 1; - new_msg = (HOOK_MODIFIER(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - modifier, - modifier_data, - message_modified); - ptr_hook->running = 0; - - /* empty string returned => message dropped */ - if (new_msg && !new_msg[0]) - { - free (message_modified); - hook_exec_end (); - return new_msg; - } - - /* new message => keep it as base for next modifier */ - if (new_msg) - { - free (message_modified); - message_modified = new_msg; - } - } - - ptr_hook = next_hook; - } - - hook_exec_end (); - - return message_modified; -} - -/* - * Hooks an info. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_info (struct t_weechat_plugin *plugin, const char *info_name, - const char *description, const char *args_description, - t_hook_callback_info *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_info *new_hook_info; - int priority; - const char *ptr_info_name; - - if (!info_name || !info_name[0] || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_info = malloc (sizeof (*new_hook_info)); - if (!new_hook_info) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (info_name, &priority, &ptr_info_name); - hook_init_data (new_hook, plugin, HOOK_TYPE_INFO, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_info; - new_hook_info->callback = callback; - new_hook_info->info_name = strdup ((ptr_info_name) ? - ptr_info_name : info_name); - new_hook_info->description = strdup ((description) ? description : ""); - new_hook_info->args_description = strdup ((args_description) ? - args_description : ""); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Gets info (as string) via info hook. - */ - -const char * -hook_info_get (struct t_weechat_plugin *plugin, const char *info_name, - const char *arguments) -{ - struct t_hook *ptr_hook, *next_hook; - const char *value; - - /* make C compiler happy */ - (void) plugin; - - if (!info_name || !info_name[0]) - return NULL; - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_INFO]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (string_strcasecmp (HOOK_INFO(ptr_hook, info_name), - info_name) == 0)) - { - ptr_hook->running = 1; - value = (HOOK_INFO(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - info_name, - arguments); - ptr_hook->running = 0; - - hook_exec_end (); - return value; - } - - ptr_hook = next_hook; - } - - hook_exec_end (); - - /* info not found */ - return NULL; -} - -/* - * Hooks an info using hashtable. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_info_hashtable (struct t_weechat_plugin *plugin, const char *info_name, - const char *description, const char *args_description, - const char *output_description, - t_hook_callback_info_hashtable *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_info_hashtable *new_hook_info_hashtable; - int priority; - const char *ptr_info_name; - - if (!info_name || !info_name[0] || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_info_hashtable = malloc (sizeof (*new_hook_info_hashtable)); - if (!new_hook_info_hashtable) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (info_name, &priority, &ptr_info_name); - hook_init_data (new_hook, plugin, HOOK_TYPE_INFO_HASHTABLE, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_info_hashtable; - new_hook_info_hashtable->callback = callback; - new_hook_info_hashtable->info_name = strdup ((ptr_info_name) ? - ptr_info_name : info_name); - new_hook_info_hashtable->description = strdup ((description) ? description : ""); - new_hook_info_hashtable->args_description = strdup ((args_description) ? - args_description : ""); - new_hook_info_hashtable->output_description = strdup ((output_description) ? - output_description : ""); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Gets info (as hashtable) via info hook. - */ - -struct t_hashtable * -hook_info_get_hashtable (struct t_weechat_plugin *plugin, const char *info_name, - struct t_hashtable *hashtable) -{ - struct t_hook *ptr_hook, *next_hook; - struct t_hashtable *value; - - /* make C compiler happy */ - (void) plugin; - - if (!info_name || !info_name[0]) - return NULL; - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_INFO_HASHTABLE]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (string_strcasecmp (HOOK_INFO_HASHTABLE(ptr_hook, info_name), - info_name) == 0)) - { - ptr_hook->running = 1; - value = (HOOK_INFO_HASHTABLE(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - info_name, - hashtable); - ptr_hook->running = 0; - - hook_exec_end (); - return value; - } - - ptr_hook = next_hook; - } - - hook_exec_end (); - - /* info not found */ - return NULL; -} - -/* - * Hooks an infolist. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_infolist (struct t_weechat_plugin *plugin, const char *infolist_name, - const char *description, const char *pointer_description, - const char *args_description, - t_hook_callback_infolist *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_infolist *new_hook_infolist; - int priority; - const char *ptr_infolist_name; - - if (!infolist_name || !infolist_name[0] || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_infolist = malloc (sizeof (*new_hook_infolist)); - if (!new_hook_infolist) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (infolist_name, &priority, &ptr_infolist_name); - hook_init_data (new_hook, plugin, HOOK_TYPE_INFOLIST, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_infolist; - new_hook_infolist->callback = callback; - new_hook_infolist->infolist_name = strdup ((ptr_infolist_name) ? - ptr_infolist_name : infolist_name); - new_hook_infolist->description = strdup ((description) ? description : ""); - new_hook_infolist->pointer_description = strdup ((pointer_description) ? - pointer_description : ""); - new_hook_infolist->args_description = strdup ((args_description) ? - args_description : ""); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Gets an infolist via infolist hook. - */ - -struct t_infolist * -hook_infolist_get (struct t_weechat_plugin *plugin, const char *infolist_name, - void *pointer, const char *arguments) -{ - struct t_hook *ptr_hook, *next_hook; - struct t_infolist *value; - - /* make C compiler happy */ - (void) plugin; - - if (!infolist_name || !infolist_name[0]) - return NULL; - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_INFOLIST]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (string_strcasecmp (HOOK_INFOLIST(ptr_hook, infolist_name), - infolist_name) == 0)) - { - ptr_hook->running = 1; - value = (HOOK_INFOLIST(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - infolist_name, - pointer, - arguments); - ptr_hook->running = 0; - - hook_exec_end (); - return value; - } - - ptr_hook = next_hook; - } - - hook_exec_end (); - - /* infolist not found */ - return NULL; -} - -/* - * Hooks a hdata. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_hdata (struct t_weechat_plugin *plugin, const char *hdata_name, - const char *description, - t_hook_callback_hdata *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_hdata *new_hook_hdata; - int priority; - const char *ptr_hdata_name; - - if (!hdata_name || !hdata_name[0] || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_hdata = malloc (sizeof (*new_hook_hdata)); - if (!new_hook_hdata) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (hdata_name, &priority, &ptr_hdata_name); - hook_init_data (new_hook, plugin, HOOK_TYPE_HDATA, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_hdata; - new_hook_hdata->callback = callback; - new_hook_hdata->hdata_name = strdup ((ptr_hdata_name) ? - ptr_hdata_name : hdata_name); - new_hook_hdata->description = strdup ((description) ? description : ""); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Gets hdata via hdata hook. - */ - -struct t_hdata * -hook_hdata_get (struct t_weechat_plugin *plugin, const char *hdata_name) -{ - struct t_hook *ptr_hook, *next_hook; - struct t_hdata *value; - - /* make C compiler happy */ - (void) plugin; - - if (!hdata_name || !hdata_name[0]) - return NULL; - - if (weechat_hdata) - { - value = hashtable_get (weechat_hdata, hdata_name); - if (value) - return value; - } - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_HDATA]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && (strcmp (HOOK_HDATA(ptr_hook, hdata_name), hdata_name) == 0)) - { - ptr_hook->running = 1; - value = (HOOK_HDATA(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - HOOK_HDATA(ptr_hook, hdata_name)); - ptr_hook->running = 0; - - hook_exec_end (); - return value; - } - - ptr_hook = next_hook; - } - - hook_exec_end (); - - /* hdata not found */ - return NULL; -} - -/* - * Hooks a focus. - * - * Returns pointer to new hook, NULL if error. - */ - -struct t_hook * -hook_focus (struct t_weechat_plugin *plugin, - const char *area, - t_hook_callback_focus *callback, - const void *callback_pointer, - void *callback_data) -{ - struct t_hook *new_hook; - struct t_hook_focus *new_hook_focus; - int priority; - const char *ptr_area; - - if (!area || !area[0] || !callback) - return NULL; - - new_hook = malloc (sizeof (*new_hook)); - if (!new_hook) - return NULL; - new_hook_focus = malloc (sizeof (*new_hook_focus)); - if (!new_hook_focus) - { - free (new_hook); - return NULL; - } - - hook_get_priority_and_name (area, &priority, &ptr_area); - hook_init_data (new_hook, plugin, HOOK_TYPE_FOCUS, priority, - callback_pointer, callback_data); - - new_hook->hook_data = new_hook_focus; - new_hook_focus->callback = callback; - new_hook_focus->area = strdup ((ptr_area) ? ptr_area : area); - - hook_add_to_list (new_hook); - - return new_hook; -} - -/* - * Adds keys of a hashtable into another. - */ - -void -hook_focus_hashtable_map_cb (void *data, - struct t_hashtable *hashtable, - const void *key, const void *value) -{ - struct t_hashtable *hashtable1; - - /* make C compiler happy */ - (void) hashtable; - - hashtable1 = (struct t_hashtable *)data; - - if (hashtable1 && key && value) - hashtable_set (hashtable1, (const char *)key, (const char *)value); -} - -/* - * Adds keys of a hashtable into another (adding suffix "2" to keys). - */ - -void -hook_focus_hashtable_map2_cb (void *data, - struct t_hashtable *hashtable, - const void *key, const void *value) -{ - struct t_hashtable *hashtable1; - int length; - char *key2; - - /* make C compiler happy */ - (void) hashtable; - - hashtable1 = (struct t_hashtable *)data; - - length = strlen ((const char *)key) + 1 + 1; - key2 = malloc (length); - if (key2) - { - snprintf (key2, length, "%s2", (const char *)key); - if (hashtable1 && key && value) - hashtable_set (hashtable1, key2, (const char *)value); - free (key2); - } -} - -/* - * Gets data for focus on (x,y) on screen. - * - * Argument hashtable_focus2 is not NULL only for a mouse gesture (it's for - * point where mouse button has been released). - */ - -struct t_hashtable * -hook_focus_get_data (struct t_hashtable *hashtable_focus1, - struct t_hashtable *hashtable_focus2) -{ - struct t_hook *ptr_hook, *next_hook; - struct t_hashtable *hashtable1, *hashtable2, *hashtable_ret; - const char *focus1_chat, *focus1_bar_item_name, *keys; - char **list_keys, *new_key; - int num_keys, i, length, focus1_is_chat; - - if (!hashtable_focus1) - return NULL; - - focus1_chat = hashtable_get (hashtable_focus1, "_chat"); - focus1_is_chat = (focus1_chat && (strcmp (focus1_chat, "1") == 0)); - focus1_bar_item_name = hashtable_get (hashtable_focus1, "_bar_item_name"); - - hashtable1 = hashtable_dup (hashtable_focus1); - if (!hashtable1) - return NULL; - hashtable2 = (hashtable_focus2) ? hashtable_dup (hashtable_focus2) : NULL; - - hook_exec_start (); - - ptr_hook = weechat_hooks[HOOK_TYPE_FOCUS]; - while (ptr_hook) - { - next_hook = ptr_hook->next_hook; - - if (!ptr_hook->deleted - && !ptr_hook->running - && ((focus1_is_chat - && (strcmp (HOOK_FOCUS(ptr_hook, area), "chat") == 0)) - || (focus1_bar_item_name && focus1_bar_item_name[0] - && (strcmp (HOOK_FOCUS(ptr_hook, area), focus1_bar_item_name) == 0)))) - { - /* run callback for focus #1 */ - ptr_hook->running = 1; - hashtable_ret = (HOOK_FOCUS(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - hashtable1); - ptr_hook->running = 0; - if (hashtable_ret) - { - if (hashtable_ret != hashtable1) - { - /* - * add keys of hashtable_ret into hashtable1 - * and destroy it - */ - hashtable_map (hashtable_ret, - &hook_focus_hashtable_map_cb, - hashtable1); - hashtable_free (hashtable_ret); - } - } - - /* run callback for focus #2 */ - if (hashtable2) - { - ptr_hook->running = 1; - hashtable_ret = (HOOK_FOCUS(ptr_hook, callback)) - (ptr_hook->callback_pointer, - ptr_hook->callback_data, - hashtable2); - ptr_hook->running = 0; - if (hashtable_ret) - { - if (hashtable_ret != hashtable2) - { - /* - * add keys of hashtable_ret into hashtable2 - * and destroy it - */ - hashtable_map (hashtable_ret, - &hook_focus_hashtable_map_cb, - hashtable2); - hashtable_free (hashtable_ret); - } - } - } - } - - ptr_hook = next_hook; - } - - if (hashtable2) - { - hashtable_map (hashtable2, - &hook_focus_hashtable_map2_cb, hashtable1); - hashtable_free (hashtable2); - } - else - { - keys = hashtable_get_string (hashtable1, "keys"); - if (keys) - { - list_keys = string_split (keys, ",", 0, 0, &num_keys); - if (list_keys) - { - for (i = 0; i < num_keys; i++) - { - length = strlen (list_keys[i]) + 1 + 1; - new_key = malloc (length); - if (new_key) - { - snprintf (new_key, length, "%s2", list_keys[i]); - hashtable_set (hashtable1, new_key, - hashtable_get (hashtable1, - list_keys[i])); - free (new_key); - } - } - string_free_split (list_keys); - } - } - } - - hook_exec_end (); - - return hashtable1; -} - -/* * Sets a hook property (string). */ @@ -3871,8 +540,6 @@ hook_set (struct t_hook *hook, const char *property, const char *value) void unhook (struct t_hook *hook) { - int i; - /* invalid hook? */ if (!hook_valid (hook)) return; @@ -3891,409 +558,7 @@ unhook (struct t_hook *hook) } /* free data specific to the hook */ - if (hook->hook_data) - { - switch (hook->type) - { - case HOOK_TYPE_COMMAND: - if (HOOK_COMMAND(hook, command)) - { - free (HOOK_COMMAND(hook, command)); - HOOK_COMMAND(hook, command) = NULL; - } - if (HOOK_COMMAND(hook, description)) - { - free (HOOK_COMMAND(hook, description)); - HOOK_COMMAND(hook, description) = NULL; - } - if (HOOK_COMMAND(hook, args)) - { - free (HOOK_COMMAND(hook, args)); - HOOK_COMMAND(hook, args) = NULL; - } - if (HOOK_COMMAND(hook, args_description)) - { - free (HOOK_COMMAND(hook, args_description)); - HOOK_COMMAND(hook, args_description) = NULL; - } - if (HOOK_COMMAND(hook, completion)) - { - free (HOOK_COMMAND(hook, completion)); - HOOK_COMMAND(hook, completion) = NULL; - } - if (HOOK_COMMAND(hook, cplt_templates)) - { - for (i = 0; i < HOOK_COMMAND(hook, cplt_num_templates); i++) - { - if (HOOK_COMMAND(hook, cplt_templates)[i]) - free (HOOK_COMMAND(hook, cplt_templates)[i]); - if (HOOK_COMMAND(hook, cplt_templates_static)[i]) - free (HOOK_COMMAND(hook, cplt_templates_static)[i]); - string_free_split (HOOK_COMMAND(hook, cplt_template_args)[i]); - } - free (HOOK_COMMAND(hook, cplt_templates)); - } - if (HOOK_COMMAND(hook, cplt_templates_static)) - { - free (HOOK_COMMAND(hook, cplt_templates_static)); - HOOK_COMMAND(hook, cplt_templates_static) = NULL; - } - if (HOOK_COMMAND(hook, cplt_template_num_args)) - { - free (HOOK_COMMAND(hook, cplt_template_num_args)); - HOOK_COMMAND(hook, cplt_template_num_args) = NULL; - } - if (HOOK_COMMAND(hook, cplt_template_args)) - { - free (HOOK_COMMAND(hook, cplt_template_args)); - HOOK_COMMAND(hook, cplt_template_args) = NULL; - } - if (HOOK_COMMAND(hook, cplt_template_args_concat)) - { - for (i = 0; - i < HOOK_COMMAND(hook, cplt_template_num_args_concat); - i++) - { - free (HOOK_COMMAND(hook, cplt_template_args_concat[i])); - } - free (HOOK_COMMAND(hook, cplt_template_args_concat)); - HOOK_COMMAND(hook, cplt_template_args_concat) = NULL; - } - break; - case HOOK_TYPE_COMMAND_RUN: - if (HOOK_COMMAND_RUN(hook, command)) - { - free (HOOK_COMMAND_RUN(hook, command)); - HOOK_COMMAND_RUN(hook, command) = NULL; - } - break; - case HOOK_TYPE_TIMER: - break; - case HOOK_TYPE_FD: - break; - case HOOK_TYPE_PROCESS: - if (HOOK_PROCESS(hook, command)) - { - free (HOOK_PROCESS(hook, command)); - HOOK_PROCESS(hook, command) = NULL; - } - if (HOOK_PROCESS(hook, options)) - { - hashtable_free (HOOK_PROCESS(hook, options)); - HOOK_PROCESS(hook, options) = NULL; - } - if (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN])) - { - unhook (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN])); - HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN]) = NULL; - } - if (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT])) - { - unhook (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT])); - HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT]) = NULL; - } - if (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR])) - { - unhook (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR])); - HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR]) = NULL; - } - if (HOOK_PROCESS(hook, hook_timer)) - { - unhook (HOOK_PROCESS(hook, hook_timer)); - HOOK_PROCESS(hook, hook_timer) = NULL; - } - if (HOOK_PROCESS(hook, child_pid) > 0) - { - kill (HOOK_PROCESS(hook, child_pid), SIGKILL); - waitpid (HOOK_PROCESS(hook, child_pid), NULL, 0); - HOOK_PROCESS(hook, child_pid) = 0; - } - if (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN]) != -1) - { - close (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN])); - HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN]) = -1; - } - if (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]) != -1) - { - close (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN])); - HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]) = -1; - } - if (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT]) != -1) - { - close (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT])); - HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT]) = -1; - } - if (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT]) != -1) - { - close (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT])); - HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT]) = -1; - } - if (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR]) != -1) - { - close (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR])); - HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR]) = -1; - } - if (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR]) != -1) - { - close (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR])); - HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR]) = -1; - } - if (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDIN])) - { - free (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDIN])); - HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDIN]) = NULL; - } - if (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDOUT])) - { - free (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDOUT])); - HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDOUT]) = NULL; - } - if (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDERR])) - { - free (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDERR])); - HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDERR]) = NULL; - } - break; - case HOOK_TYPE_CONNECT: - if (HOOK_CONNECT(hook, proxy)) - { - free (HOOK_CONNECT(hook, proxy)); - HOOK_CONNECT(hook, proxy) = NULL; - } - if (HOOK_CONNECT(hook, address)) - { - free (HOOK_CONNECT(hook, address)); - HOOK_CONNECT(hook, address) = NULL; - } -#ifdef HAVE_GNUTLS - if (HOOK_CONNECT(hook, gnutls_priorities)) - { - free (HOOK_CONNECT(hook, gnutls_priorities)); - HOOK_CONNECT(hook, gnutls_priorities) = NULL; - } -#endif /* HAVE_GNUTLS */ - if (HOOK_CONNECT(hook, local_hostname)) - { - free (HOOK_CONNECT(hook, local_hostname)); - HOOK_CONNECT(hook, local_hostname) = NULL; - } - if (HOOK_CONNECT(hook, hook_child_timer)) - { - unhook (HOOK_CONNECT(hook, hook_child_timer)); - HOOK_CONNECT(hook, hook_child_timer) = NULL; - } - if (HOOK_CONNECT(hook, hook_fd)) - { - unhook (HOOK_CONNECT(hook, hook_fd)); - HOOK_CONNECT(hook, hook_fd) = NULL; - } - if (HOOK_CONNECT(hook, handshake_hook_fd)) - { - unhook (HOOK_CONNECT(hook, handshake_hook_fd)); - HOOK_CONNECT(hook, handshake_hook_fd) = NULL; - } - if (HOOK_CONNECT(hook, handshake_hook_timer)) - { - unhook (HOOK_CONNECT(hook, handshake_hook_timer)); - HOOK_CONNECT(hook, handshake_hook_timer) = NULL; - } - if (HOOK_CONNECT(hook, handshake_ip_address)) - { - free (HOOK_CONNECT(hook, handshake_ip_address)); - HOOK_CONNECT(hook, handshake_ip_address) = NULL; - } - if (HOOK_CONNECT(hook, child_pid) > 0) - { - kill (HOOK_CONNECT(hook, child_pid), SIGKILL); - waitpid (HOOK_CONNECT(hook, child_pid), NULL, 0); - HOOK_CONNECT(hook, child_pid) = 0; - } - if (HOOK_CONNECT(hook, child_read) != -1) - { - close (HOOK_CONNECT(hook, child_read)); - HOOK_CONNECT(hook, child_read) = -1; - } - if (HOOK_CONNECT(hook, child_write) != -1) - { - close (HOOK_CONNECT(hook, child_write)); - HOOK_CONNECT(hook, child_write) = -1; - } - if (HOOK_CONNECT(hook, child_recv) != -1) - { - close (HOOK_CONNECT(hook, child_recv)); - HOOK_CONNECT(hook, child_recv) = -1; - } - if (HOOK_CONNECT(hook, child_send) != -1) - { - close (HOOK_CONNECT(hook, child_send)); - HOOK_CONNECT(hook, child_send) = -1; - } - if (!hook_socketpair_ok) - { - for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) - { - if (HOOK_CONNECT(hook, sock_v4[i]) != -1) - { - close (HOOK_CONNECT(hook, sock_v4[i])); - HOOK_CONNECT(hook, sock_v4[i]) = -1; - } - if (HOOK_CONNECT(hook, sock_v6[i]) != -1) - { - close (HOOK_CONNECT(hook, sock_v6[i])); - HOOK_CONNECT(hook, sock_v6[i]) = -1; - } - } - } - break; - case HOOK_TYPE_LINE: - if (HOOK_LINE(hook, tags_array)) - { - string_free_split_tags (HOOK_LINE(hook, tags_array)); - HOOK_LINE(hook, tags_array) = NULL; - } - break; - case HOOK_TYPE_PRINT: - if (HOOK_PRINT(hook, tags_array)) - { - string_free_split_tags (HOOK_PRINT(hook, tags_array)); - HOOK_PRINT(hook, tags_array) = NULL; - } - if (HOOK_PRINT(hook, message)) - { - free (HOOK_PRINT(hook, message)); - HOOK_PRINT(hook, message) = NULL; - } - break; - case HOOK_TYPE_SIGNAL: - if (HOOK_SIGNAL(hook, signal)) - { - free (HOOK_SIGNAL(hook, signal)); - HOOK_SIGNAL(hook, signal) = NULL; - } - break; - case HOOK_TYPE_HSIGNAL: - if (HOOK_HSIGNAL(hook, signal)) - { - free (HOOK_HSIGNAL(hook, signal)); - HOOK_HSIGNAL(hook, signal) = NULL; - } - break; - case HOOK_TYPE_CONFIG: - if (HOOK_CONFIG(hook, option)) - { - free (HOOK_CONFIG(hook, option)); - HOOK_CONFIG(hook, option) = NULL; - } - break; - case HOOK_TYPE_COMPLETION: - if (HOOK_COMPLETION(hook, completion_item)) - { - free (HOOK_COMPLETION(hook, completion_item)); - HOOK_COMPLETION(hook, completion_item) = NULL; - } - if (HOOK_COMPLETION(hook, description)) - { - free (HOOK_COMPLETION(hook, description)); - HOOK_COMPLETION(hook, description) = NULL; - } - break; - case HOOK_TYPE_MODIFIER: - if (HOOK_MODIFIER(hook, modifier)) - { - free (HOOK_MODIFIER(hook, modifier)); - HOOK_MODIFIER(hook, modifier) = NULL; - } - break; - case HOOK_TYPE_INFO: - if (HOOK_INFO(hook, info_name)) - { - free (HOOK_INFO(hook, info_name)); - HOOK_INFO(hook, info_name) = NULL; - } - if (HOOK_INFO(hook, description)) - { - free (HOOK_INFO(hook, description)); - HOOK_INFO(hook, description) = NULL; - } - if (HOOK_INFO(hook, args_description)) - { - free (HOOK_INFO(hook, args_description)); - HOOK_INFO(hook, args_description) = NULL; - } - break; - case HOOK_TYPE_INFO_HASHTABLE: - if (HOOK_INFO_HASHTABLE(hook, info_name)) - { - free (HOOK_INFO_HASHTABLE(hook, info_name)); - HOOK_INFO_HASHTABLE(hook, info_name) = NULL; - } - if (HOOK_INFO_HASHTABLE(hook, description)) - { - free (HOOK_INFO_HASHTABLE(hook, description)); - HOOK_INFO_HASHTABLE(hook, description) = NULL; - } - if (HOOK_INFO_HASHTABLE(hook, args_description)) - { - free (HOOK_INFO_HASHTABLE(hook, args_description)); - HOOK_INFO_HASHTABLE(hook, args_description) = NULL; - } - if (HOOK_INFO_HASHTABLE(hook, output_description)) - { - free (HOOK_INFO_HASHTABLE(hook, output_description)); - HOOK_INFO_HASHTABLE(hook, output_description) = NULL; - } - break; - case HOOK_TYPE_INFOLIST: - if (HOOK_INFOLIST(hook, infolist_name)) - { - free (HOOK_INFOLIST(hook, infolist_name)); - HOOK_INFOLIST(hook, infolist_name) = NULL; - } - if (HOOK_INFOLIST(hook, description)) - { - free (HOOK_INFOLIST(hook, description)); - HOOK_INFOLIST(hook, description) = NULL; - } - if (HOOK_INFOLIST(hook, pointer_description)) - { - free (HOOK_INFOLIST(hook, pointer_description)); - HOOK_INFOLIST(hook, pointer_description) = NULL; - } - if (HOOK_INFOLIST(hook, args_description)) - { - free (HOOK_INFOLIST(hook, args_description)); - HOOK_INFOLIST(hook, args_description) = NULL; - } - break; - case HOOK_TYPE_HDATA: - if (HOOK_HDATA(hook, hdata_name)) - { - free (HOOK_HDATA(hook, hdata_name)); - HOOK_HDATA(hook, hdata_name) = NULL; - } - if (HOOK_HDATA(hook, description)) - { - free (HOOK_HDATA(hook, description)); - HOOK_HDATA(hook, description) = NULL; - } - break; - case HOOK_TYPE_FOCUS: - if (HOOK_FOCUS(hook, area)) - { - free (HOOK_FOCUS(hook, area)); - HOOK_FOCUS(hook, area) = NULL; - } - break; - case HOOK_NUM_TYPES: - /* - * this constant is used to count types only, - * it is never used as type - */ - break; - } - free (hook->hook_data); - hook->hook_data = NULL; - } + (hook_callback_free_data[hook->type]) (hook); /* free data common to all hooks */ if (hook->subplugin) @@ -4384,7 +649,6 @@ int hook_add_to_infolist_pointer (struct t_infolist *infolist, struct t_hook *hook) { struct t_infolist_item *ptr_item; - char value[64]; ptr_item = infolist_new_item (infolist); if (!ptr_item) @@ -4418,320 +682,8 @@ hook_add_to_infolist_pointer (struct t_infolist *infolist, struct t_hook *hook) return 1; /* hook not deleted: add extra hook info */ - switch (hook->type) - { - case HOOK_TYPE_COMMAND: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_COMMAND(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "command", HOOK_COMMAND(hook, command))) - return 0; - if (!infolist_new_var_string (ptr_item, "description", - HOOK_COMMAND(hook, description))) - return 0; - if (!infolist_new_var_string (ptr_item, "description_nls", - (HOOK_COMMAND(hook, description) - && HOOK_COMMAND(hook, description)[0]) ? - _(HOOK_COMMAND(hook, description)) : "")) - return 0; - if (!infolist_new_var_string (ptr_item, "args", - HOOK_COMMAND(hook, args))) - return 0; - if (!infolist_new_var_string (ptr_item, "args_nls", - (HOOK_COMMAND(hook, args) - && HOOK_COMMAND(hook, args)[0]) ? - _(HOOK_COMMAND(hook, args)) : "")) - return 0; - if (!infolist_new_var_string (ptr_item, "args_description", - HOOK_COMMAND(hook, args_description))) - return 0; - if (!infolist_new_var_string (ptr_item, "args_description_nls", - (HOOK_COMMAND(hook, args_description) - && HOOK_COMMAND(hook, args_description)[0]) ? - _(HOOK_COMMAND(hook, args_description)) : "")) - return 0; - if (!infolist_new_var_string (ptr_item, "completion", HOOK_COMMAND(hook, completion))) - return 0; - break; - case HOOK_TYPE_COMMAND_RUN: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_COMMAND_RUN(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "command", HOOK_COMMAND_RUN(hook, command))) - return 0; - break; - case HOOK_TYPE_TIMER: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_TIMER(hook, callback))) - return 0; - snprintf (value, sizeof (value), "%ld", HOOK_TIMER(hook, interval)); - if (!infolist_new_var_string (ptr_item, "interval", value)) - return 0; - if (!infolist_new_var_integer (ptr_item, "align_second", HOOK_TIMER(hook, align_second))) - return 0; - if (!infolist_new_var_integer (ptr_item, "remaining_calls", HOOK_TIMER(hook, remaining_calls))) - return 0; - if (!infolist_new_var_buffer (ptr_item, "last_exec", - &(HOOK_TIMER(hook, last_exec)), - sizeof (HOOK_TIMER(hook, last_exec)))) - return 0; - if (!infolist_new_var_buffer (ptr_item, "next_exec", - &(HOOK_TIMER(hook, next_exec)), - sizeof (HOOK_TIMER(hook, next_exec)))) - return 0; - break; - case HOOK_TYPE_FD: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_FD(hook, callback))) - return 0; - if (!infolist_new_var_integer (ptr_item, "fd", HOOK_FD(hook, fd))) - return 0; - if (!infolist_new_var_integer (ptr_item, "flags", HOOK_FD(hook, flags))) - return 0; - if (!infolist_new_var_integer (ptr_item, "error", HOOK_FD(hook, error))) - return 0; - break; - case HOOK_TYPE_PROCESS: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_PROCESS(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "command", HOOK_PROCESS(hook, command))) - return 0; - if (!infolist_new_var_string (ptr_item, "options", hashtable_get_string (HOOK_PROCESS(hook, options), "keys_values"))) - return 0; - if (!infolist_new_var_integer (ptr_item, "detached", HOOK_PROCESS(hook, detached))) - return 0; - if (!infolist_new_var_integer (ptr_item, "timeout", (int)(HOOK_PROCESS(hook, timeout)))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_read_stdin", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN]))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_write_stdin", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_read_stdout", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT]))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_write_stdout", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT]))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_read_stderr", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR]))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_write_stderr", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR]))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_pid", HOOK_PROCESS(hook, child_pid))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "hook_fd_stdin", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN]))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "hook_fd_stdout", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT]))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "hook_fd_stderr", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR]))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "hook_timer", HOOK_PROCESS(hook, hook_timer))) - return 0; - break; - case HOOK_TYPE_CONNECT: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_CONNECT(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "address", HOOK_CONNECT(hook, address))) - return 0; - if (!infolist_new_var_integer (ptr_item, "port", HOOK_CONNECT(hook, port))) - return 0; - if (!infolist_new_var_integer (ptr_item, "sock", HOOK_CONNECT(hook, sock))) - return 0; - if (!infolist_new_var_integer (ptr_item, "ipv6", HOOK_CONNECT(hook, ipv6))) - return 0; - if (!infolist_new_var_integer (ptr_item, "retry", HOOK_CONNECT(hook, retry))) - return 0; -#ifdef HAVE_GNUTLS - if (!infolist_new_var_pointer (ptr_item, "gnutls_sess", HOOK_CONNECT(hook, gnutls_sess))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "gnutls_cb", HOOK_CONNECT(hook, gnutls_cb))) - return 0; - if (!infolist_new_var_integer (ptr_item, "gnutls_dhkey_size", HOOK_CONNECT(hook, gnutls_dhkey_size))) - return 0; -#endif /* HAVE_GNUTLS */ - if (!infolist_new_var_string (ptr_item, "local_hostname", HOOK_CONNECT(hook, local_hostname))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_read", HOOK_CONNECT(hook, child_read))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_write", HOOK_CONNECT(hook, child_write))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_recv", HOOK_CONNECT(hook, child_recv))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_send", HOOK_CONNECT(hook, child_send))) - return 0; - if (!infolist_new_var_integer (ptr_item, "child_pid", HOOK_CONNECT(hook, child_pid))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "hook_child_timer", HOOK_CONNECT(hook, hook_child_timer))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "hook_fd", HOOK_CONNECT(hook, hook_fd))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "handshake_hook_fd", HOOK_CONNECT(hook, handshake_hook_fd))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "handshake_hook_timer", HOOK_CONNECT(hook, handshake_hook_timer))) - return 0; - if (!infolist_new_var_integer (ptr_item, "handshake_fd_flags", HOOK_CONNECT(hook, handshake_fd_flags))) - return 0; - if (!infolist_new_var_string (ptr_item, "handshake_ip_address", HOOK_CONNECT(hook, handshake_ip_address))) - return 0; - break; - case HOOK_TYPE_LINE: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_LINE(hook, callback))) - return 0; - if (!infolist_new_var_integer (ptr_item, "buffer_type", HOOK_LINE(hook, buffer_type))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "buffers", HOOK_LINE(hook, buffers))) - return 0; - if (!infolist_new_var_integer (ptr_item, "num_buffers", HOOK_LINE(hook, num_buffers))) - return 0; - if (!infolist_new_var_integer (ptr_item, "tags_count", HOOK_LINE(hook, tags_count))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "tags_array", HOOK_LINE(hook, tags_array))) - return 0; - break; - case HOOK_TYPE_PRINT: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_PRINT(hook, callback))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "buffer", HOOK_PRINT(hook, buffer))) - return 0; - if (!infolist_new_var_integer (ptr_item, "tags_count", HOOK_PRINT(hook, tags_count))) - return 0; - if (!infolist_new_var_pointer (ptr_item, "tags_array", HOOK_PRINT(hook, tags_array))) - return 0; - if (!infolist_new_var_string (ptr_item, "message", HOOK_PRINT(hook, message))) - return 0; - if (!infolist_new_var_integer (ptr_item, "strip_colors", HOOK_PRINT(hook, strip_colors))) - return 0; - break; - case HOOK_TYPE_SIGNAL: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_SIGNAL(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "signal", HOOK_SIGNAL(hook, signal))) - return 0; - break; - case HOOK_TYPE_HSIGNAL: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_HSIGNAL(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "signal", HOOK_HSIGNAL(hook, signal))) - return 0; - break; - case HOOK_TYPE_CONFIG: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_CONFIG(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "option", HOOK_CONFIG(hook, option))) - return 0; - break; - case HOOK_TYPE_COMPLETION: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_COMPLETION(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "completion_item", HOOK_COMPLETION(hook, completion_item))) - return 0; - if (!infolist_new_var_string (ptr_item, "description", HOOK_COMPLETION(hook, description))) - return 0; - if (!infolist_new_var_string (ptr_item, "description_nls", - (HOOK_COMPLETION(hook, description) - && HOOK_COMPLETION(hook, description)[0]) ? - _(HOOK_COMPLETION(hook, description)) : "")) - return 0; - break; - case HOOK_TYPE_MODIFIER: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_MODIFIER(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "modifier", HOOK_MODIFIER(hook, modifier))) - return 0; - break; - case HOOK_TYPE_INFO: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_INFO(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "info_name", HOOK_INFO(hook, info_name))) - return 0; - if (!infolist_new_var_string (ptr_item, "description", HOOK_INFO(hook, description))) - return 0; - if (!infolist_new_var_string (ptr_item, "description_nls", - (HOOK_INFO(hook, description) - && HOOK_INFO(hook, description)[0]) ? - _(HOOK_INFO(hook, description)) : "")) - return 0; - if (!infolist_new_var_string (ptr_item, "args_description", HOOK_INFO(hook, args_description))) - return 0; - if (!infolist_new_var_string (ptr_item, "args_description_nls", - (HOOK_INFO(hook, args_description) - && HOOK_INFO(hook, args_description)[0]) ? - _(HOOK_INFO(hook, args_description)) : "")) - return 0; - break; - case HOOK_TYPE_INFO_HASHTABLE: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_INFO_HASHTABLE(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "info_name", HOOK_INFO_HASHTABLE(hook, info_name))) - return 0; - if (!infolist_new_var_string (ptr_item, "description", HOOK_INFO_HASHTABLE(hook, description))) - return 0; - if (!infolist_new_var_string (ptr_item, "description_nls", - (HOOK_INFO_HASHTABLE(hook, description) - && HOOK_INFO_HASHTABLE(hook, description)[0]) ? - _(HOOK_INFO_HASHTABLE(hook, description)) : "")) - return 0; - if (!infolist_new_var_string (ptr_item, "args_description", HOOK_INFO_HASHTABLE(hook, args_description))) - return 0; - if (!infolist_new_var_string (ptr_item, "args_description_nls", - (HOOK_INFO_HASHTABLE(hook, args_description) - && HOOK_INFO_HASHTABLE(hook, args_description)[0]) ? - _(HOOK_INFO_HASHTABLE(hook, args_description)) : "")) - return 0; - if (!infolist_new_var_string (ptr_item, "output_description", HOOK_INFO_HASHTABLE(hook, output_description))) - return 0; - if (!infolist_new_var_string (ptr_item, "output_description_nls", - (HOOK_INFO_HASHTABLE(hook, output_description) - && HOOK_INFO_HASHTABLE(hook, output_description)[0]) ? - _(HOOK_INFO_HASHTABLE(hook, output_description)) : "")) - return 0; - break; - case HOOK_TYPE_INFOLIST: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_INFOLIST(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "infolist_name", HOOK_INFOLIST(hook, infolist_name))) - return 0; - if (!infolist_new_var_string (ptr_item, "description", HOOK_INFOLIST(hook, description))) - return 0; - if (!infolist_new_var_string (ptr_item, "description_nls", - (HOOK_INFOLIST(hook, description) - && HOOK_INFOLIST(hook, description)[0]) ? - _(HOOK_INFOLIST(hook, description)) : "")) - return 0; - if (!infolist_new_var_string (ptr_item, "pointer_description", HOOK_INFOLIST(hook, pointer_description))) - return 0; - if (!infolist_new_var_string (ptr_item, "pointer_description_nls", - (HOOK_INFOLIST(hook, pointer_description) - && HOOK_INFOLIST(hook, pointer_description)[0]) ? - _(HOOK_INFOLIST(hook, pointer_description)) : "")) - return 0; - if (!infolist_new_var_string (ptr_item, "args_description", HOOK_INFOLIST(hook, args_description))) - return 0; - if (!infolist_new_var_string (ptr_item, "args_description_nls", - (HOOK_INFOLIST(hook, args_description) - && HOOK_INFOLIST(hook, args_description)[0]) ? - _(HOOK_INFOLIST(hook, args_description)) : "")) - return 0; - break; - case HOOK_TYPE_HDATA: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_HDATA(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "hdata_name", HOOK_HDATA(hook, hdata_name))) - return 0; - if (!infolist_new_var_string (ptr_item, "description", HOOK_HDATA(hook, description))) - return 0; - if (!infolist_new_var_string (ptr_item, "description_nls", - (HOOK_HDATA(hook, description) - && HOOK_HDATA(hook, description)[0]) ? - _(HOOK_HDATA(hook, description)) : "")) - return 0; - break; - case HOOK_TYPE_FOCUS: - if (!infolist_new_var_pointer (ptr_item, "callback", HOOK_FOCUS(hook, callback))) - return 0; - if (!infolist_new_var_string (ptr_item, "area", HOOK_FOCUS(hook, area))) - return 0; - break; - case HOOK_NUM_TYPES: - /* - * this constant is used to count types only, - * it is never used as type - */ - break; - } + if (!(hook_callback_add_to_infolist[hook->type]) (ptr_item, hook)) + return 0; return 1; } @@ -4836,11 +788,8 @@ hook_add_to_infolist (struct t_infolist *infolist, struct t_hook *pointer, void hook_print_log () { - int type, i, j; + int type; struct t_hook *ptr_hook; - struct tm *local_time; - time_t seconds; - char text_time[1024]; for (type = 0; type < HOOK_NUM_TYPES; type++) { @@ -4861,256 +810,9 @@ hook_print_log () log_printf (" callback_data . . . . . : 0x%lx", ptr_hook->callback_data); if (ptr_hook->deleted) continue; - switch (ptr_hook->type) - { - case HOOK_TYPE_COMMAND: - log_printf (" command data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_COMMAND(ptr_hook, callback)); - log_printf (" command . . . . . . . : '%s'", HOOK_COMMAND(ptr_hook, command)); - log_printf (" description . . . . . : '%s'", HOOK_COMMAND(ptr_hook, description)); - log_printf (" args. . . . . . . . . : '%s'", HOOK_COMMAND(ptr_hook, args)); - log_printf (" args_description. . . : '%s'", HOOK_COMMAND(ptr_hook, args_description)); - log_printf (" completion. . . . . . : '%s'", HOOK_COMMAND(ptr_hook, completion)); - log_printf (" cplt_num_templates. . : %d", HOOK_COMMAND(ptr_hook, cplt_num_templates)); - for (i = 0; i < HOOK_COMMAND(ptr_hook, cplt_num_templates); i++) - { - log_printf (" cplt_templates[%04d] . . . : '%s'", - i, HOOK_COMMAND(ptr_hook, cplt_templates)[i]); - log_printf (" cplt_templates_static[%04d]: '%s'", - i, HOOK_COMMAND(ptr_hook, cplt_templates_static)[i]); - log_printf (" num_args. . . . . . : %d", - HOOK_COMMAND(ptr_hook, cplt_template_num_args)[i]); - for (j = 0; j < HOOK_COMMAND(ptr_hook, cplt_template_num_args)[i]; j++) - { - log_printf (" args[%04d]. . . . . : '%s'", - j, HOOK_COMMAND(ptr_hook, cplt_template_args)[i][j]); - } - } - log_printf (" num_args_concat . . . : %d", HOOK_COMMAND(ptr_hook, cplt_template_num_args_concat)); - for (i = 0; i < HOOK_COMMAND(ptr_hook, cplt_template_num_args_concat); i++) - { - log_printf (" args_concat[%04d] . . : '%s'", - i, HOOK_COMMAND(ptr_hook, cplt_template_args_concat)[i]); - } - break; - case HOOK_TYPE_COMMAND_RUN: - log_printf (" command_run data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_COMMAND_RUN(ptr_hook, callback)); - log_printf (" command . . . . . . . : '%s'", HOOK_COMMAND_RUN(ptr_hook, command)); - break; - case HOOK_TYPE_TIMER: - log_printf (" timer data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_TIMER(ptr_hook, callback)); - log_printf (" interval. . . . . . . : %ld", HOOK_TIMER(ptr_hook, interval)); - log_printf (" align_second. . . . . : %d", HOOK_TIMER(ptr_hook, align_second)); - log_printf (" remaining_calls . . . : %d", HOOK_TIMER(ptr_hook, remaining_calls)); - text_time[0] = '\0'; - seconds = HOOK_TIMER(ptr_hook, last_exec).tv_sec; - local_time = localtime (&seconds); - if (local_time) - { - if (strftime (text_time, sizeof (text_time), - "%d/%m/%Y %H:%M:%S", local_time) == 0) - text_time[0] = '\0'; - } - log_printf (" last_exec.tv_sec. . . : %lld (%s)", - (long long)(HOOK_TIMER(ptr_hook, last_exec.tv_sec)), - text_time); - log_printf (" last_exec.tv_usec . . : %ld", HOOK_TIMER(ptr_hook, last_exec.tv_usec)); - text_time[0] = '\0'; - seconds = HOOK_TIMER(ptr_hook, next_exec).tv_sec; - local_time = localtime (&seconds); - if (local_time) - { - if (strftime (text_time, sizeof (text_time), - "%d/%m/%Y %H:%M:%S", local_time) == 0) - text_time[0] = '\0'; - } - log_printf (" next_exec.tv_sec. . . : %lld (%s)", - (long long)(HOOK_TIMER(ptr_hook, next_exec.tv_sec)), - text_time); - log_printf (" next_exec.tv_usec . . : %ld", HOOK_TIMER(ptr_hook, next_exec.tv_usec)); - break; - case HOOK_TYPE_FD: - log_printf (" fd data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_FD(ptr_hook, callback)); - log_printf (" fd. . . . . . . . . . : %d", HOOK_FD(ptr_hook, fd)); - log_printf (" flags . . . . . . . . : %d", HOOK_FD(ptr_hook, flags)); - log_printf (" error . . . . . . . . : %d", HOOK_FD(ptr_hook, error)); - break; - case HOOK_TYPE_PROCESS: - log_printf (" process data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_PROCESS(ptr_hook, callback)); - log_printf (" command . . . . . . . : '%s'", HOOK_PROCESS(ptr_hook, command)); - log_printf (" options . . . . . . . : 0x%lx (hashtable: '%s')", - HOOK_PROCESS(ptr_hook, options), - hashtable_get_string (HOOK_PROCESS(ptr_hook, options), - "keys_values")); - log_printf (" detached. . . . . . . : %d", HOOK_PROCESS(ptr_hook, detached)); - log_printf (" timeout . . . . . . . : %ld", HOOK_PROCESS(ptr_hook, timeout)); - log_printf (" child_read[stdin] . . : %d", HOOK_PROCESS(ptr_hook, child_read[HOOK_PROCESS_STDIN])); - log_printf (" child_write[stdin]. . : %d", HOOK_PROCESS(ptr_hook, child_write[HOOK_PROCESS_STDIN])); - log_printf (" child_read[stdout]. . : %d", HOOK_PROCESS(ptr_hook, child_read[HOOK_PROCESS_STDOUT])); - log_printf (" child_write[stdout] . : %d", HOOK_PROCESS(ptr_hook, child_write[HOOK_PROCESS_STDOUT])); - log_printf (" child_read[stderr]. . : %d", HOOK_PROCESS(ptr_hook, child_read[HOOK_PROCESS_STDERR])); - log_printf (" child_write[stderr] . : %d", HOOK_PROCESS(ptr_hook, child_write[HOOK_PROCESS_STDERR])); - log_printf (" child_pid . . . . . . : %d", HOOK_PROCESS(ptr_hook, child_pid)); - log_printf (" hook_fd[stdin]. . . . : 0x%lx", HOOK_PROCESS(ptr_hook, hook_fd[HOOK_PROCESS_STDIN])); - log_printf (" hook_fd[stdout] . . . : 0x%lx", HOOK_PROCESS(ptr_hook, hook_fd[HOOK_PROCESS_STDOUT])); - log_printf (" hook_fd[stderr] . . . : 0x%lx", HOOK_PROCESS(ptr_hook, hook_fd[HOOK_PROCESS_STDERR])); - log_printf (" hook_timer. . . . . . : 0x%lx", HOOK_PROCESS(ptr_hook, hook_timer)); - break; - case HOOK_TYPE_CONNECT: - log_printf (" connect data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_CONNECT(ptr_hook, callback)); - log_printf (" address . . . . . . . : '%s'", HOOK_CONNECT(ptr_hook, address)); - log_printf (" port. . . . . . . . . : %d", HOOK_CONNECT(ptr_hook, port)); - log_printf (" sock. . . . . . . . . : %d", HOOK_CONNECT(ptr_hook, sock)); - log_printf (" ipv6. . . . . . . . . : %d", HOOK_CONNECT(ptr_hook, ipv6)); - log_printf (" retry . . . . . . . . : %d", HOOK_CONNECT(ptr_hook, retry)); -#ifdef HAVE_GNUTLS - log_printf (" gnutls_sess . . . . . : 0x%lx", HOOK_CONNECT(ptr_hook, gnutls_sess)); - log_printf (" gnutls_cb . . . . . . : 0x%lx", HOOK_CONNECT(ptr_hook, gnutls_cb)); - log_printf (" gnutls_dhkey_size . . : %d", HOOK_CONNECT(ptr_hook, gnutls_dhkey_size)); - log_printf (" gnutls_priorities . . : '%s'", HOOK_CONNECT(ptr_hook, gnutls_priorities)); -#endif /* HAVE_GNUTLS */ - log_printf (" local_hostname. . . . : '%s'", HOOK_CONNECT(ptr_hook, local_hostname)); - log_printf (" child_read. . . . . . : %d", HOOK_CONNECT(ptr_hook, child_read)); - log_printf (" child_write . . . . . : %d", HOOK_CONNECT(ptr_hook, child_write)); - log_printf (" child_recv. . . . . . : %d", HOOK_CONNECT(ptr_hook, child_recv)); - log_printf (" child_send. . . . . . : %d", HOOK_CONNECT(ptr_hook, child_send)); - log_printf (" child_pid . . . . . . : %d", HOOK_CONNECT(ptr_hook, child_pid)); - log_printf (" hook_child_timer. . . : 0x%lx", HOOK_CONNECT(ptr_hook, hook_child_timer)); - log_printf (" hook_fd . . . . . . . : 0x%lx", HOOK_CONNECT(ptr_hook, hook_fd)); - log_printf (" handshake_hook_fd . . : 0x%lx", HOOK_CONNECT(ptr_hook, handshake_hook_fd)); - log_printf (" handshake_hook_timer. : 0x%lx", HOOK_CONNECT(ptr_hook, handshake_hook_timer)); - log_printf (" handshake_fd_flags. . : %d", HOOK_CONNECT(ptr_hook, handshake_fd_flags)); - log_printf (" handshake_ip_address. : '%s'", HOOK_CONNECT(ptr_hook, handshake_ip_address)); - if (!hook_socketpair_ok) - { - for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) - { - log_printf (" sock_v4[%d]. . . . . . : '%d'", HOOK_CONNECT(ptr_hook, sock_v4[i])); - log_printf (" sock_v6[%d]. . . . . . : '%d'", HOOK_CONNECT(ptr_hook, sock_v6[i])); - } - } - break; - case HOOK_TYPE_LINE: - log_printf (" line data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_LINE(ptr_hook, callback)); - log_printf (" buffer_type . . . . . : %d", HOOK_LINE(ptr_hook, buffer_type)); - log_printf (" buffers . . . . . . . : 0x%lx", HOOK_LINE(ptr_hook, buffers)); - log_printf (" num_buffers . . . . . : %d", HOOK_LINE(ptr_hook, num_buffers)); - for (i = 0; i < HOOK_LINE(ptr_hook, num_buffers); i++) - { - log_printf (" buffers[%03d]. . . : '%s'", - i, HOOK_LINE(ptr_hook, buffers)[i]); - } - log_printf (" tags_count. . . . . . : %d", HOOK_LINE(ptr_hook, tags_count)); - log_printf (" tags_array. . . . . . : 0x%lx", HOOK_LINE(ptr_hook, tags_array)); - if (HOOK_LINE(ptr_hook, tags_array)) - { - for (i = 0; i < HOOK_LINE(ptr_hook, tags_count); i++) - { - for (j = 0; HOOK_LINE(ptr_hook, tags_array)[i][j]; j++) - { - log_printf (" tags_array[%03d][%03d]: '%s'", - i, - j, - HOOK_LINE(ptr_hook, tags_array)[i][j]); - } - } - } - break; - case HOOK_TYPE_PRINT: - log_printf (" print data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_PRINT(ptr_hook, callback)); - log_printf (" buffer. . . . . . . . : 0x%lx", HOOK_PRINT(ptr_hook, buffer)); - log_printf (" tags_count. . . . . . : %d", HOOK_PRINT(ptr_hook, tags_count)); - log_printf (" tags_array. . . . . . : 0x%lx", HOOK_PRINT(ptr_hook, tags_array)); - if (HOOK_PRINT(ptr_hook, tags_array)) - { - for (i = 0; i < HOOK_PRINT(ptr_hook, tags_count); i++) - { - for (j = 0; HOOK_PRINT(ptr_hook, tags_array)[i][j]; j++) - { - log_printf (" tags_array[%03d][%03d]: '%s'", - i, - j, - HOOK_PRINT(ptr_hook, tags_array)[i][j]); - } - } - } - log_printf (" message . . . . . . . : '%s'", HOOK_PRINT(ptr_hook, message)); - log_printf (" strip_colors. . . . . : %d", HOOK_PRINT(ptr_hook, strip_colors)); - break; - case HOOK_TYPE_SIGNAL: - log_printf (" signal data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_SIGNAL(ptr_hook, callback)); - log_printf (" signal. . . . . . . . : '%s'", HOOK_SIGNAL(ptr_hook, signal)); - break; - case HOOK_TYPE_HSIGNAL: - log_printf (" signal data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_HSIGNAL(ptr_hook, callback)); - log_printf (" signal. . . . . . . . : '%s'", HOOK_HSIGNAL(ptr_hook, signal)); - break; - case HOOK_TYPE_CONFIG: - log_printf (" config data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_CONFIG(ptr_hook, callback)); - log_printf (" option. . . . . . . . : '%s'", HOOK_CONFIG(ptr_hook, option)); - break; - case HOOK_TYPE_COMPLETION: - log_printf (" completion data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_COMPLETION(ptr_hook, callback)); - log_printf (" completion_item . . . : '%s'", HOOK_COMPLETION(ptr_hook, completion_item)); - log_printf (" description . . . . . : '%s'", HOOK_COMPLETION(ptr_hook, description)); - break; - case HOOK_TYPE_MODIFIER: - log_printf (" modifier data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_MODIFIER(ptr_hook, callback)); - log_printf (" modifier. . . . . . . : '%s'", HOOK_MODIFIER(ptr_hook, modifier)); - break; - case HOOK_TYPE_INFO: - log_printf (" info data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_INFO(ptr_hook, callback)); - log_printf (" info_name . . . . . . : '%s'", HOOK_INFO(ptr_hook, info_name)); - log_printf (" description . . . . . : '%s'", HOOK_INFO(ptr_hook, description)); - log_printf (" args_description. . . : '%s'", HOOK_INFO(ptr_hook, args_description)); - break; - case HOOK_TYPE_INFO_HASHTABLE: - log_printf (" info_hashtable data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_INFO_HASHTABLE(ptr_hook, callback)); - log_printf (" info_name . . . . . . : '%s'", HOOK_INFO_HASHTABLE(ptr_hook, info_name)); - log_printf (" description . . . . . : '%s'", HOOK_INFO_HASHTABLE(ptr_hook, description)); - log_printf (" args_description. . . : '%s'", HOOK_INFO_HASHTABLE(ptr_hook, args_description)); - log_printf (" output_description. . : '%s'", HOOK_INFO_HASHTABLE(ptr_hook, output_description)); - break; - case HOOK_TYPE_INFOLIST: - log_printf (" infolist data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_INFOLIST(ptr_hook, callback)); - log_printf (" infolist_name . . . . : '%s'", HOOK_INFOLIST(ptr_hook, infolist_name)); - log_printf (" description . . . . . : '%s'", HOOK_INFOLIST(ptr_hook, description)); - log_printf (" pointer_description . : '%s'", HOOK_INFOLIST(ptr_hook, pointer_description)); - log_printf (" args_description. . . : '%s'", HOOK_INFOLIST(ptr_hook, args_description)); - break; - case HOOK_TYPE_HDATA: - log_printf (" hdata data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_HDATA(ptr_hook, callback)); - log_printf (" hdata_name. . . . . . : '%s'", HOOK_HDATA(ptr_hook, hdata_name)); - log_printf (" description . . . . . : '%s'", HOOK_HDATA(ptr_hook, description)); - break; - case HOOK_TYPE_FOCUS: - log_printf (" focus data:"); - log_printf (" callback. . . . . . . : 0x%lx", HOOK_FOCUS(ptr_hook, callback)); - log_printf (" area. . . . . . . . . : '%s'", HOOK_FOCUS(ptr_hook, area)); - break; - case HOOK_NUM_TYPES: - /* - * this constant is used to count types only, - * it is never used as type - */ - break; - } + + (hook_callback_print_log[ptr_hook->type]) (ptr_hook); + log_printf (" prev_hook . . . . . . . : 0x%lx", ptr_hook->prev_hook); log_printf (" next_hook . . . . . . . : 0x%lx", ptr_hook->next_hook); } diff --git a/src/core/wee-hook.h b/src/core/wee-hook.h index 70d521c78..d31067f24 100644 --- a/src/core/wee-hook.h +++ b/src/core/wee-hook.h @@ -1,6 +1,5 @@ /* * Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org> - * Copyright (C) 2012 Simon Arlott * * This file is part of WeeChat, the extensible chat client. * @@ -21,16 +20,26 @@ #ifndef WEECHAT_HOOK_H #define WEECHAT_HOOK_H -#include <unistd.h> -#include <time.h> - -#ifdef HAVE_GNUTLS -#include <gnutls/gnutls.h> -#endif - -/* used if socketpair() function is NOT available */ -#define HOOK_CONNECT_MAX_SOCKETS 4 - +#include "hook/wee-hook-command-run.h" +#include "hook/wee-hook-command.h" +#include "hook/wee-hook-completion.h" +#include "hook/wee-hook-config.h" +#include "hook/wee-hook-connect.h" +#include "hook/wee-hook-fd.h" +#include "hook/wee-hook-focus.h" +#include "hook/wee-hook-hdata.h" +#include "hook/wee-hook-hsignal.h" +#include "hook/wee-hook-info-hashtable.h" +#include "hook/wee-hook-info.h" +#include "hook/wee-hook-infolist.h" +#include "hook/wee-hook-line.h" +#include "hook/wee-hook-modifier.h" +#include "hook/wee-hook-print.h" +#include "hook/wee-hook-process.h" +#include "hook/wee-hook-signal.h" +#include "hook/wee-hook-timer.h" + +struct t_hook; struct t_gui_bar; struct t_gui_buffer; struct t_gui_line; @@ -39,6 +48,7 @@ struct t_gui_window; struct t_weelist; struct t_hashtable; struct t_infolist; +struct t_infolist_item; /* hook types */ @@ -72,47 +82,9 @@ enum t_hook_type */ #define HOOK_PRIORITY_DEFAULT 1000 -/* max calls that can be done for a command (recursive calls) */ -#define HOOK_COMMAND_MAX_CALLS 5 - -/* return code when a command is executed */ -#define HOOK_COMMAND_EXEC_OK 1 -#define HOOK_COMMAND_EXEC_ERROR 0 -#define HOOK_COMMAND_EXEC_NOT_FOUND -1 -#define HOOK_COMMAND_EXEC_AMBIGUOUS_PLUGINS -2 -#define HOOK_COMMAND_EXEC_AMBIGUOUS_INCOMPLETE -3 -#define HOOK_COMMAND_EXEC_RUNNING -4 - -/* flags for fd hooks */ -#define HOOK_FD_FLAG_READ 1 -#define HOOK_FD_FLAG_WRITE 2 -#define HOOK_FD_FLAG_EXCEPTION 4 - -/* constants for hook process */ -#define HOOK_PROCESS_STDIN 0 -#define HOOK_PROCESS_STDOUT 1 -#define HOOK_PROCESS_STDERR 2 -#define HOOK_PROCESS_BUFFER_SIZE 65536 - -/* macros to access hook specific data */ -#define HOOK_COMMAND(hook, var) (((struct t_hook_command *)hook->hook_data)->var) -#define HOOK_COMMAND_RUN(hook, var) (((struct t_hook_command_run *)hook->hook_data)->var) -#define HOOK_TIMER(hook, var) (((struct t_hook_timer *)hook->hook_data)->var) -#define HOOK_FD(hook, var) (((struct t_hook_fd *)hook->hook_data)->var) -#define HOOK_PROCESS(hook, var) (((struct t_hook_process *)hook->hook_data)->var) -#define HOOK_CONNECT(hook, var) (((struct t_hook_connect *)hook->hook_data)->var) -#define HOOK_LINE(hook, var) (((struct t_hook_line *)hook->hook_data)->var) -#define HOOK_PRINT(hook, var) (((struct t_hook_print *)hook->hook_data)->var) -#define HOOK_SIGNAL(hook, var) (((struct t_hook_signal *)hook->hook_data)->var) -#define HOOK_HSIGNAL(hook, var) (((struct t_hook_hsignal *)hook->hook_data)->var) -#define HOOK_CONFIG(hook, var) (((struct t_hook_config *)hook->hook_data)->var) -#define HOOK_COMPLETION(hook, var) (((struct t_hook_completion *)hook->hook_data)->var) -#define HOOK_MODIFIER(hook, var) (((struct t_hook_modifier *)hook->hook_data)->var) -#define HOOK_INFO(hook, var) (((struct t_hook_info *)hook->hook_data)->var) -#define HOOK_INFO_HASHTABLE(hook, var) (((struct t_hook_info_hashtable *)hook->hook_data)->var) -#define HOOK_INFOLIST(hook, var) (((struct t_hook_infolist *)hook->hook_data)->var) -#define HOOK_HDATA(hook, var) (((struct t_hook_hdata *)hook->hook_data)->var) -#define HOOK_FOCUS(hook, var) (((struct t_hook_focus *)hook->hook_data)->var) +typedef void (t_callback_hook)(struct t_hook *hook); +typedef int (t_callback_hook_infolist)(struct t_infolist_item *item, + struct t_hook *hook); struct t_hook { @@ -136,328 +108,6 @@ struct t_hook struct t_hook *next_hook; /* link to next hook */ }; -/* hook command */ - -typedef int (t_hook_callback_command)(const void *pointer, void *data, - struct t_gui_buffer *buffer, - int argc, char **argv, char **argv_eol); - -struct t_hook_command -{ - t_hook_callback_command *callback; /* command callback */ - char *command; /* name of command (without '/') */ - char *description; /* (for /help) short cmd description*/ - char *args; /* (for /help) command arguments */ - char *args_description; /* (for /help) args long description*/ - char *completion; /* template for completion */ - - /* templates */ - int cplt_num_templates; /* number of templates for compl. */ - char **cplt_templates; /* completion templates */ - char **cplt_templates_static; /* static part of template (at */ - /* beginning */ - - /* arguments for each template */ - int *cplt_template_num_args; /* number of arguments for template */ - char ***cplt_template_args; /* arguments for each template */ - - /* concatenation of arg N for each template */ - int cplt_template_num_args_concat; /* number of concatenated arguments */ - char **cplt_template_args_concat; /* concatenated arguments */ -}; - -/* hook command run */ - -typedef int (t_hook_callback_command_run)(const void *pointer, void *data, - struct t_gui_buffer *buffer, - const char *command); - -struct t_hook_command_run -{ - t_hook_callback_command_run *callback; /* command_run callback */ - char *command; /* name of command (without '/') */ -}; - -/* hook timer */ - -typedef int (t_hook_callback_timer)(const void *pointer, void *data, - int remaining_calls); - -struct t_hook_timer -{ - t_hook_callback_timer *callback; /* timer callback */ - long interval; /* timer interval (milliseconds) */ - int align_second; /* alignment on a second */ - /* for ex.: 60 = each min. at 0 sec */ - int remaining_calls; /* calls remaining (0 = unlimited) */ - struct timeval last_exec; /* last time hook was executed */ - struct timeval next_exec; /* next scheduled execution */ -}; - -/* hook fd */ - -typedef int (t_hook_callback_fd)(const void *pointer, void *data, int fd); - -struct t_hook_fd -{ - t_hook_callback_fd *callback; /* fd callback */ - int fd; /* socket or file descriptor */ - int flags; /* fd flags (read,write,..) */ - int error; /* contains errno if error occurred */ - /* with fd */ -}; - -/* hook process */ - -typedef int (t_hook_callback_process)(const void *pointer, void *data, - const char *command, - int return_code, - const char *out, const char *err); - -struct t_hook_process -{ - t_hook_callback_process *callback; /* process callback (after child end)*/ - char *command; /* command executed by child */ - struct t_hashtable *options; /* options for process (see doc) */ - int detached; /* detached mode (background) */ - long timeout; /* timeout (ms) (0 = no timeout) */ - int child_read[3]; /* read stdin/out/err data from child*/ - int child_write[3]; /* write stdin/out/err data for child*/ - pid_t child_pid; /* pid of child process */ - struct t_hook *hook_fd[3]; /* hook fd for stdin/out/err */ - struct t_hook *hook_timer; /* timer to check if child has died */ - char *buffer[3]; /* buffers for child stdin/out/err */ - int buffer_size[3]; /* size of child stdin/out/err */ - int buffer_flush; /* bytes to flush output buffers */ -}; - -/* hook connect */ - -typedef int (t_hook_callback_connect)(const void *pointer, void *data, - int status, int gnutls_rc, int sock, - const char *error, - const char *ip_address); - -#ifdef HAVE_GNUTLS -typedef int (gnutls_callback_t)(const void *pointer, void *data, - gnutls_session_t tls_session, - const gnutls_datum_t *req_ca, int nreq, - const gnutls_pk_algorithm_t *pk_algos, - int pk_algos_len, -#if LIBGNUTLS_VERSION_NUMBER >= 0x020b00 /* 2.11.0 */ - gnutls_retr2_st *answer, -#else - gnutls_retr_st *answer, -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020b00 */ - int action); -#endif /* HAVE_GNUTLS */ - -struct t_hook_connect -{ - t_hook_callback_connect *callback; /* connect callback */ - char *proxy; /* proxy (optional) */ - char *address; /* peer address */ - int port; /* peer port */ - int ipv6; /* use IPv6 */ - int sock; /* socket (set when connected) */ - int retry; /* retry count */ -#ifdef HAVE_GNUTLS - gnutls_session_t *gnutls_sess; /* GnuTLS session (SSL connection) */ - gnutls_callback_t *gnutls_cb; /* GnuTLS callback during handshake */ - int gnutls_dhkey_size; /* Diffie Hellman Key Exchange size */ - char *gnutls_priorities; /* GnuTLS priorities */ -#endif /* HAVE_GNUTLS */ - char *local_hostname; /* force local hostname (optional) */ - int child_read; /* to read data in pipe from child */ - int child_write; /* to write data in pipe for child */ - int child_recv; /* to read data from child socket */ - int child_send; /* to write data to child socket */ - pid_t child_pid; /* pid of child process (connecting) */ - struct t_hook *hook_child_timer; /* timer for child process timeout */ - struct t_hook *hook_fd; /* pointer to fd hook */ - struct t_hook *handshake_hook_fd; /* fd hook for handshake */ - struct t_hook *handshake_hook_timer; /* timer for handshake timeout */ - int handshake_fd_flags; /* socket flags saved for handshake */ - char *handshake_ip_address; /* ip address (used for handshake) */ - /* sockets used if socketpair() is NOT available */ - int sock_v4[HOOK_CONNECT_MAX_SOCKETS]; /* IPv4 sockets for connecting */ - int sock_v6[HOOK_CONNECT_MAX_SOCKETS]; /* IPv6 sockets for connecting */ -}; - -/* hook line */ - -typedef struct t_hashtable *(t_hook_callback_line)(const void *pointer, - void *data, - struct t_hashtable *line); - -struct t_hook_line -{ - t_hook_callback_line *callback; /* line callback */ - int buffer_type; /* -1 = any type, ≥ 0: only this type*/ - char **buffers; /* list of buffer masks where the */ - /* hook is executed (see the */ - /* function "buffer_match_list") */ - int num_buffers; /* number of buffers in list */ - int tags_count; /* number of tags selected */ - char ***tags_array; /* tags selected (NULL = any) */ -}; - -/* hook print */ - -typedef int (t_hook_callback_print)(const void *pointer, void *data, - struct t_gui_buffer *buffer, - time_t date, int tags_count, - const char **tags, int displayed, - int highlight, const char *prefix, - const char *message); - -struct t_hook_print -{ - t_hook_callback_print *callback; /* print callback */ - struct t_gui_buffer *buffer; /* buffer selected (NULL = all) */ - int tags_count; /* number of tags selected */ - char ***tags_array; /* tags selected (NULL = any) */ - char *message; /* part of message (NULL/empty = all)*/ - int strip_colors; /* strip colors in msg for callback? */ -}; - -/* hook signal */ - -typedef int (t_hook_callback_signal)(const void *pointer, void *data, - const char *signal, const char *type_data, - void *signal_data); - -struct t_hook_signal -{ - t_hook_callback_signal *callback; /* signal callback */ - char *signal; /* signal selected (may begin or end */ - /* with "*", "*" == any signal) */ -}; - -/* hook hsignal */ - -typedef int (t_hook_callback_hsignal)(const void *pointer, void *data, - const char *signal, - struct t_hashtable *hashtable); - -struct t_hook_hsignal -{ - t_hook_callback_hsignal *callback; /* signal callback */ - char *signal; /* signal selected (may begin or end */ - /* with "*", "*" == any signal) */ -}; - -/* hook config */ - -typedef int (t_hook_callback_config)(const void *pointer, void *data, - const char *option, const char *value); - -struct t_hook_config -{ - t_hook_callback_config *callback; /* config callback */ - char *option; /* config option for hook */ - /* (NULL = hook for all options) */ -}; - -/* hook completion */ - -typedef int (t_hook_callback_completion)(const void *pointer, void *data, - const char *completion_item, - struct t_gui_buffer *buffer, - struct t_gui_completion *completion); - -struct t_hook_completion -{ - t_hook_callback_completion *callback; /* completion callback */ - char *completion_item; /* name of completion */ - char *description; /* description */ -}; - -/* hook modifier */ - -typedef char *(t_hook_callback_modifier)(const void *pointer, void *data, - const char *modifier, - const char *modifier_data, - const char *string); - -struct t_hook_modifier -{ - t_hook_callback_modifier *callback; /* modifier callback */ - char *modifier; /* name of modifier */ -}; - -/* hook info */ - -typedef const char *(t_hook_callback_info)(const void *pointer, void *data, - const char *info_name, - const char *arguments); - -struct t_hook_info -{ - t_hook_callback_info *callback; /* info callback */ - char *info_name; /* name of info returned */ - char *description; /* description */ - char *args_description; /* description of arguments */ -}; - -/* hook info (hashtable) */ - -typedef struct t_hashtable *(t_hook_callback_info_hashtable)(const void *pointer, - void *data, - const char *info_name, - struct t_hashtable *hashtable); - -struct t_hook_info_hashtable -{ - t_hook_callback_info_hashtable *callback; /* info_hashtable callback */ - char *info_name; /* name of info returned */ - char *description; /* description */ - char *args_description; /* description of arguments */ - char *output_description; /* description of output (hashtable) */ -}; - -/* hook infolist */ - -typedef struct t_infolist *(t_hook_callback_infolist)(const void *pointer, - void *data, - const char *infolist_name, - void *obj_pointer, - const char *arguments); - -struct t_hook_infolist -{ - t_hook_callback_infolist *callback; /* infolist callback */ - char *infolist_name; /* name of infolist returned */ - char *description; /* description */ - char *pointer_description; /* description of pointer */ - char *args_description; /* description of arguments */ -}; - -/* hook hdata */ - -typedef struct t_hdata *(t_hook_callback_hdata)(const void *pointer, - void *data, - const char *hdata_name); - -struct t_hook_hdata -{ - t_hook_callback_hdata *callback; /* hdata callback */ - char *hdata_name; /* hdata name */ - char *description; /* description */ -}; - -/* hook focus */ - -typedef struct t_hashtable *(t_hook_callback_focus)(const void *pointer, - void *data, - struct t_hashtable *info); - -struct t_hook_focus -{ - t_hook_callback_focus *callback; /* focus callback */ - char *area; /* "chat" or bar item name */ -}; - /* hook variables */ extern char *hook_type_string[]; @@ -470,185 +120,16 @@ extern int hook_socketpair_ok; /* hook functions */ extern void hook_init (); +extern void hook_add_to_list (struct t_hook *new_hook); +extern void hook_get_priority_and_name (const char *string, int *priority, + const char **name); +extern void hook_init_data (struct t_hook *hook, + struct t_weechat_plugin *plugin, + int type, int priority, + const void *callback_pointer, void *callback_data); extern int hook_valid (struct t_hook *hook); -extern struct t_hook *hook_command (struct t_weechat_plugin *plugin, - const char *command, - const char *description, - const char *args, - const char *args_description, - const char *completion, - t_hook_callback_command *callback, - const void *callback_pointer, - void *callback_data); -extern int hook_command_exec (struct t_gui_buffer *buffer, int any_plugin, - struct t_weechat_plugin *plugin, - const char *string); -extern struct t_hook *hook_command_run (struct t_weechat_plugin *plugin, - const char *command, - t_hook_callback_command_run *callback, - const void *callback_pointer, - void *callback_data); -extern int hook_command_run_exec (struct t_gui_buffer *buffer, - const char *command); -extern struct t_hook *hook_timer (struct t_weechat_plugin *plugin, - long interval, int align_second, - int max_calls, - t_hook_callback_timer *callback, - const void *callback_pointer, - void *callback_data); -extern void hook_timer_exec (); -extern struct t_hook *hook_fd (struct t_weechat_plugin *plugin, int fd, - int flag_read, int flag_write, - int flag_exception, - t_hook_callback_fd *callback, - const void *callback_pointer, - void *callback_data); -extern void hook_fd_exec (); -extern struct t_hook *hook_process (struct t_weechat_plugin *plugin, - const char *command, - int timeout, - t_hook_callback_process *callback, - const void *callback_pointer, - void *callback_data); -extern struct t_hook *hook_process_hashtable (struct t_weechat_plugin *plugin, - const char *command, - struct t_hashtable *options, - int timeout, - t_hook_callback_process *callback, - const void *callback_pointer, - void *callback_data); -extern void hook_process_exec (); -extern struct t_hook *hook_connect (struct t_weechat_plugin *plugin, - const char *proxy, const char *address, - int port, int ipv6, int retry, - void *gnutls_session, void *gnutls_cb, - int gnutls_dhkey_size, - const char *gnutls_priorities, - const char *local_hostname, - t_hook_callback_connect *callback, - const void *callback_pointer, - void *callback_data); -#ifdef HAVE_GNUTLS -extern int hook_connect_gnutls_verify_certificates (gnutls_session_t tls_session); -extern int hook_connect_gnutls_set_certificates (gnutls_session_t tls_session, - const gnutls_datum_t *req_ca, int nreq, - const gnutls_pk_algorithm_t *pk_algos, - int pk_algos_len, -#if LIBGNUTLS_VERSION_NUMBER >= 0x020b00 /* 2.11.0 */ - gnutls_retr2_st *answer); -#else - gnutls_retr_st *answer); -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020b00 */ -#endif /* HAVE_GNUTLS */ -extern struct t_hook *hook_line (struct t_weechat_plugin *plugin, - const char *buffer_type, - const char *buffer_name, - const char *tags, - t_hook_callback_line *callback, - const void *callback_pointer, - void *callback_data); -extern void hook_line_exec (struct t_gui_line *line); -extern struct t_hook *hook_print (struct t_weechat_plugin *plugin, - struct t_gui_buffer *buffer, - const char *tags, const char *message, - int strip_colors, - t_hook_callback_print *callback, - const void *callback_pointer, - void *callback_data); -extern void hook_print_exec (struct t_gui_buffer *buffer, - struct t_gui_line *line); -extern struct t_hook *hook_signal (struct t_weechat_plugin *plugin, - const char *signal, - t_hook_callback_signal *callback, - const void *callback_pointer, - void *callback_data); -extern int hook_signal_send (const char *signal, const char *type_data, - void *signal_data); -extern struct t_hook *hook_hsignal (struct t_weechat_plugin *plugin, - const char *signal, - t_hook_callback_hsignal *callback, - const void *callback_pointer, - void *callback_data); -extern int hook_hsignal_send (const char *signal, - struct t_hashtable *hashtable); -extern struct t_hook *hook_config (struct t_weechat_plugin *plugin, - const char *option, - t_hook_callback_config *callback, - const void *callback_pointer, - void *callback_data); -extern void hook_config_exec (const char *option, const char *value); -extern struct t_hook *hook_completion (struct t_weechat_plugin *plugin, - const char *completion_item, - const char *description, - t_hook_callback_completion *callback, - const void *callback_pointer, - void *callback_data); -extern const char *hook_completion_get_string (struct t_gui_completion *completion, - const char *property); -extern void hook_completion_list_add (struct t_gui_completion *completion, - const char *word, int nick_completion, - const char *where); -extern void hook_completion_exec (struct t_weechat_plugin *plugin, - const char *completion_item, - struct t_gui_buffer *buffer, - struct t_gui_completion *completion); -extern struct t_hook *hook_modifier (struct t_weechat_plugin *plugin, - const char *modifier, - t_hook_callback_modifier *callback, - const void *callback_pointer, - void *callback_data); -extern char *hook_modifier_exec (struct t_weechat_plugin *plugin, - const char *modifier, - const char *modifier_data, - const char *string); -extern struct t_hook *hook_info (struct t_weechat_plugin *plugin, - const char *info_name, - const char *description, - const char *args_description, - t_hook_callback_info *callback, - const void *callback_pointer, - void *callback_data); -extern const char *hook_info_get (struct t_weechat_plugin *plugin, - const char *info_name, - const char *arguments); -extern struct t_hook *hook_info_hashtable (struct t_weechat_plugin *plugin, - const char *info_name, - const char *description, - const char *args_description, - const char *output_description, - t_hook_callback_info_hashtable *callback, - const void *callback_pointer, - void *callback_data); -extern struct t_hashtable *hook_info_get_hashtable (struct t_weechat_plugin *plugin, - const char *info_name, - struct t_hashtable *hashtable); -extern struct t_hook *hook_infolist (struct t_weechat_plugin *plugin, - const char *infolist_name, - const char *description, - const char *pointer_description, - const char *args_description, - t_hook_callback_infolist *callback, - const void *callback_pointer, - void *callback_data); -extern struct t_infolist *hook_infolist_get (struct t_weechat_plugin *plugin, - const char *infolist_name, - void *pointer, - const char *arguments); -extern struct t_hook *hook_hdata (struct t_weechat_plugin *plugin, - const char *hdata_name, - const char *description, - t_hook_callback_hdata *callback, - const void *callback_pointer, - void *callback_data); -extern struct t_hdata *hook_hdata_get (struct t_weechat_plugin *plugin, - const char *hdata_name); -extern struct t_hook *hook_focus (struct t_weechat_plugin *plugin, - const char *area, - t_hook_callback_focus *callback, - const void *callback_pointer, - void *callback_data); -extern struct t_hashtable *hook_focus_get_data (struct t_hashtable *hashtable_focus1, - struct t_hashtable *hashtable_focus2); +extern void hook_exec_start (); +extern void hook_exec_end (); extern void hook_set (struct t_hook *hook, const char *property, const char *value); extern void unhook (struct t_hook *hook); |