diff options
-rw-r--r-- | degesch.c | 892 |
1 files changed, 429 insertions, 463 deletions
@@ -895,44 +895,19 @@ REF_COUNTABLE_METHODS (channel) enum buffer_line_flags { - BUFFER_LINE_HIGHLIGHT = 1 << 0 ///< The user was highlighted by this -}; - -enum buffer_line_type -{ - BUFFER_LINE_PRIVMSG, ///< PRIVMSG - BUFFER_LINE_ACTION, ///< PRIVMSG ACTION - BUFFER_LINE_NOTICE, ///< NOTICE - BUFFER_LINE_JOIN, ///< JOIN - BUFFER_LINE_PART, ///< PART - BUFFER_LINE_KICK, ///< KICK - BUFFER_LINE_NICK, ///< NICK - BUFFER_LINE_TOPIC, ///< TOPIC - BUFFER_LINE_QUIT, ///< QUIT - BUFFER_LINE_STATUS, ///< Whatever status messages - BUFFER_LINE_ERROR ///< Whatever error messages -}; - -struct buffer_line_args -{ - char *who; ///< Name of the origin or NULL (user) - char *object; ///< Object of action - char *text; ///< Text of message - char *reason; ///< Reason for PART, KICK, QUIT - char *prefixes; ///< Channel user prefixes + BUFFER_LINE_STATUS = 1 << 0, ///< Status message + BUFFER_LINE_ERROR = 1 << 1, ///< Error message + BUFFER_LINE_HIGHLIGHT = 1 << 2, ///< The user was highlighted by this + BUFFER_LINE_SKIP_FILE = 1 << 3 ///< Don't log this to file }; struct buffer_line { LIST_HEADER (struct buffer_line) - // We use the "type" and "flags" mostly just as formatting hints - - enum buffer_line_type type; ///< Type of the event int flags; ///< Flags - time_t when; ///< Time of the event - struct buffer_line_args args; ///< Arguments + struct formatter *formatter; ///< Line data }; struct buffer_line * @@ -942,14 +917,15 @@ buffer_line_new (void) return self; } +// FIXME: see if we can't rearrange the code in some way to get rid of this +static void formatter_free (struct formatter *self); + static void buffer_line_destroy (struct buffer_line *self) { - free (self->args.who); - free (self->args.object); - free (self->args.text); - free (self->args.reason); - free (self->args.prefixes); + if (self->formatter) + formatter_free (self->formatter); + free (self->formatter); free (self); } @@ -2006,14 +1982,20 @@ attribute_printer_update (struct attribute_printer *self) // then flush it either to a terminal, or a log file with formatting stripped. // // Format strings use a #-quoted notation, to differentiate from printf: -// #s inserts a string -// #d inserts a signed integer; also supports the #<N> and #0<N> notation +// #s inserts a string (expected to be in UTF-8) +// #d inserts a signed integer +// +// #S inserts a string from the server with unknown encoding // #m inserts a mIRC-formatted string (auto-resets at boundaries) +// #n cuts the nickname from a string and automatically colours it // // #a inserts named attributes (auto-resets) // #r resets terminal attributes // #c sets foreground color // #C sets background color +// +// Modifiers: +// & free() the string argument after using it enum formatter_item_type { @@ -2021,7 +2003,8 @@ enum formatter_item_type FORMATTER_ITEM_ATTR, ///< Formatting attributes FORMATTER_ITEM_FG_COLOR, ///< Foreground color FORMATTER_ITEM_BG_COLOR, ///< Background color - FORMATTER_ITEM_SIMPLE ///< For mIRC formatting only so far + FORMATTER_ITEM_SIMPLE, ///< For mIRC formatting only so far + FORMATTER_ITEM_IGNORE_ATTR ///< Un/set attribute ignoration }; struct formatter_item @@ -2053,17 +2036,19 @@ formatter_item_destroy (struct formatter_item *self) struct formatter { struct app_context *ctx; ///< Application context - bool ignore_new_attributes; ///< Whether to ignore new attributes + struct server *s; ///< Server struct formatter_item *items; ///< Items struct formatter_item *items_tail; ///< Tail of items }; static void -formatter_init (struct formatter *self, struct app_context *ctx) +formatter_init (struct formatter *self, + struct app_context *ctx, struct server *s) { memset (self, 0, sizeof *self); self->ctx = ctx; + self->s = s; } static void @@ -2076,8 +2061,6 @@ formatter_free (struct formatter *self) static void formatter_add_item (struct formatter *self, struct formatter_item template_) { - if (template_.type != FORMATTER_ITEM_TEXT && self->ignore_new_attributes) - return; if (template_.text) template_.text = xstrdup (template_.text); @@ -2198,36 +2181,70 @@ formatter_parse_mirc (struct formatter *self, const char *s) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// FIXME: try to reorder the code so that we don't need these +static char * irc_to_utf8 (struct app_context *ctx, const char *text); +static bool irc_is_this_us (struct server *s, const char *prefix); + +static void +formatter_parse_nick (struct formatter *self, char *s) +{ + char *nick = irc_cut_nickname (s); + int color = str_map_hash (nick, strlen (nick)) % 8; + + // We always use the default color for ourselves + if (self->s && irc_is_this_us (self->s, nick)) + color = -1; + + // Never use the black colour, could become transparent on black terminals + if (color == COLOR_BLACK) + color = -1; + + FORMATTER_ADD_ITEM (self, FG_COLOR, .color = color); + + char *x = irc_to_utf8 (self->ctx, nick); + free (nick); + FORMATTER_ADD_TEXT (self, x); + free (x); + + // Need to reset the color afterwards + FORMATTER_ADD_ITEM (self, FG_COLOR, .color = -1); +} + static const char * formatter_parse_field (struct formatter *self, const char *field, struct str *buf, va_list *ap) { - size_t width = 0; - bool zero_padded = false; + bool free_string = false; + char *s = NULL; + char *tmp = NULL; int c; restart: switch ((c = *field++)) { - char *s; - // We can push boring text content to the caller's buffer // and let it flush the buffer only when it's actually needed + case 'd': + tmp = xstrdup_printf ("%d", va_arg (*ap, int)); + str_append (buf, tmp); + free (tmp); + break; case 's': - s = va_arg (*ap, char *); - for (size_t len = strlen (s); len < width; len++) - str_append_c (buf, ' '); - str_append (buf, s); + str_append (buf, (s = va_arg (*ap, char *))); break; - case 'd': - s = xstrdup_printf ("%d", va_arg (*ap, int)); - for (size_t len = strlen (s); len < width; len++) - str_append_c (buf, " 0"[zero_padded]); - str_append (buf, s); - free (s); + + case 'S': + tmp = irc_to_utf8 (self->ctx, (s = va_arg (*ap, char *))); + str_append (buf, tmp); + free (tmp); break; case 'm': - formatter_parse_mirc (self, va_arg (*ap, char *)); + tmp = irc_to_utf8 (self->ctx, (s = va_arg (*ap, char *))); + formatter_parse_mirc (self, tmp); + free (tmp); + break; + case 'n': + formatter_parse_nick (self, (s = va_arg (*ap, char *))); break; case 'a': @@ -2244,28 +2261,28 @@ restart: break; default: - if (c == '0' && !zero_padded) - zero_padded = true; - else if (isdigit_ascii (c)) - width = width * 10 + (c - '0'); + if (c == '&' && !free_string) + free_string = true; else if (c) hard_assert (!"unexpected format specifier"); else hard_assert (!"unexpected end of format string"); goto restart; } + + if (free_string) + free (s); return field; } +// I was unable to take a pointer of a bare "va_list" when it was passed in +// as a function argument, so it has to be a pointer from the beginning static void -formatter_add (struct formatter *self, const char *format, ...) +formatter_addv (struct formatter *self, const char *format, va_list *ap) { struct str buf; str_init (&buf); - va_list ap; - va_start (ap, format); - while (*format) { if (*format != '#' || *++format == '#') @@ -2279,17 +2296,61 @@ formatter_add (struct formatter *self, const char *format, ...) str_reset (&buf); } - format = formatter_parse_field (self, format, &buf, &ap); + format = formatter_parse_field (self, format, &buf, ap); } if (buf.len) FORMATTER_ADD_TEXT (self, buf.str); str_free (&buf); +} + +static void +formatter_add (struct formatter *self, const char *format, ...) +{ + va_list ap; + va_start (ap, format); + formatter_addv (self, format, &ap); va_end (ap); } static void +formatter_add_from (struct formatter *self, struct formatter *other) +{ + for (struct formatter_item *iter = other->items; iter; iter = iter->next) + formatter_add_item (self, *iter); +} + +static bool +formatter_flush_attr + (struct attribute_printer *state, struct formatter_item *item) +{ + switch (item->type) + { + case FORMATTER_ITEM_ATTR: + attribute_printer_apply (state, item->attribute); + state->want = 0; + state->want_foreground = -1; + state->want_background = -1; + return true; + case FORMATTER_ITEM_SIMPLE: + state->want |= item->attribute; + attribute_printer_update (state); + return true; + case FORMATTER_ITEM_FG_COLOR: + state->want_foreground = item->color; + attribute_printer_update (state); + return true; + case FORMATTER_ITEM_BG_COLOR: + state->want_background = item->color; + attribute_printer_update (state); + return true; + default: + return false; + } +} + +static void formatter_flush (struct formatter *self, FILE *stream) { terminal_printer_fn printer = get_attribute_printer (stream); @@ -2305,6 +2366,7 @@ formatter_flush (struct formatter *self, FILE *stream) attribute_printer_init (&state, self->ctx, printer); attribute_printer_reset (&state); + int attribute_ignore = 0; LIST_FOR_EACH (struct formatter_item, iter, self->items) { switch (iter->type) @@ -2316,24 +2378,13 @@ formatter_flush (struct formatter *self, FILE *stream) fputs (term, stream); free (term); break; - case FORMATTER_ITEM_ATTR: - attribute_printer_apply (&state, iter->attribute); - state.want = 0; - state.want_foreground = -1; - state.want_background = -1; - break; - case FORMATTER_ITEM_SIMPLE: - state.want |= iter->attribute; - attribute_printer_update (&state); - break; - case FORMATTER_ITEM_FG_COLOR: - state.want_foreground = iter->color; - attribute_printer_update (&state); - break; - case FORMATTER_ITEM_BG_COLOR: - state.want_background = iter->color; - attribute_printer_update (&state); + case FORMATTER_ITEM_IGNORE_ATTR: + attribute_ignore += iter->attribute; break; + default: + if (attribute_ignore <= 0 + && !formatter_flush_attr (&state, iter)) + hard_assert (!"unhandled formatter item type"); } } attribute_printer_reset (&state); @@ -2373,125 +2424,30 @@ buffer_line_display (struct app_context *ctx, // confused as to when an event has happened buffer_update_time (ctx, line->when); - struct buffer_line_args *a = &line->args; - - char *nick = NULL; - const char *userhost = NULL; - - // TODO: always assign the default colour to ourselves - // FIXME: never use the black colour, use the default instead - int nick_color = -1; - int object_color = -1; - - if (a->who) - { - nick = irc_cut_nickname (a->who); - userhost = irc_find_userhost (a->who); - nick_color = str_map_hash (nick, strlen (nick)) % 8; - } - if (a->object) - object_color = str_map_hash (a->object, strlen (a->object)) % 8; - struct formatter f; - formatter_init (&f, ctx); + formatter_init (&f, ctx, NULL); struct tm current; if (!localtime_r (&line->when, ¤t)) print_error ("%s: %s", "localtime_r", strerror (errno)); else - formatter_add (&f, "#a#02d:#02d:#02d#r ", - ATTR_TIMESTAMP, current.tm_hour, current.tm_min, current.tm_sec); + formatter_add (&f, "#a#&s#r ", ATTR_TIMESTAMP, + xstrdup_printf ("%02d:%02d:%02d", + current.tm_hour, current.tm_min, current.tm_sec)); // Ignore all formatting for messages coming from other buffers, that is // either from the global or server buffer. Instead print them in grey. if (is_external) { formatter_add (&f, "#a", ATTR_EXTERNAL); - f.ignore_new_attributes = true; + FORMATTER_ADD_ITEM (&f, IGNORE_ATTR, .attribute = 1); } - switch (line->type) - { - case BUFFER_LINE_PRIVMSG: - if (line->flags & BUFFER_LINE_HIGHLIGHT) - formatter_add (&f, "#a<#s#s>#r #m", - ATTR_HIGHLIGHT, a->prefixes ? a->prefixes : "", nick, a->text); - else - formatter_add (&f, "<#s#c#s#r> #m", - a->prefixes ? a->prefixes : "", nick_color, nick, a->text); - break; - case BUFFER_LINE_ACTION: - if (line->flags & BUFFER_LINE_HIGHLIGHT) - formatter_add (&f, " #a*#r ", ATTR_HIGHLIGHT); - else - formatter_add (&f, " #a*#r ", ATTR_ACTION); - formatter_add (&f, "#c#s#r #m", nick_color, nick, a->text); - break; - case BUFFER_LINE_NOTICE: - formatter_add (&f, " - "); - if (line->flags & BUFFER_LINE_HIGHLIGHT) - formatter_add (&f, "#a#s(#s)#r: #m", - ATTR_HIGHLIGHT, "Notice", nick, a->text); - else - formatter_add (&f, "#s(#c#s#r): #m", - "Notice", nick_color, nick, a->text); - break; - case BUFFER_LINE_JOIN: - formatter_add (&f, "#a-->#r ", ATTR_JOIN); - formatter_add (&f, "#c#s#r (#a#s#r) #a#s#r #s", - nick_color, nick, ATTR_USERHOST, userhost, - ATTR_JOIN, "has joined", a->object); - break; - case BUFFER_LINE_PART: - formatter_add (&f, "#a<--#r ", ATTR_PART); - formatter_add (&f, "#c#s#r (#a#s#r) #a#s#r #s", - nick_color, nick, ATTR_USERHOST, userhost, - ATTR_PART, "has left", a->object); - if (a->reason) - formatter_add (&f, " (#m)", a->reason); - break; - case BUFFER_LINE_KICK: - formatter_add (&f, "#a<--#r ", ATTR_PART); - formatter_add (&f, "#c#s#r (#a#s#r) #a#s#r #c#s#r", - nick_color, nick, ATTR_USERHOST, userhost, - ATTR_PART, "has kicked", object_color, a->object); - if (a->reason) - formatter_add (&f, " (#m)", a->reason); - break; - case BUFFER_LINE_NICK: - formatter_add (&f, " - "); - if (a->who) - formatter_add (&f, "#c#s#r #s #c#s#r", - nick_color, nick, - "is now known as", object_color, a->object); - else - formatter_add (&f, "#s #s", - "You are now known as", a->object); - break; - case BUFFER_LINE_TOPIC: + if (line->flags & BUFFER_LINE_STATUS) formatter_add (&f, " - "); - formatter_add (&f, "#c#s#r #s \"#m\"", - nick_color, nick, - "has changed the topic to", a->text); - break; - case BUFFER_LINE_QUIT: - formatter_add (&f, "#a<--#r ", ATTR_PART); - formatter_add (&f, "#c#s#r (#a#s#r) #a#s#r", - nick_color, nick, ATTR_USERHOST, userhost, - ATTR_PART, "has quit"); - if (a->reason) - formatter_add (&f, " (#m)", a->reason); - break; - case BUFFER_LINE_STATUS: - formatter_add (&f, " - "); - formatter_add (&f, "#s", a->text); - break; - case BUFFER_LINE_ERROR: + if (line->flags & BUFFER_LINE_ERROR) formatter_add (&f, "#a=!=#r ", ATTR_ERROR); - formatter_add (&f, "#s", a->text); - } - - free (nick); + formatter_add_from (&f, line->formatter); input_hide (&ctx->input); @@ -2506,18 +2462,19 @@ buffer_line_display (struct app_context *ctx, } static void -buffer_send_internal (struct app_context *ctx, struct buffer *buffer, - enum buffer_line_type type, int flags, - struct buffer_line_args a) +log_formatter (struct app_context *ctx, + struct buffer *buffer, int flags, struct formatter *f) { if (!buffer) buffer = ctx->global_buffer; struct buffer_line *line = buffer_line_new (); - line->type = type; line->flags = flags; line->when = time (NULL); - line->args = a; + line->formatter = xmalloc (sizeof *line->formatter); + + // Move the formater inside + *line->formatter = *f; LIST_APPEND_WITH_TAIL (buffer->lines, buffer->lines_tail, line); buffer->lines_count++; @@ -2551,16 +2508,37 @@ buffer_send_internal (struct app_context *ctx, struct buffer *buffer, } } -#define buffer_send(ctx, buffer, type, flags, ...) \ - buffer_send_internal ((ctx), (buffer), (type), (flags), \ - (struct buffer_line_args) { __VA_ARGS__ }) +static void +log_full (struct app_context *ctx, struct server *s, struct buffer *buffer, + int flags, const char *format, ...) +{ + va_list ap; + va_start (ap, format); + + struct formatter f; + formatter_init (&f, ctx, s); + formatter_addv (&f, format, &ap); + log_formatter (ctx, buffer, flags, &f); + + va_end (ap); +} + +#define log_global(ctx, flags, ...) \ + log_full ((ctx), NULL, (ctx)->global_buffer, flags, __VA_ARGS__) +#define log_server(s, buffer, flags, ...) \ + log_full ((s)->ctx, s, (buffer), flags, __VA_ARGS__) + +#define log_global_status(ctx, ...) \ + log_global ((ctx), BUFFER_LINE_STATUS, __VA_ARGS__) +#define log_global_error(ctx, ...) \ + log_global ((ctx), BUFFER_LINE_ERROR, __VA_ARGS__) -#define buffer_send_status(ctx, buffer, ...) \ - buffer_send (ctx, buffer, BUFFER_LINE_STATUS, 0, \ - .text = xstrdup_printf (__VA_ARGS__)) -#define buffer_send_error(ctx, buffer, ...) \ - buffer_send (ctx, buffer, BUFFER_LINE_ERROR, 0, \ - .text = xstrdup_printf (__VA_ARGS__)) +#define log_server_status(s, buffer, ...) \ + log_server ((s), (buffer), BUFFER_LINE_STATUS, __VA_ARGS__) +#define log_server_error(s, buffer, ...) \ + log_server ((s), (buffer), BUFFER_LINE_ERROR, __VA_ARGS__) + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static struct buffer * buffer_by_name (struct app_context *ctx, const char *name) @@ -2656,8 +2634,9 @@ buffer_merge (struct app_context *ctx, { // XXX: anything better to do? This situation is arguably rare and I'm // not entirely sure what action to take. - buffer_send_status (ctx, buffer, - "Buffer %s was merged into this buffer", merged->name); + // XXX: what good is log_*_status() when we can't use it? + log_full (ctx, NULL, buffer, BUFFER_LINE_STATUS, + "Buffer #s was merged into this buffer", merged->name); // Find all lines from "merged" newer than the newest line in "buffer" struct buffer_line *start = merged->lines; @@ -2688,8 +2667,8 @@ buffer_merge (struct app_context *ctx, buffer->lines_tail = tail; buffer->lines_count += n; - // XXX: we don't want to log this entry to a file - buffer_send_status (ctx, buffer, "End of merged content"); + log_full (ctx, NULL, buffer, BUFFER_LINE_STATUS | BUFFER_LINE_SKIP_FILE, + "End of merged content"); } static void @@ -2928,8 +2907,8 @@ irc_left_channel (struct channel *channel) static void remove_conflicting_buffer (struct server *s, struct buffer *buffer) { - buffer_send_status (s->ctx, NULL, - "Removed buffer %s because of casemapping conflict", buffer->name); + log_server_status (s, s->buffer, + "Removed buffer #s because of casemapping conflict", buffer->name); if (s->ctx->current_buffer == buffer) buffer_activate (s->ctx, s->buffer); buffer_remove (s->ctx, buffer); @@ -3081,8 +3060,9 @@ irc_queue_reconnect (struct server *s) // TODO: exponentional backoff // XXX: maybe add a state for when a connect is queued? hard_assert (s->state == IRC_DISCONNECTED); - buffer_send_status (s->ctx, s->buffer, - "Trying to reconnect in %ld seconds...", delay); + log_server_status (s, s->buffer, + "Trying to reconnect in #&s seconds...", + xstrdup_printf ("%" PRId64, delay)); poller_timer_set (&s->reconnect_tmr, delay * 1000); } @@ -3129,7 +3109,7 @@ ca_error: } // Only inform the user if we're not actually verifying - buffer_send_error (s->ctx, s->buffer, "%s", error->message); + log_server_error (s, s->buffer, "#s", error->message); error_free (error); return true; } @@ -3153,13 +3133,13 @@ irc_initialize_ssl (struct server *s, struct error **e) { char *path = resolve_config_filename (ssl_cert); if (!path) - buffer_send_error (s->ctx, NULL, - "%s: %s", "Cannot open file", ssl_cert); + log_server_error (s, s->buffer, + "#s: #s", "Cannot open file", ssl_cert); // XXX: perhaps we should read the file ourselves for better messages else if (!SSL_use_certificate_file (s->ssl, path, SSL_FILETYPE_PEM) || !SSL_use_PrivateKey_file (s->ssl, path, SSL_FILETYPE_PEM)) - buffer_send_error (s->ctx, NULL, - "%s: %s", "Setting the SSL client certificate failed", + log_server_error (s, s->buffer, + "#s: #s", "Setting the SSL client certificate failed", ERR_error_string (ERR_get_error (), NULL)); free (path); } @@ -3318,7 +3298,7 @@ try_finish_quit (struct app_context *ctx) static void initiate_quit (struct app_context *ctx) { - buffer_send_status (ctx, NULL, "Shutting down"); + log_global_status (ctx, "Shutting down"); // Destroy the user interface input_stop (&ctx->input); @@ -3425,7 +3405,7 @@ static void on_irc_ping_timeout (void *user_data) { struct server *s = user_data; - buffer_send_error (s->ctx, s->buffer, "Connection timeout"); + log_server_error (s, s->buffer, "Connection timeout"); on_irc_disconnected (s); } @@ -3531,12 +3511,12 @@ on_irc_readable (const struct pollfd *fd, struct server *s) case IRC_READ_AGAIN: goto end; case IRC_READ_ERROR: - buffer_send_error (s->ctx, s->buffer, + log_server_error (s, s->buffer, "Reading from the IRC server failed"); disconnected = true; goto end; case IRC_READ_EOF: - buffer_send_error (s->ctx, s->buffer, + log_server_error (s, s->buffer, "The IRC server closed the connection"); disconnected = true; goto end; @@ -3546,7 +3526,7 @@ on_irc_readable (const struct pollfd *fd, struct server *s) if (buf->len >= (1 << 20)) { - buffer_send_error (s->ctx, s->buffer, + log_server_error (s, s->buffer, "The IRC server seems to spew out data frantically"); irc_shutdown (s); goto end; @@ -3636,7 +3616,7 @@ irc_finish_connection (struct server *s, int socket) bool use_ssl = get_config_boolean (s->config, "ssl"); if (use_ssl && !irc_initialize_ssl (s, &e)) { - buffer_send_error (ctx, s->buffer, "Connection failed: %s", e->message); + log_server_error (s, s->buffer, "Connection failed: #s", e->message); error_free (e); xclose (s->socket); @@ -3646,7 +3626,7 @@ irc_finish_connection (struct server *s, int socket) return; } - buffer_send_status (ctx, s->buffer, "Connection established"); + log_server_status (s, s->buffer, "Connection established"); s->state = IRC_CONNECTED; poller_fd_init (&s->read_event, &ctx->poller, s->socket); @@ -3664,14 +3644,14 @@ static void irc_on_connector_connecting (void *user_data, const char *address) { struct server *s = user_data; - buffer_send_status (s->ctx, s->buffer, "Connecting to %s...", address); + log_server_status (s, s->buffer, "Connecting to #s...", address); } static void irc_on_connector_error (void *user_data, const char *error) { struct server *s = user_data; - buffer_send_error (s->ctx, s->buffer, "Connection failed: %s", error); + log_server_error (s, s->buffer, "Connection failed: #s", error); } static void @@ -3741,8 +3721,6 @@ static bool irc_initiate_connect_socks (struct server *s, const struct str_vector *addresses, struct error **e) { - struct app_context *ctx = s->ctx; - const char *socks_host = get_config_string (s->config, "socks_host"); int64_t socks_port_int = get_config_integer (s->config, "socks_port"); @@ -3760,12 +3738,9 @@ irc_initiate_connect_socks (struct server *s, char *socks_port = xstrdup_printf ("%" PRIi64, socks_port_int); - char *address = format_host_port_pair (irc_host, irc_port); - char *socks_address = format_host_port_pair (socks_host, socks_port); - buffer_send_status (ctx, s->buffer, - "Connecting to %s via %s...", address, socks_address); - free (socks_address); - free (address); + log_server_status (s, s->buffer, "Connecting to #&s via #&s...", + format_host_port_pair (irc_host, irc_port), + format_host_port_pair (socks_host, socks_port)); // TODO: the SOCKS code needs a rewrite so that we don't block on it either; // perhaps it could act as a special kind of connector @@ -3790,13 +3765,12 @@ static void irc_initiate_connect (struct server *s) { hard_assert (s->state == IRC_DISCONNECTED); - struct app_context *ctx = s->ctx; const char *addresses = get_config_string (s->config, "addresses"); if (!addresses || !addresses[strspn (addresses, ",")]) { // No sense in trying to reconnect - buffer_send_error (ctx, s->buffer, + log_server_error (s, s->buffer, "No addresses specified in configuration"); return; } @@ -3813,7 +3787,7 @@ irc_initiate_connect (struct server *s) if (e) { - buffer_send_error (s->ctx, s->buffer, "%s", e->message); + log_server_error (s, s->buffer, "#s", e->message); error_free (e); irc_queue_reconnect (s); } @@ -3972,6 +3946,8 @@ irc_cut_nickname (const char *prefix) return xstrndup (prefix, strcspn (prefix, "!@")); } +// XXX: shouldn't we rather return "" on failure? Or just make a wrapper +// for logging purposes, to be more resilient? static const char * irc_find_userhost (const char *prefix) { @@ -4307,27 +4283,20 @@ irc_handle_invite (struct server *s, const struct irc_message *msg) if (!msg->prefix || msg->params.len < 2) return; - const char *target = msg->params.vector[0]; + const char *target = msg->params.vector[0]; const char *channel_name = msg->params.vector[1]; struct buffer *buffer; if (!(buffer = str_map_find (&s->irc_buffer_map, channel_name))) buffer = s->buffer; - // FIXME: logging - char *who = irc_cut_nickname (msg->prefix); - char *who_utf8 = irc_to_utf8 (s->ctx, who); - free (who); - // IRCv3.2 invite-notify extension allows the target to be someone else if (irc_is_this_us (s, target)) - buffer_send_status (s->ctx, buffer, - "%s has invited you to %s", who_utf8, channel_name); + log_server_status (s, buffer, + "#n has invited you to #S", msg->prefix, channel_name); else - buffer_send_status (s->ctx, buffer, - "%s has invited %s to %s", who_utf8, target, channel_name); - - free (who_utf8); + log_server_status (s, buffer, + "#n has invited #n to #S", msg->prefix, target, channel_name); } static void @@ -4375,9 +4344,10 @@ irc_handle_join (struct server *s, const struct irc_message *msg) // Finally log the message if (buffer) { - buffer_send (s->ctx, buffer, BUFFER_LINE_JOIN, 0, - .who = irc_to_utf8 (s->ctx, msg->prefix), - .object = irc_to_utf8 (s->ctx, channel_name)); + log_server (s, buffer, 0, "#a-->#r #n (#a#S#r) #a#s#r #S", + ATTR_JOIN, msg->prefix, + ATTR_USERHOST, irc_find_userhost (msg->prefix), + ATTR_JOIN, "has joined", channel_name); } } @@ -4393,7 +4363,7 @@ irc_handle_kick (struct server *s, const struct irc_message *msg) || irc_is_channel (s, target)) return; - const char *message = ""; + const char *message = NULL; if (msg->params.len > 2) message = msg->params.vector[2]; @@ -4414,10 +4384,15 @@ irc_handle_kick (struct server *s, const struct irc_message *msg) if (buffer) { - buffer_send (s->ctx, buffer, BUFFER_LINE_KICK, 0, - .who = irc_to_utf8 (s->ctx, msg->prefix), - .object = irc_to_utf8 (s->ctx, target), - .reason = irc_to_utf8 (s->ctx, message)); + struct formatter f; + formatter_init (&f, s->ctx, s); + formatter_add (&f, "#a<--#r #n (#a#S#r) #a#s#r #n", + ATTR_PART, msg->prefix, + ATTR_USERHOST, irc_find_userhost (msg->prefix), + ATTR_PART, "has kicked", target); + if (message) + formatter_add (&f, " (#m)", message); + log_formatter (s->ctx, buffer, 0, &f); } } @@ -4427,17 +4402,14 @@ irc_handle_mode (struct server *s, const struct irc_message *msg) if (!msg->prefix || msg->params.len < 1) return; - char *who = irc_cut_nickname (msg->prefix); const char *context = msg->params.vector[0]; // Join the modes back to a single string struct str_vector copy; str_vector_init (©); str_vector_add_vector (©, msg->params.vector + 1); - char *reconstructed = join_str_vector (©, ' '); + char *modes = join_str_vector (©, ' '); str_vector_free (©); - char *modes = irc_to_utf8 (s->ctx, reconstructed); - free (reconstructed); if (irc_is_channel (s, context)) { @@ -4449,27 +4421,23 @@ irc_handle_mode (struct server *s, const struct irc_message *msg) if (channel) irc_handle_mode_channel (s, channel, msg->params.vector + 1); - // FIXME: logging if (buffer) { - buffer_send_status (s->ctx, buffer, - "Mode %s [%s] by %s", context, modes, who); + log_server_status (s, buffer, + "Mode #S [#S] by #n", context, modes, msg->prefix); } } else if (irc_is_this_us (s, context)) { irc_handle_mode_user (s, msg->params.vector + 1); - - // FIXME: logging - buffer_send_status (s->ctx, s->buffer, - "User mode [%s] by %s", modes, who); + log_server_status (s, s->buffer, + "User mode [#S] by #n", modes, msg->prefix); } else { // XXX: this shouldn't happen, reconnect? } - free (who); free (modes); // Our own modes might have changed @@ -4504,12 +4472,13 @@ irc_handle_nick (struct server *s, const struct irc_message *msg) str_map_find (&s->irc_buffer_map, user->nickname); if (pm_buffer) { - char *who = irc_is_this_us (s, msg->prefix) - ? NULL - : irc_to_utf8 (s->ctx, msg->prefix); - buffer_send (s->ctx, pm_buffer, BUFFER_LINE_NICK, 0, - .who = who, - .object = irc_to_utf8 (s->ctx, new_nickname)); + // TODO: make macros of the two cases? (They are repeated below.) + if (irc_is_this_us (s, msg->prefix)) + log_server (s, pm_buffer, BUFFER_LINE_STATUS, "#s #n", + "You are now known as", new_nickname); + else + log_server (s, pm_buffer, BUFFER_LINE_STATUS, "#n #s #n", + msg->prefix, "is now known as", new_nickname); } // The new nickname may collide with a user referenced by a PM buffer, @@ -4566,9 +4535,8 @@ irc_handle_nick (struct server *s, const struct irc_message *msg) // We've already done that if (buffer == pm_buffer) continue; - - buffer_send (s->ctx, buffer, BUFFER_LINE_NICK, 0, - .object = irc_to_utf8 (s->ctx, new_nickname)); + log_server (s, buffer, BUFFER_LINE_STATUS, "#s #n", + "You are now known as", new_nickname); } } else @@ -4579,9 +4547,9 @@ irc_handle_nick (struct server *s, const struct irc_message *msg) struct buffer *buffer = str_map_find (&s->irc_buffer_map, iter->channel->name); hard_assert (buffer != NULL); - buffer_send (s->ctx, buffer, BUFFER_LINE_NICK, 0, - .who = irc_to_utf8 (s->ctx, msg->prefix), - .object = irc_to_utf8 (s->ctx, new_nickname)); + + log_server (s, pm_buffer, BUFFER_LINE_STATUS, "#n #s #n", + msg->prefix, "is now known as", new_nickname); } } @@ -4600,18 +4568,8 @@ static void irc_handle_ctcp_reply (struct server *s, const struct irc_message *msg, struct ctcp_chunk *chunk) { - char *nickname = irc_cut_nickname (msg->prefix); - char *nickname_utf8 = irc_to_utf8 (s->ctx, nickname); - char *tag_utf8 = irc_to_utf8 (s->ctx, chunk->tag.str); - char *text_utf8 = irc_to_utf8 (s->ctx, chunk->text.str); - - buffer_send_status (s->ctx, s->buffer, - "CTCP reply from %s: %s %s", nickname_utf8, tag_utf8, text_utf8); - - free (nickname); - free (nickname_utf8); - free (tag_utf8); - free (text_utf8); + log_server_status (s, s->buffer, "CTCP reply from #n: #S #S", + msg->prefix, chunk->tag.str, chunk->text.str); } static void @@ -4623,12 +4581,16 @@ irc_handle_notice_text (struct server *s, if (buffer) { - int flags = irc_is_highlight (s, text->str) - ? BUFFER_LINE_HIGHLIGHT - : 0; - buffer_send (s->ctx, buffer, BUFFER_LINE_NOTICE, flags, - .who = irc_to_utf8 (s->ctx, msg->prefix), - .text = irc_to_utf8 (s->ctx, text->str)); + // TODO: factor out? + char *nick = irc_cut_nickname (msg->prefix); + // IRCv3.2 echo-message could otherwise cause us to highlight ourselves + if (!irc_is_this_us (s, msg->prefix) && irc_is_highlight (s, text->str)) + log_server (s, buffer, BUFFER_LINE_STATUS | BUFFER_LINE_HIGHLIGHT, + "#a#s(#S)#r: #m", ATTR_HIGHLIGHT, "Notice", nick, text->str); + else + log_server (s, buffer, BUFFER_LINE_STATUS, + "#s(#n): #m", "Notice", msg->prefix, text->str); + free (nick); } } @@ -4682,10 +4644,15 @@ irc_handle_part (struct server *s, const struct irc_message *msg) if (buffer) { - buffer_send (s->ctx, buffer, BUFFER_LINE_PART, 0, - .who = irc_to_utf8 (s->ctx, msg->prefix), - .object = irc_to_utf8 (s->ctx, channel_name), - .reason = irc_to_utf8 (s->ctx, message)); + struct formatter f; + formatter_init (&f, s->ctx, s); + formatter_add (&f, "#a<--#r #n (#a#s#r) #a#s#r #S", + ATTR_PART, msg->prefix, + ATTR_USERHOST, irc_find_userhost (msg->prefix), + ATTR_PART, "has left", channel_name); + if (message) + formatter_add (&f, " (#m)", message); + log_formatter (s->ctx, buffer, 0, &f); } } @@ -4727,27 +4694,19 @@ irc_send_ctcp_reply (struct server *s, va_end (ap); irc_send (s, "NOTICE %s :\x01%s\x01", recipient, m.str); - - char *text_utf8 = irc_to_utf8 (s->ctx, m.str); - char *recipient_utf8 = irc_to_utf8 (s->ctx, recipient); - str_free (&m); - - buffer_send_status (s->ctx, s->buffer, - "CTCP reply to %s: %s", recipient_utf8, text_utf8); - free (text_utf8); - free (recipient_utf8); + // FIXME: CAP echo-message? + log_server_status (s, s->buffer, + "CTCP reply to #S: #&S", recipient, str_steal (&m)); } static void irc_handle_ctcp_request (struct server *s, const struct irc_message *msg, struct ctcp_chunk *chunk) { - char *nickname = irc_cut_nickname (msg->prefix); - char *nickname_utf8 = irc_to_utf8 (s->ctx, nickname); - char *tag_utf8 = irc_to_utf8 (s->ctx, chunk->tag.str); + char *nickname = irc_cut_nickname (msg->prefix); - buffer_send_status (s->ctx, s->buffer, - "CTCP requested by %s: %s", nickname_utf8, tag_utf8); + log_server_status (s, s->buffer, + "CTCP requested by #n: #S", nickname, chunk->tag.str); const char *target = msg->params.vector[0]; const char *recipient = nickname; @@ -4778,8 +4737,6 @@ irc_handle_ctcp_request (struct server *s, } free (nickname); - free (nickname_utf8); - free (tag_utf8); } static void @@ -4808,16 +4765,23 @@ irc_handle_privmsg_text (struct server *s, if (buffer) { // TODO: some more obvious indication of highlights - int flags = irc_is_highlight (s, text->str) - ? BUFFER_LINE_HIGHLIGHT - : 0; - enum buffer_line_type type = is_action - ? BUFFER_LINE_ACTION - : BUFFER_LINE_PRIVMSG; - buffer_send (s->ctx, buffer, type, flags, - .who = irc_to_utf8 (s->ctx, msg->prefix), - .text = irc_to_utf8 (s->ctx, text->str), - .prefixes = xstrdup (prefixes)); + // IRCv3.2 echo-message could otherwise cause us to highlight ourselves + bool is_highlight = !irc_is_this_us (s, msg->prefix) + && irc_is_highlight (s, text->str); + + // TODO: factor out? + char *nick = irc_cut_nickname (msg->prefix); + if (is_action) + log_server (s, buffer, is_highlight ? BUFFER_LINE_HIGHLIGHT : 0, + " #a*#r #n #m", + is_highlight ? ATTR_HIGHLIGHT : ATTR_ACTION, + msg->prefix, text->str); + else if (is_highlight) + log_server (s, buffer, BUFFER_LINE_HIGHLIGHT, + "#a<#s#s>#r #m", ATTR_HIGHLIGHT, prefixes, nick, text->str); + else + log_server (s, buffer, 0, "<#s#n> #m", prefixes, nick, text->str); + free (nick); } } @@ -4840,6 +4804,20 @@ irc_handle_privmsg (struct server *s, const struct irc_message *msg) } static void +log_quit (struct server *s, + struct buffer *buffer, const char *prefix, const char *reason) +{ + struct formatter f; + formatter_init (&f, s->ctx, s); + formatter_add (&f, "#a<--#r #n (#a#S#r) #a#s#r", + ATTR_PART, prefix, ATTR_USERHOST, irc_find_userhost (prefix), + ATTR_PART, "has quit"); + if (reason) + formatter_add (&f, " (#m)", reason); + log_formatter (s->ctx, buffer, 0, &f); +} + +static void irc_handle_quit (struct server *s, const struct irc_message *msg) { if (!msg->prefix) @@ -4864,9 +4842,7 @@ irc_handle_quit (struct server *s, const struct irc_message *msg) str_map_find (&s->irc_buffer_map, user->nickname); if (buffer) { - buffer_send (s->ctx, buffer, BUFFER_LINE_QUIT, 0, - .who = irc_to_utf8 (s->ctx, msg->prefix), - .reason = irc_to_utf8 (s->ctx, message)); + log_quit (s, buffer, msg->prefix, message); // TODO: set some kind of a flag in the buffer and when the user // reappears on a channel (JOIN), log a "is back online" message. @@ -4877,11 +4853,8 @@ irc_handle_quit (struct server *s, const struct irc_message *msg) // Log a message in all channels the user is in LIST_FOR_EACH (struct user_channel, iter, user->channels) { - buffer = str_map_find (&s->irc_buffer_map, iter->channel->name); - if (buffer) - buffer_send (s->ctx, buffer, BUFFER_LINE_QUIT, 0, - .who = irc_to_utf8 (s->ctx, msg->prefix), - .reason = irc_to_utf8 (s->ctx, message)); + if ((buffer = str_map_find (&s->irc_buffer_map, iter->channel->name))) + log_quit (s, buffer, msg->prefix, message); // This destroys "iter" which doesn't matter to us irc_remove_user_from_channel (user, iter->channel); @@ -4913,9 +4886,8 @@ irc_handle_topic (struct server *s, const struct irc_message *msg) if (buffer) { - buffer_send (s->ctx, buffer, BUFFER_LINE_TOPIC, 0, - .who = irc_to_utf8 (s->ctx, msg->prefix), - .text = irc_to_utf8 (s->ctx, topic)); + log_server (s, buffer, BUFFER_LINE_STATUS, "#n #s \"#m\"", + msg->prefix, "has changed the topic to", topic); } } @@ -5139,8 +5111,7 @@ irc_process_names (struct server *s, struct channel *channel) struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel->name); if (buffer) { - // FIXME: logging - buffer_send_status (s->ctx, buffer, "Users on %s: %s", + log_server_status (s, buffer, "Users on #S: #S", channel->name, all_users); } @@ -5188,10 +5159,7 @@ irc_handle_rpl_topic (struct server *s, const struct irc_message *msg) } if (buffer) - { - // FIXME: logging - buffer_send_status (s->ctx, buffer, "The topic is: %s", topic); - } + log_server_status (s, buffer, "The topic is: #m", topic); } static void @@ -5248,9 +5216,8 @@ irc_handle_rpl_creationtime (struct server *s, const struct irc_message *msg) if (buffer) { - char *x = make_time_string (created); - buffer_send_status (s->ctx, buffer, "Channel created on %s", x); - free (x); + log_server_status (s, buffer, "Channel created on #&s", + make_time_string (created)); } } @@ -5273,13 +5240,11 @@ irc_handle_rpl_topicwhotime (struct server *s, const struct irc_message *msg) hard_assert ((channel && buffer) || (channel && !buffer) || (!channel && !buffer)); - // Topic set by x (y@z) on ... if (buffer) { - // FIXME: logging - char *x = make_time_string (changed); - buffer_send_status (s->ctx, buffer, "Topic set by %s on %s", who, x); - free (x); + log_server_status (s, buffer, "Topic set by #n (#a#S#r) on #&s", + who, ATTR_USERHOST, irc_find_userhost (who), + make_time_string (changed)); } } @@ -5296,9 +5261,8 @@ irc_handle_rpl_inviting (struct server *s, const struct irc_message *msg) if (!(buffer = str_map_find (&s->irc_buffer_map, channel_name))) buffer = s->buffer; - // FIXME: logging - buffer_send_status (s->ctx, buffer, - "You have invited %s to %s", nickname, channel_name); + log_server_status (s, buffer, + "You have invited #n to #S", nickname, channel_name); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5538,10 +5502,8 @@ irc_process_numeric (struct server *s, { // Join the parameter vector back, recode it to our internal encoding // and send it to the server buffer - char *reconstructed = join_str_vector (©, ' '); - buffer_send (s->ctx, buffer, BUFFER_LINE_STATUS, 0, - .text = irc_to_utf8 (s->ctx, reconstructed)); - free (reconstructed); + log_server (s, buffer, BUFFER_LINE_STATUS, + "#&S", join_str_vector (©, ' ')); } str_vector_free (©); @@ -5721,8 +5683,7 @@ send_autosplit_message (struct server *s, struct send_autosplit_args a) struct error *e = NULL; if (!irc_autosplit_message (s, a.message, fixed_part, &lines, &e)) { - buffer_send_error (s->ctx, - buffer ? buffer : s->buffer, "%s", e->message); + log_server_error (s, buffer ? buffer : s->buffer, "#s", e->message); error_free (e); goto end; } @@ -5745,9 +5706,11 @@ log_outcoming_action (struct server *s, (void) a; if (buffer && soft_assert (s->irc_user)) - buffer_send (s->ctx, buffer, BUFFER_LINE_ACTION, 0, - .who = irc_to_utf8 (s->ctx, s->irc_user->nickname), - .text = irc_to_utf8 (s->ctx, line)); + { + // TODO: factor out, copied from irc_handle_privmsg_text() + log_server (s, buffer, 0, " #a*#r #n #m", + ATTR_ACTION, s->irc_user->nickname, line); + } // This can only be sent from a user or channel buffer } @@ -5774,14 +5737,13 @@ log_outcoming_privmsg (struct server *s, } if (buffer && soft_assert (s->irc_user)) - buffer_send (s->ctx, buffer, BUFFER_LINE_PRIVMSG, 0, - .who = irc_to_utf8 (s->ctx, s->irc_user->nickname), - .text = irc_to_utf8 (s->ctx, line), - .prefixes = xstrdup (prefixes)); + { + // TODO: factor out, copied from irc_handle_privmsg_text() + log_server (s, buffer, 0, + "<#s#n> #m", prefixes, s->irc_user->nickname, line); + } else - // TODO: fix logging and encoding - buffer_send (s->ctx, s->buffer, BUFFER_LINE_STATUS, 0, - .text = xstrdup_printf ("MSG(%s): %s", a->target, line)); + log_server_status (s, s->buffer, "MSG(#n): #m", a->target, line); } #define SEND_AUTOSPLIT_PRIVMSG(s, target, message) \ @@ -5793,13 +5755,13 @@ log_outcoming_notice (struct server *s, struct send_autosplit_args *a, struct buffer *buffer, const char *line) { if (buffer && soft_assert (s->irc_user)) - buffer_send (s->ctx, buffer, BUFFER_LINE_NOTICE, 0, - .who = irc_to_utf8 (s->ctx, s->irc_user->nickname), - .text = irc_to_utf8 (s->ctx, line)); + { + // TODO: factor out, copied from irc_handle_notice_text() + log_server (s, buffer, BUFFER_LINE_STATUS, + "#s(#n): #m", "Notice", s->irc_user->nickname, line); + } else - // TODO: fix logging and encoding - buffer_send (s->ctx, s->buffer, BUFFER_LINE_STATUS, 0, - .text = xstrdup_printf ("Notice -> %s: %s", a->target, line)); + log_server_status (s, s->buffer, "Notice -> #n: #m", a->target, line); } #define SEND_AUTOSPLIT_NOTICE(s, target, message) \ @@ -6049,7 +6011,7 @@ try_handle_buffer_goto (struct app_context *ctx, const char *word) return false; if (n > INT_MAX || !buffer_goto (ctx, n)) - buffer_send_error (ctx, NULL, "%s: %s", "No such buffer", word); + log_global_error (ctx, "#s: #s", "No such buffer", word); return true; } @@ -6069,12 +6031,12 @@ try_decode_buffer (struct app_context *ctx, const char *word) static void show_buffers_list (struct app_context *ctx) { - buffer_send_status (ctx, NULL, "%s", ""); - buffer_send_status (ctx, NULL, "Buffers list:"); + log_global_status (ctx, ""); + log_global_status (ctx, "Buffers list:"); int i = 1; LIST_FOR_EACH (struct buffer, iter, ctx->buffers) - buffer_send_status (ctx, NULL, " [%d] %s", i++, iter->name); + log_global_status (ctx, " [#d] #s", i++, iter->name); } static void @@ -6088,11 +6050,11 @@ handle_buffer_close (struct app_context *ctx, struct handler_args *a) buffer = try_decode_buffer (ctx, (which = cut_word (&a->arguments))); if (!buffer) - buffer_send_error (ctx, NULL, "%s: %s", "No such buffer", which); + log_global_error (ctx, "#s: #s", "No such buffer", which); else if (buffer == ctx->global_buffer) - buffer_send_error (ctx, NULL, "Can't close the global buffer"); + log_global_error (ctx, "Can't close the global buffer"); else if (buffer->type == BUFFER_SERVER) - buffer_send_error (ctx, NULL, "Can't close a server buffer"); + log_global_error (ctx, "Can't close a server buffer"); else { if (buffer == ctx->current_buffer) @@ -6114,7 +6076,7 @@ handle_buffer_move (struct app_context *ctx, struct handler_args *a) if (request == 0 || request > total) { - buffer_send_error (ctx, NULL, "%s: %s", + log_global_error (ctx, "#s: #s", "Can't move buffer", "requested position is out of range"); return true; } @@ -6241,8 +6203,8 @@ handle_command_set_assign_item (struct app_context *ctx, if (e) { - buffer_send_error (ctx, NULL, - "Failed to set option \"%s\": %s", key, e->message); + log_global_error (ctx, + "Failed to set option \"#s\": #s", key, e->message); error_free (e); } else @@ -6250,7 +6212,7 @@ handle_command_set_assign_item (struct app_context *ctx, struct str_vector tmp; str_vector_init (&tmp); dump_matching_options (ctx, key, &tmp); - buffer_send_status (ctx, NULL, "Option changed: %s", tmp.vector[0]); + log_global_status (ctx, "Option changed: #s", tmp.vector[0]); str_vector_free (&tmp); } } @@ -6275,15 +6237,14 @@ handle_command_set_assign config_item_parse (arguments, strlen (arguments), true, &e); if (e) { - buffer_send_error (ctx, NULL, "Invalid value: %s", e->message); + log_global_error (ctx, "Invalid value: #s", e->message); error_free (e); return true; } if ((add | remove) && !config_item_type_is_string (new_->type)) { - buffer_send_error (ctx, NULL, - "+= / -= operators need a string argument"); + log_global_error (ctx, "+= / -= operators need a string argument"); config_item_destroy (new_); return true; } @@ -6310,12 +6271,12 @@ handle_command_set (struct app_context *ctx, struct handler_args *a) bool result = true; if (!all.len) - buffer_send_error (ctx, NULL, "No matches: %s", option); + log_global_error (ctx, "No matches: #s", option); else if (!*a->arguments) { - buffer_send_status (ctx, NULL, "%s", ""); + log_global_status (ctx, ""); for (size_t i = 0; i < all.len; i++) - buffer_send_status (ctx, NULL, "%s", all.vector[i]); + log_global_status (ctx, "#s", all.vector[i]); } else result = handle_command_set_assign (ctx, &all, a->arguments); @@ -6340,13 +6301,12 @@ handle_command_save (struct app_context *ctx, struct handler_args *a) if (!filename) { - buffer_send_error (ctx, NULL, - "%s: %s", "Saving configuration failed", e->message); + log_global_error (ctx, + "#s: #s", "Saving configuration failed", e->message); error_free (e); } else - buffer_send_status (ctx, NULL, - "Configuration written to `%s'", filename); + log_global_status (ctx, "Configuration written to `#s'", filename); free (filename); return true; } @@ -6354,12 +6314,14 @@ handle_command_save (struct app_context *ctx, struct handler_args *a) static bool handle_command_msg (struct app_context *ctx, struct handler_args *a) { + (void) ctx; + if (!*a->arguments) return false; char *target = cut_word (&a->arguments); if (!*a->arguments) - buffer_send_error (ctx, a->s->buffer, "No text to send"); + log_server_error (a->s, a->s->buffer, "No text to send"); else SEND_AUTOSPLIT_PRIVMSG (a->s, target, a->arguments); return true; @@ -6373,9 +6335,9 @@ handle_command_query (struct app_context *ctx, struct handler_args *a) char *target = cut_word (&a->arguments); if (irc_is_channel (a->s, target)) - buffer_send_error (ctx, a->s->buffer, "Cannot query a channel"); + log_server_error (a->s, a->s->buffer, "Cannot query a channel"); else if (!*a->arguments) - buffer_send_error (ctx, a->s->buffer, "No text to send"); + log_server_error (a->s, a->s->buffer, "No text to send"); else { buffer_activate (ctx, irc_get_or_make_user_buffer (a->s, target)); @@ -6387,12 +6349,14 @@ handle_command_query (struct app_context *ctx, struct handler_args *a) static bool handle_command_notice (struct app_context *ctx, struct handler_args *a) { + (void) ctx; + if (!*a->arguments) return false; char *target = cut_word (&a->arguments); if (!*a->arguments) - buffer_send_error (ctx, a->s->buffer, "No text to send"); + log_server_error (a->s, a->s->buffer, "No text to send"); else SEND_AUTOSPLIT_NOTICE (a->s, target, a->arguments); return true; @@ -6401,6 +6365,8 @@ handle_command_notice (struct app_context *ctx, struct handler_args *a) static bool handle_command_ctcp (struct app_context *ctx, struct handler_args *a) { + (void) ctx; + if (!*a->arguments) return false; @@ -6417,13 +6383,15 @@ handle_command_ctcp (struct app_context *ctx, struct handler_args *a) else irc_send (a->s, "PRIVMSG %s :\x01%s\x01", target, tag); - buffer_send_status (ctx, a->s->buffer, "CTCP query to %s: %s", target, tag); + log_server_status (a->s, a->s->buffer, "CTCP query to #S: #S", target, tag); return true; } static bool handle_command_me (struct app_context *ctx, struct handler_args *a) { + (void) ctx; + if (a->buffer->type == BUFFER_CHANNEL) SEND_AUTOSPLIT_ACTION (a->s, a->buffer->channel->name, a->arguments); @@ -6431,8 +6399,8 @@ handle_command_me (struct app_context *ctx, struct handler_args *a) SEND_AUTOSPLIT_ACTION (a->s, a->buffer->user->nickname, a->arguments); else - buffer_send_error (ctx, a->s->buffer, - "Can't do this from a server buffer (%s)", + log_server_error (a->s, a->s->buffer, + "Can't do this from a server buffer (#s)", "send CTCP actions"); return true; } @@ -6457,18 +6425,19 @@ handle_command_quit (struct app_context *ctx, struct handler_args *a) static bool handle_command_join (struct app_context *ctx, struct handler_args *a) { + (void) ctx; + // XXX: send the last known channel key? if (irc_is_channel (a->s, a->arguments)) // XXX: we may want to split the list of channels irc_send (a->s, "JOIN %s", a->arguments); else if (a->buffer->type != BUFFER_CHANNEL) - buffer_send_error (ctx, a->buffer, - "%s: %s", "Can't join", + log_server_error (a->s, a->buffer, "#s: #s", "Can't join", "no channel name given and this buffer is not a channel"); // TODO: have a better way of checking if we're on the channel else if (a->buffer->channel->users) - buffer_send_error (ctx, a->buffer, - "%s: %s", "Can't join", "you already are on the channel"); + log_server_error (a->s, a->buffer, "#s: #s", "Can't join", + "you already are on the channel"); else if (*a->arguments) irc_send (a->s, "JOIN %s :%s", a->buffer->channel->name, a->arguments); else @@ -6488,6 +6457,8 @@ part_channel (struct server *s, const char *channel_name, const char *reason) static bool handle_command_part (struct app_context *ctx, struct handler_args *a) { + (void) ctx; + if (irc_is_channel (a->s, a->arguments)) { struct str_vector v; @@ -6498,13 +6469,12 @@ handle_command_part (struct app_context *ctx, struct handler_args *a) str_vector_free (&v); } else if (a->buffer->type != BUFFER_CHANNEL) - buffer_send_error (ctx, a->buffer, - "%s: %s", "Can't part", + log_server_error (a->s, a->buffer, "#s: #s", "Can't part", "no channel name given and this buffer is not a channel"); // TODO: have a better way of checking if we're on the channel else if (!a->buffer->channel->users) - buffer_send_error (ctx, a->buffer, - "%s: %s", "Can't part", "you're not on the channel"); + log_server_error (a->s, a->buffer, "#s: #s", "Can't part", + "you're not on the channel"); else part_channel (a->s, a->buffer->channel->name, a->arguments); return true; @@ -6533,6 +6503,8 @@ cycle_channel (struct server *s, const char *channel_name, const char *reason) static bool handle_command_cycle (struct app_context *ctx, struct handler_args *a) { + (void) ctx; + if (irc_is_channel (a->s, a->arguments)) { struct str_vector v; @@ -6543,13 +6515,12 @@ handle_command_cycle (struct app_context *ctx, struct handler_args *a) str_vector_free (&v); } else if (a->buffer->type != BUFFER_CHANNEL) - buffer_send_error (ctx, a->buffer, - "%s: %s", "Can't cycle", + log_server_error (a->s, a->buffer, "#s: #s", "Can't cycle", "no channel name given and this buffer is not a channel"); // TODO: have a better way of checking if we're on the channel else if (!a->buffer->channel->users) - buffer_send_error (ctx, a->buffer, - "%s: %s", "Can't cycle", "you're not on the channel"); + log_server_error (a->s, a->buffer, "#s: #s", "Can't cycle", + "you're not on the channel"); else cycle_channel (a->s, a->buffer->channel->name, a->arguments); return true; @@ -6558,6 +6529,8 @@ handle_command_cycle (struct app_context *ctx, struct handler_args *a) static bool handle_command_mode (struct app_context *ctx, struct handler_args *a) { + (void) ctx; + // Channel names prefixed by "+" collide with mode strings, // so we just disallow specifying these channels char *target = NULL; @@ -6576,8 +6549,7 @@ handle_command_mode (struct app_context *ctx, struct handler_args *a) target = cut_word (&a->arguments); if (!target) - buffer_send_error (ctx, a->buffer, - "%s: %s", "Can't change mode", + log_server_error (a->s, a->buffer, "#s: #s", "Can't change mode", "no target given and this buffer is neither a PM nor a channel"); else if (*a->arguments) // XXX: split channel mode params as necessary using irc_max_modes? @@ -6738,11 +6710,11 @@ resolve_server (struct app_context *ctx, struct handler_args *a, { char *server_name = cut_word (&a->arguments); if (!(s = str_map_find (&ctx->servers, server_name))) - buffer_send_error (ctx, NULL, "/%s: %s: %s", + log_global_error (ctx, "/#s: #s: #s", command_name, "no such server", server_name); } else if (a->buffer->type == BUFFER_GLOBAL) - buffer_send_error (ctx, NULL, "/%s: %s", + log_global_error (ctx, "/#s: #s", command_name, "no server name given and this buffer is global"); else s = a->buffer->server; @@ -6758,7 +6730,7 @@ handle_command_connect (struct app_context *ctx, struct handler_args *a) if (irc_is_connected (s)) { - buffer_send_error (ctx, s->buffer, "Already connected"); + log_server_error (s, s->buffer, "Already connected"); return true; } if (s->state == IRC_CONNECTING) @@ -6778,11 +6750,11 @@ handle_command_disconnect (struct app_context *ctx, struct handler_args *a) if (s->state == IRC_CONNECTING) { - buffer_send_status (ctx, s->buffer, "Connecting aborted"); + log_server_status (s, s->buffer, "Connecting aborted"); irc_destroy_connector (s); } else if (!irc_is_connected (s)) - buffer_send_error (ctx, s->buffer, "Not connected"); + log_server_error (s, s->buffer, "Not connected"); else irc_initiate_disconnect (s, *a->arguments ? a->arguments : NULL); return true; @@ -6804,6 +6776,8 @@ handle_command_names (struct app_context *ctx, struct handler_args *a) static bool handle_command_whois (struct app_context *ctx, struct handler_args *a) { + (void) ctx; + if (*a->arguments) irc_send (a->s, "WHOIS %s", a->arguments); else if (a->buffer->type == BUFFER_PM) @@ -6811,8 +6785,7 @@ handle_command_whois (struct app_context *ctx, struct handler_args *a) else if (a->buffer->type == BUFFER_SERVER) irc_send (a->s, "WHOIS %s", a->s->irc_user->nickname); else - buffer_send_error (ctx, a->buffer, - "%s: %s", "Can't request info", + log_server_error (a->s, a->buffer, "#s: #s", "Can't request info", "no target given and this buffer is not a PM nor a server"); return true; } @@ -6820,13 +6793,14 @@ handle_command_whois (struct app_context *ctx, struct handler_args *a) static bool handle_command_whowas (struct app_context *ctx, struct handler_args *a) { + (void) ctx; + if (*a->arguments) irc_send (a->s, "WHOWAS %s", a->arguments); else if (a->buffer->type == BUFFER_PM) irc_send (a->s, "WHOWAS %s", a->buffer->user->nickname); else - buffer_send_error (ctx, a->buffer, - "%s: %s", "Can't request info", + log_server_error (a->s, a->buffer, "#s: #s", "Can't request info", "no target given and this buffer is not a PM"); return true; } @@ -7038,25 +7012,21 @@ try_handle_command_help_option (struct app_context *ctx, const char *name) struct config_schema *schema = item->schema; if (!schema) { - buffer_send_error (ctx, NULL, "%s: %s", "Option not recognized", name); + log_global_error (ctx, "#s: #s", "Option not recognized", name); return true; } - buffer_send_status (ctx, NULL, "%s", ""); - buffer_send_status (ctx, NULL, - "Option \"%s\":", name); - buffer_send_status (ctx, NULL, - " Description: %s", schema->comment); - buffer_send_status (ctx, NULL, - " Type: %s", config_item_type_name (schema->type)); - buffer_send_status (ctx, NULL, - " Default: %s", schema->default_ ? schema->default_ : "null"); + log_global_status (ctx, ""); + log_global_status (ctx, "Option \"#s\":", name); + log_global_status (ctx, " Description: #s", schema->comment); + log_global_status (ctx, " Type: #s", config_item_type_name (schema->type)); + log_global_status (ctx, " Default: #s", + schema->default_ ? schema->default_ : "null"); struct str tmp; str_init (&tmp); config_item_write (item, false, &tmp); - buffer_send_status (ctx, NULL, - " Current value: %s", tmp.str); + log_global_status (ctx, " Current value: #s", tmp.str); str_free (&tmp); return true; } @@ -7066,8 +7036,8 @@ handle_command_help (struct app_context *ctx, struct handler_args *a) { if (!*a->arguments) { - buffer_send_status (ctx, NULL, "%s", ""); - buffer_send_status (ctx, NULL, "Commands:"); + log_global_status (ctx, ""); + log_global_status (ctx, "Commands:"); int longest = 0; for (size_t i = 0; i < N_ELEMENTS (g_command_handlers); i++) @@ -7078,8 +7048,8 @@ handle_command_help (struct app_context *ctx, struct handler_args *a) for (size_t i = 0; i < N_ELEMENTS (g_command_handlers); i++) { struct command_handler *handler = &g_command_handlers[i]; - buffer_send_status (ctx, NULL, - " %-*s %s", longest, handler->name, handler->description); + log_global_status (ctx, " #&s", xstrdup_printf + ("%-*s %s", longest, handler->name, handler->description)); } return true; } @@ -7091,17 +7061,16 @@ handle_command_help (struct app_context *ctx, struct handler_args *a) if (strcasecmp_ascii (command, handler->name)) continue; - buffer_send_status (ctx, NULL, "%s", ""); - buffer_send_status (ctx, NULL, "%s: %s", + log_global_status (ctx, ""); + log_global_status (ctx, "#s: #s", handler->name, handler->description); - buffer_send_status (ctx, NULL, " Arguments: %s", + log_global_status (ctx, " Arguments: #s", handler->usage ? handler->usage : "(none)"); return true; } if (!try_handle_command_help_option (ctx, command)) - buffer_send_error (ctx, NULL, - "%s: %s", "No such command or option", command); + log_global_error (ctx, "#s: #s", "No such command or option", command); return true; } @@ -7163,29 +7132,28 @@ process_user_command (struct app_context *ctx, char *input) struct command_handler *handler = str_map_find (&partial, command_name); if (!handler) - buffer_send_error (ctx, NULL, - "%s: %s", "No such command", command_name); + log_global_error (ctx, "#s: #s", "No such command", command_name); else if ((handler->flags & HANDLER_SERVER) && args.buffer->type == BUFFER_GLOBAL) - buffer_send_error (ctx, NULL, - "/%s: %s", command_name, "can't do this from a global buffer"); + log_global_error (ctx, "/#s: #s", + command_name, "can't do this from a global buffer"); else if ((handler->flags & HANDLER_SERVER) && !irc_is_connected ((args.s = args.buffer->server))) - buffer_send_error (ctx, args.s->buffer, "Not connected"); + log_server_error (args.s, args.s->buffer, "Not connected"); else if ((handler->flags & HANDLER_NEEDS_REG) && args.s->state != IRC_REGISTERED) - buffer_send_error (ctx, args.s->buffer, "Not registered"); + log_server_error (args.s, args.s->buffer, "Not registered"); else if (((handler->flags & HANDLER_CHANNEL_FIRST) && !(args.channel_name = try_get_channel (&args, maybe_cut_word))) || ((handler->flags & HANDLER_CHANNEL_LAST) && !(args.channel_name = try_get_channel (&args, maybe_cut_word_from_end)))) - buffer_send_error (ctx, args.buffer, "/%s: %s", command_name, + log_server_error (args.s, args.buffer, "/#s: #s", command_name, "no channel name given and this buffer is not a channel"); else if (!handler->handler (ctx, &args)) - buffer_send_error (ctx, NULL, - "%s: /%s %s", "Usage", handler->name, handler->usage); + log_global_error (ctx, + "#s: /#s #s", "Usage", handler->name, handler->usage); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -7195,12 +7163,9 @@ send_message_to_target (struct server *s, const char *target, char *message, struct buffer *buffer) { if (!irc_is_connected (s)) - { - buffer_send_error (s->ctx, buffer, "Not connected"); - return; - } - - SEND_AUTOSPLIT_PRIVMSG (s, target, message); + log_server_error (s, buffer, "Not connected"); + else + SEND_AUTOSPLIT_PRIVMSG (s, target, message); } static void @@ -7213,7 +7178,8 @@ send_message_to_current_buffer (struct app_context *ctx, char *message) { case BUFFER_GLOBAL: case BUFFER_SERVER: - buffer_send_error (ctx, buffer, "This buffer is not a channel"); + log_full (ctx, NULL, buffer, BUFFER_LINE_ERROR, + "This buffer is not a channel"); break; case BUFFER_CHANNEL: send_message_to_target (buffer->server, |