diff options
-rw-r--r-- | kike-replies | 2 | ||||
-rw-r--r-- | kike.c | 77 |
2 files changed, 76 insertions, 3 deletions
diff --git a/kike-replies b/kike-replies index decc880..c5deec5 100644 --- a/kike-replies +++ b/kike-replies @@ -28,6 +28,7 @@ 331 IRC_RPL_NOTOPIC "%s :No topic is set" 332 IRC_RPL_TOPIC "%s :%s" 333 IRC_RPL_TOPICWHOTIME "%s %s %lld" +341 IRC_RPL_INVITING "%s %s" 346 IRC_RPL_INVITELIST "%s %s" 347 IRC_RPL_ENDOFINVITELIST "%s :End of channel invite list" 348 IRC_RPL_EXCEPTLIST "%s %s" @@ -57,6 +58,7 @@ 433 IRC_ERR_NICKNAMEINUSE "%s :Nickname is already in use" 441 IRC_ERR_USERNOTINCHANNEL "%s %s :They aren't on that channel" 442 IRC_ERR_NOTONCHANNEL "%s :You're not on that channel" +443 IRC_ERR_USERONCHANNEL "%s %s :is already on channel" 445 IRC_ERR_SUMMONDISABLED ":SUMMON has been disabled" 446 IRC_ERR_USERSDISABLED ":USERS has been disabled" 451 IRC_ERR_NOTREGISTERED ":You have not registered" @@ -327,6 +327,8 @@ struct client unsigned mode; ///< User's mode char *away_message; ///< Away message time_t last_active; ///< Last PRIVMSG, to get idle time + // XXX: maybe this should rather be a str_map + struct str_vector invites; ///< Channel invitations struct flood_detector antiflood; ///< Flood detector }; @@ -340,6 +342,7 @@ client_init (struct client *self) str_init (&self->write_buffer); // TODO: make this configurable and more fine-grained flood_detector_init (&self->antiflood, 10, 20); + str_vector_init (&self->invites); } static void @@ -361,6 +364,7 @@ client_free (struct client *self) free (self->address); free (self->away_message); flood_detector_free (&self->antiflood); + str_vector_free (&self->invites); } static void client_close_link (struct client *, const char *); @@ -2296,6 +2300,67 @@ irc_handle_kick (const struct irc_message *msg, struct client *c) str_vector_free (&users); } +static bool +irc_is_invited (struct client *c, const char *channel_name) +{ + for (size_t i = 0; i < c->invites.len; i++) + if (!irc_strcmp (c->invites.vector[i], channel_name)) + return true; + return false; +} + +static void +irc_handle_invite (const struct irc_message *msg, struct client *c) +{ + if (msg->params.len < 2) + RETURN_WITH_REPLY (c, IRC_ERR_NEEDMOREPARAMS, msg->command); + + const char *target = msg->params.vector[0]; + const char *channel_name = msg->params.vector[1]; + + struct client *client = str_map_find (&c->ctx->users, target); + if (!client) + RETURN_WITH_REPLY (c, IRC_ERR_NOSUCHNICK, target); + + struct channel *chan = str_map_find (&c->ctx->channels, channel_name); + if (chan) + { + struct channel_user *inviting_user; + if (!(inviting_user = channel_get_user (chan, c))) + RETURN_WITH_REPLY (c, IRC_ERR_NOTONCHANNEL, channel_name); + if (channel_get_user (chan, client)) + RETURN_WITH_REPLY (c, IRC_ERR_USERONCHANNEL, target, channel_name); + + if ((chan->modes & IRC_CHAN_MODE_INVITE_ONLY)) + { + if (!(inviting_user->modes & IRC_CHAN_MODE_OPERATOR)) + RETURN_WITH_REPLY (c, IRC_ERR_CHANOPRIVSNEEDED, channel_name); + + // Only storing the invite if it makes sense + if (!irc_is_invited (client, channel_name)) + str_vector_add (&client->invites, channel_name); + } + } + + client_send (client, ":%s!%s@%s INVITE %s %s", + c->nickname, c->username, c->hostname, client->nickname, channel_name); + if (client->away_message) + irc_send_reply (c, IRC_RPL_AWAY, + client->nickname, client->away_message); + irc_send_reply (c, IRC_RPL_INVITING, client->nickname, channel_name); +} + +static void +irc_remove_invite (struct client *c, const char *channel_name) +{ + for (size_t i = 0; i < c->invites.len; i++) + if (!irc_strcmp (c->invites.vector[i], channel_name)) + { + str_vector_remove (&c->invites, i); + return; + } +} + static void irc_try_join (struct client *c, const char *channel_name, const char *key) { @@ -2311,9 +2376,10 @@ irc_try_join (struct client *c, const char *channel_name, const char *key) else if (channel_get_user (chan, c)) return; + bool invited = irc_is_invited (c, channel_name); if ((chan->modes & IRC_CHAN_MODE_INVITE_ONLY) - && !client_in_mask_list (c, &chan->invite_list)) - // TODO: exceptions caused by INVITE + && !client_in_mask_list (c, &chan->invite_list) + && !invited) RETURN_WITH_REPLY (c, IRC_ERR_INVITEONLYCHAN, channel_name); if (chan->key && (!key || strcmp (key, chan->key))) RETURN_WITH_REPLY (c, IRC_ERR_BADCHANNELKEY, channel_name); @@ -2321,9 +2387,13 @@ irc_try_join (struct client *c, const char *channel_name, const char *key) && channel_user_count (chan) >= (size_t) chan->user_limit) RETURN_WITH_REPLY (c, IRC_ERR_CHANNELISFULL, channel_name); if (client_in_mask_list (c, &chan->ban_list) - && !client_in_mask_list (c, &chan->exception_list)) + && !client_in_mask_list (c, &chan->exception_list) + && !invited) RETURN_WITH_REPLY (c, IRC_ERR_BANNEDFROMCHAN, channel_name); + // Destroy any invitation as there's no other way to get rid of it + irc_remove_invite (c, channel_name); + channel_add_user (chan, c)->modes = user_mode; char *message = xstrdup_printf (":%s!%s@%s JOIN %s", @@ -2494,6 +2564,7 @@ irc_register_handlers (struct server_context *ctx) { "JOIN", true, irc_handle_join }, { "PART", true, irc_handle_part }, { "KICK", true, irc_handle_kick }, + { "INVITE", true, irc_handle_invite }, { "TOPIC", true, irc_handle_topic }, { "LIST", true, irc_handle_list }, { "NAMES", true, irc_handle_names }, |