aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/kike.c114
1 files changed, 100 insertions, 14 deletions
diff --git a/src/kike.c b/src/kike.c
index d3415e7..cdb20ca 100644
--- a/src/kike.c
+++ b/src/kike.c
@@ -292,20 +292,24 @@ client_free (struct client *self)
free (self->away_message);
}
+static void
+client_mode_to_str (unsigned m, struct str *out)
+{
+ if (m & IRC_USER_MODE_INVISIBLE) str_append_c (out, 'i');
+ if (m & IRC_USER_MODE_RX_WALLOPS) str_append_c (out, 'w');
+ if (m & IRC_USER_MODE_RESTRICTED) str_append_c (out, 'r');
+ if (m & IRC_USER_MODE_OPERATOR) str_append_c (out, 'o');
+ if (m & IRC_USER_MODE_RX_SERVER_NOTICES) str_append_c (out, 's');
+}
+
static char *
client_get_mode (struct client *self)
{
struct str mode;
str_init (&mode);
-
- if (self->away_message) str_append_c (&mode, 'a');
-
- unsigned m = self->mode;
- if (m & IRC_USER_MODE_INVISIBLE) str_append_c (&mode, 'i');
- if (m & IRC_USER_MODE_RX_WALLOPS) str_append_c (&mode, 'w');
- if (m & IRC_USER_MODE_RESTRICTED) str_append_c (&mode, 'r');
- if (m & IRC_USER_MODE_OPERATOR) str_append_c (&mode, 'o');
- if (m & IRC_USER_MODE_RX_SERVER_NOTICES) str_append_c (&mode, 's');
+ if (self->away_message)
+ str_append_c (&mode, 'a');
+ client_mode_to_str (self->mode, &mode);
return str_steal (&mode);
}
@@ -887,6 +891,7 @@ enum
IRC_ERR_NOPRIVILEGES = 481,
IRC_ERR_CHANOPRIVSNEEDED = 482,
+ IRC_ERR_UMODEUNKNOWNFLAG = 501,
IRC_ERR_USERSDONTMATCH = 502
};
@@ -963,6 +968,7 @@ static const char *g_default_replies[] =
[IRC_ERR_NOPRIVILEGES] = ":Permission Denied- You're not an IRC operator",
[IRC_ERR_CHANOPRIVSNEEDED] = "%s :You're not channel operator",
+ [IRC_ERR_UMODEUNKNOWNFLAG] = ":Unknown MODE flag",
[IRC_ERR_USERSDONTMATCH] = ":Cannot change mode for other users",
};
@@ -1296,6 +1302,83 @@ irc_maybe_send_channel_list (struct client *c, struct channel *chan,
}
static void
+irc_modify_mode (unsigned *mask, unsigned mode, bool add)
+{
+ if (add)
+ *mask |= mode;
+ else
+ *mask &= ~mode;
+}
+
+static void
+irc_update_user_mode (struct client *c, unsigned new_mode)
+{
+ unsigned old_mode = c->mode;
+ c->mode = new_mode;
+
+ unsigned added = new_mode & ~old_mode;
+ unsigned removed = old_mode & ~new_mode;
+
+ struct str diff;
+ str_init (&diff);
+
+ if (added)
+ {
+ str_append_c (&diff, '+');
+ client_mode_to_str (added, &diff);
+ }
+ if (removed)
+ {
+ str_append_c (&diff, '-');
+ client_mode_to_str (removed, &diff);
+ }
+
+ if (diff.len)
+ irc_send (c, ":%s MODE %s :%s",
+ c->nickname, c->nickname, diff.str);
+ str_free (&diff);
+}
+
+static void
+irc_handle_user_mode_change (struct client *c, const char *mode_string)
+{
+ unsigned new_mode = c->mode;
+ bool adding = true;
+
+ while (*mode_string)
+ switch (*mode_string++)
+ {
+ case '+': adding = true; break;
+ case '-': adding = false; break;
+
+ case 'a':
+ // Ignore, the client should use AWAY
+ break;
+ case 'i':
+ irc_modify_mode (&new_mode, IRC_USER_MODE_INVISIBLE, adding);
+ break;
+ case 'w':
+ irc_modify_mode (&new_mode, IRC_USER_MODE_RX_WALLOPS, adding);
+ break;
+ case 'r':
+ if (adding)
+ irc_modify_mode (&new_mode, IRC_USER_MODE_RESTRICTED, true);
+ break;
+ case 'o':
+ if (!adding)
+ irc_modify_mode (&new_mode, IRC_USER_MODE_OPERATOR, false);
+ // TODO: check public key fingerprint when adding
+ break;
+ case 's':
+ irc_modify_mode (&new_mode, IRC_USER_MODE_RX_SERVER_NOTICES, adding);
+ break;
+ default:
+ RETURN_WITH_REPLY (c, IRC_ERR_UMODEUNKNOWNFLAG);
+ }
+ irc_update_user_mode (c, new_mode);
+}
+
+static void
irc_handle_mode (const struct irc_message *msg, struct client *c)
{
if (msg->params.len < 1)
@@ -1308,11 +1391,14 @@ irc_handle_mode (const struct irc_message *msg, struct client *c)
if (irc_strcmp (target, c->nickname))
RETURN_WITH_REPLY (c, IRC_ERR_USERSDONTMATCH);
- char *mode = client_get_mode (client);
- irc_send_reply (c, IRC_RPL_UMODEIS, mode);
- free (mode);
-
- // TODO: mode modification
+ if (msg->params.len < 2)
+ {
+ char *mode = client_get_mode (client);
+ irc_send_reply (c, IRC_RPL_UMODEIS, mode);
+ free (mode);
+ }
+ else
+ irc_handle_user_mode_change (c, msg->params.vector[1]);
return;
}