diff options
| -rw-r--r-- | kike-replies | 1 | ||||
| -rw-r--r-- | kike.c | 67 | 
2 files changed, 68 insertions, 0 deletions
diff --git a/kike-replies b/kike-replies index c5deec5..c585740 100644 --- a/kike-replies +++ b/kike-replies @@ -48,6 +48,7 @@  403 IRC_ERR_NOSUCHCHANNEL "%s :No such channel"  404 IRC_ERR_CANNOTSENDTOCHAN "%s :Cannot send to channel"  409 IRC_ERR_NOORIGIN ":No origin specified" +410 IRC_ERR_INVALIDCAPCMD "%s :%s"  411 IRC_ERR_NORECIPIENT ":No recipient given (%s)"  412 IRC_ERR_NOTEXTTOSEND ":No text to send"  421 IRC_ERR_UNKNOWNCOMMAND "%s: Unknown command" @@ -308,10 +308,13 @@ struct client  	struct poller_timer kill_timer;     ///< Hard kill timeout  	bool initialized;                   ///< Has any data been received yet? +	bool cap_negotiating;               ///< Negotiating capabilities  	bool registered;                    ///< The user has registered  	bool closing_link;                  ///< Closing link  	bool half_closed;                   ///< Closing link: conn. is half-closed +	unsigned long cap_version;          ///< CAP protocol version +  	bool ssl_rx_want_tx;                ///< SSL_read() wants to write  	bool ssl_tx_want_rx;                ///< SSL_write() wants to read  	SSL *ssl;                           ///< SSL connection @@ -339,6 +342,7 @@ client_init (struct client *self)  	self->socket_fd = -1;  	str_init (&self->read_buffer);  	str_init (&self->write_buffer); +	self->cap_version = 301;  	// TODO: make this configurable and more fine-grained  	flood_detector_init (&self->antiflood, 10, 20);  	str_map_init (&self->invites); @@ -1078,6 +1082,8 @@ irc_try_finish_registration (struct client *c)  	struct server_context *ctx = c->ctx;  	if (!c->nickname || !c->username || !c->realname)  		return; +	if (c->registered || c->cap_negotiating) +		return;  	c->registered = true;  	irc_send_reply (c, IRC_RPL_WELCOME, c->nickname, c->username, c->hostname); @@ -1110,6 +1116,66 @@ irc_try_finish_registration (struct client *c)  }  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); + +	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); +	} + +	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; +		// TODO: actually implement a few capabilities +		client_send (c, "CAP %s LS :", target); +	} +	else if (!irc_strcmp (subcommand, "LIST")) +	{ +		// TODO: list currently enabled capabilities +		client_send (c, "CAP %s LIST :", target); +	} +	else if (!irc_strcmp (subcommand, "REQ")) +	{ +		c->cap_negotiating = true; +		// TODO: process the capability change request, "-" disables +		if (v.len) +			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 +		irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, +			subcommand, "Invalid CAP subcommand"); + +	str_vector_free (&v); +} + +static void  irc_handle_pass (const struct irc_message *msg, struct client *c)  {  	if (c->registered) @@ -2516,6 +2582,7 @@ irc_register_handlers (struct server_context *ctx)  	// TODO: add a field for oper-only commands?  	static const struct irc_command message_handlers[] =  	{ +		{ "CAP",      false, irc_handle_cap      },  		{ "PASS",     false, irc_handle_pass     },  		{ "NICK",     false, irc_handle_nick     },  		{ "USER",     false, irc_handle_user     },  | 
