diff options
Diffstat (limited to 'src/irc/core/modes.c')
-rw-r--r-- | src/irc/core/modes.c | 320 |
1 files changed, 216 insertions, 104 deletions
diff --git a/src/irc/core/modes.c b/src/irc/core/modes.c index 199aa5fe..c3331dfe 100644 --- a/src/irc/core/modes.c +++ b/src/irc/core/modes.c @@ -28,7 +28,8 @@ #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) +static void nick_mode_change(CHANNEL_REC *channel, const char *nick, + const char mode, int type) { NICK_REC *nickrec; @@ -38,26 +39,173 @@ static void nick_mode_change(CHANNEL_REC *channel, const char *nick, const char nickrec = nicklist_find(channel, nick); if (nickrec == NULL) return; /* No /names list got yet */ - if (mode == '@') nickrec->op = set; - if (mode == '+') nickrec->voice = set; + if (mode == '@') nickrec->op = type == '+'; + if (mode == '+') nickrec->voice = type == '+'; + if (mode == '%') nickrec->halfop = type == '+'; signal_emit("nick mode changed", 2, channel, nickrec); } +static int mode_is_set(const char *str, char mode) +{ + char *end, *pos; + + g_return_val_if_fail(str != NULL, FALSE); + + end = strchr(str, ' '); + pos = strchr(str, mode); + return pos != NULL && (end == NULL || pos < end); +} + +/* add argument to specified position */ +static void mode_add_arg(GString *str, int pos, const char *arg) +{ + char *p; + + for (p = str->str; *p != '\0'; p++) { + if (*p != ' ') + continue; + + if (pos == 0) + break; + pos--; + } + + /* .. GLib shouldn't fail when inserting at the end of the string */ + if (*p == '\0') { + g_string_append_c(str, ' '); + g_string_append(str, arg); + } else { + pos = (int) (p-str->str); + g_string_insert_c(str, pos, ' '); + g_string_insert(str, pos+1, arg); + } +} + +/* Add mode character to list sorted alphabetically */ +static void mode_add_sorted(GString *str, char mode, const char *arg) +{ + char *p; + int argpos = 0; + + /* check that mode isn't already set */ + if (mode_is_set(str->str, mode)) + return; + + for (p = str->str; *p != '\0' && *p != ' '; p++) { + if (mode < *p) + break; + if (HAS_MODE_ARG_SET(*p)) + argpos++; + } + + /* .. GLib shouldn't fail when inserting at the end of the string */ + if (*p == '\0') + g_string_append_c(str, mode); + else + g_string_insert_c(str, (int) (p-str->str), mode); + if (arg != NULL) + mode_add_arg(str, argpos+1, arg); +} + +/* remove the n'th argument */ +static void node_remove_arg(GString *str, int pos) +{ + char *p; + int startpos; + + startpos = -1; + for (p = str->str; *p != '\0'; p++) { + if (*p != ' ') + continue; + + if (pos < 0) + break; + if (pos == 0) + startpos = (int) (p-str->str); + pos--; + } + + if (startpos == -1) + return; /* not found */ + + g_string_erase(str, startpos, (int) (p-str->str)-startpos); +} + +/* remove mode (and it's argument) from string */ +static void mode_remove(GString *str, char mode) +{ + char *p; + int argpos = 0; + + for (p = str->str; *p != '\0' && *p != ' '; p++) { + if (mode == *p) { + g_string_erase(str, (int) (p-str->str), 1); + if (HAS_MODE_ARG_SET(mode)) + node_remove_arg(str, argpos); + break; + } + if (HAS_MODE_ARG_SET(*p)) + argpos++; + } +} + +static void mode_set(GString *str, char type, char mode) +{ + g_return_if_fail(str != NULL); + + if (type == '-') + mode_remove(str, mode); + else + mode_add_sorted(str, mode, NULL); +} + +static void mode_set_arg(GString *str, char type, char mode, const char *arg) +{ + g_return_if_fail(str != NULL); + g_return_if_fail(type == '-' || arg != NULL); + + if (type == '-') + mode_remove(str, mode); + else + mode_add_sorted(str, mode, arg); +} + +int channel_mode_is_set(CHANNEL_REC *channel, char mode) +{ + g_return_val_if_fail(channel != NULL, FALSE); + + return channel->mode == NULL ? FALSE : + mode_is_set(channel->mode, mode); +} + /* Parse channel mode string */ -void parse_channel_modes(CHANNEL_REC *channel, const char *setby, const char *mode) +void parse_channel_modes(CHANNEL_REC *channel, const char *setby, + const char *mode) { - char *dup, *modestr, *ptr, *curmode, type; + GString *newmode; + char *dup, *modestr, *arg, *curmode, type; g_return_if_fail(channel != NULL); - g_return_if_fail(setby != NULL); g_return_if_fail(mode != NULL); type = '+'; + newmode = g_string_new(channel->mode); dup = modestr = g_strdup(mode); curmode = cmd_get_param(&modestr); while (*curmode != '\0') { + if (HAS_MODE_ARG(type, *curmode)) { + /* get the argument for the mode. since we're + expecting argument, ignore the mode if there's + no argument (shouldn't happen). */ + arg = cmd_get_param(&modestr); + if (*arg == '\0') + continue; + } else { + arg = NULL; + } + switch (*curmode) { case '+': case '-': @@ -65,98 +213,50 @@ void parse_channel_modes(CHANNEL_REC *channel, const char *setby, const char *mo break; case 'b': - ptr = cmd_get_param(&modestr); - if (*ptr == '\0') break; - if (type == '+') - banlist_add(channel, ptr, setby, time(NULL)); + banlist_add(channel, arg, setby, time(NULL)); else - banlist_remove(channel, ptr); + banlist_remove(channel, arg); break; - case 'e': - ptr = cmd_get_param(&modestr); - if (*ptr == '\0') break; - if (type == '+') - banlist_exception_add(channel, ptr, setby, time(NULL)); + banlist_exception_add(channel, arg, setby, + time(NULL)); else - banlist_exception_remove(channel, ptr); + banlist_exception_remove(channel, arg); break; - case 'I': - ptr = cmd_get_param(&modestr); - if (*ptr == '\0') break; - if (type == '+') - invitelist_add(channel, ptr); + invitelist_add(channel, arg); else - invitelist_remove(channel, ptr); - break; - - case 'v': - ptr = cmd_get_param(&modestr); - if (*ptr != '\0') - nick_mode_change(channel, ptr, '+', type == '+'); + invitelist_remove(channel, arg); break; case 'o': - ptr = cmd_get_param(&modestr); - if (*ptr == '\0') break; - - if (g_strcasecmp(channel->server->nick, ptr) == 0) - channel->chanop = type == '+' ? TRUE : FALSE; - nick_mode_change(channel, ptr, '@', type == '+'); + if (g_strcasecmp(channel->server->nick, arg) == 0) + channel->chanop = type == '+'; + nick_mode_change(channel, arg, '@', type); + break; + case 'h': + nick_mode_change(channel, arg, '%', type); + break; + case 'v': + nick_mode_change(channel, arg, '+', 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); + mode_set_arg(newmode, type, 'l', arg); + channel->limit = type == '-' ? 0 : atoi(arg); break; case 'k': - ptr = cmd_get_param(&modestr); - if (*ptr != '\0' || type == '-') { - g_free_and_null(channel->key); - channel->mode_key = type == '+'; - if (type == '+') - channel->key = g_strdup(ptr); - } - signal_emit("channel mode changed", 1, channel); + mode_set_arg(newmode, type, 'k', arg); + g_free_and_null(channel->key); + if (type == '+') + channel->key = g_strdup(arg); 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); + mode_set(newmode, type, *curmode); break; } @@ -164,52 +264,57 @@ void parse_channel_modes(CHANNEL_REC *channel, const char *setby, const char *mo } g_free(dup); - if (!channel->mode_key && channel->key != NULL) { + if (strchr(channel->mode, 'k') == NULL && 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; + if (strcmp(newmode->str, channel->mode) != 0) { + g_free(channel->mode); + channel->mode = g_strdup(newmode->str); - return *c1 < *c2 ? -1 : - (*c1 > *c2 ? 1 : 0); + signal_emit("channel mode changed", 1, channel); + } + + g_string_free(newmode, TRUE); } /* add `mode' to `old' - return newly allocated mode. */ char *modes_join(const char *old, const char *mode) { GString *newmode; - char type, *p; + char *dup, *modestr, *curmode, type; 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); + dup = modestr = g_strdup(mode); + curmode = cmd_get_param(&modestr); + while (*curmode != '\0' && *curmode != ' ') { + if (*curmode == '+' || *curmode == '-') { + type = *curmode; + curmode++; + continue; } - mode++; - } + if (!HAS_MODE_ARG(type, *curmode)) + mode_set(newmode, type, *curmode); + else { + mode_set_arg(newmode, type, *curmode, + cmd_get_param(&modestr)); + } - qsort(newmode->str, sizeof(char), newmode->len, compare_char); + curmode++; + } + g_free(dup); - p = newmode->str; + modestr = newmode->str; g_string_free(newmode, FALSE); - return p; + return modestr; } /* Parse user mode string */ @@ -280,7 +385,8 @@ static void event_unaway(const char *data, IRC_SERVER_REC *server) signal_emit("away mode changed", 1, server); } -void channel_set_singlemode(IRC_SERVER_REC *server, const char *channel, const char *nicks, const char *mode) +void channel_set_singlemode(IRC_SERVER_REC *server, const char *channel, + const char *nicks, const char *mode) { GString *str; int num, modepos; @@ -323,9 +429,10 @@ void channel_set_singlemode(IRC_SERVER_REC *server, const char *channel, const c g_string_free(str, TRUE); } -void channel_set_mode(IRC_SERVER_REC *server, const char *channel, const char *mode) +void channel_set_mode(IRC_SERVER_REC *server, const char *channel, + const char *mode) { - char *modestr, *curmode, *orig; + char *modestr, *curmode, *orig, type; GString *tmode, *targs; int count; @@ -339,9 +446,14 @@ void channel_set_mode(IRC_SERVER_REC *server, const char *channel, const char *m orig = modestr = g_strdup(mode); + type = '+'; curmode = cmd_get_param(&modestr); for (; *curmode != '\0'; curmode++) { - if (count == server->max_modes_in_cmd && HAS_MODE_ARG(*curmode)) { + if (*curmode == '+' || *curmode == '-') + type = *curmode; + + if (count == server->max_modes_in_cmd && + HAS_MODE_ARG(type, *curmode)) { irc_send_cmdv(server, "MODE %s %s%s", channel, tmode->str, targs->str); count = 0; @@ -351,7 +463,7 @@ void channel_set_mode(IRC_SERVER_REC *server, const char *channel, const char *m g_string_append_c(tmode, *curmode); - if (HAS_MODE_ARG(*curmode)) { + if (HAS_MODE_ARG(type, *curmode)) { char *arg; count++; |