degesch: log messages from /quote and plugins

That is, parse back all output messages and log based on that.
This commit is contained in:
Přemysl Eric Janouch 2016-02-09 05:10:41 +01:00
parent 0c19a384f1
commit 3315b16f79
2 changed files with 151 additions and 93 deletions

2
NEWS
View File

@ -16,6 +16,8 @@
* degesch: optimize buffer memory usage * degesch: optimize buffer memory usage
* degesch: added logging of messages sent from /quote and plugins
* kike: add support for IRCv3.2 server-time * kike: add support for IRCv3.2 server-time
* Remote addresses are now resolved asynchronously * Remote addresses are now resolved asynchronously

240
degesch.c
View File

@ -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, static void irc_send (struct server *s,
const char *format, ...) ATTRIBUTE_PRINTF (2, 3); 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); 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_append_str (&s->write_buffer, &str);
str_free (&str); str_free (&str);
str_append (&s->write_buffer, "\r\n"); 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); 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 ---------------------------------------------------------- // --- Input handling ----------------------------------------------------------
static void static void
@ -5496,8 +5628,6 @@ irc_handle_cap (struct server *s, const struct irc_message *msg)
str_vector_free (&use); str_vector_free (&use);
irc_send (s, "CAP REQ :%s", chosen_str); irc_send (s, "CAP REQ :%s", chosen_str);
log_server_status (s, s->buffer,
"#s: #S", "Capabilities requested", chosen_str);
free (chosen_str); free (chosen_str);
} }
@ -5927,9 +6057,6 @@ irc_send_ctcp_reply (struct server *s,
va_end (ap); va_end (ap);
irc_send (s, "NOTICE %s :\x01%s\x01", recipient, m.str); 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);
} }
@ -6125,12 +6252,7 @@ irc_handle_topic (struct server *s, const struct irc_message *msg)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static struct irc_handler static struct irc_handler g_irc_handlers[] =
{
const char *name;
void (*handler) (struct server *s, const struct irc_message *msg);
}
g_irc_handlers[] =
{ {
// This list needs to stay sorted // This list needs to stay sorted
{ "CAP", irc_handle_cap }, { "CAP", irc_handle_cap },
@ -6148,14 +6270,6 @@ g_irc_handlers[] =
{ "TOPIC", irc_handle_topic }, { "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 static bool
irc_try_parse_word_for_userhost (struct server *s, const char *word) 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; 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 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); struct buffer *buffer = str_map_find (&s->irc_buffer_map, target);
int fixed_part = strlen (a.command) + 1 + strlen (a.target) + 1 + 1 int fixed_part = strlen (command) + 1 + strlen (target) + 1 + 1
+ strlen (a.prefix) + strlen (a.suffix); + strlen (prefix) + strlen (suffix);
// We might also want to preserve attributes across splits but // We might also want to preserve attributes across splits but
// that would make this code a lot more complicated // 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; struct str_vector lines;
str_vector_init (&lines); str_vector_init (&lines);
struct error *e = NULL; 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); log_server_error (s, buffer ? buffer : s->buffer, "#s", e->message);
error_free (e); error_free (e);
goto end;
} }
else
for (size_t i = 0; i < lines.len; i++)
{ {
irc_send (s, "%s %s :%s%s%s", a.command, a.target, for (size_t i = 0; i < lines.len; i++)
a.prefix, lines.vector[i], a.suffix); irc_send (s, "%s %s :%s%s%s", command, target,
if (!s->cap_echo_message) prefix, lines.vector[i], suffix);
a.logger (s, &a, buffer, lines.vector[i]);
} }
end:
str_vector_free (&lines); 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) \ #define SEND_AUTOSPLIT_ACTION(s, target, message) \
send_autosplit_message ((s), (struct send_autosplit_args) \ send_autosplit_message ((s), "PRIVMSG", (target), (message), \
{ "PRIVMSG", (target), (message), log_autosplit_action, \ "\x01" "ACTION ", "\x01")
"\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);
}
#define SEND_AUTOSPLIT_PRIVMSG(s, target, message) \ #define SEND_AUTOSPLIT_PRIVMSG(s, target, message) \
send_autosplit_message ((s), (struct send_autosplit_args) \ send_autosplit_message ((s), "PRIVMSG", (target), (message), "", "")
{ "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);
}
#define SEND_AUTOSPLIT_NOTICE(s, target, message) \ #define SEND_AUTOSPLIT_NOTICE(s, target, message) \
send_autosplit_message ((s), (struct send_autosplit_args) \ send_autosplit_message ((s), "NOTICE", (target), (message), "", "")
{ "NOTICE", (target), (message), log_autosplit_notice, "", "" })
// --- Configuration dumper ---------------------------------------------------- // --- 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); irc_send (a->s, "PRIVMSG %s :\x01%s %s\x01", target, tag, a->arguments);
else else
irc_send (a->s, "PRIVMSG %s :\x01%s\x01", target, tag); 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; return true;
} }