summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--degesch.c171
1 files changed, 168 insertions, 3 deletions
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)
{