kike: hackplement channel MODE changes
This commit is contained in:
parent
8645f12f9f
commit
9d86d81851
318
src/kike.c
318
src/kike.c
|
@ -224,6 +224,12 @@ irc_is_valid_key (const char *key)
|
||||||
#undef LE
|
#undef LE
|
||||||
#undef SP
|
#undef SP
|
||||||
|
|
||||||
|
static bool
|
||||||
|
irc_is_valid_user_mask (const char *mask)
|
||||||
|
{
|
||||||
|
return irc_regex_match ("^[^!@]+![^!@]+@[^@!]+$", mask);
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
irc_is_valid_fingerprint (const char *fp)
|
irc_is_valid_fingerprint (const char *fp)
|
||||||
{
|
{
|
||||||
|
@ -934,7 +940,9 @@ enum
|
||||||
IRC_ERR_NOTREGISTERED = 451,
|
IRC_ERR_NOTREGISTERED = 451,
|
||||||
IRC_ERR_NEEDMOREPARAMS = 461,
|
IRC_ERR_NEEDMOREPARAMS = 461,
|
||||||
IRC_ERR_ALREADYREGISTERED = 462,
|
IRC_ERR_ALREADYREGISTERED = 462,
|
||||||
|
IRC_ERR_KEYSET = 467,
|
||||||
IRC_ERR_CHANNELISFULL = 471,
|
IRC_ERR_CHANNELISFULL = 471,
|
||||||
|
IRC_ERR_UNKNOWNMODE = 472,
|
||||||
IRC_ERR_INVITEONLYCHAN = 473,
|
IRC_ERR_INVITEONLYCHAN = 473,
|
||||||
IRC_ERR_BANNEDFROMCHAN = 474,
|
IRC_ERR_BANNEDFROMCHAN = 474,
|
||||||
IRC_ERR_BADCHANNELKEY = 475,
|
IRC_ERR_BADCHANNELKEY = 475,
|
||||||
|
@ -1012,7 +1020,9 @@ static const char *g_default_replies[] =
|
||||||
[IRC_ERR_NOTREGISTERED] = ":You have not registered",
|
[IRC_ERR_NOTREGISTERED] = ":You have not registered",
|
||||||
[IRC_ERR_NEEDMOREPARAMS] = "%s :Not enough parameters",
|
[IRC_ERR_NEEDMOREPARAMS] = "%s :Not enough parameters",
|
||||||
[IRC_ERR_ALREADYREGISTERED] = ":Unauthorized command (already registered)",
|
[IRC_ERR_ALREADYREGISTERED] = ":Unauthorized command (already registered)",
|
||||||
|
[IRC_ERR_KEYSET] = "%s :Channel key already set",
|
||||||
[IRC_ERR_CHANNELISFULL] = "%s :Cannot join channel (+l)",
|
[IRC_ERR_CHANNELISFULL] = "%s :Cannot join channel (+l)",
|
||||||
|
[IRC_ERR_UNKNOWNMODE] = "%c :is unknown mode char to me for %s",
|
||||||
[IRC_ERR_INVITEONLYCHAN] = "%s :Cannot join channel (+i)",
|
[IRC_ERR_INVITEONLYCHAN] = "%s :Cannot join channel (+i)",
|
||||||
[IRC_ERR_BANNEDFROMCHAN] = "%s :Cannot join channel (+b)",
|
[IRC_ERR_BANNEDFROMCHAN] = "%s :Cannot join channel (+b)",
|
||||||
[IRC_ERR_BADCHANNELKEY] = "%s :Cannot join channel (+k)",
|
[IRC_ERR_BADCHANNELKEY] = "%s :Cannot join channel (+k)",
|
||||||
|
@ -1329,43 +1339,15 @@ irc_channel_multicast (struct channel *chan, const char *message,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
irc_send_channel_list (struct client *c, const char *channel_name,
|
|
||||||
const struct str_vector *list, int reply, int end_reply)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < list->len; i++)
|
|
||||||
irc_send_reply (c, reply, channel_name, list->vector[i]);
|
|
||||||
irc_send_reply (c, end_reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
irc_maybe_send_channel_list (struct client *c, struct channel *chan,
|
|
||||||
const char *mode_string)
|
|
||||||
{
|
|
||||||
if (*mode_string == '+')
|
|
||||||
mode_string++;
|
|
||||||
|
|
||||||
if (!strcmp (mode_string, "b"))
|
|
||||||
irc_send_channel_list (c, chan->name, &chan->ban_list,
|
|
||||||
IRC_RPL_BANLIST, IRC_RPL_ENDOFBANLIST);
|
|
||||||
else if (!strcmp (mode_string, "e"))
|
|
||||||
irc_send_channel_list (c, chan->name, &chan->exception_list,
|
|
||||||
IRC_RPL_EXCEPTLIST, IRC_RPL_ENDOFEXCEPTLIST);
|
|
||||||
else if (!strcmp (mode_string, "I"))
|
|
||||||
irc_send_channel_list (c, chan->name, &chan->invite_list,
|
|
||||||
IRC_RPL_INVITELIST, IRC_RPL_ENDOFINVITELIST);
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
irc_modify_mode (unsigned *mask, unsigned mode, bool add)
|
irc_modify_mode (unsigned *mask, unsigned mode, bool add)
|
||||||
{
|
{
|
||||||
|
unsigned orig = *mask;
|
||||||
if (add)
|
if (add)
|
||||||
*mask |= mode;
|
*mask |= mode;
|
||||||
else
|
else
|
||||||
*mask &= ~mode;
|
*mask &= ~mode;
|
||||||
|
return *mask != orig;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1419,15 +1401,16 @@ irc_handle_user_mode_change (struct client *c, const char *mode_string)
|
||||||
irc_modify_mode (&new_mode, IRC_USER_MODE_RX_WALLOPS, adding);
|
irc_modify_mode (&new_mode, IRC_USER_MODE_RX_WALLOPS, adding);
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
|
// It's not possible to un-restrict yourself
|
||||||
if (adding)
|
if (adding)
|
||||||
irc_modify_mode (&new_mode, IRC_USER_MODE_RESTRICTED, true);
|
new_mode |= IRC_USER_MODE_RESTRICTED;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
if (!adding)
|
if (!adding)
|
||||||
irc_modify_mode (&new_mode, IRC_USER_MODE_OPERATOR, false);
|
new_mode &= ~IRC_USER_MODE_OPERATOR;
|
||||||
else if (c->ssl_cert_fingerprint
|
else if (c->ssl_cert_fingerprint
|
||||||
&& str_map_find (&c->ctx->operators, c->ssl_cert_fingerprint))
|
&& str_map_find (&c->ctx->operators, c->ssl_cert_fingerprint))
|
||||||
irc_modify_mode (&new_mode, IRC_USER_MODE_OPERATOR, true);
|
new_mode |= IRC_USER_MODE_OPERATOR;
|
||||||
else
|
else
|
||||||
irc_send (c, ":%s NOTICE %s :Either you're not using an SSL"
|
irc_send (c, ":%s NOTICE %s :Either you're not using an SSL"
|
||||||
" client certificate, or the fingerprint doesn't match",
|
" client certificate, or the fingerprint doesn't match",
|
||||||
|
@ -1442,6 +1425,267 @@ irc_handle_user_mode_change (struct client *c, const char *mode_string)
|
||||||
irc_update_user_mode (c, new_mode);
|
irc_update_user_mode (c, new_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
irc_send_channel_list (struct client *c, const char *channel_name,
|
||||||
|
const struct str_vector *list, int reply, int end_reply)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < list->len; i++)
|
||||||
|
irc_send_reply (c, reply, channel_name, list->vector[i]);
|
||||||
|
irc_send_reply (c, end_reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
irc_check_expand_user_mask (const char *mask)
|
||||||
|
{
|
||||||
|
struct str result;
|
||||||
|
str_init (&result);
|
||||||
|
str_append (&result, mask);
|
||||||
|
|
||||||
|
// Make sure it is a complete mask
|
||||||
|
if (!strchr (result.str, '!'))
|
||||||
|
str_append (&result, "!*");
|
||||||
|
if (!strchr (result.str, '@'))
|
||||||
|
str_append (&result, "@*");
|
||||||
|
|
||||||
|
// And validate whatever the result is
|
||||||
|
if (!irc_is_valid_user_mask (result.str))
|
||||||
|
{
|
||||||
|
str_free (&result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return str_steal (&result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
irc_handle_chan_mode_change (struct client *c,
|
||||||
|
struct channel *chan, char *params[])
|
||||||
|
{
|
||||||
|
struct channel_user *user = channel_get_user (chan, c);
|
||||||
|
|
||||||
|
// This is by far the worst command to implement from the whole RFC;
|
||||||
|
// don't blame me if it doesn't work exactly as expected.
|
||||||
|
|
||||||
|
struct str added; struct str_vector added_params;
|
||||||
|
struct str removed; struct str_vector removed_params;
|
||||||
|
|
||||||
|
str_init (&added); str_vector_init (&added_params);
|
||||||
|
str_init (&removed); str_vector_init (&removed_params);
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
// TODO: try to convert this madness into functions; that will most
|
||||||
|
// likely require creating a special parser class
|
||||||
|
#define NEEDS_OPER \
|
||||||
|
if (!user || (!(user->modes & IRC_CHAN_MODE_OPERATOR) \
|
||||||
|
&& !(c->mode & IRC_USER_MODE_OPERATOR))) \
|
||||||
|
{ \
|
||||||
|
irc_send_reply (c, IRC_ERR_CHANOPRIVSNEEDED, chan->name); \
|
||||||
|
continue; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HANDLE_USER(mode) \
|
||||||
|
if (!(target = *params)) \
|
||||||
|
continue; \
|
||||||
|
params++; \
|
||||||
|
NEEDS_OPER \
|
||||||
|
if (!(client = str_map_find (&c->ctx->users, target))) \
|
||||||
|
irc_send_reply (c, IRC_ERR_NOSUCHNICK, target); \
|
||||||
|
else if (!(target_user = channel_get_user (chan, client))) \
|
||||||
|
irc_send_reply (c, IRC_ERR_USERNOTINCHANNEL, \
|
||||||
|
target, chan->name); \
|
||||||
|
else if (irc_modify_mode (&target_user->modes, (mode), adding)) \
|
||||||
|
{ \
|
||||||
|
str_append_c (output, mode_char); \
|
||||||
|
str_vector_add (output_params, target_user->nickname); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HANDLE_LIST(list, list_msg, end_msg) \
|
||||||
|
{ \
|
||||||
|
if (!(target = *params)) \
|
||||||
|
{ \
|
||||||
|
if (adding) \
|
||||||
|
irc_send_channel_list (c, chan->name, list, list_msg, end_msg); \
|
||||||
|
continue; \
|
||||||
|
} \
|
||||||
|
params++; \
|
||||||
|
NEEDS_OPER \
|
||||||
|
char *mask = irc_check_expand_user_mask (target); \
|
||||||
|
if (!mask) \
|
||||||
|
continue; \
|
||||||
|
size_t i; \
|
||||||
|
for (i = 0; i < (list)->len; i++) \
|
||||||
|
if (!irc_strcmp ((list)->vector[i], mask)) \
|
||||||
|
break; \
|
||||||
|
if (!((i != (list)->len) ^ adding)) \
|
||||||
|
{ \
|
||||||
|
free (mask); \
|
||||||
|
continue; \
|
||||||
|
} \
|
||||||
|
if (adding) \
|
||||||
|
str_vector_add ((list), mask); \
|
||||||
|
else \
|
||||||
|
str_vector_remove ((list), i); \
|
||||||
|
str_append_c (output, mode_char); \
|
||||||
|
str_vector_add (output_params, mask); \
|
||||||
|
free (mask); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HANDLE_MODE(mode) \
|
||||||
|
NEEDS_OPER \
|
||||||
|
if (irc_modify_mode (&chan->modes, (mode), adding)) \
|
||||||
|
str_append_c (output, mode_char);
|
||||||
|
|
||||||
|
#define REMOVE_MODE(removed_mode, removed_char) \
|
||||||
|
if (adding && irc_modify_mode (&chan->modes, (removed_mode), false)) \
|
||||||
|
str_append_c (&removed, (removed_char));
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
const char *mode_string;
|
||||||
|
while ((mode_string = *params++))
|
||||||
|
{
|
||||||
|
bool adding = true;
|
||||||
|
struct str *output = &added;
|
||||||
|
struct str_vector *output_params = &added_params;
|
||||||
|
|
||||||
|
const char *target;
|
||||||
|
struct channel_user *target_user;
|
||||||
|
struct client *client;
|
||||||
|
|
||||||
|
char mode_char;
|
||||||
|
while (*mode_string)
|
||||||
|
switch ((mode_char = *mode_string++))
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
adding = true;
|
||||||
|
output = &added;
|
||||||
|
output_params = &added_params;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
adding = false;
|
||||||
|
output = &removed;
|
||||||
|
output_params = &removed_params;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'o': HANDLE_USER (IRC_CHAN_MODE_OPERATOR) break;
|
||||||
|
case 'v': HANDLE_USER (IRC_CHAN_MODE_VOICE) break;
|
||||||
|
|
||||||
|
case 'i': HANDLE_MODE (IRC_CHAN_MODE_INVITE_ONLY) break;
|
||||||
|
case 'm': HANDLE_MODE (IRC_CHAN_MODE_MODERATED) break;
|
||||||
|
case 'n': HANDLE_MODE (IRC_CHAN_MODE_NO_OUTSIDE_MSGS) break;
|
||||||
|
case 'q': HANDLE_MODE (IRC_CHAN_MODE_QUIET) break;
|
||||||
|
case 't': HANDLE_MODE (IRC_CHAN_MODE_PROTECTED_TOPIC) break;
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
HANDLE_MODE (IRC_CHAN_MODE_PRIVATE)
|
||||||
|
REMOVE_MODE (IRC_CHAN_MODE_SECRET, 's')
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
HANDLE_MODE (IRC_CHAN_MODE_SECRET)
|
||||||
|
REMOVE_MODE (IRC_CHAN_MODE_PRIVATE, 'p')
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
HANDLE_LIST (&chan->ban_list,
|
||||||
|
IRC_RPL_BANLIST, IRC_RPL_ENDOFBANLIST)
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
HANDLE_LIST (&chan->exception_list,
|
||||||
|
IRC_RPL_EXCEPTLIST, IRC_RPL_ENDOFEXCEPTLIST)
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
HANDLE_LIST (&chan->invite_list,
|
||||||
|
IRC_RPL_INVITELIST, IRC_RPL_ENDOFINVITELIST)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'k':
|
||||||
|
NEEDS_OPER
|
||||||
|
if (!adding)
|
||||||
|
{
|
||||||
|
if (!(target = *params))
|
||||||
|
continue;
|
||||||
|
params++;
|
||||||
|
if (!chan->key || irc_strcmp (target, chan->key))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
str_append_c (&removed, mode_char);
|
||||||
|
str_vector_add (&removed_params, chan->key);
|
||||||
|
free (chan->key);
|
||||||
|
chan->key = NULL;
|
||||||
|
}
|
||||||
|
else if (!(target = *params))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
params++;
|
||||||
|
if (chan->key)
|
||||||
|
irc_send_reply (c, IRC_ERR_KEYSET, chan->name);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chan->key = xstrdup (target);
|
||||||
|
str_append_c (&added, mode_char);
|
||||||
|
str_vector_add (&added_params, chan->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
NEEDS_OPER
|
||||||
|
if (!adding)
|
||||||
|
{
|
||||||
|
if (chan->user_limit == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
chan->user_limit = -1;
|
||||||
|
str_append_c (&removed, mode_char);
|
||||||
|
}
|
||||||
|
else if (!(target = *params))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
params++;
|
||||||
|
unsigned long x;
|
||||||
|
if (xstrtoul (&x, target, 10) && x > 0 && x <= LONG_MAX)
|
||||||
|
{
|
||||||
|
chan->user_limit = x;
|
||||||
|
str_append_c (&added, mode_char);
|
||||||
|
str_vector_add (&added_params, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
RETURN_WITH_REPLY (c, IRC_ERR_UNKNOWNMODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NEEDS_OPER
|
||||||
|
#undef HANDLE_USER
|
||||||
|
#undef HANDLE_LIST
|
||||||
|
#undef HANDLE_MODE
|
||||||
|
#undef REMOVE_MODE
|
||||||
|
|
||||||
|
if (added.len || removed.len)
|
||||||
|
{
|
||||||
|
struct str message;
|
||||||
|
str_init (&message);
|
||||||
|
str_append_printf (&message, ":%s!%s@%s MODE %s ",
|
||||||
|
c->nickname, c->username, c->hostname, chan->name);
|
||||||
|
if (added.len)
|
||||||
|
str_append_printf (&message, "+%s", added.str);
|
||||||
|
if (removed.len)
|
||||||
|
str_append_printf (&message, "-%s", removed.str);
|
||||||
|
for (size_t i = 0; i < added_params.len; i++)
|
||||||
|
str_append_printf (&message, " %s", added_params.vector[i]);
|
||||||
|
for (size_t i = 0; i < removed_params.len; i++)
|
||||||
|
str_append_printf (&message, " %s", removed_params.vector[i]);
|
||||||
|
irc_channel_multicast (chan, message.str, NULL);
|
||||||
|
str_free (&message);
|
||||||
|
}
|
||||||
|
|
||||||
|
str_free (&added); str_vector_free (&added_params);
|
||||||
|
str_free (&removed); str_vector_free (&removed_params);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
irc_handle_mode (const struct irc_message *msg, struct client *c)
|
irc_handle_mode (const struct irc_message *msg, struct client *c)
|
||||||
{
|
{
|
||||||
|
@ -1474,13 +1718,9 @@ irc_handle_mode (const struct irc_message *msg, struct client *c)
|
||||||
char *mode = channel_get_mode (chan, channel_get_user (chan, c));
|
char *mode = channel_get_mode (chan, channel_get_user (chan, c));
|
||||||
irc_send_reply (c, IRC_RPL_CHANNELMODEIS, target, mode);
|
irc_send_reply (c, IRC_RPL_CHANNELMODEIS, target, mode);
|
||||||
free (mode);
|
free (mode);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (msg->params.len < 3
|
else
|
||||||
&& irc_maybe_send_channel_list (c, chan, msg->params.vector[1]))
|
irc_handle_chan_mode_change (c, chan, &msg->params.vector[1]);
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: mode modification
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue