summaryrefslogtreecommitdiff
path: root/src/irc/core/modes.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/irc/core/modes.c')
-rw-r--r--src/irc/core/modes.c451
1 files changed, 451 insertions, 0 deletions
diff --git a/src/irc/core/modes.c b/src/irc/core/modes.c
new file mode 100644
index 00000000..b73d5eb8
--- /dev/null
+++ b/src/irc/core/modes.c
@@ -0,0 +1,451 @@
+/*
+ modes.c : irssi
+
+ Copyright (C) 1999-2000 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "commands.h"
+#include "signals.h"
+
+#include "irc.h"
+#include "mode-lists.h"
+#include "nicklist.h"
+
+/* Change nick's mode in channel */
+static void nick_mode_change(CHANNEL_REC *channel, const char *nick, const char mode, gboolean set)
+{
+ NICK_REC *nickrec;
+
+ g_return_if_fail(channel != NULL);
+ g_return_if_fail(nick != NULL);
+
+ nickrec = nicklist_find(channel, nick);
+ if (nickrec == NULL) return; /* No /names list got yet */
+
+ if (mode == '@') nickrec->op = set;
+ if (mode == '+') nickrec->voice = set;
+
+ signal_emit("nick mode changed", 2, channel, nickrec);
+}
+
+/* Parse channel mode string */
+void parse_channel_modes(CHANNEL_REC *channel, const char *setby, const char *mode)
+{
+ char *dup, *modestr, *ptr, *curmode, type;
+
+ g_return_if_fail(channel != NULL);
+ g_return_if_fail(setby != NULL);
+ g_return_if_fail(modestr != NULL);
+
+ type = '+';
+
+ dup = modestr = g_strdup(mode);
+ curmode = cmd_get_param(&modestr);
+ while (*curmode != '\0') {
+ switch (*curmode) {
+ case '+':
+ case '-':
+ type = *curmode;
+ break;
+
+ case 'b':
+ ptr = cmd_get_param(&modestr);
+ if (*ptr == '\0') break;
+
+ if (type == '+')
+ banlist_add(channel, ptr, setby, time(NULL));
+ else
+ banlist_remove(channel, ptr);
+ break;
+
+ case 'e':
+ ptr = cmd_get_param(&modestr);
+ if (*ptr == '\0') break;
+
+ if (type == '+')
+ banlist_exception_add(channel, ptr, setby, time(NULL));
+ else
+ banlist_exception_remove(channel, ptr);
+ break;
+
+ case 'I':
+ ptr = cmd_get_param(&modestr);
+ if (*ptr == '\0') break;
+
+ if (type == '+')
+ invitelist_add(channel, ptr);
+ else
+ invitelist_remove(channel, ptr);
+ break;
+
+ case 'v':
+ ptr = cmd_get_param(&modestr);
+ if (*ptr != '\0')
+ nick_mode_change(channel, ptr, '+', type == '+');
+ break;
+
+ case 'o':
+ ptr = cmd_get_param(&modestr);
+ if (*ptr == '\0') break;
+
+ if (strcmp(channel->server->nick, ptr) == 0)
+ channel->chanop = type == '+' ? TRUE : FALSE;
+ nick_mode_change(channel, ptr, '@', type == '+');
+ break;
+
+ case 'l':
+ if (type == '-')
+ channel->limit = 0;
+ else {
+ ptr = cmd_get_param(&modestr);
+ sscanf(ptr, "%d", &channel->limit);
+ }
+ signal_emit("channel mode changed", 1, channel);
+ break;
+ case 'k':
+ ptr = cmd_get_param(&modestr);
+ if (*ptr != '\0' || type == '-') {
+ g_free_and_null(channel->key);
+ if (type == '+') {
+ channel->key = g_strdup(ptr);
+ channel->mode_key = TRUE;
+ }
+ }
+ signal_emit("channel mode changed", 1, channel);
+ break;
+
+ default:
+ switch (*curmode) {
+ case 'i':
+ channel->mode_invite = type == '+';
+ break;
+ case 'm':
+ channel->mode_moderate = type == '+';
+ break;
+ case 's':
+ channel->mode_secret = type == '+';
+ break;
+ case 'p':
+ channel->mode_private = type == '+';
+ break;
+ case 'n':
+ channel->mode_nomsgs = type == '+';
+ break;
+ case 't':
+ channel->mode_optopic = type == '+';
+ break;
+ case 'a':
+ channel->mode_anonymous = type == '+';
+ break;
+ case 'r':
+ channel->mode_reop = type == '+';
+ break;
+ }
+ signal_emit("channel mode changed", 1, channel);
+ break;
+ }
+
+ curmode++;
+ }
+ g_free(dup);
+
+ if (!channel->mode_key && channel->key != NULL) {
+ /* join was used with key but there's no key set
+ in channel modes.. */
+ g_free(channel->key);
+ channel->key = NULL;
+ }
+}
+
+static int compare_char(const void *p1, const void *p2)
+{
+ const char *c1 = p1, *c2 = p2;
+
+ return *c1 < *c2 ? -1 :
+ (*c1 > *c2 ? 1 : 0);
+}
+
+/* add `mode' to `old' - return newly allocated mode. */
+char *modes_join(const char *old, const char *mode)
+{
+ GString *newmode;
+ char type, *p;
+
+ g_return_val_if_fail(mode != NULL, NULL);
+
+ type = '+';
+ newmode = g_string_new(old);
+ while (*mode != '\0' && *mode != ' ') {
+ if (*mode == '+' || *mode == '-') {
+ type = *mode;
+ } else {
+ p = strchr(newmode->str, *mode);
+
+ if (type == '+' && p == NULL)
+ g_string_append_c(newmode, *mode);
+ else if (type == '-' && p != NULL)
+ g_string_erase(newmode, (int) (p-newmode->str), 1);
+ }
+
+ mode++;
+ }
+
+ qsort(newmode->str, sizeof(char), newmode->len, compare_char);
+
+ p = newmode->str;
+ g_string_free(newmode, FALSE);
+ return p;
+}
+
+/* Parse user mode string */
+static void parse_user_mode(IRC_SERVER_REC *server, const char *modestr)
+{
+ char *newmode, *oldmode;
+
+ g_return_if_fail(server != NULL);
+ g_return_if_fail(modestr != NULL);
+
+ newmode = modes_join(server->usermode, modestr);
+ oldmode = server->usermode;
+ server->usermode = newmode;
+ server->server_operator = (strchr(newmode, 'o') != NULL);
+
+ signal_emit("user mode changed", 2, server, oldmode);
+ g_free_not_null(oldmode);
+}
+
+static void event_user_mode(const char *data, IRC_SERVER_REC *server)
+{
+ char *params, *nick, *mode;
+
+ g_return_if_fail(data != NULL);
+
+ params = event_get_params(data, 3, NULL, &nick, &mode);
+ parse_user_mode(server, mode);
+
+ g_free(params);
+}
+
+static void event_mode(const char *data, IRC_SERVER_REC *server, const char *nick)
+{
+ CHANNEL_REC *chanrec;
+ char *params, *channel, *mode;
+
+ g_return_if_fail(data != NULL);
+
+ params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &channel, &mode);
+
+ if (!ischannel(*channel)) {
+ /* user mode change */
+ parse_user_mode(server, mode);
+ } else {
+ /* channel mode change */
+ chanrec = channel_find(server, channel);
+ if (chanrec != NULL)
+ parse_channel_modes(chanrec, nick, mode);
+ }
+
+ g_free(params);
+}
+
+static void event_away(const char *data, IRC_SERVER_REC *server)
+{
+ g_return_if_fail(server != NULL);
+
+ server->usermode_away = TRUE;
+ signal_emit("away mode changed", 1, server);
+}
+
+static void event_unaway(const char *data, IRC_SERVER_REC *server)
+{
+ g_return_if_fail(server != NULL);
+
+ server->usermode_away = FALSE;
+ g_free_and_null(server->away_reason);
+ signal_emit("away mode changed", 1, server);
+}
+
+void channel_set_singlemode(IRC_SERVER_REC *server, const char *channel, const char *nicks, const char *mode)
+{
+ GString *str;
+ int num, modepos;
+ char **nick, **nicklist;
+
+ g_return_if_fail(server != NULL);
+ g_return_if_fail(channel != NULL);
+ g_return_if_fail(nicks != NULL);
+ g_return_if_fail(mode != NULL);
+ if (*nicks == '\0') return;
+
+ num = modepos = 0;
+ str = g_string_new(NULL);
+
+ nicklist = g_strsplit(nicks, " ", -1);
+ for (nick = nicklist; *nick != NULL; nick++) {
+ if (num == 0)
+ {
+ g_string_sprintf(str, "MODE %s %s", channel, mode);
+ modepos = str->len;
+ } else {
+ /* insert the mode string */
+ g_string_insert(str, modepos, mode);
+ }
+
+ g_string_sprintfa(str, " %s", *nick);
+
+ if (++num == server->connrec->max_modes) {
+ /* max. modes / command reached, send to server */
+ irc_send_cmd(server, str->str);
+ num = 0;
+ }
+ }
+ if (num > 0) irc_send_cmd(server, str->str);
+
+ g_strfreev(nicklist);
+ g_string_free(str, TRUE);
+}
+
+#define MODE_HAS_ARG(c) ((c) == 'b' || (c) == 'e' || (c) == 'I' || \
+ (c) == 'v' || (c) == 'o' || (c) == 'l' || (c) == 'k')
+
+void channel_set_mode(IRC_SERVER_REC *server, const char *channel, const char *mode)
+{
+ char *modestr, *curmode, type, *orig;
+ GString *tmode, *targs;
+ int count;
+
+ g_return_if_fail(server != NULL);
+ g_return_if_fail(channel != NULL);
+ g_return_if_fail(modestr != NULL);
+
+ tmode = g_string_new(NULL);
+ targs = g_string_new(NULL);
+ type = '+'; count = 0;
+
+ orig = modestr = g_strdup(mode);
+
+ curmode = cmd_get_param(&modestr);
+ for (; *curmode != '\0'; curmode++) {
+ if (*curmode == '+' || *curmode == '-') {
+ type = *curmode;
+ continue;
+ }
+
+ if (count == server->connrec->max_modes && MODE_HAS_ARG(*curmode)) {
+ irc_send_cmdv(server, "MODE %s %s%s", channel, tmode->str, targs->str);
+
+ count = 0;
+ g_string_truncate(tmode, 0);
+ g_string_truncate(targs, 0);
+ }
+
+ g_string_append_c(tmode, *curmode);
+
+ if (MODE_HAS_ARG(*curmode)) {
+ char *arg;
+
+ count++;
+ arg = cmd_get_param(&modestr);
+ if (*arg != '\0') g_string_sprintfa(targs, " %s", arg);
+ }
+ }
+
+ if (tmode->len > 0)
+ irc_send_cmdv(server, "MODE %s %s%s", channel, tmode->str, targs->str);
+
+ g_string_free(tmode, TRUE);
+ g_string_free(targs, TRUE);
+ g_free(orig);
+}
+
+static void cmd_op(gchar *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
+{
+ if (!irc_item_channel(item)) return;
+ channel_set_singlemode(server, item->name, data, "+o");
+}
+
+static void cmd_deop(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
+{
+ if (!irc_item_channel(item)) return;
+ channel_set_singlemode(server, item->name, data, "-o");
+}
+
+static void cmd_voice(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
+{
+ if (!irc_item_channel(item)) return;
+ channel_set_singlemode(server, item->name, data, "+v");
+}
+
+static void cmd_devoice(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
+{
+ if (!irc_item_channel(item)) return;
+ channel_set_singlemode(server, item->name, data, "-v");
+}
+
+static void cmd_mode(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
+{
+ char *params, *target, *mode;
+
+ g_return_if_fail(data != NULL);
+ if (server == NULL || !server->connected || !irc_server_check(server))
+ cmd_return_error(CMDERR_NOT_CONNECTED);
+
+ params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &mode);
+ if (strcmp(target, "*") == 0) {
+ if (!irc_item_channel(item))
+ cmd_return_error(CMDERR_NOT_JOINED);
+
+ target = item->name;
+ }
+ if (*target == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+ if (ischannel(*target))
+ channel_set_mode(server, target, mode);
+ else
+ irc_send_cmdv(server, "MODE %s %s", target, mode);
+
+ g_free(params);
+}
+
+void modes_init(void)
+{
+ signal_add("event 221", (SIGNAL_FUNC) event_user_mode);
+ signal_add("event 305", (SIGNAL_FUNC) event_unaway);
+ signal_add("event 306", (SIGNAL_FUNC) event_away);
+ signal_add("event mode", (SIGNAL_FUNC) event_mode);
+
+ command_bind("op", NULL, (SIGNAL_FUNC) cmd_op);
+ command_bind("deop", NULL, (SIGNAL_FUNC) cmd_deop);
+ command_bind("voice", NULL, (SIGNAL_FUNC) cmd_voice);
+ command_bind("devoice", NULL, (SIGNAL_FUNC) cmd_devoice);
+ command_bind("mode", NULL, (SIGNAL_FUNC) cmd_mode);
+}
+
+void modes_deinit(void)
+{
+ signal_remove("event 221", (SIGNAL_FUNC) event_user_mode);
+ signal_remove("event 305", (SIGNAL_FUNC) event_unaway);
+ signal_remove("event 306", (SIGNAL_FUNC) event_away);
+ signal_remove("event mode", (SIGNAL_FUNC) event_mode);
+
+ command_unbind("op", (SIGNAL_FUNC) cmd_op);
+ command_unbind("deop", (SIGNAL_FUNC) cmd_deop);
+ command_unbind("voice", (SIGNAL_FUNC) cmd_voice);
+ command_unbind("devoice", (SIGNAL_FUNC) cmd_devoice);
+ command_unbind("mode", (SIGNAL_FUNC) cmd_mode);
+}