diff --git a/kike.c b/kike.c index 783c6e2..d13def9 100644 --- a/kike.c +++ b/kike.c @@ -2093,8 +2093,26 @@ irc_append_prefixes (struct client *c, struct channel_user *user, str_free (&prefixes); } +static char * +irc_make_rpl_namreply_item + (struct client *c, struct client *target, struct channel_user *user) +{ + struct str result; + str_init (&result); + + if (user) + irc_append_prefixes (c, user, &result); + + str_append (&result, target->nickname); + if (c->caps_enabled & IRC_CAP_USERHOST_IN_NAMES) + str_append_printf (&result, + "!%s@%s", target->username, target->hostname); + return str_steal (&result); +} + static void -irc_send_rpl_namreply (struct client *c, const struct channel *chan) +irc_send_rpl_namreply (struct client *c, const struct channel *chan, + struct str_map *used_nicks) { struct str_vector nicks; str_vector_init (&nicks); @@ -2110,15 +2128,10 @@ irc_send_rpl_namreply (struct client *c, const struct channel *chan) { if (!on_channel && (iter->c->mode & IRC_USER_MODE_INVISIBLE)) continue; - - struct str result; - str_init (&result); - irc_append_prefixes (c, iter, &result); - str_append (&result, iter->c->nickname); - if (c->caps_enabled & IRC_CAP_USERHOST_IN_NAMES) - str_append_printf (&result, - "!%s@%s", iter->c->username, iter->c->hostname); - str_vector_add_owned (&nicks, str_steal (&result)); + if (used_nicks) + str_map_set (used_nicks, iter->c->nickname, (void *) 1); + str_vector_add_owned (&nicks, + irc_make_rpl_namreply_item (c, iter->c, iter)); } irc_send_reply_vector (c, IRC_RPL_NAMREPLY, @@ -2126,6 +2139,30 @@ irc_send_rpl_namreply (struct client *c, const struct channel *chan) str_vector_free (&nicks); } +static void +irc_send_disassociated_names (struct client *c, struct str_map *used) +{ + struct str_vector nicks; + str_vector_init (&nicks); + + struct str_map_iter iter; + str_map_iter_init (&iter, &c->ctx->users); + struct client *target; + while ((target = str_map_iter_next (&iter))) + { + if ((target->mode & IRC_USER_MODE_INVISIBLE) + || str_map_find (used, target->nickname)) + continue; + str_vector_add_owned (&nicks, + irc_make_rpl_namreply_item (c, target, NULL)); + } + + if (nicks.len) + irc_send_reply_vector (c, IRC_RPL_NAMREPLY, + nicks.vector, '*', "*", ""); + str_vector_free (&nicks); +} + static void irc_handle_names (const struct irc_message *msg, struct client *c) { @@ -2135,18 +2172,21 @@ irc_handle_names (const struct irc_message *msg, struct client *c) struct channel *chan; if (msg->params.len == 0) { + struct str_map used; + str_map_init (&used); + used.key_xfrm = irc_strxfrm; + struct str_map_iter iter; str_map_iter_init (&iter, &c->ctx->channels); while ((chan = str_map_iter_next (&iter))) if (!(chan->modes & (IRC_CHAN_MODE_PRIVATE | IRC_CHAN_MODE_SECRET)) || channel_get_user (chan, c)) - irc_send_rpl_namreply (c, chan); + irc_send_rpl_namreply (c, chan, &used); + + // Also send all visible users we haven't listed yet + irc_send_disassociated_names (c, &used); + str_map_free (&used); - // TODO - // If no parameter is given, a list of all channels and their - // occupants is returned. At the end of this list, a list of users who - // are visible but either not on any channel or not on a visible channel - // are listed as being on `channel' "*". irc_send_reply (c, IRC_RPL_ENDOFNAMES, "*"); } else @@ -2159,7 +2199,7 @@ irc_handle_names (const struct irc_message *msg, struct client *c) && (!(chan->modes & IRC_CHAN_MODE_SECRET) || channel_get_user (chan, c))) { - irc_send_rpl_namreply (c, chan); + irc_send_rpl_namreply (c, chan, NULL); irc_send_reply (c, IRC_RPL_ENDOFNAMES, channels.vector[i]); } str_vector_free (&channels); @@ -2606,7 +2646,7 @@ irc_try_join (struct client *c, const char *channel_name, const char *key) free (message); irc_send_rpl_topic (c, chan); - irc_send_rpl_namreply (c, chan); + irc_send_rpl_namreply (c, chan, NULL); irc_send_reply (c, IRC_RPL_ENDOFNAMES, chan->name); }