+static bool
+write_whoreply(CLIENT *Client, CLIENT *c, const char *channelname, const char *flags)
+{
+ return IRC_WriteStrClient(Client, RPL_WHOREPLY_MSG, Client_ID(Client), channelname,
+ Client_User(c), Client_Hostname(c), Client_ID(Client_Introducer(c)), Client_ID(c),
+ flags, Client_Hops(c), Client_Info(c));
+}
+
+
+static const char *
+who_flags_status(const char *client_modes)
+{
+ if (strchr(client_modes, 'a'))
+ return "G"; /* away */
+ return "H";
+}
+
+
+static const char *
+who_flags_qualifier(const char *chan_user_modes)
+{
+ if (strchr(chan_user_modes, 'o'))
+ return "@";
+ else if (strchr(chan_user_modes, 'v'))
+ return "+";
+ return "";
+}
+
+
+static bool
+IRC_Send_WHO(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
+{
+ bool is_visible, is_member, is_ircop;
+ CL2CHAN *cl2chan;
+ const char *client_modes;
+ const char *chan_user_modes;
+ char flags[8];
+ CLIENT *c;
+
+ assert( Client != NULL );
+ assert( Chan != NULL );
+
+ is_member = Channel_IsMemberOf(Chan, Client);
+
+ /* Secret channel? */
+ if (!is_member && strchr(Channel_Modes(Chan), 's'))
+ return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), Channel_Name(Chan));
+
+ cl2chan = Channel_FirstMember(Chan);
+ for (; cl2chan ; cl2chan = Channel_NextMember(Chan, cl2chan)) {
+ c = Channel_GetClient(cl2chan);
+
+ client_modes = Client_Modes(c);
+ is_ircop = strchr(client_modes, 'o') != NULL;
+ if (OnlyOps && !is_ircop)
+ continue;
+
+ is_visible = strchr(client_modes, 'i') == NULL;
+ if (is_member || is_visible) {
+ strcpy(flags, who_flags_status(client_modes));
+ if (is_ircop)
+ strlcat(flags, "*", sizeof(flags));
+
+ chan_user_modes = Channel_UserModes(Chan, c);
+ strlcat(flags, who_flags_qualifier(chan_user_modes), sizeof(flags));
+
+ if (!write_whoreply(Client, c, Channel_Name(Chan), flags))
+ return DISCONNECTED;
+ }
+ }
+ return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), Channel_Name(Chan));
+} /* IRC_Send_WHO */
+
+
+GLOBAL bool