kike: implement the WHOIS command
The code is starting to stink a bit, refactor imminent.
This commit is contained in:
		
							parent
							
								
									ad7d17d2d8
								
							
						
					
					
						commit
						eab2d1765a
					
				
							
								
								
									
										99
									
								
								src/kike.c
									
									
									
									
									
								
							
							
						
						
									
										99
									
								
								src/kike.c
									
									
									
									
									
								
							| @ -29,6 +29,7 @@ | ||||
| static struct config_item g_config_table[] = | ||||
| { | ||||
| 	{ "server_name",     NULL,              "Server name"                    }, | ||||
| 	{ "server_info",     "My server",       "Brief server description"       }, | ||||
| 	{ "motd",            NULL,              "MOTD filename"                  }, | ||||
| 	{ "catalog",         NULL,              "catgets localization catalog"   }, | ||||
| 
 | ||||
| @ -259,6 +260,7 @@ struct client | ||||
| 
 | ||||
| 	unsigned mode;                      ///< User's mode
 | ||||
| 	char *away_message;                 ///< Away message
 | ||||
| 	time_t last_active;                 ///< Last PRIVMSG, to get idle time
 | ||||
| }; | ||||
| 
 | ||||
| static void | ||||
| @ -811,7 +813,13 @@ enum | ||||
| 	IRC_RPL_ISON                  = 303, | ||||
| 	IRC_RPL_UNAWAY                = 305, | ||||
| 	IRC_RPL_NOWAWAY               = 306, | ||||
| 	IRC_RPL_WHOISUSER             = 311, | ||||
| 	IRC_RPL_WHOISSERVER           = 312, | ||||
| 	IRC_RPL_WHOISOPERATOR         = 313, | ||||
| 	IRC_RPL_ENDOFWHO              = 315, | ||||
| 	IRC_RPL_WHOISIDLE             = 317, | ||||
| 	IRC_RPL_ENDOFWHOIS            = 318, | ||||
| 	IRC_RPL_WHOISCHANNELS         = 319, | ||||
| 	IRC_RPL_LIST                  = 322, | ||||
| 	IRC_RPL_LISTEND               = 323, | ||||
| 	IRC_RPL_CHANNELMODEIS         = 324, | ||||
| @ -880,7 +888,13 @@ static const char *g_default_replies[] = | ||||
| 	[IRC_RPL_ISON] = ":%s", | ||||
| 	[IRC_RPL_UNAWAY] = ":You are no longer marked as being away", | ||||
| 	[IRC_RPL_NOWAWAY] = ":You have been marked as being away", | ||||
| 	[IRC_RPL_WHOISUSER] = "%s %s %s * :%s", | ||||
| 	[IRC_RPL_WHOISSERVER] = "%s %s :%s", | ||||
| 	[IRC_RPL_WHOISOPERATOR] = "%s :is an IRC operator", | ||||
| 	[IRC_RPL_ENDOFWHO] = "%s :End of WHO list", | ||||
| 	[IRC_RPL_WHOISIDLE] = "%s %d :seconds idle", | ||||
| 	[IRC_RPL_ENDOFWHOIS] = "%s :End of WHOIS list", | ||||
| 	[IRC_RPL_WHOISCHANNELS] = "%s :%s", | ||||
| 	[IRC_RPL_LIST] = "%s %d :%s", | ||||
| 	[IRC_RPL_LISTEND] = ":End of LIST", | ||||
| 	[IRC_RPL_CHANNELMODEIS] = "%s +%s", | ||||
| @ -1347,6 +1361,8 @@ static void | ||||
| irc_handle_privmsg (const struct irc_message *msg, struct client *c) | ||||
| { | ||||
| 	irc_handle_user_message (msg, c, "PRIVMSG", true); | ||||
| 	// Let's not care too much about success or failure
 | ||||
| 	c->last_active = time (NULL); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| @ -1578,6 +1594,87 @@ irc_handle_who (const struct irc_message *msg, struct client *c) | ||||
| 	irc_send_reply (c, IRC_RPL_ENDOFWHO, shown_mask); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| irc_send_whois_reply (struct client *c, const struct client *target) | ||||
| { | ||||
| 	const char *nick = target->nickname; | ||||
| 	irc_send_reply (c, IRC_RPL_WHOISUSER, nick, target->username, | ||||
| 		target->hostname, target->realname); | ||||
| 	irc_send_reply (c, IRC_RPL_WHOISSERVER, nick, target->ctx->server_name, | ||||
| 		str_map_find (&c->ctx->config, "server_info")); | ||||
| 	if (target->mode & IRC_USER_MODE_OPERATOR) | ||||
| 		irc_send_reply (c, IRC_RPL_WHOISOPERATOR, nick); | ||||
| 	irc_send_reply (c, IRC_RPL_WHOISIDLE, nick, | ||||
| 		(int) (time (NULL) - target->last_active)); | ||||
| 
 | ||||
| 	struct str_map_iter iter; | ||||
| 	str_map_iter_init (&iter, &c->ctx->channels); | ||||
| 	struct channel *chan; | ||||
| 	struct channel_user *channel_user; | ||||
| 	while ((chan = str_map_iter_next (&iter))) | ||||
| 		if ((channel_user = channel_get_user (chan, target)) | ||||
| 		 && (!(chan->modes & (IRC_CHAN_MODE_PRIVATE | IRC_CHAN_MODE_SECRET)) | ||||
| 			|| channel_get_user (chan, c))) | ||||
| 		{ | ||||
| 			struct str item; | ||||
| 			str_init (&item); | ||||
| 			if (channel_user->modes & IRC_CHAN_MODE_OPERATOR) | ||||
| 				str_append_c (&item, '@'); | ||||
| 			else if (channel_user->modes & IRC_CHAN_MODE_VOICE) | ||||
| 				str_append_c (&item, '+'); | ||||
| 			str_append (&item, chan->name); | ||||
| 			str_append_c (&item, ' '); | ||||
| 
 | ||||
| 			// TODO: try to merge the results into as few messages as possible
 | ||||
| 			irc_send_reply (c, IRC_RPL_WHOISCHANNELS, nick, item.str); | ||||
| 
 | ||||
| 			str_free (&item); | ||||
| 		} | ||||
| 
 | ||||
| 	irc_send_reply (c, IRC_RPL_ENDOFWHOIS, nick); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| irc_handle_whois (const struct irc_message *msg, struct client *c) | ||||
| { | ||||
| 	if (msg->params.len < 1) | ||||
| 		RETURN_WITH_REPLY (c, IRC_ERR_NEEDMOREPARAMS, msg->command); | ||||
| 	if (msg->params.len > 1 && !irc_is_this_me (c->ctx, msg->params.vector[0])) | ||||
| 		RETURN_WITH_REPLY (c, IRC_ERR_NOSUCHSERVER, msg->params.vector[0]); | ||||
| 
 | ||||
| 	struct str_vector masks; | ||||
| 	str_vector_init (&masks); | ||||
| 	const char *masks_str = msg->params.vector[msg->params.len > 1]; | ||||
| 	split_str_ignore_empty (masks_str, ',', &masks); | ||||
| 	for (size_t i = 0; i < masks.len; i++) | ||||
| 	{ | ||||
| 		const char *mask = masks.vector[i]; | ||||
| 		struct client *target; | ||||
| 		if (!strpbrk (mask, "*?")) | ||||
| 		{ | ||||
| 			if (!(target = str_map_find (&c->ctx->users, mask))) | ||||
| 				irc_send_reply (c, IRC_ERR_NOSUCHNICK, mask); | ||||
| 			else | ||||
| 				irc_send_whois_reply (c, target); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			struct str_map_iter iter; | ||||
| 			str_map_iter_init (&iter, &c->ctx->users); | ||||
| 			bool found = false; | ||||
| 			while ((target = str_map_iter_next (&iter)) | ||||
| 				&& !irc_fnmatch (mask, target->nickname)) | ||||
| 			{ | ||||
| 				irc_send_whois_reply (c, target); | ||||
| 				found = true; | ||||
| 			} | ||||
| 			if (!found) | ||||
| 				irc_send_reply (c, IRC_ERR_NOSUCHNICK, mask); | ||||
| 		} | ||||
| 	} | ||||
| 	str_vector_free (&masks); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| irc_send_rpl_topic (struct client *c, struct channel *chan) | ||||
| { | ||||
| @ -1901,6 +1998,7 @@ irc_register_handlers (struct server_context *ctx) | ||||
| 		{ "LIST",     true,  irc_handle_list     }, | ||||
| 		{ "NAMES",    true,  irc_handle_names    }, | ||||
| 		{ "WHO",      true,  irc_handle_who      }, | ||||
| 		{ "WHOIS",    true,  irc_handle_whois    }, | ||||
| 		{ "ISON",     true,  irc_handle_ison     }, | ||||
| 	}; | ||||
| 
 | ||||
| @ -2239,6 +2337,7 @@ on_irc_client_available (const struct pollfd *pfd, void *user_data) | ||||
| 		c->ctx = ctx; | ||||
| 		c->socket_fd = fd; | ||||
| 		c->hostname = xstrdup (host); | ||||
| 		c->last_active = time (NULL); | ||||
| 		LIST_PREPEND (ctx->clients, c); | ||||
| 		ctx->n_clients++; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user