diff options
| -rw-r--r-- | degesch.c | 125 | 
1 files changed, 106 insertions, 19 deletions
| @@ -1310,10 +1310,9 @@ struct user  	REF_COUNTABLE_HEADER  	char *nickname;                     ///< Literal nickname -	// TODO: write code to poll for the away status  	bool away;                          ///< User is away -	struct user_channel *channels;      ///< Channels the user is on +	struct user_channel *channels;      ///< Channels the user is on (with us)  };  static struct ispect_field g_user_ispect[] = @@ -1379,6 +1378,8 @@ struct channel  {  	REF_COUNTABLE_HEADER +	struct server *s;                   ///< Server +  	char *name;                         ///< Channel name  	char *topic;                        ///< Channel topic @@ -1391,6 +1392,7 @@ struct channel  	size_t users_len;                   ///< User count  	bool left_manually;                 ///< Don't rejoin on reconnect +	bool show_names_after_who;          ///< RPL_ENDOFWHO delays RPL_ENDOFNAMES  };  static struct ispect_field g_channel_ispect[] = @@ -1405,12 +1407,12 @@ static struct ispect_field g_channel_ispect[] =  };  static struct channel * -channel_new (char *name, char *topic) +channel_new (struct server *s, char *name)  {  	struct channel *self = xcalloc (1, sizeof *self);  	self->ref_count = 1; +	self->s = s;  	self->name = name; -	self->topic = topic;  	self->no_param_modes = str_make ();  	self->param_modes = str_map_make (free);  	self->names_buf = strv_make (); @@ -1714,7 +1716,8 @@ struct server  	char *irc_user_host;                ///< Our current user@host  	bool autoaway_active;               ///< Autoaway is currently active -	bool cap_echo_message;              ///< Whether the server echos messages +	bool cap_echo_message;              ///< Whether the server echoes messages +	bool cap_away_notify;               ///< Whether we get AWAY notifications  	// Server-specific information (from RPL_ISUPPORT): @@ -2329,7 +2332,8 @@ static struct config_schema g_config_server[] =  	  .comment   = "Capabilities to use if supported by server",  	  .type      = CONFIG_ITEM_STRING_ARRAY,  	  .validate  = config_validate_nonjunk_string, -	  .default_  = "\"multi-prefix,invite-notify,server-time,echo-message\"" }, +	  .default_  = "\"multi-prefix,invite-notify,server-time,echo-message," +		"message-tags,away-notify\"" },  	{ .name      = "tls",  	  .comment   = "Whether to use TLS", @@ -4523,6 +4527,11 @@ irc_channel_unlink_user  			user_channel_destroy (iter);  		} +	// TODO: poll the away status for users we don't share a channel with. +	//   It might or might not be worth to auto-set this on with RPL_AWAY. +	if (!user->channels && user != channel->s->irc_user) +		user->away = false; +  	// Then just unlink the user from the channel  	LIST_UNLINK (channel->users, channel_user);  	channel_user_destroy (channel_user); @@ -4545,7 +4554,7 @@ irc_make_channel (struct server *s, char *name)  {  	hard_assert (!str_map_find (&s->irc_channels, name)); -	struct channel *channel = channel_new (name, NULL); +	struct channel *channel = channel_new (s, name);  	(void) channel_weak_ref (channel, irc_channel_on_destroy, s);  	str_map_set (&s->irc_channels, channel->name, channel);  	return channel; @@ -4571,6 +4580,9 @@ irc_remove_user_from_channel (struct user *user, struct channel *channel)  static void  irc_left_channel (struct channel *channel)  { +	strv_reset (&channel->names_buf); +	channel->show_names_after_who = false; +  	LIST_FOR_EACH (struct channel_user, iter, channel->users)  		irc_channel_unlink_user (channel, iter);  } @@ -6457,6 +6469,21 @@ irc_process_sent_message (const struct irc_message *msg, struct server *s)  // --- Input handling ----------------------------------------------------------  static void +irc_handle_away (struct server *s, const struct irc_message *msg) +{ +	if (!msg->prefix) +		return; + +	char *nickname = irc_cut_nickname (msg->prefix); +	struct user *user = str_map_find (&s->irc_users, nickname); +	free (nickname); + +	// Let's allow the server to make us away +	if (user) +		user->away = !!msg->params.len; +} + +static void  irc_handle_cap (struct server *s, const struct irc_message *msg)  {  	if (msg->params.len < 2) @@ -6483,6 +6510,8 @@ irc_handle_cap (struct server *s, const struct irc_message *msg)  			}  			if (!strcasecmp_ascii (cap, "echo-message"))  				s->cap_echo_message = active; +			if (!strcasecmp_ascii (cap, "away-notify")) +				s->cap_away_notify = active;  		}  		irc_send (s, "CAP END");  	} @@ -6596,6 +6625,9 @@ irc_handle_join (struct server *s, const struct irc_message *msg)  		str_reset (&channel->no_param_modes);  		str_map_clear (&channel->param_modes);  		irc_send (s, "MODE %s", channel_name); + +		if ((channel->show_names_after_who = s->cap_away_notify)) +			irc_send (s, "WHO %s", channel_name);  	}  	// Add the user to the channel @@ -7153,6 +7185,7 @@ irc_handle_topic (struct server *s, const struct irc_message *msg)  static struct irc_handler g_irc_handlers[] =  {  	// This list needs to stay sorted +	{ "AWAY",    irc_handle_away    },  	{ "CAP",     irc_handle_cap     },  	{ "ERROR",   irc_handle_error   },  	{ "INVITE",  irc_handle_invite  }, @@ -7359,11 +7392,16 @@ make_channel_users_list (struct server *s, struct channel *channel)  	qsort (entries, n_users, sizeof *entries, channel_user_sort_entry_cmp); +	// Make names of users that are away italicised, constructing a formatter +	// and adding a new attribute seems like unnecessary work  	struct str list = str_make ();  	for (i = 0; i < n_users; i++)  	{ -		irc_get_channel_user_prefix (s, entries[i].channel_user, &list); -		str_append (&list, entries[i].channel_user->user->nickname); +		struct channel_user *channel_user = entries[i].channel_user; +		if (channel_user->user->away) str_append_c (&list, '\x1d'); +		irc_get_channel_user_prefix (s, channel_user, &list); +		str_append (&list, channel_user->user->nickname); +		if (channel_user->user->away) str_append_c (&list, '\x1d');  		str_append_c (&list, ' ');  	}  	if (list.len) @@ -7393,6 +7431,17 @@ irc_sync_channel_user (struct server *s, struct channel *channel,  }  static void +irc_process_names_finish (struct server *s, struct channel *channel) +{ +	struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel->name); +	if (buffer) +	{ +		log_server_status (s, buffer, "Users on #S: #&m", +			channel->name, make_channel_users_list (s, channel)); +	} +} + +static void  irc_process_names (struct server *s, struct channel *channel)  {  	struct str_map present = str_map_make (NULL); @@ -7421,15 +7470,8 @@ irc_process_names (struct server *s, struct channel *channel)  	str_map_free (&present);  	strv_reset (&channel->names_buf); -	char *all_users = make_channel_users_list (s, channel); -	struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel->name); -	if (buffer) -	{ -		log_server_status (s, buffer, "Users on #S: #S", -			channel->name, all_users); -	} - -	free (all_users); +	if (!channel->show_names_after_who) +		irc_process_names_finish (s, channel);  }  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -7453,6 +7495,47 @@ irc_handle_rpl_endofnames (struct server *s, const struct irc_message *msg)  		irc_process_names (s, channel);  } +static bool +irc_handle_rpl_whoreply (struct server *s, const struct irc_message *msg) +{ +	if (msg->params.len < 7) +		return false; + +	// Sequence: channel, user, host, server, nick, chars +	const char *channel_name = msg->params.vector[1]; +	const char *nickname     = msg->params.vector[5]; +	const char *chars        = msg->params.vector[6]; + +	struct channel *channel = str_map_find (&s->irc_channels, channel_name); +	struct user *user = str_map_find (&s->irc_users, nickname); + +	// This makes sense to set only with the away-notify capability so far. +	// We track ourselves by other means and we can't track PM-only users yet. +	if (!channel || !channel->show_names_after_who +	 || !user || user == s->irc_user || !user->channels) +		return false; + +	user->away = *chars == 'G'; +	return true; +} + +static bool +irc_handle_rpl_endofwho (struct server *s, const struct irc_message *msg) +{ +	if (msg->params.len < 2) +		return false; + +	const char *target = msg->params.vector[1]; + +	struct channel *channel = str_map_find (&s->irc_channels, target); +	if (!channel || !channel->show_names_after_who) +		return false; + +	irc_process_names_finish (s, channel); +	channel->show_names_after_who = false; +	return true; +} +  static void  irc_handle_rpl_topic (struct server *s, const struct irc_message *msg)  { @@ -7790,9 +7873,12 @@ irc_process_numeric (struct server *s,  		if (s->irc_user) s->irc_user->away = false;  		break; -	case IRC_RPL_LIST:  	case IRC_RPL_WHOREPLY: +		if (irc_handle_rpl_whoreply (s, msg))  buffer = NULL; break;  	case IRC_RPL_ENDOFWHO: +		if (irc_handle_rpl_endofwho (s, msg))  buffer = NULL; break; + +	case IRC_RPL_LIST:  	case IRC_ERR_UNKNOWNCOMMAND:  	case IRC_ERR_NEEDMOREPARAMS: @@ -8255,6 +8341,7 @@ server_remove (struct app_context *ctx, struct server *s)  static void  server_rename (struct app_context *ctx, struct server *s, const char *new_name)  { +	hard_assert (!str_map_find (&ctx->servers, new_name));  	str_map_set (&ctx->servers, new_name,  		str_map_steal (&ctx->servers, s->name)); | 
