degesch: halfplement /msg, /query, /notice
Which involved some refactoring.
This commit is contained in:
		
							parent
							
								
									2633eda69a
								
							
						
					
					
						commit
						c088f081d7
					
				
							
								
								
									
										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 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user