diff --git a/src/kike.c b/src/kike.c index a6ca384..01f7716 100644 --- a/src/kike.c +++ b/src/kike.c @@ -836,6 +836,7 @@ enum IRC_ERR_NONICKNAMEGIVEN = 431, IRC_ERR_ERRONEOUSNICKNAME = 432, IRC_ERR_NICKNAMEINUSE = 433, + IRC_ERR_USERNOTINCHANNEL = 441, IRC_ERR_NOTONCHANNEL = 442, IRC_ERR_SUMMONDISABLED = 445, IRC_ERR_USERSDISABLED = 446, @@ -903,6 +904,7 @@ static const char *g_default_replies[] = [IRC_ERR_NONICKNAMEGIVEN] = ":No nickname given", [IRC_ERR_ERRONEOUSNICKNAME] = "%s :Erroneous nickname", [IRC_ERR_NICKNAMEINUSE] = "%s :Nickname is already in use", + [IRC_ERR_USERNOTINCHANNEL] = "%s %s :They aren't on that channel", [IRC_ERR_NOTONCHANNEL] = "%s :You're not on that channel", [IRC_ERR_SUMMONDISABLED] = ":SUMMON has been disabled", [IRC_ERR_USERSDISABLED] = ":USERS has been disabled", @@ -1597,7 +1599,7 @@ irc_handle_topic (const struct irc_message *msg, struct client *c) if ((chan->modes & IRC_CHAN_MODE_PROTECTED_TOPIC) && !(user->modes & IRC_CHAN_MODE_OPERATOR)) - RETURN_WITH_REPLY (c, IRC_ERR_CHANOPRIVSNEEDED); + RETURN_WITH_REPLY (c, IRC_ERR_CHANOPRIVSNEEDED, target); free (chan->topic); chan->topic = xstrdup (msg->params.vector[1]); @@ -1665,6 +1667,65 @@ irc_handle_part (const struct irc_message *msg, struct client *c) str_vector_free (&channels); } +static void +irc_try_kick (struct client *c, const char *channel_name, const char *nick, + const char *reason) +{ + struct channel *chan; + if (!(chan = str_map_find (&c->ctx->channels, channel_name))) + RETURN_WITH_REPLY (c, IRC_ERR_NOSUCHCHANNEL, channel_name); + + struct channel_user *user; + if (!(user = channel_get_user (chan, c))) + RETURN_WITH_REPLY (c, IRC_ERR_NOTONCHANNEL, channel_name); + if (!(user->modes & IRC_CHAN_MODE_OPERATOR)) + RETURN_WITH_REPLY (c, IRC_ERR_CHANOPRIVSNEEDED, channel_name); + + struct client *client; + if (!(client = str_map_find (&c->ctx->users, nick)) + || !(user = channel_get_user (chan, client))) + RETURN_WITH_REPLY (c, IRC_ERR_USERNOTINCHANNEL, nick, channel_name); + + char *message = xstrdup_printf (":%s!%s@%s KICK %s %s :%s", + c->nickname, c->username, c->hostname, channel_name, nick, reason); + if (!(chan->modes & IRC_CHAN_MODE_QUIET)) + irc_channel_multicast (chan, message); + else + irc_send (c, "%s", message); + free (message); + + channel_remove_user (chan, user); + channel_destroy_if_empty (c->ctx, chan); +} + +static void +irc_handle_kick (const struct irc_message *msg, struct client *c) +{ + if (msg->params.len < 2) + RETURN_WITH_REPLY (c, IRC_ERR_NEEDMOREPARAMS, msg->command); + + const char *reason = c->nickname; + if (msg->params.len > 2) + reason = msg->params.vector[2]; + + struct str_vector channels; + struct str_vector users; + str_vector_init (&channels); + str_vector_init (&users); + split_str_ignore_empty (msg->params.vector[0], ',', &channels); + split_str_ignore_empty (msg->params.vector[1], ',', &users); + + if (channels.len == 1) + for (size_t i = 0; i < users.len; i++) + irc_try_kick (c, channels.vector[0], users.vector[i], reason); + else + for (size_t i = 0; i < channels.len && i < users.len; i++) + irc_try_kick (c, channels.vector[i], users.vector[i], reason); + + str_vector_free (&channels); + str_vector_free (&users); +} + static void irc_try_join (struct client *c, const char *channel_name, const char *key) { @@ -1804,6 +1865,7 @@ irc_register_handlers (struct server_context *ctx) { "NOTICE", true, irc_handle_notice }, { "JOIN", true, irc_handle_join }, { "PART", true, irc_handle_part }, + { "KICK", true, irc_handle_kick }, { "TOPIC", true, irc_handle_topic }, { "LIST", true, irc_handle_list }, { "NAMES", true, irc_handle_names },