diff options
Diffstat (limited to 'src/irc/core/irc-commands.c')
-rw-r--r-- | src/irc/core/irc-commands.c | 1128 |
1 files changed, 1128 insertions, 0 deletions
diff --git a/src/irc/core/irc-commands.c b/src/irc/core/irc-commands.c new file mode 100644 index 0000000..5f234c5 --- /dev/null +++ b/src/irc/core/irc-commands.c @@ -0,0 +1,1128 @@ +/* + irc-commands.c : irssi + + Copyright (C) 1999 Timo Sirainen + + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/misc.h> +#include <irssi/src/core/recode.h> +#include <irssi/src/core/special-vars.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/core/window-item-def.h> + +#include <irssi/src/core/servers-reconnect.h> +#include <irssi/src/irc/core/servers-redirect.h> +#include <irssi/src/core/servers-setup.h> +#include <irssi/src/core/nicklist.h> + +#include <irssi/src/irc/core/bans.h> +#include <irssi/src/irc/core/irc-commands.h> +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-channels.h> +#include <irssi/src/irc/core/irc-queries.h> + +/* How often to check if there's anyone to be unbanned in knockout list */ +#define KNOCKOUT_TIMECHECK 10000 + +/* /LIST: Max. number of channels in IRC network before -yes option + is required */ +#define LIST_MAX_CHANNELS_PASS 1000 + +/* When /PARTing a channel, if there's more messages in output queue + than this, purge the output for channel. The idea behind this is that + if you accidentally pasted some large text and /PART the channel, the + text won't be fully pasted. Note that this counter is the whole size + of the output queue, not channel specific.. */ +#define MAX_COMMANDS_ON_PART_UNTIL_PURGE 10 + +typedef struct { + IRC_CHANNEL_REC *channel; + char *ban; + time_t unban_time; +} KNOCKOUT_REC; + +static GString *tmpstr; +static int knockout_tag; + +/* SYNTAX: NOTICE <targets> <message> */ +static void cmd_notice(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + const char *target, *msg; + char *recoded; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &target, &msg)) + return; + if (g_strcmp0(target, "*") == 0) + target = item == NULL ? NULL : window_item_get_target(item); + if (target == NULL || *target == '\0' || *msg == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + recoded = recode_out(SERVER(server), msg, target); + g_string_printf(tmpstr, "NOTICE %s :%s", target, recoded); + g_free(recoded); + + irc_send_cmd_split(server, tmpstr->str, 2, server->max_msgs_in_cmd); + + cmd_params_free(free_arg); +} + +/* SYNTAX: CTCP <targets> <ctcp command> [<ctcp data>] */ +static void cmd_ctcp(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + const char *target; + char *ctcpcmd, *ctcpdata; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST, + &target, &ctcpcmd, &ctcpdata)) + return; + if (g_strcmp0(target, "*") == 0) + target = item == NULL ? NULL : window_item_get_target(item); + if (target == NULL || *target == '\0' || *ctcpcmd == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + ascii_strup(ctcpcmd); + if (*ctcpdata == '\0') + g_string_printf(tmpstr, "PRIVMSG %s :\001%s\001", target, ctcpcmd); + else { + char *recoded; + + recoded = recode_out(SERVER(server), ctcpdata, target); + g_string_printf(tmpstr, "PRIVMSG %s :\001%s %s\001", target, ctcpcmd, recoded); + g_free(recoded); + } + + irc_send_cmd_split(server, tmpstr->str, 2, server->max_msgs_in_cmd); + + cmd_params_free(free_arg); +} + +/* SYNTAX: NCTCP <targets> <ctcp command> [<ctcp data>] */ +static void cmd_nctcp(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + const char *target; + char *ctcpcmd, *ctcpdata, *recoded; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST, + &target, &ctcpcmd, &ctcpdata)) + return; + if (g_strcmp0(target, "*") == 0) + target = item == NULL ? NULL : window_item_get_target(item); + if (target == NULL || *target == '\0' || *ctcpcmd == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + ascii_strup(ctcpcmd); + recoded = recode_out(SERVER(server), ctcpdata, target); + g_string_printf(tmpstr, "NOTICE %s :\001%s %s\001", target, ctcpcmd, recoded); + g_free(recoded); + + irc_send_cmd_split(server, tmpstr->str, 2, server->max_msgs_in_cmd); + + cmd_params_free(free_arg); +} + +/* SYNTAX: PART [<channels>] [<message>] */ +static void cmd_part(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + char *channame, *msg; + char *recoded = NULL; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | + PARAM_FLAG_OPTCHAN, item, &channame, &msg)) + return; + if (*channame == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + if (*msg == '\0') msg = (char *) settings_get_str("part_message"); + + if (server->cmdcount > MAX_COMMANDS_ON_PART_UNTIL_PURGE) + irc_server_purge_output(server, channame); + + if (*msg != '\0') + recoded = recode_out(SERVER(server), msg, channame); + + if (recoded == NULL) + irc_send_cmdv(server, "PART %s", channame); + else + irc_send_cmdv(server, "PART %s :%s", channame, recoded); + + g_free(recoded); + cmd_params_free(free_arg); +} + +/* SYNTAX: KICK [<channel>] <nicks> [<reason>] */ +static void cmd_kick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item) +{ + char *channame, *nicks, *reason, *recoded; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST | + PARAM_FLAG_OPTCHAN, item, + &channame, &nicks, &reason)) + return; + + if (*channame == '\0' || *nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + if (!server_ischannel(SERVER(server), channame)) cmd_param_error(CMDERR_NOT_JOINED); + + recoded = recode_out(SERVER(server), reason, channame); + g_string_printf(tmpstr, "KICK %s %s :%s", channame, nicks, recoded); + g_free(recoded); + + irc_send_cmd_split(server, tmpstr->str, 3, server->max_kicks_in_cmd); + + cmd_params_free(free_arg); +} + +/* SYNTAX: TOPIC [-delete] [<channel>] [<topic>] */ +static void cmd_topic(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item) +{ + GHashTable *optlist; + char *channame, *topic; + char *recoded = NULL; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | + PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST, + item, "topic", &optlist, &channame, &topic)) + return; + + if (*topic != '\0' || g_hash_table_lookup(optlist, "delete") != NULL) + recoded = recode_out(SERVER(server), topic, channame); + + if (recoded == NULL) + irc_send_cmdv(server, "TOPIC %s", channame); + else + irc_send_cmdv(server, "TOPIC %s :%s", channame, recoded); + + g_free(recoded); + + cmd_params_free(free_arg); +} + +/* SYNTAX: INVITE <nick> [<channel>] */ +static void cmd_invite(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item) +{ + char *nick, *channame; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2, &nick, &channame)) + return; + + if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + if (*channame == '\0' || g_strcmp0(channame, "*") == 0) { + if (!IS_IRC_CHANNEL(item)) + cmd_param_error(CMDERR_NOT_JOINED); + + channame = IRC_CHANNEL(item)->name; + } + + irc_send_cmdv(server, "INVITE %s %s", nick, channame); + cmd_params_free(free_arg); +} + +/* SYNTAX: LIST [-yes] [<channel>] */ +static void cmd_list(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + GHashTable *optlist; + char *str; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, + "list", &optlist, &str)) + return; + + if (*str == '\0' && g_hash_table_lookup(optlist, "yes") == NULL && + (server->channels_formed <= 0 || + server->channels_formed > LIST_MAX_CHANNELS_PASS)) + cmd_param_error(CMDERR_NOT_GOOD_IDEA); + + irc_send_cmdv(server, "LIST %s", str); + cmd_params_free(free_arg); +} + +/* SYNTAX: WHO [<nicks> | <channels> | **] */ +static void cmd_who(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + char *channel, *rest; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | + PARAM_FLAG_STRIP_TRAILING_WS, &channel, &rest)) + return; + + if (g_strcmp0(channel, "*") == 0 || *channel == '\0') { + if (!IS_IRC_CHANNEL(item)) + cmd_param_error(CMDERR_NOT_JOINED); + + channel = IRC_CHANNEL(item)->name; + } + if (g_strcmp0(channel, "**") == 0) { + /* ** displays all nicks.. */ + *channel = '\0'; + } + + if (rest[0] == '\0') + irc_send_cmdv(server, "WHO %s", channel); + else + irc_send_cmdv(server, "WHO %s %s", channel, rest); + + cmd_params_free(free_arg); +} + +static void cmd_names(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + GHashTable *optlist; + char *channel; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, + "names", &optlist, &channel)) + return; + + if (g_strcmp0(channel, "*") == 0 || *channel == '\0') { + if (!IS_IRC_CHANNEL(item)) + cmd_param_error(CMDERR_NOT_JOINED); + + channel = IRC_CHANNEL(item)->name; + } + + if (g_strcmp0(channel, "**") == 0) { + /* ** displays all nicks.. */ + irc_send_cmd(server, "NAMES"); + } else { + irc_send_cmdv(server, "NAMES %s", channel); + } + + cmd_params_free(free_arg); +} + +/* SYNTAX: NICK <new nick> */ +static void cmd_nick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item) +{ + char *nick; + void *free_arg; + + g_return_if_fail(data != NULL); + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 1, &nick)) + return; + + g_free(server->last_nick); + server->last_nick = g_strdup(nick); + + irc_send_cmdv(server, "NICK %s", nick); + cmd_params_free(free_arg); +} + +static char *get_redirect_nicklist(const char *nicks, int *free) +{ + char *str, *ret; + + if (*nicks != '!' && strchr(nicks, ',') == NULL) { + *free = FALSE; + return (char *) nicks; + } + + *free = TRUE; + + /* ratbox-style operspy whois takes !nick, echoes that + * in RPL_ENDOFWHOIS as normal but gives output about the + * plain nick + */ + str = g_strdup(*nicks == '!' ? nicks + 1 : nicks); + g_strdelimit(str, ",", ' '); + ret = g_strconcat(str, " ", nicks, NULL); + g_free(str); + + return ret; +} + +/* SYNTAX: WHOIS [-<server tag>] [<server>] [<nicks>] */ +static void cmd_whois(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + GHashTable *optlist; + char *qserver, *query, *event_402, *str; + void *free_arg; + int free_nick; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_UNKNOWN_OPTIONS, + "whois", &optlist, &qserver, &query)) + return; + + /* -<server tag> */ + server = IRC_SERVER(cmd_options_get_server("whois", optlist, + SERVER(server))); + if (server == NULL) { + cmd_params_free(free_arg); + return; + } + + if (*query == '\0') { + query = qserver; + qserver = ""; + } + if (*query == '\0') { + QUERY_REC *queryitem = QUERY(item); + if (queryitem == NULL) + query = server->nick; + else + query = qserver = queryitem->name; + } + + if (g_strcmp0(query, "*") == 0 && + g_hash_table_lookup(optlist, "yes") == NULL) + cmd_param_error(CMDERR_NOT_GOOD_IDEA); + + event_402 = "event 402"; + if (*qserver == '\0') + g_string_printf(tmpstr, "WHOIS %s", query); + else { + g_string_printf(tmpstr, "WHOIS %s %s", qserver, query); + if (g_ascii_strcasecmp(qserver, query) == 0) + event_402 = "whois event not found"; + } + + query = get_redirect_nicklist(query, &free_nick); + + str = g_strconcat(qserver, " ", query, NULL); + server_redirect_event( + server, "whois", 1, str, TRUE, /* */ + NULL, /* */ + "event 318", "whois end", /* */ + "event 402", event_402, /* */ + "event 301", "whois away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */ + "event 313", "whois oper", /* */ + "event 330", "whois account", /* */ + "event 401", + (settings_get_bool("auto_whowas") ? "whois try whowas" : "whois event not found"), + "event 311", "whois event", /* */ + "", "whois default event", NULL); + g_free(str); + + server->whois_found = FALSE; + irc_send_cmd_split(server, tmpstr->str, 2, server->max_whois_in_cmd); + + if (free_nick) g_free(query); + cmd_params_free(free_arg); +} + +static void event_whois(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + server->whois_found = TRUE; + signal_emit("event 311", 4, server, data, nick, addr); +} + +static void sig_whois_try_whowas(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &nick); + + server->whowas_found = FALSE; + server_redirect_event(server, "whowas", 1, nick, -1, NULL, + "event 314", "whowas event", + "event 369", "whowas event end", + "event 406", "event empty", NULL); + irc_send_cmdv(server, "WHOWAS %s 1", nick); + + g_free(params); +} + +static void event_end_of_whois(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + signal_emit("event 318", 4, server, data, nick, addr); + server->whois_found = FALSE; +} + +static void event_whowas(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + server->whowas_found = TRUE; + signal_emit("event 314", 4, server, data, nick, addr); +} + +/* SYNTAX: WHOWAS [<nicks> [<count> [server]]] */ +static void cmd_whowas(const char *data, IRC_SERVER_REC *server) +{ + char *nicks, *rest, *nicks_redir; + void *free_arg; + int free_nick; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, + &nicks, &rest)) + return; + if (*nicks == '\0') nicks = server->nick; + + nicks_redir = get_redirect_nicklist(nicks, &free_nick); + server_redirect_event(server, "whowas", 1, nicks_redir, -1, NULL, + "event 301", "whowas away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */ + "event 314", "whowas event", NULL); + if (free_nick) g_free(nicks_redir); + + server->whowas_found = FALSE; + + if (rest[0] == '\0') + irc_send_cmdv(server, "WHOWAS %s", nicks); + else + irc_send_cmdv(server, "WHOWAS %s %s", nicks, rest); + + cmd_params_free(free_arg); +} + +/* SYNTAX: PING [<nick> | <channel> | *] */ +static void cmd_ping(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item) +{ + gint64 tv; + char *str; + + CMD_IRC_SERVER(server); + + if (*data == '\0') { + if (!IS_QUERY(item)) + cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + data = window_item_get_target(item); + } + + tv = g_get_real_time(); + + str = g_strdup_printf("%s PING " + "%" G_GINT64_FORMAT " " + "%" G_GINT64_FORMAT, + data, tv / G_TIME_SPAN_SECOND, tv % G_TIME_SPAN_SECOND); + signal_emit("command ctcp", 3, str, server, item); + g_free(str); +} + +/* SYNTAX: AWAY [-one | -all] [<reason>] */ +static void cmd_away(const char *data, IRC_SERVER_REC *server) +{ + GHashTable *optlist; + char *reason; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_GETREST, "away", &optlist, &reason)) return; + + if (g_hash_table_lookup(optlist, "one") != NULL) + irc_server_send_away(server, reason); + else + g_slist_foreach(servers, (GFunc) irc_server_send_away, reason); + + cmd_params_free(free_arg); +} + +/* SYNTAX: SCONNECT <new server> [[<port>] <existing server>] */ +static void cmd_sconnect(const char *data, IRC_SERVER_REC *server) +{ + CMD_IRC_SERVER(server); + if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + + irc_send_cmdv(server, "CONNECT %s", data); +} + +/* SYNTAX: QUOTE <data> */ +static void cmd_quote(const char *data, IRC_SERVER_REC *server) +{ + if (server != NULL && !IS_IRC_SERVER(server)) + return; + if (server == NULL || server->connect_time == 0) + cmd_return_error(CMDERR_NOT_CONNECTED); + + if (!server->connected) + irc_send_cmd_now(server, data); + else + irc_send_cmd(server, data); +} + +static void cmd_wall_hash(gpointer key, NICK_REC *nick, GSList **nicks) +{ + if (nick->op) *nicks = g_slist_append(*nicks, nick); +} + +/* SYNTAX: WAIT [-<server tag>] <milliseconds> */ +static void cmd_wait(const char *data, IRC_SERVER_REC *server) +{ + GHashTable *optlist; + char *msecs; + void *free_arg; + int n; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST, + NULL, &optlist, &msecs)) + return; + + if (*msecs == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + /* -<server tag> */ + server = IRC_SERVER(cmd_options_get_server(NULL, optlist, + SERVER(server))); + + n = atoi(msecs); + if (server != NULL && n > 0) { + server->wait_cmd = g_get_real_time(); + server->wait_cmd += n * G_TIME_SPAN_MILLISECOND; + } + cmd_params_free(free_arg); +} + +/* SYNTAX: WALL [<channel>] <message> */ +static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item) +{ + char *channame, *msg, *args, *recoded; + void *free_arg; + IRC_CHANNEL_REC *chanrec; + GSList *tmp, *nicks; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | + PARAM_FLAG_GETREST, item, &channame, &msg)) + return; + if (*msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + chanrec = irc_channel_find(server, channame); + if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND); + + recoded = recode_out(SERVER(server), msg, channame); + /* See if the server has advertised support of wallchops */ + if (g_hash_table_lookup(chanrec->server->isupport, "statusmsg") || + g_hash_table_lookup(chanrec->server->isupport, "wallchops")) + irc_send_cmdv(server, "NOTICE @%s :%s", chanrec->name, recoded); + else { + /* Fall back to manually noticing each op */ + nicks = NULL; + g_hash_table_foreach(chanrec->nicks, + (GHFunc) cmd_wall_hash, &nicks); + + args = g_strconcat(chanrec->name, " ", recoded, NULL); + msg = parse_special_string(settings_get_str("wall_format"), + SERVER(server), item, args, NULL, 0); + g_free(args); + + for (tmp = nicks; tmp != NULL; tmp = tmp->next) { + NICK_REC *rec = tmp->data; + + if (rec != chanrec->ownnick) { + irc_send_cmdv(server, "NOTICE %s :%s", + rec->nick, msg); + } + } + + g_free(msg); + g_slist_free(nicks); + } + + g_free(recoded); + cmd_params_free(free_arg); +} + +/* SYNTAX: KICKBAN [<channel>] <nicks> <reason> */ +static void cmd_kickban(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + IRC_CHANNEL_REC *chanrec; + char *channel, *nicks, *reason, *kickcmd, *bancmd, *recoded; + char **nicklist, *spacenicks; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST, + item, &channel, &nicks, &reason)) + return; + + if (*channel == '\0' || *nicks == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + chanrec = irc_channel_find(server, channel); + if (chanrec == NULL) + cmd_param_error(CMDERR_CHAN_NOT_FOUND); + + nicklist = g_strsplit(nicks, ",", -1); + spacenicks = g_strjoinv(" ", nicklist); + g_strfreev(nicklist); + + recoded = recode_out(SERVER(server), reason, channel); + kickcmd = g_strdup_printf("%s %s %s", chanrec->name, nicks, recoded); + g_free(recoded); + + bancmd = g_strdup_printf("%s %s", chanrec->name, spacenicks); + g_free(spacenicks); + + if (settings_get_bool("kick_first_on_kickban")) { + signal_emit("command kick", 3, kickcmd, server, chanrec); + signal_emit("command ban", 3, bancmd, server, chanrec); + } else { + signal_emit("command ban", 3, bancmd, server, chanrec); + signal_emit("command kick", 3, kickcmd, server, chanrec); + } + g_free(kickcmd); + g_free(bancmd); + + cmd_params_free(free_arg); +} + +static void knockout_destroy(IRC_SERVER_REC *server, KNOCKOUT_REC *rec) +{ + server->knockoutlist = g_slist_remove(server->knockoutlist, rec); + g_free(rec->ban); + g_free(rec); +} + +/* timeout function: knockout */ +static void knockout_timeout_server(IRC_SERVER_REC *server) +{ + GSList *tmp, *next; + time_t now; + + g_return_if_fail(server != NULL); + + if (!IS_IRC_SERVER(server)) + return; + + now = time(NULL); + for (tmp = server->knockoutlist; tmp != NULL; tmp = next) { + KNOCKOUT_REC *rec = tmp->data; + + next = tmp->next; + if (rec->unban_time <= now) { + /* timeout, unban. */ + signal_emit("command unban", 3, rec->ban, server, rec->channel); + knockout_destroy(server, rec); + } + } +} + +static int knockout_timeout(void) +{ + g_slist_foreach(servers, (GFunc) knockout_timeout_server, NULL); + return 1; +} + +/* SYNTAX: KNOCKOUT [<time>] <nicks> <reason> */ +static void cmd_knockout(const char *data, IRC_SERVER_REC *server, + IRC_CHANNEL_REC *channel) +{ + KNOCKOUT_REC *rec; + char *nicks, *reason, *timeoutstr, *kickcmd, *bancmd, *recoded; + char **nicklist, *spacenicks, *banmasks; + void *free_arg; + int timeleft; + GSList *ptr; + + CMD_IRC_SERVER(server); + + if (!IS_IRC_CHANNEL(channel)) + cmd_return_error(CMDERR_NOT_JOINED); + + if (i_isdigit(*data)) { + /* first argument is the timeout */ + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST, + &timeoutstr, &nicks, &reason)) + return; + + if (!parse_time_interval(timeoutstr, &timeleft)) + cmd_param_error(CMDERR_INVALID_TIME); + } else { + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &nicks, &reason)) + return; + timeleft = settings_get_time("knockout_time"); + } + + if (*nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + nicklist = g_strsplit(nicks, ",", -1); + spacenicks = g_strjoinv(" ", nicklist); + g_strfreev(nicklist); + + banmasks = ban_get_masks(channel, spacenicks, 0); + g_free(spacenicks); + + recoded = recode_out(SERVER(server), reason, channel->name); + kickcmd = g_strdup_printf("%s %s %s", channel->name, nicks, recoded); + g_free(recoded); + + bancmd = *banmasks == '\0'? NULL : + g_strdup_printf("%s %s", channel->name, banmasks); + + if (settings_get_bool("kick_first_on_kickban")) { + signal_emit("command kick", 3, kickcmd, server, channel); + if (bancmd != NULL) + signal_emit("command ban", 3, bancmd, server, channel); + } else { + if (bancmd != NULL) + signal_emit("command ban", 3, bancmd, server, channel); + signal_emit("command kick", 3, kickcmd, server, channel); + } + g_free(kickcmd); + g_free_not_null(bancmd); + + if (*banmasks == '\0') + g_free(banmasks); + else { + /* check if we already have this knockout */ + for (ptr = server->knockoutlist; ptr != NULL; ptr = ptr->next) { + rec = ptr->data; + if (channel == rec->channel && + !g_strcmp0(rec->ban, banmasks)) + break; + } + if (ptr == NULL) { + rec = g_new(KNOCKOUT_REC, 1); + rec->channel = channel; + rec->ban = banmasks; + server->knockoutlist = g_slist_append(server->knockoutlist, rec); + } + rec->unban_time = time(NULL)+timeleft/1000; + } + + cmd_params_free(free_arg); +} + +/* SYNTAX: SERVER PURGE [<target>] */ +static void cmd_server_purge(const char *data, IRC_SERVER_REC *server) +{ + char *target; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 1, &target)) + return; + + irc_server_purge_output(server, *target == '\0' ? NULL : target); + + cmd_params_free(free_arg); +} + +/* destroy all knockouts in server */ +static void sig_server_disconnected(IRC_SERVER_REC *server) +{ + g_return_if_fail(server != NULL); + + if (!IS_IRC_SERVER(server)) + return; + + g_free(server->last_nick); + + while (server->knockoutlist != NULL) + knockout_destroy(server, server->knockoutlist->data); +} + +/* destroy all knockouts in channel */ +static void sig_channel_destroyed(IRC_CHANNEL_REC *channel) +{ + GSList *tmp, *next; + + if (!IS_IRC_CHANNEL(channel) || !IS_IRC_SERVER(channel->server)) + return; + + for (tmp = channel->server->knockoutlist; tmp != NULL; tmp = next) { + KNOCKOUT_REC *rec = tmp->data; + + next = tmp->next; + if (rec->channel == channel) + knockout_destroy(channel->server, rec); + } +} + +/* SYNTAX: OPER [<nick> [<password>]] */ +static void cmd_oper(const char *data, IRC_SERVER_REC *server) +{ + char *nick, *password; + void *free_arg; + + CMD_IRC_SERVER(server); + + /* asking for password is handled by fe-common */ + if (!cmd_get_params(data, &free_arg, 2, &nick, &password)) + return; + if (*password == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + irc_send_cmdv(server, "OPER %s %s", nick, password); + cmd_params_free(free_arg); +} + +/* SYNTAX: ACCEPT [[-]nick,...] */ +static void cmd_accept(const char *data, IRC_SERVER_REC *server) +{ + CMD_IRC_SERVER(server); + + if (*data == '\0') + irc_send_cmd(server, "ACCEPT *"); + else + irc_send_cmdv(server, "ACCEPT %s", data); +} + +/* SYNTAX: UNSILENCE <nick!user@host> */ +static void cmd_unsilence(const char *data, IRC_SERVER_REC *server) +{ + CMD_IRC_SERVER(server); + + if (*data == '\0') + cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + + irc_send_cmdv(server, "SILENCE -%s", data); +} + +static void command_self(const char *data, IRC_SERVER_REC *server) +{ + CMD_IRC_SERVER(server); + + if (data[0] == '\0') + irc_send_cmdv(server, "%s", current_command); + else + irc_send_cmdv(server, "%s %s", current_command, data); +} + +static void command_1self(const char *data, IRC_SERVER_REC *server) +{ + g_return_if_fail(data != NULL); + if (!IS_IRC_SERVER(server) || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); + if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + + irc_send_cmdv(server, "%s :%s", current_command, data); +} + +static void command_2self(const char *data, IRC_SERVER_REC *server) +{ + char *target, *text; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &text)) + return; + if (*target == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + irc_send_cmdv(server, "%s %s :%s", current_command, target, text); + cmd_params_free(free_arg); +} + +void irc_commands_init(void) +{ + tmpstr = g_string_new(NULL); + + settings_add_str("misc", "part_message", ""); + settings_add_time("misc", "knockout_time", "5min"); + settings_add_str("misc", "wall_format", "[Wall/$0] $1-"); + settings_add_bool("misc", "kick_first_on_kickban", FALSE); + settings_add_bool("misc", "auto_whowas", TRUE); + + knockout_tag = g_timeout_add(KNOCKOUT_TIMECHECK, (GSourceFunc) knockout_timeout, NULL); + + command_bind_irc("notice", NULL, (SIGNAL_FUNC) cmd_notice); + command_bind_irc("ctcp", NULL, (SIGNAL_FUNC) cmd_ctcp); + command_bind_irc("nctcp", NULL, (SIGNAL_FUNC) cmd_nctcp); + command_bind_irc("part", NULL, (SIGNAL_FUNC) cmd_part); + command_bind_irc("kick", NULL, (SIGNAL_FUNC) cmd_kick); + command_bind_irc("topic", NULL, (SIGNAL_FUNC) cmd_topic); + command_bind_irc("invite", NULL, (SIGNAL_FUNC) cmd_invite); + command_bind_irc("list", NULL, (SIGNAL_FUNC) cmd_list); + command_bind_irc("who", NULL, (SIGNAL_FUNC) cmd_who); + command_bind_irc("names", NULL, (SIGNAL_FUNC) cmd_names); + command_bind_irc("nick", NULL, (SIGNAL_FUNC) cmd_nick); + command_bind_irc("whois", NULL, (SIGNAL_FUNC) cmd_whois); + command_bind_irc("whowas", NULL, (SIGNAL_FUNC) cmd_whowas); + command_bind_irc("ping", NULL, (SIGNAL_FUNC) cmd_ping); + /* SYNTAX: KILL <nick> <reason> */ + command_bind_irc("kill", NULL, (SIGNAL_FUNC) command_2self); + command_bind_irc("away", NULL, (SIGNAL_FUNC) cmd_away); + /* SYNTAX: ISON <nicks> */ + command_bind_irc("ison", NULL, (SIGNAL_FUNC) command_1self); + command_bind_irc("accept", NULL, (SIGNAL_FUNC) cmd_accept); + /* SYNTAX: ADMIN [<server>|<nickname>] */ + command_bind_irc("admin", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: INFO [<server>] */ + command_bind_irc("info", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: KNOCK <channel> */ + command_bind_irc("knock", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: LINKS [[<server>] <mask>] */ + command_bind_irc("links", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: LUSERS [<server mask> [<remote server>]] */ + command_bind_irc("lusers", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: MAP */ + command_bind_irc("map", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: MOTD [<server>|<nick>] */ + command_bind_irc("motd", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: REHASH [<option>] */ + command_bind_irc("rehash", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: STATS <type> [<server>] */ + command_bind_irc("stats", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: TIME [<server>|<nick>] */ + command_bind_irc("time", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: TRACE [<server>|<nick>] */ + command_bind_irc("trace", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: VERSION [<server>|<nick>] */ + command_bind_irc("version", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: SERVLIST [<mask> [<type>]] */ + command_bind_irc("servlist", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: SILENCE [[+|-]<nick!user@host>] + SILENCE [<nick>] */ + command_bind_irc("silence", NULL, (SIGNAL_FUNC) command_self); + command_bind_irc("unsilence", NULL, (SIGNAL_FUNC) cmd_unsilence); + command_bind_irc("sconnect", NULL, (SIGNAL_FUNC) cmd_sconnect); + /* SYNTAX: SQUERY <service> [<message>] */ + command_bind_irc("squery", NULL, (SIGNAL_FUNC) command_2self); + /* SYNTAX: DIE */ + command_bind_irc("die", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: HASH */ + command_bind_irc("hash", NULL, (SIGNAL_FUNC) command_self); + command_bind_irc("oper", NULL, (SIGNAL_FUNC) cmd_oper); + /* SYNTAX: RESTART */ + command_bind_irc("restart", NULL, (SIGNAL_FUNC) command_self); + /* SYNTAX: SQUIT <server>|<mask> <reason> */ + command_bind_irc("squit", NULL, (SIGNAL_FUNC) command_2self); + /* SYNTAX: USERHOST <nicks> */ + command_bind_irc("userhost", NULL, (SIGNAL_FUNC) command_self); + command_bind_irc("quote", NULL, (SIGNAL_FUNC) cmd_quote); + command_bind_irc("wall", NULL, (SIGNAL_FUNC) cmd_wall); + command_bind_irc("wait", NULL, (SIGNAL_FUNC) cmd_wait); + /* SYNTAX: WALLOPS <message> */ + command_bind_irc("wallops", NULL, (SIGNAL_FUNC) command_1self); + command_bind_irc("kickban", NULL, (SIGNAL_FUNC) cmd_kickban); + command_bind_irc("knockout", NULL, (SIGNAL_FUNC) cmd_knockout); + command_bind_irc("setname", NULL, (SIGNAL_FUNC) command_1self); + command_bind_irc("server purge", NULL, (SIGNAL_FUNC) cmd_server_purge); + + signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); + signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_add("whois try whowas", (SIGNAL_FUNC) sig_whois_try_whowas); + signal_add("whois event", (SIGNAL_FUNC) event_whois); + signal_add("whois end", (SIGNAL_FUNC) event_end_of_whois); + signal_add("whowas event", (SIGNAL_FUNC) event_whowas); + + command_set_options("connect", "+ircnet starttls disallow_starttls nocap"); + command_set_options("topic", "delete"); + command_set_options("list", "yes"); + command_set_options("away", "one all"); + command_set_options("whois", "yes"); +} + +void irc_commands_deinit(void) +{ + g_source_remove(knockout_tag); + + command_unbind("notice", (SIGNAL_FUNC) cmd_notice); + command_unbind("ctcp", (SIGNAL_FUNC) cmd_ctcp); + command_unbind("nctcp", (SIGNAL_FUNC) cmd_nctcp); + command_unbind("part", (SIGNAL_FUNC) cmd_part); + command_unbind("kick", (SIGNAL_FUNC) cmd_kick); + command_unbind("topic", (SIGNAL_FUNC) cmd_topic); + command_unbind("invite", (SIGNAL_FUNC) cmd_invite); + command_unbind("list", (SIGNAL_FUNC) cmd_list); + command_unbind("who", (SIGNAL_FUNC) cmd_who); + command_unbind("names", (SIGNAL_FUNC) cmd_names); + command_unbind("nick", (SIGNAL_FUNC) cmd_nick); + command_unbind("whois", (SIGNAL_FUNC) cmd_whois); + command_unbind("whowas", (SIGNAL_FUNC) cmd_whowas); + command_unbind("ping", (SIGNAL_FUNC) cmd_ping); + command_unbind("kill", (SIGNAL_FUNC) command_2self); + command_unbind("away", (SIGNAL_FUNC) cmd_away); + command_unbind("ison", (SIGNAL_FUNC) command_1self); + command_unbind("accept", (SIGNAL_FUNC) cmd_accept); + command_unbind("admin", (SIGNAL_FUNC) command_self); + command_unbind("info", (SIGNAL_FUNC) command_self); + command_unbind("knock", (SIGNAL_FUNC) command_self); + command_unbind("links", (SIGNAL_FUNC) command_self); + command_unbind("lusers", (SIGNAL_FUNC) command_self); + command_unbind("map", (SIGNAL_FUNC) command_self); + command_unbind("motd", (SIGNAL_FUNC) command_self); + command_unbind("rehash", (SIGNAL_FUNC) command_self); + command_unbind("stats", (SIGNAL_FUNC) command_self); + command_unbind("time", (SIGNAL_FUNC) command_self); + command_unbind("trace", (SIGNAL_FUNC) command_self); + command_unbind("version", (SIGNAL_FUNC) command_self); + command_unbind("servlist", (SIGNAL_FUNC) command_self); + command_unbind("silence", (SIGNAL_FUNC) command_self); + command_unbind("unsilence", (SIGNAL_FUNC) cmd_unsilence); + command_unbind("sconnect", (SIGNAL_FUNC) cmd_sconnect); + command_unbind("squery", (SIGNAL_FUNC) command_2self); + command_unbind("die", (SIGNAL_FUNC) command_self); + command_unbind("hash", (SIGNAL_FUNC) command_self); + command_unbind("oper", (SIGNAL_FUNC) cmd_oper); + command_unbind("restart", (SIGNAL_FUNC) command_self); + command_unbind("squit", (SIGNAL_FUNC) command_2self); + command_unbind("userhost", (SIGNAL_FUNC) command_self); + command_unbind("quote", (SIGNAL_FUNC) cmd_quote); + command_unbind("wall", (SIGNAL_FUNC) cmd_wall); + command_unbind("wait", (SIGNAL_FUNC) cmd_wait); + command_unbind("wallops", (SIGNAL_FUNC) command_1self); + command_unbind("kickban", (SIGNAL_FUNC) cmd_kickban); + command_unbind("setname", (SIGNAL_FUNC) command_1self); + command_unbind("knockout", (SIGNAL_FUNC) cmd_knockout); + command_unbind("server purge", (SIGNAL_FUNC) cmd_server_purge); + + signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); + signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_remove("whois try whowas", (SIGNAL_FUNC) sig_whois_try_whowas); + signal_remove("whois event", (SIGNAL_FUNC) event_whois); + signal_remove("whois end", (SIGNAL_FUNC) event_end_of_whois); + signal_remove("whowas event", (SIGNAL_FUNC) event_whowas); + + g_string_free(tmpstr, TRUE); +} |