degesch: halfplement /msg, /query, /notice
Which involved some refactoring.
This commit is contained in:
		
							
								
								
									
										314
									
								
								degesch.c
									
									
									
									
									
								
							
							
						
						
									
										314
									
								
								degesch.c
									
									
									
									
									
								
							@@ -2381,6 +2381,155 @@ irc_process_message (const struct irc_message *msg,
 | 
			
		||||
		irc_process_numeric (ctx, msg, numeric);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Message autosplitting magic ---------------------------------------------
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
wrap_text (const char *message,
 | 
			
		||||
	int line_max, struct str_vector *output, struct error **e)
 | 
			
		||||
{
 | 
			
		||||
	// Attempt to split the message if it doesn't completely fit into a single
 | 
			
		||||
	// IRC protocol message while trying not to break UTF-8.  Unicode can still
 | 
			
		||||
	// end up being wrong, though.  As well as any mIRC formatting.
 | 
			
		||||
	//
 | 
			
		||||
	// TODO: at least try to word-wrap if nothing else
 | 
			
		||||
 | 
			
		||||
	for (int message_left = strlen (message); message_left; )
 | 
			
		||||
	{
 | 
			
		||||
		struct str m;
 | 
			
		||||
		str_init (&m);
 | 
			
		||||
 | 
			
		||||
		int part_left = MIN (line_max, message_left);
 | 
			
		||||
		bool empty = true;
 | 
			
		||||
		while (true)
 | 
			
		||||
		{
 | 
			
		||||
			const char *next = utf8_next (message, message_left);
 | 
			
		||||
			hard_assert (next);
 | 
			
		||||
 | 
			
		||||
			int char_len = message - next;
 | 
			
		||||
			if (char_len > part_left)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			str_append_data (&m, message, char_len);
 | 
			
		||||
 | 
			
		||||
			message += char_len;
 | 
			
		||||
			message_left -= char_len;
 | 
			
		||||
			empty = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!empty)
 | 
			
		||||
			str_vector_add (output, m.str);
 | 
			
		||||
 | 
			
		||||
		str_free (&m);
 | 
			
		||||
 | 
			
		||||
		if (empty)
 | 
			
		||||
		{
 | 
			
		||||
			// Well, that's just weird
 | 
			
		||||
			error_set (e,
 | 
			
		||||
				"Message splitting was unsuccessful as there was "
 | 
			
		||||
				"too little room for UTF-8 characters");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
			
		||||
 | 
			
		||||
/// Automatically splits messages that arrive at other clients with our prefix
 | 
			
		||||
/// so that they don't arrive cut off by the server
 | 
			
		||||
static bool
 | 
			
		||||
irc_autosplit_message (struct app_context *ctx, const char *message,
 | 
			
		||||
	int fixed_part, struct str_vector *output, struct error **e)
 | 
			
		||||
{
 | 
			
		||||
	// :<nick>!<user>@<host> <fixed-part><message>
 | 
			
		||||
	int space_in_one_message = 0;
 | 
			
		||||
	if (ctx->irc_user_host)
 | 
			
		||||
		space_in_one_message = 510
 | 
			
		||||
			- 1 - (int) strlen (ctx->irc_user->nickname)
 | 
			
		||||
			- 1 - (int) strlen (ctx->irc_user_host)
 | 
			
		||||
			- 1 - fixed_part;
 | 
			
		||||
 | 
			
		||||
	// However we don't always have the full info for message splitting
 | 
			
		||||
	if (!space_in_one_message)
 | 
			
		||||
		str_vector_add (output, message);
 | 
			
		||||
	else if (!wrap_text (message, space_in_one_message, output, e))
 | 
			
		||||
		return false;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct send_autosplit_args;
 | 
			
		||||
 | 
			
		||||
typedef void (*send_autosplit_logger_fn) (struct app_context *ctx,
 | 
			
		||||
	struct send_autosplit_args *args, struct buffer *buffer, const char *line);
 | 
			
		||||
 | 
			
		||||
struct send_autosplit_args
 | 
			
		||||
{
 | 
			
		||||
	const char *command;                ///< E.g. PRIVMSG or NOTICE
 | 
			
		||||
	const char *target;                 ///< User or channel
 | 
			
		||||
	const char *message;                ///< A message to be autosplit
 | 
			
		||||
	send_autosplit_logger_fn logger;    ///< Logger for all resulting lines
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
send_autosplit_message (struct app_context *ctx, struct send_autosplit_args a)
 | 
			
		||||
{
 | 
			
		||||
	struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, a.target);
 | 
			
		||||
	int fixed_part = strlen (a.command) + 1 + strlen (a.target) + 1 + 1;
 | 
			
		||||
 | 
			
		||||
	struct str_vector lines;
 | 
			
		||||
	str_vector_init (&lines);
 | 
			
		||||
	struct error *e = NULL;
 | 
			
		||||
	if (!irc_autosplit_message (ctx, a.message, fixed_part, &lines, &e))
 | 
			
		||||
	{
 | 
			
		||||
		buffer_send_error (ctx,
 | 
			
		||||
			buffer ? buffer : ctx->server_buffer, "%s", e->message);
 | 
			
		||||
		error_free (e);
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i < lines.len; i++)
 | 
			
		||||
	{
 | 
			
		||||
		irc_send (ctx, "%s %s :%s", a.command, a.target, lines.vector[i]);
 | 
			
		||||
		a.logger (ctx, &a, buffer, lines.vector[i]);
 | 
			
		||||
	}
 | 
			
		||||
end:
 | 
			
		||||
	str_vector_free (&lines);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
log_outcoming_privmsg (struct app_context *ctx,
 | 
			
		||||
	struct send_autosplit_args *a, struct buffer *buffer, const char *line)
 | 
			
		||||
{
 | 
			
		||||
	if (buffer)
 | 
			
		||||
		buffer_send (ctx, buffer, BUFFER_LINE_PRIVMSG, 0,
 | 
			
		||||
			ctx->irc_user->nickname, NULL, "%s", line);
 | 
			
		||||
	else
 | 
			
		||||
		// TODO: fix logging
 | 
			
		||||
		buffer_send (ctx, ctx->server_buffer, BUFFER_LINE_STATUS, 0,
 | 
			
		||||
			NULL, NULL, "MSG(%s): %s", a->target, line);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define SEND_AUTOSPLIT_PRIVMSG(ctx, target, message)                           \
 | 
			
		||||
	send_autosplit_message ((ctx), (struct send_autosplit_args)                \
 | 
			
		||||
		{ "PRIVMSG", (target), (message), log_outcoming_privmsg })
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
log_outcoming_notice (struct app_context *ctx,
 | 
			
		||||
	struct send_autosplit_args *a, struct buffer *buffer, const char *line)
 | 
			
		||||
{
 | 
			
		||||
	if (buffer)
 | 
			
		||||
		buffer_send (ctx, buffer, BUFFER_LINE_NOTICE, 0,
 | 
			
		||||
			ctx->irc_user->nickname, NULL, "%s", line);
 | 
			
		||||
	else
 | 
			
		||||
		// TODO: fix logging
 | 
			
		||||
		buffer_send (ctx, ctx->server_buffer, BUFFER_LINE_STATUS, 0,
 | 
			
		||||
			NULL, NULL, "Notice -> %s: %s", a->target, line);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define SEND_AUTOSPLIT_NOTICE(ctx, target, message)                            \
 | 
			
		||||
	send_autosplit_message ((ctx), (struct send_autosplit_args)                \
 | 
			
		||||
		{ "NOTICE", (target), (message), log_outcoming_notice })
 | 
			
		||||
 | 
			
		||||
// --- User input handling -----------------------------------------------------
 | 
			
		||||
 | 
			
		||||
static void handle_command_help (struct app_context *, char *);
 | 
			
		||||
@@ -2490,6 +2639,108 @@ handle_command_buffer (struct app_context *ctx, char *arguments)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
handle_command_msg (struct app_context *ctx, char *arguments)
 | 
			
		||||
{
 | 
			
		||||
	if (ctx->current_buffer->type == BUFFER_GLOBAL)
 | 
			
		||||
	{
 | 
			
		||||
		buffer_send_error (ctx, ctx->current_buffer,
 | 
			
		||||
			"Can't send messages from a global buffer");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ctx->irc_fd == -1)
 | 
			
		||||
	{
 | 
			
		||||
		buffer_send_error (ctx, ctx->server_buffer, "Not connected");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!*arguments)
 | 
			
		||||
	{
 | 
			
		||||
		// TODO: show usage or something
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	char *target = cut_word (&arguments);
 | 
			
		||||
	if (!*arguments)
 | 
			
		||||
		buffer_send_error (ctx, ctx->server_buffer, "No text to send");
 | 
			
		||||
	else
 | 
			
		||||
		SEND_AUTOSPLIT_PRIVMSG (ctx, target, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
handle_command_query (struct app_context *ctx, char *arguments)
 | 
			
		||||
{
 | 
			
		||||
	if (ctx->current_buffer->type == BUFFER_GLOBAL)
 | 
			
		||||
	{
 | 
			
		||||
		buffer_send_error (ctx, ctx->current_buffer,
 | 
			
		||||
			"Can't send messages from a global buffer");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ctx->irc_fd == -1)
 | 
			
		||||
	{
 | 
			
		||||
		buffer_send_error (ctx, ctx->server_buffer, "Not connected");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!*arguments)
 | 
			
		||||
	{
 | 
			
		||||
		// TODO: show usage or something
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	char *target = cut_word (&arguments);
 | 
			
		||||
	if (irc_is_channel (ctx, target))
 | 
			
		||||
	{
 | 
			
		||||
		buffer_send_error (ctx, ctx->server_buffer, "Cannot query a channel");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!*arguments)
 | 
			
		||||
		buffer_send_error (ctx, ctx->server_buffer, "No text to send");
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, target);
 | 
			
		||||
		if (!buffer)
 | 
			
		||||
		{
 | 
			
		||||
			// TODO: create a buffer for this user
 | 
			
		||||
			// TODO: see irc_handle_privmsg
 | 
			
		||||
		}
 | 
			
		||||
		buffer_activate (ctx, buffer);
 | 
			
		||||
		SEND_AUTOSPLIT_PRIVMSG (ctx, target, arguments);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
handle_command_notice (struct app_context *ctx, char *arguments)
 | 
			
		||||
{
 | 
			
		||||
	if (ctx->current_buffer->type == BUFFER_GLOBAL)
 | 
			
		||||
	{
 | 
			
		||||
		buffer_send_error (ctx, ctx->current_buffer,
 | 
			
		||||
			"Can't send messages from a global buffer");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ctx->irc_fd == -1)
 | 
			
		||||
	{
 | 
			
		||||
		buffer_send_error (ctx, ctx->server_buffer, "Not connected");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!*arguments)
 | 
			
		||||
	{
 | 
			
		||||
		// TODO: show usage or something
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	char *target = cut_word (&arguments);
 | 
			
		||||
	if (!*arguments)
 | 
			
		||||
		buffer_send_error (ctx, ctx->server_buffer, "No text to send");
 | 
			
		||||
	else
 | 
			
		||||
		SEND_AUTOSPLIT_NOTICE (ctx, target, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
handle_command_quit (struct app_context *ctx, char *arguments)
 | 
			
		||||
{
 | 
			
		||||
@@ -2599,10 +2850,14 @@ g_command_handlers[] =
 | 
			
		||||
	  "[message]" },
 | 
			
		||||
	{ "buffer",  handle_command_buffer, "Manage buffers",
 | 
			
		||||
	  "list | clear | move | { close [<number> | <name>] } | <number>" },
 | 
			
		||||
 | 
			
		||||
	{ "msg",     handle_command_msg,    "Send message to a nick or channel",
 | 
			
		||||
	  "<target> <message>" },
 | 
			
		||||
	{ "query",   handle_command_query,  "Send a private message to a nick",
 | 
			
		||||
	  "<nick> <message>" },
 | 
			
		||||
	{ "notice",  handle_command_notice, "Send notice to a nick or channel",
 | 
			
		||||
	  "<target> <message>" },
 | 
			
		||||
#if 0
 | 
			
		||||
	{ "msg",     NULL, "", "" },
 | 
			
		||||
	{ "query",   NULL, "", "" },
 | 
			
		||||
	{ "notice",  NULL, "", "" },
 | 
			
		||||
	{ "ctcp",    NULL, "", "" },
 | 
			
		||||
	{ "me",      NULL, "", "" },
 | 
			
		||||
#endif
 | 
			
		||||
@@ -2722,58 +2977,7 @@ send_message_to_target (struct app_context *ctx,
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We don't always have the full info for message splitting
 | 
			
		||||
	int space_in_one_message = 0;
 | 
			
		||||
	if (ctx->irc_user_host)
 | 
			
		||||
		// :<nick>!<user>@<host> PRIVMSG <target> :<message>
 | 
			
		||||
		space_in_one_message = 510
 | 
			
		||||
			- 1 - (int) strlen (ctx->irc_user->nickname)
 | 
			
		||||
			- 1 - (int) strlen (ctx->irc_user_host)
 | 
			
		||||
			- 1 - 7 - 1 - strlen (target) - 1 - 1;
 | 
			
		||||
 | 
			
		||||
	// Attempt to split the message if it doesn't completely fit into
 | 
			
		||||
	// a single IRC protocol message while trying not to break UTF-8.
 | 
			
		||||
	// Unicode can still end up being wrong, though.
 | 
			
		||||
	// TODO: at least try to word-wrap if nothing else
 | 
			
		||||
	for (int message_left = strlen (message); message_left; )
 | 
			
		||||
	{
 | 
			
		||||
		struct str m;
 | 
			
		||||
		str_init (&m);
 | 
			
		||||
 | 
			
		||||
		int part_left = MIN (space_in_one_message, message_left);
 | 
			
		||||
		if (!space_in_one_message)
 | 
			
		||||
			part_left = message_left;
 | 
			
		||||
 | 
			
		||||
		bool empty = true;
 | 
			
		||||
		while (true)
 | 
			
		||||
		{
 | 
			
		||||
			const char *next = utf8_next (message, message_left);
 | 
			
		||||
			hard_assert (next);
 | 
			
		||||
 | 
			
		||||
			int char_len = message - next;
 | 
			
		||||
			if (char_len > part_left)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			str_append_data (&m, message, char_len);
 | 
			
		||||
 | 
			
		||||
			message += char_len;
 | 
			
		||||
			message_left -= char_len;
 | 
			
		||||
			empty = false;
 | 
			
		||||
		}
 | 
			
		||||
		if (empty)
 | 
			
		||||
		{
 | 
			
		||||
			// Well, that's just weird
 | 
			
		||||
			buffer_send_error (ctx, buffer, "%s",
 | 
			
		||||
				"Message splitting was unsuccessful as there was "
 | 
			
		||||
				"too little room for UTF-8 characters");
 | 
			
		||||
			message_left = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		irc_send (ctx, "PRIVMSG %s :%s", target, m.str);
 | 
			
		||||
		buffer_send (ctx, buffer, BUFFER_LINE_PRIVMSG, 0,
 | 
			
		||||
			ctx->irc_user->nickname, NULL, "%s", m.str);
 | 
			
		||||
		str_free (&m);
 | 
			
		||||
	}
 | 
			
		||||
	SEND_AUTOSPLIT_PRIVMSG (ctx, target, message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user