kike: fix RPL_WHOISCHANNELS and RPL_NAMREPLY
Now we automatically split the lists and send multiple replies, but only if it's needed.
This commit is contained in:
		
							
								
								
									
										81
									
								
								kike.c
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								kike.c
									
									
									
									
									
								
							@@ -944,23 +944,66 @@ client_set_ping_timer (struct client *c)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// --- IRC command handling ----------------------------------------------------
 | 
					// --- IRC command handling ----------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					irc_make_reply (struct client *c, int id, va_list ap, struct str *output)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						str_append_printf (output, ":%s %03d %s ",
 | 
				
			||||||
 | 
							c->ctx->server_name, id, c->nickname ? c->nickname : "");
 | 
				
			||||||
 | 
						str_append_vprintf (output,
 | 
				
			||||||
 | 
							irc_get_text (c->ctx, id, g_default_replies[id]), ap);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// XXX: this way we cannot typecheck the arguments, so we must be careful
 | 
					// XXX: this way we cannot typecheck the arguments, so we must be careful
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
irc_send_reply (struct client *c, int id, ...)
 | 
					irc_send_reply (struct client *c, int id, ...)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct str tmp;
 | 
						struct str reply;
 | 
				
			||||||
	str_init (&tmp);
 | 
						str_init (&reply);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	va_list ap;
 | 
						va_list ap;
 | 
				
			||||||
	va_start (ap, id);
 | 
						va_start (ap, id);
 | 
				
			||||||
	str_append_printf (&tmp, ":%s %03d %s ",
 | 
						irc_make_reply (c, id, ap, &reply);
 | 
				
			||||||
		c->ctx->server_name, id, c->nickname ? c->nickname : "");
 | 
					 | 
				
			||||||
	str_append_vprintf (&tmp,
 | 
					 | 
				
			||||||
		irc_get_text (c->ctx, id, g_default_replies[id]), ap);
 | 
					 | 
				
			||||||
	va_end (ap);
 | 
						va_end (ap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	client_send_str (c, &tmp);
 | 
						client_send_str (c, &reply);
 | 
				
			||||||
	str_free (&tmp);
 | 
						str_free (&reply);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Send a space-separated list of words across as many replies as needed
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					irc_send_reply_vector (struct client *c, int id, char **items, ...)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct str common;
 | 
				
			||||||
 | 
						str_init (&common);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						va_list ap;
 | 
				
			||||||
 | 
						va_start (ap, items);
 | 
				
			||||||
 | 
						irc_make_reply (c, id, ap, &common);
 | 
				
			||||||
 | 
						va_end (ap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We always send at least one message (there might be a client that
 | 
				
			||||||
 | 
						// expects us to send this message at least once)
 | 
				
			||||||
 | 
						do
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							struct str reply;
 | 
				
			||||||
 | 
							str_init (&reply);
 | 
				
			||||||
 | 
							str_append_str (&reply, &common);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// If not even a single item fits in the limit (which may happen,
 | 
				
			||||||
 | 
							// in theory) it just gets cropped.  We could also skip it.
 | 
				
			||||||
 | 
							if (*items)
 | 
				
			||||||
 | 
								str_append (&reply, *items++);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Append as many items as fits in a single message
 | 
				
			||||||
 | 
							while (*items
 | 
				
			||||||
 | 
								&& reply.len + 1 + strlen (*items) <= IRC_MAX_MESSAGE_LENGTH)
 | 
				
			||||||
 | 
								str_append_printf (&reply, " %s", *items++);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							client_send_str (c, &reply);
 | 
				
			||||||
 | 
							str_free (&reply);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						while (*items);
 | 
				
			||||||
 | 
						str_free (&common);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define RETURN_WITH_REPLY(c, ...)                                              \
 | 
					#define RETURN_WITH_REPLY(c, ...)                                              \
 | 
				
			||||||
@@ -1858,13 +1901,8 @@ irc_send_rpl_namreply (struct client *c, const struct channel *chan)
 | 
				
			|||||||
		str_vector_add_owned (&nicks, str_steal (&result));
 | 
							str_vector_add_owned (&nicks, str_steal (&result));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nicks.len)
 | 
						irc_send_reply_vector (c, IRC_RPL_NAMREPLY,
 | 
				
			||||||
	{
 | 
							nicks.vector, type, chan->name, "");
 | 
				
			||||||
		// FIXME: split it into multiple messages if it's too long
 | 
					 | 
				
			||||||
		char *reply = join_str_vector (&nicks, ' ');
 | 
					 | 
				
			||||||
		irc_send_reply (c, IRC_RPL_NAMREPLY, type, chan->name, reply);
 | 
					 | 
				
			||||||
		free (reply);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	str_vector_free (&nicks);
 | 
						str_vector_free (&nicks);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2024,6 +2062,9 @@ irc_send_whois_reply (struct client *c, const struct client *target)
 | 
				
			|||||||
	if (target->away_message)
 | 
						if (target->away_message)
 | 
				
			||||||
		irc_send_reply (c, IRC_RPL_AWAY, nick, target->away_message);
 | 
							irc_send_reply (c, IRC_RPL_AWAY, nick, target->away_message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct str_vector channels;
 | 
				
			||||||
 | 
						str_vector_init (&channels);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct str_map_iter iter;
 | 
						struct str_map_iter iter;
 | 
				
			||||||
	str_map_iter_init (&iter, &c->ctx->channels);
 | 
						str_map_iter_init (&iter, &c->ctx->channels);
 | 
				
			||||||
	struct channel *chan;
 | 
						struct channel *chan;
 | 
				
			||||||
@@ -2040,14 +2081,12 @@ irc_send_whois_reply (struct client *c, const struct client *target)
 | 
				
			|||||||
			else if (channel_user->modes & IRC_CHAN_MODE_VOICE)
 | 
								else if (channel_user->modes & IRC_CHAN_MODE_VOICE)
 | 
				
			||||||
				str_append_c (&item, '+');
 | 
									str_append_c (&item, '+');
 | 
				
			||||||
			str_append (&item, chan->name);
 | 
								str_append (&item, chan->name);
 | 
				
			||||||
			str_append_c (&item, ' ');
 | 
								str_vector_add_owned (&channels, str_steal (&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_vector (c, IRC_RPL_WHOISCHANNELS, channels.vector, nick, "");
 | 
				
			||||||
 | 
						str_vector_free (&channels);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	irc_send_reply (c, IRC_RPL_ENDOFWHOIS, nick);
 | 
						irc_send_reply (c, IRC_RPL_ENDOFWHOIS, nick);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user