kike: refactor CAP processing
This commit is contained in:
		
							parent
							
								
									bf01fb7aa3
								
							
						
					
					
						commit
						1d53b87016
					
				
							
								
								
									
										225
									
								
								kike.c
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								kike.c
									
									
									
									
									
								
							| @ -319,7 +319,7 @@ struct client | ||||
| 	bool half_closed;                   ///< Closing link: conn. is half-closed
 | ||||
| 
 | ||||
| 	unsigned long cap_version;          ///< CAP protocol version
 | ||||
| 	unsigned caps;                      ///< Enabled capabilities
 | ||||
| 	unsigned caps_enabled;              ///< Enabled capabilities
 | ||||
| 
 | ||||
| 	bool ssl_rx_want_tx;                ///< SSL_read() wants to write
 | ||||
| 	bool ssl_tx_want_rx;                ///< SSL_write() wants to read
 | ||||
| @ -545,6 +545,7 @@ struct server_context | ||||
| 	struct str_map users;               ///< Maps nicknames to clients
 | ||||
| 	struct str_map channels;            ///< Maps channel names to data
 | ||||
| 	struct str_map handlers;            ///< Message handlers
 | ||||
| 	struct str_map cap_handlers;        ///< CAP message handlers
 | ||||
| 
 | ||||
| 	struct poller poller;               ///< Manages polled description
 | ||||
| 	struct poller_timer quit_timer;     ///< Quit timeout timer
 | ||||
| @ -582,6 +583,8 @@ server_context_init (struct server_context *self) | ||||
| 	self->channels.free = (void (*) (void *)) channel_delete; | ||||
| 	str_map_init (&self->handlers); | ||||
| 	self->handlers.key_xfrm = irc_strxfrm; | ||||
| 	str_map_init (&self->cap_handlers); | ||||
| 	self->cap_handlers.key_xfrm = irc_strxfrm; | ||||
| 
 | ||||
| 	poller_init (&self->poller); | ||||
| 	poller_timer_init (&self->quit_timer, &self->poller); | ||||
| @ -631,6 +634,7 @@ server_context_free (struct server_context *self) | ||||
| 	str_map_free (&self->users); | ||||
| 	str_map_free (&self->channels); | ||||
| 	str_map_free (&self->handlers); | ||||
| 	str_map_free (&self->cap_handlers); | ||||
| 	poller_free (&self->poller); | ||||
| 
 | ||||
| 	str_vector_free (&self->motd); | ||||
| @ -1121,97 +1125,157 @@ irc_try_finish_registration (struct client *c) | ||||
| 			ctx->server_name, c->nickname, c->ssl_cert_fingerprint); | ||||
| } | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| struct irc_cap_args | ||||
| { | ||||
| 	const char *subcommand;             ///< The subcommand being processed
 | ||||
| 	const char *full_params;            ///< Whole parameter string
 | ||||
| 	struct str_vector params;           ///< Split parameters
 | ||||
| 	const char *target;                 ///< Target parameter for replies
 | ||||
| }; | ||||
| 
 | ||||
| static void | ||||
| irc_handle_cap_ls (struct client *c, struct irc_cap_args *a) | ||||
| { | ||||
| 	if (a->params.len == 1 | ||||
| 	 && !xstrtoul (&c->cap_version, a->params.vector[0], 10)) | ||||
| 		irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, | ||||
| 			a->subcommand, "Ignoring invalid protocol version number"); | ||||
| 
 | ||||
| 	c->cap_negotiating = true; | ||||
| 	client_send (c, "CAP %s LS :multi-prefix", a->target); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| irc_handle_cap_list (struct client *c, struct irc_cap_args *a) | ||||
| { | ||||
| 	struct str_vector caps; | ||||
| 	str_vector_init (&caps); | ||||
| 
 | ||||
| 	if (c->caps_enabled & IRC_CAP_MULTI_PREFIX) | ||||
| 		str_vector_add (&caps, "multi-prefix"); | ||||
| 
 | ||||
| 	char *caps_str = join_str_vector (&caps, ' '); | ||||
| 	str_vector_free (&caps); | ||||
| 	client_send (c, "CAP %s LIST :%s", a->target, caps_str); | ||||
| 	free (caps_str); | ||||
| } | ||||
| 
 | ||||
| static unsigned | ||||
| irc_decode_capability (const char *name) | ||||
| { | ||||
| 	if (!strcmp (name, "multi-prefix")) | ||||
| 		return IRC_CAP_MULTI_PREFIX; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| irc_handle_cap_req (struct client *c, struct irc_cap_args *a) | ||||
| { | ||||
| 	c->cap_negotiating = true; | ||||
| 
 | ||||
| 	unsigned new_caps = c->caps_enabled; | ||||
| 	bool success = true; | ||||
| 	for (size_t i = 0; i < a->params.len; i++) | ||||
| 	{ | ||||
| 		bool removing = false; | ||||
| 		const char *name = a->params.vector[i]; | ||||
| 		if (*name == '-') | ||||
| 		{ | ||||
| 			removing = true; | ||||
| 			name++; | ||||
| 		} | ||||
| 
 | ||||
| 		unsigned cap; | ||||
| 		if (!(cap = irc_decode_capability (name))) | ||||
| 			success = false; | ||||
| 		else if (removing) | ||||
| 			new_caps &= ~cap; | ||||
| 		else | ||||
| 			new_caps |=  cap; | ||||
| 	} | ||||
| 
 | ||||
| 	if (success) | ||||
| 	{ | ||||
| 		c->caps_enabled = new_caps; | ||||
| 		client_send (c, "CAP %s ACK :%s", a->target, a->full_params); | ||||
| 	} | ||||
| 	else | ||||
| 		client_send (c, "CAP %s NAK :%s", a->target, a->full_params); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| irc_handle_cap_ack (struct client *c, struct irc_cap_args *a) | ||||
| { | ||||
| 	if (a->params.len) | ||||
| 		irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, | ||||
| 			a->subcommand, "No acknowledgable capabilities supported"); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| irc_handle_cap_end (struct client *c, struct irc_cap_args *a) | ||||
| { | ||||
| 	(void) a; | ||||
| 
 | ||||
| 	c->cap_negotiating = false; | ||||
| 	irc_try_finish_registration (c); | ||||
| } | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| struct irc_cap_command | ||||
| { | ||||
| 	const char *name; | ||||
| 	void (*handler) (struct client *, struct irc_cap_args *); | ||||
| }; | ||||
| 
 | ||||
| static void | ||||
| irc_register_cap_handlers (struct server_context *ctx) | ||||
| { | ||||
| 	static const struct irc_cap_command cap_handlers[] = | ||||
| 	{ | ||||
| 		{ "LS",   irc_handle_cap_ls   }, | ||||
| 		{ "LIST", irc_handle_cap_list }, | ||||
| 		{ "REQ",  irc_handle_cap_req  }, | ||||
| 		{ "ACK",  irc_handle_cap_ack  }, | ||||
| 		{ "END",  irc_handle_cap_end  }, | ||||
| 	}; | ||||
| 
 | ||||
| 	for (size_t i = 0; i < N_ELEMENTS (cap_handlers); i++) | ||||
| 	{ | ||||
| 		const struct irc_cap_command *cmd = &cap_handlers[i]; | ||||
| 		str_map_set (&ctx->cap_handlers, cmd->name, (void *) cmd); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| irc_handle_cap (const struct irc_message *msg, struct client *c) | ||||
| { | ||||
| 	if (msg->params.len < 1) | ||||
| 		RETURN_WITH_REPLY (c, IRC_ERR_NEEDMOREPARAMS, msg->command); | ||||
| 
 | ||||
| 	struct str_vector v; | ||||
| 	str_vector_init (&v); | ||||
| 	struct irc_cap_args args; | ||||
| 	args.target = c->nickname ? c->nickname : "*"; | ||||
| 	args.subcommand = msg->params.vector[0]; | ||||
| 	args.full_params = ""; | ||||
| 	str_vector_init (&args.params); | ||||
| 
 | ||||
| 	const char *subcommand = msg->params.vector[0]; | ||||
| 	const char *params = ""; | ||||
| 	if (msg->params.len > 1) | ||||
| 	{ | ||||
| 		params = msg->params.vector[1]; | ||||
| 		split_str_ignore_empty (params, ' ', &v); | ||||
| 		args.full_params = msg->params.vector[1]; | ||||
| 		split_str_ignore_empty (args.full_params, ' ', &args.params); | ||||
| 	} | ||||
| 
 | ||||
| 	const char *target = c->nickname ? c->nickname : "*"; | ||||
| 	if (!irc_strcmp (subcommand, "LS")) | ||||
| 	{ | ||||
| 		if (v.len == 1 && !xstrtoul (&c->cap_version, v.vector[0], 10)) | ||||
| 			irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, | ||||
| 				subcommand, "Ignoring invalid protocol version number"); | ||||
| 
 | ||||
| 		c->cap_negotiating = true; | ||||
| 		client_send (c, "CAP %s LS :multi-prefix", target); | ||||
| 	} | ||||
| 	else if (!irc_strcmp (subcommand, "LIST")) | ||||
| 	{ | ||||
| 		struct str_vector caps; | ||||
| 		str_vector_init (&caps); | ||||
| 
 | ||||
| 		if (c->caps & IRC_CAP_MULTI_PREFIX) | ||||
| 			str_vector_add (&caps, "multi-prefix"); | ||||
| 
 | ||||
| 		char *caps_str = join_str_vector (&caps, ' '); | ||||
| 		str_vector_free (&caps); | ||||
| 		client_send (c, "CAP %s LIST :%s", target, caps_str); | ||||
| 		free (caps_str); | ||||
| 	} | ||||
| 	else if (!irc_strcmp (subcommand, "REQ")) | ||||
| 	{ | ||||
| 		c->cap_negotiating = true; | ||||
| 
 | ||||
| 		unsigned new_caps = c->caps; | ||||
| 		bool success = true; | ||||
| 		for (size_t i = 0; i < v.len; i++) | ||||
| 		{ | ||||
| 			bool neg = false; | ||||
| 			const char *name = v.vector[i]; | ||||
| 			if (*name == '-') | ||||
| 			{ | ||||
| 				neg = true; | ||||
| 				name++; | ||||
| 			} | ||||
| 			unsigned cap = 0; | ||||
| 			if (!strcmp (name, "multi-prefix")) | ||||
| 				cap = IRC_CAP_MULTI_PREFIX; | ||||
| 			else | ||||
| 				success = false; | ||||
| 
 | ||||
| 			if (neg) | ||||
| 				new_caps &= ~cap; | ||||
| 			else | ||||
| 				new_caps |=  cap; | ||||
| 		} | ||||
| 
 | ||||
| 		if (success) | ||||
| 		{ | ||||
| 			c->caps = new_caps; | ||||
| 			client_send (c, "CAP %s NAK :%s", target, params); | ||||
| 		} | ||||
| 		else | ||||
| 			client_send (c, "CAP %s ACK :%s", target, params); | ||||
| 	} | ||||
| 	else if (!irc_strcmp (subcommand, "ACK")) | ||||
| 	{ | ||||
| 		if (v.len) | ||||
| 			irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, | ||||
| 				subcommand, "No acknowledgable capabilities supported"); | ||||
| 	} | ||||
| 	else if (!irc_strcmp (subcommand, "END")) | ||||
| 	{ | ||||
| 		c->cap_negotiating = false; | ||||
| 		irc_try_finish_registration (c); | ||||
| 	} | ||||
| 	else | ||||
| 	struct irc_cap_command *cmd = | ||||
| 		str_map_find (&c->ctx->cap_handlers, args.subcommand); | ||||
| 	if (!cmd) | ||||
| 		irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, | ||||
| 			subcommand, "Invalid CAP subcommand"); | ||||
| 			args.subcommand, "Invalid CAP subcommand"); | ||||
| 	else | ||||
| 		cmd->handler (c, &args); | ||||
| 
 | ||||
| 	str_vector_free (&v); | ||||
| 	str_vector_free (&args.params); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| @ -1994,7 +2058,7 @@ irc_append_prefixes (struct client *c, struct channel_user *user, | ||||
| 
 | ||||
| 	if (prefixes.len) | ||||
| 	{ | ||||
| 		if (c->caps & IRC_CAP_MULTI_PREFIX) | ||||
| 		if (c->caps_enabled & IRC_CAP_MULTI_PREFIX) | ||||
| 			str_append   (output, prefixes.str); | ||||
| 		else | ||||
| 			str_append_c (output, prefixes.str[0]); | ||||
| @ -3508,6 +3572,7 @@ main (int argc, char *argv[]) | ||||
| 	struct server_context ctx; | ||||
| 	server_context_init (&ctx); | ||||
| 	irc_register_handlers (&ctx); | ||||
| 	irc_register_cap_handlers (&ctx); | ||||
| 
 | ||||
| 	struct error *e = NULL; | ||||
| 	if (!read_config_file (&ctx.config, &e)) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user