diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/kike.c | 114 | 
1 files changed, 100 insertions, 14 deletions
| @@ -292,20 +292,24 @@ client_free (struct client *self)  	free (self->away_message);  } +static void +client_mode_to_str (unsigned m, struct str *out) +{ +	if (m & IRC_USER_MODE_INVISIBLE)           str_append_c (out, 'i'); +	if (m & IRC_USER_MODE_RX_WALLOPS)          str_append_c (out, 'w'); +	if (m & IRC_USER_MODE_RESTRICTED)          str_append_c (out, 'r'); +	if (m & IRC_USER_MODE_OPERATOR)            str_append_c (out, 'o'); +	if (m & IRC_USER_MODE_RX_SERVER_NOTICES)   str_append_c (out, 's'); +} +  static char *  client_get_mode (struct client *self)  {  	struct str mode;  	str_init (&mode); - -	if (self->away_message)                    str_append_c (&mode, 'a'); - -	unsigned m = self->mode; -	if (m & IRC_USER_MODE_INVISIBLE)           str_append_c (&mode, 'i'); -	if (m & IRC_USER_MODE_RX_WALLOPS)          str_append_c (&mode, 'w'); -	if (m & IRC_USER_MODE_RESTRICTED)          str_append_c (&mode, 'r'); -	if (m & IRC_USER_MODE_OPERATOR)            str_append_c (&mode, 'o'); -	if (m & IRC_USER_MODE_RX_SERVER_NOTICES)   str_append_c (&mode, 's'); +	if (self->away_message) +		str_append_c (&mode, 'a'); +	client_mode_to_str (self->mode, &mode);  	return str_steal (&mode);  } @@ -887,6 +891,7 @@ enum  	IRC_ERR_NOPRIVILEGES          = 481,  	IRC_ERR_CHANOPRIVSNEEDED      = 482, +	IRC_ERR_UMODEUNKNOWNFLAG      = 501,  	IRC_ERR_USERSDONTMATCH        = 502  }; @@ -963,6 +968,7 @@ static const char *g_default_replies[] =  	[IRC_ERR_NOPRIVILEGES] = ":Permission Denied- You're not an IRC operator",  	[IRC_ERR_CHANOPRIVSNEEDED] = "%s :You're not channel operator", +	[IRC_ERR_UMODEUNKNOWNFLAG] = ":Unknown MODE flag",  	[IRC_ERR_USERSDONTMATCH] = ":Cannot change mode for other users",  }; @@ -1296,6 +1302,83 @@ irc_maybe_send_channel_list (struct client *c, struct channel *chan,  }  static void +irc_modify_mode (unsigned *mask, unsigned mode, bool add) +{ +	if (add) +		*mask |= mode; +	else +		*mask &= ~mode; +} + +static void +irc_update_user_mode (struct client *c, unsigned new_mode) +{ +	unsigned old_mode = c->mode; +	c->mode = new_mode; + +	unsigned added   = new_mode & ~old_mode; +	unsigned removed = old_mode & ~new_mode; + +	struct str diff; +	str_init (&diff); + +	if (added) +	{ +		str_append_c (&diff, '+'); +		client_mode_to_str (added, &diff); +	} +	if (removed) +	{ +		str_append_c (&diff, '-'); +		client_mode_to_str (removed, &diff); +	} + +	if (diff.len) +		irc_send (c, ":%s MODE %s :%s", +			c->nickname, c->nickname, diff.str); +	str_free (&diff); +} + +static void +irc_handle_user_mode_change (struct client *c, const char *mode_string) +{ +	unsigned new_mode = c->mode; +	bool adding = true; + +	while (*mode_string) +	switch (*mode_string++) +	{ +	case '+':  adding = true;   break; +	case '-':  adding = false;  break; + +	case 'a': +		// Ignore, the client should use AWAY +		break; +	case 'i': +		irc_modify_mode (&new_mode, IRC_USER_MODE_INVISIBLE, adding); +		break; +	case 'w': +		irc_modify_mode (&new_mode, IRC_USER_MODE_RX_WALLOPS, adding); +		break; +	case 'r': +		if (adding) +			irc_modify_mode (&new_mode, IRC_USER_MODE_RESTRICTED, true); +		break; +	case 'o': +		if (!adding) +			irc_modify_mode (&new_mode, IRC_USER_MODE_OPERATOR, false); +		// TODO: check public key fingerprint when adding +		break; +	case 's': +		irc_modify_mode (&new_mode, IRC_USER_MODE_RX_SERVER_NOTICES, adding); +		break; +	default: +		RETURN_WITH_REPLY (c, IRC_ERR_UMODEUNKNOWNFLAG); +	} +	irc_update_user_mode (c, new_mode); +} + +static void  irc_handle_mode (const struct irc_message *msg, struct client *c)  {  	if (msg->params.len < 1) @@ -1308,11 +1391,14 @@ irc_handle_mode (const struct irc_message *msg, struct client *c)  		if (irc_strcmp (target, c->nickname))  			RETURN_WITH_REPLY (c, IRC_ERR_USERSDONTMATCH); -		char *mode = client_get_mode (client); -		irc_send_reply (c, IRC_RPL_UMODEIS, mode); -		free (mode); - -		// TODO: mode modification +		if (msg->params.len < 2) +		{ +			char *mode = client_get_mode (client); +			irc_send_reply (c, IRC_RPL_UMODEIS, mode); +			free (mode); +		} +		else +			irc_handle_user_mode_change (c, msg->params.vector[1]);  		return;  	} | 
