aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--degesch.c314
1 files changed, 259 insertions, 55 deletions
diff --git a/degesch.c b/degesch.c
index e561173..7b0af26 100644
--- a/degesch.c
+++ b/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 *);
@@ -2491,6 +2640,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)
{
if (ctx->irc_fd != -1)
@@ -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