/* * Copyright (c) 2003-2009 by FlashCode * See README for License detail, AUTHORS for developers list. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* weechat-trigger.c: Trigger plugin for WeeChat */ #include #include #include #include "../weechat-plugin.h" #include "weechat-trigger.h" #include "weechat-trigger-libirc.h" #include "weechat-trigger-libc.h" WEECHAT_PLUGIN_NAME("trigger"); WEECHAT_PLUGIN_DESCRIPTION("Trigger plugin for WeeChat"); WEECHAT_PLUGIN_AUTHOR("FlashCode "); WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION); WEECHAT_PLUGIN_WEECHAT_VERSION(WEECHAT_VERSION); WEECHAT_PLUGIN_LICENSE("GPL3"); t_weechat_trigger *weechat_trigger_list = NULL; t_weechat_trigger *weechat_trigger_last = NULL; t_weechat_trigger *weechat_trigger_alloc (char *pattern, char *domain, char *commands, char *channels, char *servers, char *action, char *cmd) { t_weechat_trigger *new; new = malloc (sizeof (*new)); if (new) { new->pattern = strdup (pattern); new->domain = strdup (domain); new->commands = c_explode_string (commands, ",", 0, NULL); new->channels = c_explode_string (channels, ",", 0, NULL); new->servers = c_explode_string (servers, ",", 0, NULL); new->action = strdup (action); if (cmd) new->cmds = c_split_multi_command (cmd, ';'); else new->cmds = NULL; new->prev_trigger = NULL; new->next_trigger = NULL; } return new; } void weechat_trigger_free (t_weechat_trigger *trig) { if (trig) { if (trig->pattern) free (trig->pattern); if (trig->domain) free (trig->domain); if (trig->commands) c_free_exploded_string (trig->commands); if (trig->channels) c_free_exploded_string (trig->channels); if (trig->servers) c_free_exploded_string (trig->servers); if (trig->action) free (trig->action); if (trig->cmds) c_free_multi_command (trig->cmds); free (trig); } } int weechat_trigger_action_exists (char *action) { if (strcasecmp(action, "ignore") == 0 || strcasecmp(action, "display") == 0 || strcasecmp(action, "highlight") == 0 || strcasecmp(action, "run") == 0) return 1; return 0; } int weechat_trigger_domain_exists (char *action) { if (strcasecmp(action, "*") == 0 || strcasecmp(action, "user") == 0 || strcasecmp(action, "nick") == 0 || strcasecmp(action, "userhost") == 0 || strcasecmp(action, "msg") == 0) return 1; return 0; } /* * weechat_trigger_add : create and add a new trigger in triggers list * return : * 0 : adding was successfull * 1 : memory error * 2 : bad arguments */ int weechat_trigger_add (int argc, char **argv) { int ret = 2; char *rcmd; t_weechat_trigger *trig = NULL; if (argc == 6) { if (weechat_trigger_action_exists (argv[5]) && strcasecmp (argv[5], "run") != 0) { trig = weechat_trigger_alloc ( argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], NULL); if (!trig) ret = 1; } } else if (argc > 6) { if (strcasecmp (argv[5], "run") == 0) { rcmd = c_join_string (&argv[6], " "); if (rcmd) { trig = weechat_trigger_alloc ( argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], rcmd); c_free_joined_string (rcmd); if (!trig) ret = 1; } } } if (trig) { if (weechat_trigger_last) { weechat_trigger_last->next_trigger = trig; trig->prev_trigger = weechat_trigger_last; weechat_trigger_last = trig; } else { weechat_trigger_list = trig; weechat_trigger_last = trig; } ret = 0; } return ret; } /* * */ int weechat_trigger_move (int from, int to) { t_weechat_trigger *l, *f, *t; int ret, i; if (from == to) return 0; ret = 1; f = NULL; t = NULL; l = weechat_trigger_list; i = 0; while (l) { i++; if (i == from) f = l; if (i == to) t = l; l = l->next_trigger; } if (f && t) { if (weechat_trigger_list == f) weechat_trigger_list = f->next_trigger; if (weechat_trigger_last == f) weechat_trigger_last = f->prev_trigger; if (f->prev_trigger) f->prev_trigger->next_trigger = f->next_trigger; if (f->next_trigger) f->next_trigger->prev_trigger = f->prev_trigger; if (to > from && !t->next_trigger) { t->next_trigger = f; f->prev_trigger = t; f->next_trigger = NULL; weechat_trigger_last = f; } else { if (to > from && t->next_trigger) t = t->next_trigger; if (t->prev_trigger) t->prev_trigger->next_trigger = f; f->next_trigger = t; f->prev_trigger = t->prev_trigger; t->prev_trigger = f; if (t == weechat_trigger_list) weechat_trigger_list = f; } ret = 0; } return ret; } /* * weechat_trigger_move : remove a trigger trigger in triggers list * by its number * return : 0 = success ; 1 = failed */ int weechat_trigger_remove (int n) { t_weechat_trigger *l, *p; int ret; ret = 1; l = weechat_trigger_list; p = NULL; while (l && n >= 0) { n--; if (n == 0) { p = l; break; } l = l->next_trigger; } if (p) { if (p->prev_trigger) p->prev_trigger->next_trigger = p->next_trigger; if (p->next_trigger) p->next_trigger->prev_trigger = p->prev_trigger; if (p == weechat_trigger_list) weechat_trigger_list = p->next_trigger; if (p == weechat_trigger_last) weechat_trigger_last = p->prev_trigger; weechat_trigger_free (p); ret = 0; } return ret; } void weechat_trigger_display (t_weechat_plugin *plugin) { t_weechat_trigger *l; int i; char *commands, *channels, *servers, *cmds; if (weechat_trigger_list) { plugin->print_server (plugin, "Trigger list:"); l = weechat_trigger_list; i = 1; while (l) { commands = c_join_string (l->commands, ","); channels = c_join_string (l->channels, ","); servers = c_join_string (l->servers, ","); cmds = c_join_string (l->cmds, ";"); if (strcasecmp (l->action, "run") == 0) plugin->print_server (plugin, "[%d] pattern '%s/%s' for irc command(s) '%s'" " for channel(s) '%s' on server(s) '%s'" " do '%s' command(s) '%s'", i, l->pattern, l->domain, commands, channels, servers, l->action, cmds); else plugin->print_server (plugin, "[%d] pattern '%s/%s' for irc command(s) '%s'" " for channel(s) '%s' on server(s) '%s'" " do '%s'", i, l->pattern, l->domain, commands, channels, servers, l->action); if (commands) c_free_joined_string (commands); if (channels) c_free_joined_string (channels); if (servers) c_free_joined_string (servers); if (cmds) c_free_joined_string (cmds); l = l->next_trigger; i++; } } else plugin->print_server (plugin, "Trigger list: no trigger defined."); } /* * weechat_trigger_cmd: /trigger command */ int weechat_trigger_cmd (t_weechat_plugin *plugin, int cmd_argc, char **cmd_argv, char *handler_args, void *handler_pointer) { int argc, ret, n1, n2; char **argv; /* make C compiler happy */ (void) cmd_argc; (void) handler_args; (void) handler_pointer; ret = 0; if (cmd_argv[2]) argv = c_explode_string (cmd_argv[2], " ", 0, &argc); else { argv = NULL; argc = 0; } if (argv) { if (strcasecmp (argv[0], "add") == 0) { if (argc >= 7) { switch (weechat_trigger_add (argc-1, &argv[1])) { case 0: plugin->print_server (plugin, "Trigger: trigger successfully created"); break; case 1: plugin->print_server (plugin, "Trigger error: memory error while creating trigger"); break; case 2: plugin->print_server (plugin, "Trigger error: 'action' or 'domain' option seems misused"); break; } } else plugin->print_server (plugin, "Trigger error: missing arguments"); } else if (strcasecmp (argv[0], "move") == 0) { if (argc == 3) { n1 = c_to_number (argv[1]); n2 = c_to_number (argv[2]); if (n1 > 0 && n2 > 0) { if (weechat_trigger_move (n1, n2) == 0) plugin->print_server (plugin, "Trigger: trigger sucessfully moved from psotion %d to %d", n1, n2); else plugin->print_server (plugin, "Trigger error: fail to move from %d to %d, number(s) seems invalid", n1, n2); } else plugin->print_server (plugin, "Trigger error: fail to move from %d to %d, number(s) seems invalid", n1, n2); } else plugin->print_server (plugin, "Trigger error: fail to move, missing or too much arguments"); } else if (strcasecmp (argv[0], "remove") == 0) { if (argc == 2) { n1 = c_to_number (argv[1]); if (n1 > 0) { if (weechat_trigger_remove (n1) == 0) plugin->print_server (plugin, "Trigger: trigger number %d sucessfully removed", n1); else plugin->print_server (plugin, "Trigger error: fail to remove trigger number %d, number out of bound", n1); } else plugin->print_server (plugin, "Trigger error: fail to remove trigger number %d, number seems invalid", n1); } else plugin->print_server (plugin, "Trigger error: fail to remove trigger, missing or too much arguments"); } else weechat_trigger_display (plugin); c_free_exploded_string (argv); } else weechat_trigger_display (plugin); return PLUGIN_RC_OK; } /* * weechat_trigger_match: find if a trigger match an irc message * return : 0 if it does match * 1 if it doesn't match * 2 if irc message can't be parsed */ irc_msg * weechat_trigger_match(t_weechat_trigger *trigger, char *msg, char *server, int *ret) { irc_msg *imsg; int match_c, match_h, match_s; char **pp; imsg = irc_parse_msg (msg); *ret = 1; match_c = 0; match_h = 0; match_s = 0; if (imsg) { /* p->print_server(p, "uh=[%s] n=[%s] u=[%s] h=[%s] com=[%s] chan=[%s] srv=[%s] msg=[%s]", imsg->userhost, imsg->nick, imsg->user, imsg->host, imsg->command, imsg->channel, server, imsg->message); */ if ( (imsg->user && ((strcasecmp(trigger->domain, "*") == 0) || (strcasecmp(trigger->domain, "user") == 0)) && c_imatch_string (imsg->user, trigger->pattern)) || (imsg->nick && ((strcasecmp(trigger->domain, "*") == 0) || strcasecmp(trigger->domain, "nick") == 0) && c_imatch_string (imsg->nick, trigger->pattern)) || (imsg->userhost && ((strcasecmp(trigger->domain, "*") == 0) || (strcasecmp(trigger->domain, "userhost") == 0)) && c_imatch_string (imsg->userhost, trigger->pattern)) || (imsg->message && ((strcasecmp(trigger->domain, "*") == 0) || (strcasecmp(trigger->domain, "msg") == 0)) && c_imatch_string (imsg->message, trigger->pattern)) ) { if (!imsg->command) match_c = 1; else if (trigger->commands) { for (pp = trigger->commands; *pp; pp++) { if (c_imatch_string (imsg->command, *pp)) { match_c = 1; break; } } } if (match_c) { if (!imsg->channel) match_h = 1; else if (trigger->channels) { for(pp = trigger->channels; *pp; pp++) { if (c_imatch_string (imsg->channel, *pp)) { match_h = 1; break; } } } } if (match_h) { if (!server) match_s = 1; else if (trigger->servers) { for(pp = trigger->servers; *pp; pp++) { if (c_imatch_string (server, *pp)) { match_s = 1; break; } } } } } *ret = ((match_c == 1) && (match_h == 1) && (match_s == 1)) ? 0 : 1; } else *ret = 2; return imsg; } /* * weechat_trigger_msg: trigger handler */ int weechat_trigger_msg (t_weechat_plugin *plugin, int argc, char **argv, char *handler_args, void *handler_pointer) { t_weechat_trigger *l; int ret, i, j, match; char *ccom, *tcom; irc_msg *imsg; /* make C compiler happy */ (void) argc; (void) handler_args; (void) handler_pointer; ret = PLUGIN_RC_OK; l = weechat_trigger_list; i = 0; while (l) { i++; /* argv[0] = server name */ imsg = weechat_trigger_match (l, argv[2], argv[0], &match); if (imsg && match == 2) { plugin->print_server (plugin, "Trigger error: error while parsing irc message : [%s]", argv[2]); irc_msg_free (imsg); } else if (imsg && match == 0) { if (strcasecmp (l->action, "display") == 0) plugin->print_server (plugin, "Trigger display: matching trigger number %d with message [%s]", i, argv[2]); else if (strcasecmp (l->action, "ignore") == 0) ret = PLUGIN_RC_OK_IGNORE_WEECHAT; else if (strcasecmp (l->action, "highlight") == 0) ret = PLUGIN_RC_OK_WITH_HIGHLIGHT; else if (l->cmds && strcasecmp (l->action, "run") == 0) { for(j = 0; l->cmds[j]; j++) { /* formatting commands by replacing with defined values */ ccom = strdup (l->cmds[j]); if (ccom) { tcom = c_weechat_strreplace (ccom, "%uh", imsg->userhost == NULL ? "" : imsg->userhost); if (tcom) { if (ccom) free (ccom); ccom = tcom; } tcom = c_weechat_strreplace (ccom, "%n", imsg->nick == NULL ? "" : imsg->nick); if (tcom) { if (ccom) free (ccom); ccom = tcom; } tcom = c_weechat_strreplace (ccom, "%u", imsg->user == NULL ? "" : imsg->user); if (tcom) { if (ccom) free (ccom); ccom = tcom; } tcom = c_weechat_strreplace (ccom, "%h", imsg->host == NULL ? "" : imsg->host); if (tcom) { if (ccom) free (ccom); ccom = tcom; } tcom = c_weechat_strreplace (ccom, "%c", imsg->command == NULL ? "" : imsg->command); if (tcom) { if (ccom) free (ccom); ccom = tcom; } tcom = c_weechat_strreplace (ccom, "%C", imsg->channel == NULL ? "" : imsg->channel); if (tcom) { if (ccom) free (ccom); ccom = tcom; } tcom = c_weechat_strreplace (ccom, "%S", argv[0] == NULL ? "" : argv[0]); if (tcom) { if (ccom) free (ccom); ccom = tcom; } tcom = c_weechat_strreplace (ccom, "%d", imsg->data == NULL ? "" : imsg->data); if (tcom) { if (ccom) free (ccom); ccom = tcom; } plugin->exec_command (plugin, NULL, NULL, ccom); free (ccom); } else plugin->exec_command (plugin, NULL, NULL, l->cmds[j]); } } irc_msg_free (imsg); } l = l->next_trigger; } return ret; } /* * weechat_trigger_tmsg: /tmsg command */ int weechat_trigger_tmsg (t_weechat_plugin *plugin, int cmd_argc, char **cmd_argv, char *handler_args, void *handler_pointer) { int argc; char **argv, *srv, *chan, *cmd; /* make C compiler happy */ (void) cmd_argc; (void) handler_args; (void) handler_pointer; if (cmd_argv[2]) argv = c_explode_string (cmd_argv[2], " ", 0, &argc); else { argv = NULL; argc = 0; } if (argv && argc>=3) { srv = (strcmp (argv[0], "*") == 0) ? NULL : argv[0]; chan = (strcmp (argv[1], "*") == 0) ? NULL : argv[1]; cmd = c_join_string (&argv[2], " "); plugin->exec_command (plugin, srv, chan, (cmd == NULL) ? "" : cmd); if (cmd) c_free_joined_string (cmd); } else plugin->print_server (plugin, "Trigger error: wrong argument count for command \"tmsg\""); if (argv) c_free_exploded_string (argv); return PLUGIN_RC_OK; } /* * weechat_trigger_edit: load/save triggers from/in a file */ int weechat_trigger_edit (t_weechat_plugin *plugin, int todo) { t_weechat_trigger *l; int len, ret, argc; char **argv, *commands, *channels, *servers, *cmds; char *weechat_dir, *triggerrc, line[1024], *p, *q; FILE *f; ret = 0; if ((todo != CONF_LOAD) && (todo != CONF_SAVE)) return -1; weechat_dir = plugin->get_info (plugin, "weechat_dir", NULL); if (!weechat_dir) return -1; len = strlen (weechat_dir) + strlen(DIR_SEP) + strlen(CONF_FILE) + 1; triggerrc = malloc (len); if (!triggerrc) return -1; snprintf (triggerrc, len, "%s%s%s", weechat_dir, DIR_SEP, CONF_FILE); /* saving */ if (todo == CONF_SAVE) { f = fopen (triggerrc, "w"); if (!f) { plugin->print_server (plugin, "Trigger plugin: error, unable to write file '%s'", triggerrc); return -1; } fprintf (f, "#\n"); fprintf (f, "#WeeChat trigger plugin config file\n"); fprintf (f, "# BE CAREFUL - DO NOT EDIT BY HAND\n"); fprintf (f, "#\n\n"); l = weechat_trigger_list; while (l) { commands = c_join_string (l->commands, ","); channels = c_join_string (l->channels, ","); servers = c_join_string (l->servers, ","); cmds = c_join_string (l->cmds, ";"); if (strcasecmp (l->action, "run") == 0) fprintf (f, "%s %s %s %s %s %s %s\n", l->pattern, l->domain, commands, channels, servers, l->action, cmds); else fprintf (f, "%s %s %s %s %s %s\n", l->pattern, l->domain, commands, channels, servers, l->action); ret++; l = l->next_trigger; } fclose(f); } /* loading */ if (todo == CONF_LOAD) { f = fopen (triggerrc, "r"); if (!f) { plugin->print_server (plugin, "Trigger plugin: error, unable to read file '%s'", triggerrc); return -1; } while (!feof (f)) { p = fgets (line, sizeof (line), f); if (p) { /* skip spaces ans tabs */ while(p[0] == ' ' || p[0] == '\t') p++; /* skip comments and empty lines */ if ((p[0] != '#') && (p[0] != '\r') && (p[0] != '\n')) { q = strrchr (p, '\n'); if (q) *q = '\0'; argv = c_explode_string (p, " ", 0, &argc); if (argv && argc >= 7) { if (weechat_trigger_add (argc, argv) == 0) ret++; else plugin->print_server (plugin, "Trigger: failed to load trigger \"%s\"", p); } if (argv) c_free_exploded_string (argv); } } } fclose(f); } return ret; } /* * weechat_plugin_init: init trigger plugin */ int weechat_plugin_init (t_weechat_plugin *plugin) { t_plugin_handler *cmd_handler_trigger, *cmd_handler_tmsg, *msg_handler; int n; weechat_trigger_list = NULL; weechat_trigger_last = NULL; /* loading saved triggers */ n = weechat_trigger_edit (plugin, CONF_LOAD); switch (n) { case -1: plugin->print_server (plugin, "Trigger plugin starting: error"); break; case 0: plugin->print_server (plugin, "Trigger plugin starting: no triggers found"); break; default: plugin->print_server (plugin, "Trigger plugin starting: %d triggers found and loaded", n); } /* add trigger command handler */ cmd_handler_trigger = plugin->cmd_handler_add (plugin, "trigger", "Trigger actions on IRC messages by nicks/hosts, irc messages, commands, channels and servers", " [ list ] | [ add pattern domain [type(s) | command(s)] channel(s) server(s) [action [cmd]] ] | [ move from_num to_num ] | [ remove num ]", " 'list': list triggers\n" " 'add': create a new trigger\n" " pattern: pattern to match\n" " domain: domain where the pattern is searched (user, nick, userhost, msg)\n" " type(s): messages types to trigger (privmsg, ctcp-$type, dcc, join, part, quit, ...).\n" " command(s): irc commands to trigger.\n" " channel(s): channels to trigger.\n" " server(s): servers to trigger.\n" " action: action to perform if trigger match (ignore, display, highlight, run)\n" " cmd: irc or WeeChat command(s) to run if action is 'run'\n" " possible replacements in command(s) :\n" " %uh : userhost mask\n" " %n : nickname\n" " %u : username\n" " %h : hostname\n" " %c : irc command\n" " %C : channel name\n" " %S : server name\n" " %d : extra data\n" " 'move': move trigger position in trigger's list\n" " from_num: current trigger position\n" " to_num: future trigger position\n" "'remove': remove a trigger\n" " num: position of the trigger to remove\n" "\n" "Multiples values separated by commas can be set for fields : type(s), command(s), channel(s) and server(s).\n" "It's possible to use wildcards for fields : pattern, type(s), command(s), channel(s) and server(s) options.\n\n", "list|add|move|remove *|%n *|user|nick|userhost|msg *|%I|ctcp-action|ctcp-dcc|ctcp-sed|ctcp-finger|ctcp-version|ctcp-source|ctcp-userinfo|ctcp-clientinfo|ctcp-errmsg|ctcp-ping|ctcp-time *|%c *|%s ignore|display|highlight|run", &weechat_trigger_cmd, NULL, NULL); /* add messge modifier */ msg_handler = plugin->msg_handler_add (plugin, "*", &weechat_trigger_msg, NULL, NULL); /* add tmsg command handler */ cmd_handler_tmsg = plugin->cmd_handler_add (plugin, "tmsg", "Send a message to a channel", " server receiver text", "server: server ('*' = current server)\n" "channel: channel ('*' = current channel)\n" "text: text to send\n", "*|%s *|%c", &weechat_trigger_tmsg, NULL, NULL); return PLUGIN_RC_OK; } /* * weechat_plugin_end: end trigger plugin */ void weechat_plugin_end (t_weechat_plugin *plugin) { t_weechat_trigger *l; int n; /* make C compiler happy */ (void) plugin; n = weechat_trigger_edit (plugin, CONF_SAVE); switch (n) { case -1: plugin->print_server (plugin, "Trigger plugin ending: error"); break; case 0: plugin->print_server (plugin, "Trigger plugin ending: no triggers to save"); break; default: plugin->print_server (plugin, "Trigger plugin ending: saving %d triggers", n); } while (weechat_trigger_list) { l = weechat_trigger_list; weechat_trigger_list = weechat_trigger_list->next_trigger; weechat_trigger_free (l); } }