degesch: log messages from /quote and plugins
That is, parse back all output messages and log based on that.
This commit is contained in:
parent
0c19a384f1
commit
3315b16f79
2
NEWS
2
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
|
||||
|
242
degesch.c
242
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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user