From 3315b16f79a98d43d0335f22558ff78cc4e73564 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Tue, 9 Feb 2016 05:10:41 +0100
Subject: degesch: log messages from /quote and plugins
That is, parse back all output messages and log based on that.
---
NEWS | 2 +
degesch.c | 242 ++++++++++++++++++++++++++++++++++++++------------------------
2 files changed, 151 insertions(+), 93 deletions(-)
diff --git a/NEWS b/NEWS
index 464460b..dd64c9e 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,8 @@
* degesch: optimize buffer memory usage
+ * degesch: added logging of messages sent from /quote and plugins
+
* kike: add support for IRCv3.2 server-time
* Remote addresses are now resolved asynchronously
diff --git a/degesch.c b/degesch.c
index 0a1cb6e..c509d96 100644
--- a/degesch.c
+++ b/degesch.c
@@ -3968,6 +3968,8 @@ irc_queue_reconnect (struct server *s)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+static void irc_process_sent_message
+ (const struct irc_message *msg, struct server *s);
static void irc_send (struct server *s,
const char *format, ...) ATTRIBUTE_PRINTF (2, 3);
@@ -3993,6 +3995,11 @@ irc_send (struct server *s, const char *format, ...)
log_server_debug (s, "#a<< \"#S\"#r", ATTR_PART, str.str);
+ struct irc_message msg;
+ irc_parse_message (&msg, str.str);
+ irc_process_sent_message (&msg, s);
+ irc_free_message (&msg);
+
str_append_str (&s->write_buffer, &str);
str_free (&str);
str_append (&s->write_buffer, "\r\n");
@@ -5431,6 +5438,131 @@ irc_handle_mode_user (struct server *s, char **params)
mode_processor_run (&p, params, mode_processor_apply_user);
}
+// --- Output processing -------------------------------------------------------
+
+// Both user and plugins can send whatever the heck they want to,
+// we need to parse it back so that it's evident what's happening
+
+static void
+irc_handle_sent_cap (struct server *s, const struct irc_message *msg)
+{
+ if (msg->params.len < 2)
+ return;
+
+ const char *subcommand = msg->params.vector[1];
+ const char *args = (msg->params.len > 2) ? msg->params.vector[2] : "";
+ if (!strcasecmp_ascii (subcommand, "REQ"))
+ log_server_status (s, s->buffer,
+ "#s: #S", "Capabilities requested", args);
+}
+
+static void
+irc_handle_sent_notice_text (struct server *s,
+ const struct irc_message *msg, struct str *text)
+{
+ const char *target = msg->params.vector[0];
+ struct buffer *buffer = irc_get_buffer_for_message (s, msg, target);
+ if (buffer && soft_assert (s->irc_user))
+ log_outcoming_notice (s, buffer, s->irc_user->nickname, text->str);
+ else
+ log_outcoming_orphan_notice (s, target, text->str);
+}
+
+static void
+irc_handle_sent_notice (struct server *s, const struct irc_message *msg)
+{
+ if (msg->params.len < 2 || s->cap_echo_message)
+ return;
+
+ // This ignores empty messages which we should not normally send
+ struct ctcp_chunk *chunks = ctcp_parse (msg->params.vector[1]);
+ LIST_FOR_EACH (struct ctcp_chunk, iter, chunks)
+ {
+ if (iter->is_extended)
+ log_ctcp_reply (s, msg->params.vector[0],
+ xstrdup_printf ("%s %s", iter->tag.str, iter->text.str));
+ else
+ irc_handle_sent_notice_text (s, msg, &iter->text);
+ }
+ ctcp_destroy (chunks);
+}
+
+static void
+irc_handle_sent_privmsg_text (struct server *s,
+ const struct irc_message *msg, struct str *text, bool is_action)
+{
+ const char *target = msg->params.vector[0];
+ struct buffer *buffer = irc_get_buffer_for_message (s, msg, target);
+ if (buffer && soft_assert (s->irc_user))
+ {
+ char *prefixes = irc_get_privmsg_prefix (s, s->irc_user, target);
+ if (is_action)
+ log_outcoming_action (s, buffer, s->irc_user->nickname, text->str);
+ else
+ log_outcoming_privmsg (s, buffer,
+ prefixes, s->irc_user->nickname, text->str);
+ free (prefixes);
+ }
+ else
+ // TODO: also handle actions here
+ log_outcoming_orphan_privmsg (s, target, text->str);
+}
+
+static void
+irc_handle_sent_privmsg (struct server *s, const struct irc_message *msg)
+{
+ if (msg->params.len < 2 || s->cap_echo_message)
+ return;
+
+ // This ignores empty messages which we should not normally send
+ struct ctcp_chunk *chunks = ctcp_parse (msg->params.vector[1]);
+ LIST_FOR_EACH (struct ctcp_chunk, iter, chunks)
+ {
+ if (!iter->is_extended)
+ irc_handle_sent_privmsg_text (s, msg, &iter->text, false);
+ else if (!strcmp (iter->tag.str, "ACTION"))
+ irc_handle_sent_privmsg_text (s, msg, &iter->text, true);
+ else
+ log_ctcp_query (s, msg->params.vector[0], iter->tag.str);
+ }
+ ctcp_destroy (chunks);
+}
+
+static struct irc_handler
+{
+ const char *name;
+ void (*handler) (struct server *s, const struct irc_message *msg);
+}
+g_irc_sent_handlers[] =
+{
+ // This list needs to stay sorted
+ { "CAP", irc_handle_sent_cap },
+ { "NOTICE", irc_handle_sent_notice },
+ { "PRIVMSG", irc_handle_sent_privmsg },
+};
+
+static int
+irc_handler_cmp_by_name (const void *a, const void *b)
+{
+ const struct irc_handler *first = a;
+ const struct irc_handler *second = b;
+ return strcasecmp_ascii (first->name, second->name);
+}
+
+static void
+irc_process_sent_message (const struct irc_message *msg, struct server *s)
+{
+ // The server is free to reject even a matching prefix
+ if (msg->prefix && !irc_is_this_us (s, msg->prefix))
+ return;
+
+ struct irc_handler key = { .name = msg->command };
+ struct irc_handler *handler = bsearch (&key, g_irc_sent_handlers,
+ N_ELEMENTS (g_irc_sent_handlers), sizeof key, irc_handler_cmp_by_name);
+ if (handler)
+ handler->handler (s, msg);
+}
+
// --- Input handling ----------------------------------------------------------
static void
@@ -5496,8 +5628,6 @@ irc_handle_cap (struct server *s, const struct irc_message *msg)
str_vector_free (&use);
irc_send (s, "CAP REQ :%s", chosen_str);
- log_server_status (s, s->buffer,
- "#s: #S", "Capabilities requested", chosen_str);
free (chosen_str);
}
@@ -5927,10 +6057,7 @@ irc_send_ctcp_reply (struct server *s,
va_end (ap);
irc_send (s, "NOTICE %s :\x01%s\x01", recipient, m.str);
- if (!s->cap_echo_message)
- log_ctcp_reply (s, recipient, str_steal (&m));
- else
- str_free (&m);
+ str_free (&m);
}
static void
@@ -6125,12 +6252,7 @@ irc_handle_topic (struct server *s, const struct irc_message *msg)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-static struct irc_handler
-{
- const char *name;
- void (*handler) (struct server *s, const struct irc_message *msg);
-}
-g_irc_handlers[] =
+static struct irc_handler g_irc_handlers[] =
{
// This list needs to stay sorted
{ "CAP", irc_handle_cap },
@@ -6148,14 +6270,6 @@ g_irc_handlers[] =
{ "TOPIC", irc_handle_topic },
};
-static int
-irc_handler_cmp_by_name (const void *a, const void *b)
-{
- const struct irc_handler *first = a;
- const struct irc_handler *second = b;
- return strcasecmp_ascii (first->name, second->name);
-}
-
static bool
irc_try_parse_word_for_userhost (struct server *s, const char *word)
{
@@ -6965,27 +7079,14 @@ irc_autosplit_message (struct server *s, const char *message,
return true;
}
-struct send_autosplit_args;
-
-typedef void (*send_autosplit_logger_fn) (struct server *s,
- 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
- const char *prefix; ///< E.g. "\x01ACTION"
- const char *suffix; ///< E.g. "\x01"
-};
-
static void
-send_autosplit_message (struct server *s, struct send_autosplit_args a)
+send_autosplit_message (struct server *s,
+ const char *command, const char *target, const char *message,
+ const char *prefix, const char *suffix)
{
- struct buffer *buffer = str_map_find (&s->irc_buffer_map, a.target);
- int fixed_part = strlen (a.command) + 1 + strlen (a.target) + 1 + 1
- + strlen (a.prefix) + strlen (a.suffix);
+ struct buffer *buffer = str_map_find (&s->irc_buffer_map, target);
+ int fixed_part = strlen (command) + 1 + strlen (target) + 1 + 1
+ + strlen (prefix) + strlen (suffix);
// We might also want to preserve attributes across splits but
// that would make this code a lot more complicated
@@ -6993,71 +7094,29 @@ send_autosplit_message (struct server *s, struct send_autosplit_args a)
struct str_vector lines;
str_vector_init (&lines);
struct error *e = NULL;
- if (!irc_autosplit_message (s, a.message, fixed_part, &lines, &e))
+ if (!irc_autosplit_message (s, message, fixed_part, &lines, &e))
{
log_server_error (s, buffer ? buffer : s->buffer, "#s", e->message);
error_free (e);
- goto end;
}
-
- for (size_t i = 0; i < lines.len; i++)
+ else
{
- irc_send (s, "%s %s :%s%s%s", a.command, a.target,
- a.prefix, lines.vector[i], a.suffix);
- if (!s->cap_echo_message)
- a.logger (s, &a, buffer, lines.vector[i]);
+ for (size_t i = 0; i < lines.len; i++)
+ irc_send (s, "%s %s :%s%s%s", command, target,
+ prefix, lines.vector[i], suffix);
}
-end:
str_vector_free (&lines);
}
-static void
-log_autosplit_action (struct server *s,
- struct send_autosplit_args *a, struct buffer *buffer, const char *line)
-{
- (void) a;
-
- if (buffer && soft_assert (s->irc_user))
- log_outcoming_action (s, buffer, s->irc_user->nickname, line);
-
- // This can only be sent from a user or channel buffer
-}
-
#define SEND_AUTOSPLIT_ACTION(s, target, message) \
- send_autosplit_message ((s), (struct send_autosplit_args) \
- { "PRIVMSG", (target), (message), log_autosplit_action, \
- "\x01" "ACTION ", "\x01" })
-
-static void
-log_autosplit_privmsg (struct server *s,
- struct send_autosplit_args *a, struct buffer *buffer, const char *line)
-{
- char *prefixes = irc_get_privmsg_prefix (s, s->irc_user, a->target);
- if (buffer && soft_assert (s->irc_user))
- log_outcoming_privmsg (s, buffer,
- prefixes, s->irc_user->nickname, line);
- else
- log_outcoming_orphan_privmsg (s, a->target, line);
- free (prefixes);
-}
+ send_autosplit_message ((s), "PRIVMSG", (target), (message), \
+ "\x01" "ACTION ", "\x01")
#define SEND_AUTOSPLIT_PRIVMSG(s, target, message) \
- send_autosplit_message ((s), (struct send_autosplit_args) \
- { "PRIVMSG", (target), (message), log_autosplit_privmsg, "", "" })
-
-static void
-log_autosplit_notice (struct server *s,
- struct send_autosplit_args *a, struct buffer *buffer, const char *line)
-{
- if (buffer && soft_assert (s->irc_user))
- log_outcoming_notice (s, buffer, s->irc_user->nickname, line);
- else
- log_outcoming_orphan_notice (s, a->target, line);
-}
+ send_autosplit_message ((s), "PRIVMSG", (target), (message), "", "")
#define SEND_AUTOSPLIT_NOTICE(s, target, message) \
- send_autosplit_message ((s), (struct send_autosplit_args) \
- { "NOTICE", (target), (message), log_autosplit_notice, "", "" })
+ send_autosplit_message ((s), "NOTICE", (target), (message), "", "")
// --- Configuration dumper ----------------------------------------------------
@@ -9584,9 +9643,6 @@ handle_command_ctcp (struct handler_args *a)
irc_send (a->s, "PRIVMSG %s :\x01%s %s\x01", target, tag, a->arguments);
else
irc_send (a->s, "PRIVMSG %s :\x01%s\x01", target, tag);
-
- if (!a->s->cap_echo_message)
- log_ctcp_query (a->s, target, tag);
return true;
}
--
cgit v1.2.3-70-g09d2