kike: implement the WHO command
This commit is contained in:
		
							
								
								
									
										108
									
								
								src/kike.c
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								src/kike.c
									
									
									
									
									
								
							@@ -803,6 +803,7 @@ enum
 | 
				
			|||||||
	IRC_RPL_USERHOST              = 302,
 | 
						IRC_RPL_USERHOST              = 302,
 | 
				
			||||||
	IRC_RPL_UNAWAY                = 305,
 | 
						IRC_RPL_UNAWAY                = 305,
 | 
				
			||||||
	IRC_RPL_NOWAWAY               = 306,
 | 
						IRC_RPL_NOWAWAY               = 306,
 | 
				
			||||||
 | 
						IRC_RPL_ENDOFWHO              = 315,
 | 
				
			||||||
	IRC_RPL_LIST                  = 322,
 | 
						IRC_RPL_LIST                  = 322,
 | 
				
			||||||
	IRC_RPL_LISTEND               = 323,
 | 
						IRC_RPL_LISTEND               = 323,
 | 
				
			||||||
	IRC_RPL_CHANNELMODEIS         = 324,
 | 
						IRC_RPL_CHANNELMODEIS         = 324,
 | 
				
			||||||
@@ -813,6 +814,7 @@ enum
 | 
				
			|||||||
	IRC_RPL_EXCEPTLIST            = 348,
 | 
						IRC_RPL_EXCEPTLIST            = 348,
 | 
				
			||||||
	IRC_RPL_ENDOFEXCEPTLIST       = 349,
 | 
						IRC_RPL_ENDOFEXCEPTLIST       = 349,
 | 
				
			||||||
	IRC_RPL_VERSION               = 351,
 | 
						IRC_RPL_VERSION               = 351,
 | 
				
			||||||
 | 
						IRC_RPL_WHOREPLY              = 352,
 | 
				
			||||||
	IRC_RPL_NAMREPLY              = 353,
 | 
						IRC_RPL_NAMREPLY              = 353,
 | 
				
			||||||
	IRC_RPL_ENDOFNAMES            = 366,
 | 
						IRC_RPL_ENDOFNAMES            = 366,
 | 
				
			||||||
	IRC_RPL_BANLIST               = 367,
 | 
						IRC_RPL_BANLIST               = 367,
 | 
				
			||||||
@@ -868,6 +870,7 @@ static const char *g_default_replies[] =
 | 
				
			|||||||
	[IRC_RPL_USERHOST] = ":%s",
 | 
						[IRC_RPL_USERHOST] = ":%s",
 | 
				
			||||||
	[IRC_RPL_UNAWAY] = ":You are no longer marked as being away",
 | 
						[IRC_RPL_UNAWAY] = ":You are no longer marked as being away",
 | 
				
			||||||
	[IRC_RPL_NOWAWAY] = ":You have been marked as being away",
 | 
						[IRC_RPL_NOWAWAY] = ":You have been marked as being away",
 | 
				
			||||||
 | 
						[IRC_RPL_ENDOFWHO] = "%s :End of WHO list",
 | 
				
			||||||
	[IRC_RPL_LIST] = "%s %d :%s",
 | 
						[IRC_RPL_LIST] = "%s %d :%s",
 | 
				
			||||||
	[IRC_RPL_LISTEND] = ":End of LIST",
 | 
						[IRC_RPL_LISTEND] = ":End of LIST",
 | 
				
			||||||
	[IRC_RPL_CHANNELMODEIS] = "%s +%s",
 | 
						[IRC_RPL_CHANNELMODEIS] = "%s +%s",
 | 
				
			||||||
@@ -878,6 +881,7 @@ static const char *g_default_replies[] =
 | 
				
			|||||||
	[IRC_RPL_EXCEPTLIST] = "%s %s",
 | 
						[IRC_RPL_EXCEPTLIST] = "%s %s",
 | 
				
			||||||
	[IRC_RPL_ENDOFEXCEPTLIST] = "%s :End of channel exception list",
 | 
						[IRC_RPL_ENDOFEXCEPTLIST] = "%s :End of channel exception list",
 | 
				
			||||||
	[IRC_RPL_VERSION] = "%s.%d %s :%s",
 | 
						[IRC_RPL_VERSION] = "%s.%d %s :%s",
 | 
				
			||||||
 | 
						[IRC_RPL_WHOREPLY] = "%s %s %s %s %s %s :%d %s",
 | 
				
			||||||
	[IRC_RPL_NAMREPLY] = "%c %s :%s",
 | 
						[IRC_RPL_NAMREPLY] = "%c %s :%s",
 | 
				
			||||||
	[IRC_RPL_ENDOFNAMES] = "%s :End of NAMES list",
 | 
						[IRC_RPL_ENDOFNAMES] = "%s :End of NAMES list",
 | 
				
			||||||
	[IRC_RPL_BANLIST] = "%s %s",
 | 
						[IRC_RPL_BANLIST] = "%s %s",
 | 
				
			||||||
@@ -1445,6 +1449,109 @@ irc_handle_names (const struct irc_message *msg, struct client *c)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					irc_send_rpl_whoreply (struct client *c, const struct channel *chan,
 | 
				
			||||||
 | 
						const struct client *target)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct str chars;
 | 
				
			||||||
 | 
						str_init (&chars);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						str_append_c (&chars, target->away_message ? 'G' : 'H');
 | 
				
			||||||
 | 
						if (target->mode & IRC_USER_MODE_OPERATOR)
 | 
				
			||||||
 | 
							str_append_c (&chars, '*');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct channel_user *user;
 | 
				
			||||||
 | 
						if (chan && (user = channel_get_user (chan, target)))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (user->modes & IRC_CHAN_MODE_OPERATOR)
 | 
				
			||||||
 | 
								str_append_c (&chars, '@');
 | 
				
			||||||
 | 
							else if (user->modes & IRC_CHAN_MODE_VOICE)
 | 
				
			||||||
 | 
								str_append_c (&chars, '+');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						irc_send_reply (c, IRC_RPL_WHOREPLY, chan ? chan->name : "*",
 | 
				
			||||||
 | 
							target->username, target->hostname, target->ctx->server_name,
 | 
				
			||||||
 | 
							target->nickname, chars.str, 0 /* hop count */, target->realname);
 | 
				
			||||||
 | 
						str_free (&chars);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					irc_match_send_rpl_whoreply (struct client *c, struct client *target,
 | 
				
			||||||
 | 
						const char *mask)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						bool is_roommate = false;
 | 
				
			||||||
 | 
						struct str_map_iter iter;
 | 
				
			||||||
 | 
						str_map_iter_init (&iter, &c->ctx->channels);
 | 
				
			||||||
 | 
						struct channel *chan;
 | 
				
			||||||
 | 
						while ((chan = str_map_iter_next (&iter)))
 | 
				
			||||||
 | 
							if (channel_get_user (chan, target) && channel_get_user (chan, c))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								is_roommate = true;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						if ((target->mode & IRC_USER_MODE_INVISIBLE) && !is_roommate)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fnmatch (mask, target->hostname,    0)
 | 
				
			||||||
 | 
						 && fnmatch (mask, target->nickname,    0)
 | 
				
			||||||
 | 
						 && fnmatch (mask, target->realname,    0)
 | 
				
			||||||
 | 
						 && fnmatch (mask, c->ctx->server_name, 0))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Try to find a channel they're on that's visible to us
 | 
				
			||||||
 | 
						struct channel *user_chan = NULL;
 | 
				
			||||||
 | 
						str_map_iter_init (&iter, &c->ctx->channels);
 | 
				
			||||||
 | 
						while ((chan = str_map_iter_next (&iter)))
 | 
				
			||||||
 | 
							if (channel_get_user (chan, target)
 | 
				
			||||||
 | 
							 && (!(chan->modes & (IRC_CHAN_MODE_PRIVATE | IRC_CHAN_MODE_SECRET))
 | 
				
			||||||
 | 
								|| channel_get_user (chan, c)))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								user_chan = chan;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						irc_send_rpl_whoreply (c, user_chan, target);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					irc_handle_who (const struct irc_message *msg, struct client *c)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						bool only_ops = msg->params.len > 1 && !strcmp (msg->params.vector[1], "o");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char *shown_mask = msg->params.vector[0], *used_mask;
 | 
				
			||||||
 | 
						if (!shown_mask)
 | 
				
			||||||
 | 
							used_mask = shown_mask = "*";
 | 
				
			||||||
 | 
						else if (!strcmp (shown_mask, "0"))
 | 
				
			||||||
 | 
							used_mask = "*";
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							used_mask = shown_mask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct channel *chan;
 | 
				
			||||||
 | 
						if ((chan = str_map_find (&c->ctx->channels, used_mask)))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							bool on_chan = !!channel_get_user (chan, c);
 | 
				
			||||||
 | 
							if (on_chan || !(chan->modes & IRC_CHAN_MODE_SECRET))
 | 
				
			||||||
 | 
								for (struct channel_user *iter = chan->users;
 | 
				
			||||||
 | 
									iter; iter = iter->next)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									struct client *target =
 | 
				
			||||||
 | 
										str_map_find (&c->ctx->users, iter->nickname);
 | 
				
			||||||
 | 
									if ((on_chan || !(target->mode & IRC_USER_MODE_INVISIBLE))
 | 
				
			||||||
 | 
									 && (!only_ops || (target->mode & IRC_USER_MODE_OPERATOR)))
 | 
				
			||||||
 | 
										irc_send_rpl_whoreply (c, chan, target);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							struct str_map_iter iter;
 | 
				
			||||||
 | 
							str_map_iter_init (&iter, &c->ctx->users);
 | 
				
			||||||
 | 
							struct client *target;
 | 
				
			||||||
 | 
							while ((target = str_map_iter_next (&iter)))
 | 
				
			||||||
 | 
								if (!only_ops || (target->mode & IRC_USER_MODE_OPERATOR))
 | 
				
			||||||
 | 
									irc_match_send_rpl_whoreply (c, target, used_mask);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						irc_send_reply (c, IRC_RPL_ENDOFWHO, shown_mask);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
irc_send_rpl_topic (struct client *c, struct channel *chan)
 | 
					irc_send_rpl_topic (struct client *c, struct channel *chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -1686,6 +1793,7 @@ irc_register_handlers (struct server_context *ctx)
 | 
				
			|||||||
		{ "TOPIC",    true,  irc_handle_topic    },
 | 
							{ "TOPIC",    true,  irc_handle_topic    },
 | 
				
			||||||
		{ "LIST",     true,  irc_handle_list     },
 | 
							{ "LIST",     true,  irc_handle_list     },
 | 
				
			||||||
		{ "NAMES",    true,  irc_handle_names    },
 | 
							{ "NAMES",    true,  irc_handle_names    },
 | 
				
			||||||
 | 
							{ "WHO",      true,  irc_handle_who      },
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (size_t i = 0; i < N_ELEMENTS (message_handlers); i++)
 | 
						for (size_t i = 0; i < N_ELEMENTS (message_handlers); i++)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user