diff --git a/degesch.c b/degesch.c index cbcec56..0baac98 100644 --- a/degesch.c +++ b/degesch.c @@ -3826,8 +3826,6 @@ irc_is_highlight (struct server *s, const char *message) // --- Input handling ---------------------------------------------------------- -// TODO: we will need a proper mode parser; to be shared with kike - static void irc_handle_join (struct server *s, const struct irc_message *msg) { @@ -3932,6 +3930,170 @@ irc_handle_kick (struct server *s, const struct irc_message *msg) } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct mode_processor +{ + // Inputs to set after initialization: + + char **params; ///< Mode string parameters + + struct server *s; ///< Who does the changes + struct channel *channel; ///< The channel we're modifying + + // Internals: + + bool adding; ///< Currently adding modes + char mode_char; ///< Currently processed mode char +}; + +static void +mode_processor_init (struct mode_processor *self) +{ + memset (self, 0, sizeof *self); +} + +static const char * +mode_processor_next_param (struct mode_processor *self) +{ + if (!*self->params) + return NULL; + return *self->params++; +} + +static void +mode_processor_do_user (struct mode_processor *self) +{ + const char *nickname; + struct user *user; + if (!(nickname = mode_processor_next_param (self)) + || !(user = str_map_find (&self->s->irc_users, nickname))) + return; + + // TODO: factor out, also use in unlink_user or whatever + struct channel_user *channel_user = NULL; + LIST_FOR_EACH (struct channel_user, iter, self->channel->users) + if (iter->user == user) + channel_user = iter; + if (!channel_user) + return; + + char prefix = self->s->irc_chanuser_prefixes + [strchr (self->s->irc_chanuser_modes, self->mode_char) + - self->s->irc_chanuser_modes]; + + // XXX: shouldn't this rather be a "struct str"? + char *modes = channel_user->modes; + char *pos = strchr (modes, self->mode_char); + if (self->adding == !!pos) + return; + + if (self->adding) + { + // FIXME: this doesn't give two fucks about the correct order + channel_user->modes = xstrdup_printf ("%s%c", modes, prefix); + free (modes); + } + else + memmove (pos, pos + 1, strlen (pos + 1)); +} + +static void +mode_processor_do_param_always (struct mode_processor *self) +{ + const char *param = NULL; + if (!(param = mode_processor_next_param (self))) + return; + + char key[2] = { self->mode_char, 0 }; + if (self->adding) + str_map_set (&self->channel->param_modes, key, xstrdup (param)); + else + str_map_set (&self->channel->param_modes, key, NULL); +} + +static void +mode_processor_do_param_when_set (struct mode_processor *self) +{ + const char *param = NULL; + if (self->adding && !(param = mode_processor_next_param (self))) + return; + + char key[2] = { self->mode_char, 0 }; + if (self->adding) + str_map_set (&self->channel->param_modes, key, xstrdup (param)); + else + str_map_set (&self->channel->param_modes, key, NULL); +} + +static void +mode_processor_do_param_never (struct mode_processor *self) +{ + struct str *modes = &self->channel->no_param_modes; + const char *pos = strchr (modes->str, self->mode_char); + if (self->adding == !!pos) + return; + + if (self->adding) + { + str_append_c (modes, self->mode_char); + // TODO: sort the modes + } + else + str_remove_slice (modes, pos - modes->str, 1); +} + +static bool +mode_processor_step (struct mode_processor *self, char mode_char) +{ + struct server *s = self->s; + self->mode_char = mode_char; + + if (mode_char == '+') self->adding = true; + else if (mode_char == '-') self->adding = false; + + else if (strchr (s->irc_chanuser_modes, mode_char)) + mode_processor_do_user (self); + + else if (strchr (s->irc_chanmodes_list, mode_char)) + // Nothing to do here, really + (void) mode_processor_next_param (self); + + else if (strchr (s->irc_chanmodes_param_always, mode_char)) + mode_processor_do_param_always (self); + else if (strchr (s->irc_chanmodes_param_when_set, mode_char)) + mode_processor_do_param_when_set (self); + else if (strchr (s->irc_chanmodes_param_never, mode_char)) + mode_processor_do_param_never (self); + else + // It's not safe to continue, results could be undesired + return false; + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static void +irc_handle_mode_channel + (struct server *s, struct channel *channel, char **params) +{ + struct mode_processor p; + mode_processor_init (&p); + + p.params = params; + p.s = s; + p.channel = channel; + + const char *mode_string; + while ((mode_string = mode_processor_next_param (&p))) + { + mode_processor_step (&p, '+'); + while (*mode_string) + if (!mode_processor_step (&p, *mode_string++)) + break; + } +} + static void irc_handle_mode (struct server *s, const struct irc_message *msg) { @@ -3950,7 +4112,7 @@ irc_handle_mode (struct server *s, const struct irc_message *msg) char *modes = irc_to_utf8 (s->ctx, reconstructed); free (reconstructed); - // TODO: parse the mode change and apply it + // TODO: parse the mode change and apply it (our user & channel user modes) if (irc_is_channel (s, context)) { @@ -3959,6 +4121,9 @@ irc_handle_mode (struct server *s, const struct irc_message *msg) hard_assert ((channel && buffer) || (channel && !buffer) || (!channel && !buffer)); + if (channel) + irc_handle_mode_channel (s, channel, msg->params.vector + 1); + // FIXME: logging if (buffer) {