/* * trigger-callback.c - callbacks for triggers * * Copyright (C) 2014 Sébastien Helleu * * 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 . */ #include #include #include #include #include "../weechat-plugin.h" #include "trigger.h" #include "trigger-buffer.h" /* one hashtable by hook, used in callback to evaluate "conditions" */ struct t_hashtable *trigger_callback_hashtable_options = NULL; /* * Checks conditions for a trigger. * * Returns: * 1: conditions are true (or no condition set in trigger) * 0: conditions are false */ int trigger_callback_check_conditions (struct t_trigger *trigger, struct t_hashtable *pointers, struct t_hashtable *extra_vars) { const char *conditions; char *value; int rc; conditions = weechat_config_string (trigger->options[TRIGGER_OPTION_CONDITIONS]); if (!conditions || !conditions[0]) return 1; value = weechat_string_eval_expression (conditions, pointers, extra_vars, trigger_callback_hashtable_options); rc = (value && (strcmp (value, "1") == 0)); if (value) free (value); return rc; } /* * Replaces text using one or more regex in the trigger. */ void trigger_callback_replace_regex (struct t_trigger *trigger, struct t_hashtable *extra_vars) { char *value; const char *ptr_key, *ptr_value; int i; if (trigger->regex_count == 0) return; for (i = 0; i < trigger->regex_count; i++) { ptr_key = (trigger->regex[i].variable) ? trigger->regex[i].variable : trigger_hook_regex_default_var[weechat_config_integer (trigger->options[TRIGGER_OPTION_HOOK])]; if (!ptr_key || !ptr_key[0]) { if (trigger_buffer) { weechat_printf_tags (trigger_buffer, "no_trigger", "\t regex %d: %s", i + 1, _("no variable")); } continue; } ptr_value = weechat_hashtable_get (extra_vars, ptr_key); if (!ptr_value) { if (trigger_buffer) { weechat_printf_tags (trigger_buffer, "no_trigger", "\t regex %d (%s): %s", i + 1, ptr_key, _("empty variable")); } continue; } value = weechat_string_replace_regex (ptr_value, trigger->regex[i].regex, trigger->regex[i].replace_eval); if (!value) continue; /* display debug info on trigger buffer */ if (trigger_buffer) { weechat_printf_tags (trigger_buffer, "no_trigger", "\t regex %d (%s): \"%s%s\"", i + 1, ptr_key, value, weechat_color ("reset")); } weechat_hashtable_set (extra_vars, ptr_key, value); free (value); } } /* * Executes the trigger command(s). */ void trigger_callback_run_command (struct t_trigger *trigger, struct t_gui_buffer *buffer, struct t_hashtable *pointers, struct t_hashtable *extra_vars) { const char *command; char *command_eval, **commands, **ptr_command; command = weechat_config_string (trigger->options[TRIGGER_OPTION_COMMAND]); if (!command || !command[0]) return; command_eval = weechat_string_eval_expression (command, pointers, extra_vars, NULL); if (command_eval) { commands = weechat_string_split_command (command_eval, ';'); if (commands) { for (ptr_command = commands; *ptr_command; ptr_command++) { /* display debug info on trigger buffer */ if (trigger_buffer) { weechat_printf_tags (trigger_buffer, "no_trigger", "\t running command \"%s\"", *ptr_command); } weechat_command (buffer, *ptr_command); } weechat_string_free_split_command (commands); trigger->hook_count_cmd++; } free (command_eval); } } /* * Callback for a signal hooked. */ int trigger_callback_signal_cb (void *data, const char *signal, const char *type_data, void *signal_data) { struct t_trigger *trigger; struct t_hashtable *extra_vars; const char *ptr_signal_data; char str_data[128]; int rc; /* get trigger pointer, return immediately if not found or trigger running */ trigger = (struct t_trigger *)data; if (!trigger || trigger->hook_running) return WEECHAT_RC_OK; trigger->hook_count_cb++; trigger->hook_running = 1; extra_vars = NULL; rc = trigger_return_code[weechat_config_integer (trigger->options[TRIGGER_OPTION_RETURN_CODE])]; /* create hashtable */ extra_vars = weechat_hashtable_new (32, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING, NULL, NULL); if (!extra_vars) goto end; /* add data in hashtable used for conditions/replace/command */ weechat_hashtable_set (extra_vars, "tg_signal", signal); if (strcmp (type_data, WEECHAT_HOOK_SIGNAL_STRING) == 0) { ptr_signal_data = (const char *)signal_data; } else if (strcmp (type_data, WEECHAT_HOOK_SIGNAL_INT) == 0) { snprintf (str_data, sizeof (str_data), "%d", *((int *)signal_data)); ptr_signal_data = str_data; } else if (strcmp (type_data, WEECHAT_HOOK_SIGNAL_POINTER) == 0) { str_data[0] = '\0'; if (signal_data) { snprintf (str_data, sizeof (str_data), "0x%lx", (long unsigned int)signal_data); } ptr_signal_data = str_data; } weechat_hashtable_set (extra_vars, "tg_signal_data", ptr_signal_data); /* display debug info on trigger buffer */ if (!trigger_buffer && (weechat_trigger_plugin->debug >= 1)) trigger_buffer_open (0); if (trigger_buffer) { weechat_printf_tags (trigger_buffer, "no_trigger", "signal\t%s%s", weechat_color ("chat_channel"), trigger->name); weechat_printf_tags (trigger_buffer, "no_trigger", "\t signal_data: \"%s%s\"", ptr_signal_data, weechat_color ("reset")); trigger_buffer_display_hashtable ("extra_vars", extra_vars); } /* check conditions */ if (!trigger_callback_check_conditions (trigger, NULL, extra_vars)) goto end; /* replace text with regex */ trigger_callback_replace_regex (trigger, extra_vars); /* execute command */ trigger_callback_run_command (trigger, NULL, NULL, extra_vars); end: if (extra_vars) weechat_hashtable_free (extra_vars); trigger->hook_running = 0; return rc; } /* * Callback for a hsignal hooked. */ int trigger_callback_hsignal_cb (void *data, const char *signal, struct t_hashtable *hashtable) { struct t_trigger *trigger; struct t_hashtable *pointers, *extra_vars; const char *type_values; int rc; /* get trigger pointer, return immediately if not found or trigger running */ trigger = (struct t_trigger *)data; if (!trigger || trigger->hook_running) return WEECHAT_RC_OK; trigger->hook_count_cb++; trigger->hook_running = 1; pointers = NULL; extra_vars = NULL; rc = trigger_return_code[weechat_config_integer (trigger->options[TRIGGER_OPTION_RETURN_CODE])]; /* duplicate hashtable */ if (hashtable && (strcmp (weechat_hashtable_get_string (hashtable, "type_keys"), "string") == 0)) { type_values = weechat_hashtable_get_string (hashtable, "type_values"); if (strcmp (type_values, "pointer") == 0) { pointers = weechat_hashtable_dup (hashtable); if (!pointers) goto end; } else if (strcmp (type_values, "pointer") == 0) { extra_vars = weechat_hashtable_dup (hashtable); if (!extra_vars) goto end; } } /* create hashtable (if not already created) */ if (!extra_vars) { extra_vars = weechat_hashtable_new (32, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING, NULL, NULL); if (!extra_vars) goto end; } /* add data in hashtable used for conditions/replace/command */ weechat_hashtable_set (extra_vars, "tg_signal", signal); /* display debug info on trigger buffer */ if (!trigger_buffer && (weechat_trigger_plugin->debug >= 1)) trigger_buffer_open (0); if (trigger_buffer) { weechat_printf_tags (trigger_buffer, "no_trigger", "hsignal\t%s%s", weechat_color ("chat_channel"), trigger->name); trigger_buffer_display_hashtable ("pointers", pointers); trigger_buffer_display_hashtable ("extra_vars", extra_vars); } /* check conditions */ if (!trigger_callback_check_conditions (trigger, pointers, extra_vars)) goto end; /* replace text with regex */ trigger_callback_replace_regex (trigger, extra_vars); /* execute command */ trigger_callback_run_command (trigger, NULL, pointers, extra_vars); end: if (pointers) weechat_hashtable_free (pointers); if (extra_vars) weechat_hashtable_free (extra_vars); trigger->hook_running = 0; return rc; } /* * Callback for a modifier hooked. */ char * trigger_callback_modifier_cb (void *data, const char *modifier, const char *modifier_data, const char *string) { struct t_trigger *trigger; struct t_hashtable *extra_vars; const char *ptr_string; char *string_modified, *pos, *pos2, *plugin_name, *buffer_name; char *buffer_full_name, *tags; int no_trigger, length; no_trigger = 0; /* get trigger pointer, return immediately if not found or trigger running */ trigger = (struct t_trigger *)data; if (!trigger || trigger->hook_running) return NULL; trigger->hook_count_cb++; trigger->hook_running = 1; extra_vars = NULL; /* create hashtable */ extra_vars = weechat_hashtable_new (32, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING, NULL, NULL); if (!extra_vars) goto end; /* add data in hashtable used for conditions/replace/command */ weechat_hashtable_set (extra_vars, "tg_modifier", modifier); weechat_hashtable_set (extra_vars, "tg_modifier_data", modifier_data); weechat_hashtable_set (extra_vars, "tg_string", string); /* add special variables for a WeeChat message */ if (strcmp (modifier, "weechat_print") == 0) { pos = strchr (modifier_data, ';'); if (pos) { plugin_name = weechat_strndup (modifier_data, pos - modifier_data); if (plugin_name) { weechat_hashtable_set (extra_vars, "tg_plugin", plugin_name); pos++; pos2 = strchr (pos, ';'); if (pos2) { buffer_name = weechat_strndup (pos, pos2 - pos); if (buffer_name) { length = strlen (plugin_name) + 1 + strlen (buffer_name) + 1; buffer_full_name = malloc (length); if (buffer_full_name) { snprintf (buffer_full_name, length, "%s.%s", plugin_name, buffer_name); weechat_hashtable_set (extra_vars, "tg_buffer", buffer_full_name); free (buffer_full_name); } free (buffer_name); } pos2++; if (pos2[0]) { length = 1 + strlen (pos2) + 1 + 1; tags = malloc (length); if (tags) { snprintf (tags, length, ",%s,", pos2); weechat_hashtable_set (extra_vars, "tg_tags", tags); if (strstr (tags, ",no_trigger,")) no_trigger = 1; free (tags); } } } free (plugin_name); } } } /* * ignore this modifier if "no_trigger" was in tags * (for modifier "weechat_print") */ if (no_trigger) goto end; /* display debug info on trigger buffer */ if (!trigger_buffer && (weechat_trigger_plugin->debug >= 1)) trigger_buffer_open (0); if (trigger_buffer) { weechat_printf_tags (trigger_buffer, "no_trigger", "modifier\t%s%s", weechat_color ("chat_channel"), trigger->name); weechat_printf_tags (trigger_buffer, "no_trigger", "\t modifier: %s", modifier); weechat_printf_tags (trigger_buffer, "no_trigger", "\t modifier_data: \"%s%s\"", modifier_data, weechat_color ("reset")); trigger_buffer_display_hashtable ("extra_vars", extra_vars); } /* check conditions */ if (!trigger_callback_check_conditions (trigger, NULL, extra_vars)) goto end; /* replace text with regex */ trigger_callback_replace_regex (trigger, extra_vars); /* execute command */ trigger_callback_run_command (trigger, NULL, NULL, extra_vars); end: ptr_string = weechat_hashtable_get (extra_vars, "tg_string"); string_modified = (ptr_string && (strcmp (ptr_string, string) != 0)) ? strdup (ptr_string) : NULL; if (extra_vars) weechat_hashtable_free (extra_vars); trigger->hook_running = 0; return string_modified; } /* * Callback for a print hooked. */ int trigger_callback_print_cb (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_trigger *trigger; struct t_hashtable *pointers, *extra_vars; const char *localvar_type; char *str_tags, *str_tags2, str_temp[128]; int i, rc, length, tag_notify_private; struct tm *date_tmp; /* get trigger pointer, return immediately if not found or trigger running */ trigger = (struct t_trigger *)data; if (!trigger || trigger->hook_running) return WEECHAT_RC_OK; trigger->hook_count_cb++; trigger->hook_running = 1; pointers = NULL; extra_vars = NULL; rc = trigger_return_code[weechat_config_integer (trigger->options[TRIGGER_OPTION_RETURN_CODE])]; /* do nothing if the buffer does not match buffers defined in the trigger */ if (trigger->hook_print_buffers && !weechat_buffer_match_list (buffer, trigger->hook_print_buffers)) goto end; /* create hashtables */ pointers = weechat_hashtable_new (32, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_POINTER, NULL, NULL); if (!pointers) goto end; extra_vars = weechat_hashtable_new (32, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING, NULL, NULL); if (!extra_vars) goto end; /* add data in hashtables used for conditions/replace/command */ weechat_hashtable_set (pointers, "buffer", buffer); date_tmp = localtime (&date); if (date_tmp) { strftime (str_temp, sizeof (str_temp), "%Y-%m-%d %H:%M:%S", date_tmp); weechat_hashtable_set (extra_vars, "tg_date", str_temp); } snprintf (str_temp, sizeof (str_temp), "%d", tags_count); weechat_hashtable_set (extra_vars, "tg_tags_count", str_temp); str_tags = weechat_string_build_with_split_string (tags, ","); if (str_tags) { /* build string with tags and commas around: ",tag1,tag2,tag3," */ length = 1 + strlen (str_tags) + 1 + 1; str_tags2 = malloc (length); if (str_tags2) { snprintf (str_tags2, length, ",%s,", str_tags); weechat_hashtable_set (extra_vars, "tg_tags", str_tags2); free (str_tags2); } free (str_tags); } snprintf (str_temp, sizeof (str_temp), "%d", displayed); weechat_hashtable_set (extra_vars, "tg_displayed", str_temp); snprintf (str_temp, sizeof (str_temp), "%d", highlight); weechat_hashtable_set (extra_vars, "tg_highlight", str_temp); weechat_hashtable_set (extra_vars, "tg_prefix", prefix); weechat_hashtable_set (extra_vars, "tg_message", message); localvar_type = weechat_buffer_get_string (buffer, "localvar_type"); tag_notify_private = 0; for (i = 0; i < tags_count; i++) { if (strcmp (tags[i], "no_trigger") == 0) { goto end; } else if (strcmp (tags[i], "notify_private") == 0) { tag_notify_private = 1; } } snprintf (str_temp, sizeof (str_temp), "%d", (tag_notify_private && localvar_type && (strcmp (localvar_type, "private") == 0)) ? 1 : 0); weechat_hashtable_set (extra_vars, "tg_msg_pv", str_temp); /* display debug info on trigger buffer */ if (!trigger_buffer && (weechat_trigger_plugin->debug >= 1)) trigger_buffer_open (0); if (trigger_buffer) { weechat_printf_tags (trigger_buffer, "no_trigger", "print\t%s%s", weechat_color ("chat_channel"), trigger->name); weechat_printf_tags (trigger_buffer, "no_trigger", "\t buffer: %s", weechat_buffer_get_string (buffer, "full_name")); trigger_buffer_display_hashtable ("pointers", pointers); trigger_buffer_display_hashtable ("extra_vars", extra_vars); } /* check conditions */ if (!trigger_callback_check_conditions (trigger, pointers, extra_vars)) goto end; /* replace text with regex */ trigger_callback_replace_regex (trigger, extra_vars); /* execute command */ trigger_callback_run_command (trigger, buffer, pointers, extra_vars); end: if (pointers) weechat_hashtable_free (pointers); if (extra_vars) weechat_hashtable_free (extra_vars); trigger->hook_running = 0; return rc; } /* * Callback for a command_run hooked. */ int trigger_callback_command_run_cb (void *data, struct t_gui_buffer *buffer, const char *command) { struct t_trigger *trigger; struct t_hashtable *pointers, *extra_vars; int rc; /* get trigger pointer, return immediately if not found or trigger running */ trigger = (struct t_trigger *)data; if (!trigger || trigger->hook_running) return WEECHAT_RC_OK; trigger->hook_count_cb++; trigger->hook_running = 1; pointers = NULL; extra_vars = NULL; rc = trigger_return_code[weechat_config_integer (trigger->options[TRIGGER_OPTION_RETURN_CODE])]; /* create hashtables */ pointers = weechat_hashtable_new (32, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_POINTER, NULL, NULL); if (!pointers) goto end; extra_vars = weechat_hashtable_new (32, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING, NULL, NULL); if (!extra_vars) goto end; /* add data in hashtables used for conditions/replace/command */ weechat_hashtable_set (pointers, "buffer", buffer); weechat_hashtable_set (extra_vars, "tg_command", command); /* display debug info on trigger buffer */ if (!trigger_buffer && (weechat_trigger_plugin->debug >= 1)) trigger_buffer_open (0); if (trigger_buffer) { weechat_printf_tags (trigger_buffer, "no_trigger", "command_run\t%s%s", weechat_color ("chat_channel"), trigger->name); weechat_printf_tags (trigger_buffer, "no_trigger", "\t buffer: %s", weechat_buffer_get_string (buffer, "full_name")); trigger_buffer_display_hashtable ("pointers", pointers); trigger_buffer_display_hashtable ("extra_vars", extra_vars); } /* check conditions */ if (!trigger_callback_check_conditions (trigger, pointers, extra_vars)) goto end; /* replace text with regex */ trigger_callback_replace_regex (trigger, extra_vars); /* execute command */ trigger_callback_run_command (trigger, buffer, pointers, extra_vars); end: if (pointers) weechat_hashtable_free (pointers); if (extra_vars) weechat_hashtable_free (extra_vars); trigger->hook_running = 0; return rc; } /* * Callback for a timer hooked. */ int trigger_callback_timer_cb (void *data, int remaining_calls) { struct t_trigger *trigger; struct t_hashtable *extra_vars; char str_temp[128]; int rc, i; time_t date; struct tm *date_tmp; /* get trigger pointer, return immediately if not found or trigger running */ trigger = (struct t_trigger *)data; if (!trigger || trigger->hook_running) return WEECHAT_RC_OK; /* * remove the hook if this is the last call to timer * (because WeeChat will remove the hook after this call, so the pointer * will become invalid) */ if ((remaining_calls == 0) && trigger->hooks) { for (i = 0; i < trigger->hooks_count; i++) { trigger->hooks[i] = NULL; } } trigger->hook_count_cb++; trigger->hook_running = 1; extra_vars = NULL; rc = trigger_return_code[weechat_config_integer (trigger->options[TRIGGER_OPTION_RETURN_CODE])]; /* create hashtable */ extra_vars = weechat_hashtable_new (32, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING, NULL, NULL); if (!extra_vars) goto end; /* add data in hashtable used for conditions/replace/command */ snprintf (str_temp, sizeof (str_temp), "%d", remaining_calls); weechat_hashtable_set (extra_vars, "tg_remaining_calls", str_temp); date = time (NULL); date_tmp = localtime (&date); if (date_tmp) { strftime (str_temp, sizeof (str_temp), "%Y-%m-%d %H:%M:%S", date_tmp); weechat_hashtable_set (extra_vars, "tg_date", str_temp); } /* display debug info on trigger buffer */ if (!trigger_buffer && (weechat_trigger_plugin->debug >= 1)) trigger_buffer_open (0); if (trigger_buffer) { weechat_printf_tags (trigger_buffer, "no_trigger", "timer\t%s%s", weechat_color ("chat_channel"), trigger->name); trigger_buffer_display_hashtable ("extra_vars", extra_vars); } /* check conditions */ if (!trigger_callback_check_conditions (trigger, NULL, extra_vars)) goto end; /* replace text with regex */ trigger_callback_replace_regex (trigger, extra_vars); /* execute command */ trigger_callback_run_command (trigger, NULL, NULL, extra_vars); end: if (extra_vars) weechat_hashtable_free (extra_vars); trigger->hook_running = 0; return rc; } /* * Initializes trigger callback. */ void trigger_callback_init () { trigger_callback_hashtable_options = weechat_hashtable_new (32, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING, NULL, NULL); if (trigger_callback_hashtable_options) { weechat_hashtable_set (trigger_callback_hashtable_options, "type", "condition"); } } /* * Ends trigger callback. */ void trigger_callback_end () { if (trigger_callback_hashtable_options) weechat_hashtable_free (trigger_callback_hashtable_options); }