diff --git a/degesch.c b/degesch.c index 52a7f94..e0089bf 100644 --- a/degesch.c +++ b/degesch.c @@ -389,10 +389,9 @@ struct buffer // Origin information: - struct user *user; ///< Reference to user + struct server *server; ///< Reference to server struct channel *channel; ///< Reference to channel - - // TODO: eventually a reference to the server for server buffers + struct user *user; ///< Reference to user }; static struct buffer * @@ -419,18 +418,9 @@ buffer_destroy (struct buffer *self) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct app_context +struct server { - // Configuration: - - struct str_map config; ///< User configuration - char *attrs[ATTR_COUNT]; ///< Terminal attributes - bool no_colors; ///< Colour output mode - bool reconnect; ///< Whether to reconnect on conn. fail. - unsigned long reconnect_delay; ///< Reconnect delay in seconds - bool isolate_buffers; ///< Isolate global/server buffers - - // Server connection: + struct app_context *ctx; ///< Application context int irc_fd; ///< Socket FD of the server struct str read_buffer; ///< Input yet to be processed @@ -450,6 +440,8 @@ struct app_context // maybe also broadcast all buffers about the disconnection event // TODO: when getting connected again, rejoin all current channels + struct buffer *buffer; ///< The buffer for this server + struct str_map irc_users; ///< IRC user data struct str_map irc_channels; ///< IRC channel data struct str_map irc_buffer_map; ///< Maps IRC identifiers to buffers @@ -460,12 +452,86 @@ struct app_context // Events: - struct poller_fd tty_event; ///< Terminal input event - struct poller_fd signal_event; ///< Signal FD event - struct poller_timer ping_tmr; ///< We should send a ping struct poller_timer timeout_tmr; ///< Connection seems to be dead struct poller_timer reconnect_tmr; ///< We should reconnect now +}; + +static void on_irc_ping_timeout (void *user_data); +static void on_irc_timeout (void *user_data); +static void on_irc_reconnect_timeout (void *user_data); + +static void +server_init (struct server *self, struct poller *poller) +{ + self->irc_fd = -1; + str_init (&self->read_buffer); + self->irc_ready = false; + + str_map_init (&self->irc_users); + self->irc_users.key_xfrm = irc_strxfrm; + str_map_init (&self->irc_channels); + self->irc_channels.key_xfrm = irc_strxfrm; + str_map_init (&self->irc_buffer_map); + self->irc_buffer_map.key_xfrm = irc_strxfrm; + + poller_timer_init (&self->timeout_tmr, poller); + self->timeout_tmr.dispatcher = on_irc_timeout; + self->timeout_tmr.user_data = self; + + poller_timer_init (&self->ping_tmr, poller); + self->ping_tmr.dispatcher = on_irc_ping_timeout; + self->ping_tmr.user_data = self; + + poller_timer_init (&self->reconnect_tmr, poller); + self->reconnect_tmr.dispatcher = on_irc_reconnect_timeout; + self->reconnect_tmr.user_data = self; +} + +static void +server_free (struct server *self) +{ + if (self->irc_fd != -1) + { + xclose (self->irc_fd); + poller_fd_reset (&self->irc_event); + } + str_free (&self->read_buffer); + + if (self->ssl) + SSL_free (self->ssl); + if (self->ssl_ctx) + SSL_CTX_free (self->ssl_ctx); + + if (self->irc_user) + user_unref (self->irc_user); + free (self->irc_user_mode); + free (self->irc_user_host); + + str_map_free (&self->irc_users); + str_map_free (&self->irc_channels); + str_map_free (&self->irc_buffer_map); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct app_context +{ + // Configuration: + + struct str_map config; ///< User configuration + char *attrs[ATTR_COUNT]; ///< Terminal attributes + bool no_colors; ///< Colour output mode + bool reconnect; ///< Whether to reconnect on conn. fail. + unsigned long reconnect_delay; ///< Reconnect delay in seconds + bool isolate_buffers; ///< Isolate global/server buffers + + struct server server; ///< Our only server so far + + // Events: + + struct poller_fd tty_event; ///< Terminal input event + struct poller_fd signal_event; ///< Signal FD event struct poller poller; ///< Manages polled descriptors bool quitting; ///< User requested quitting @@ -483,7 +549,6 @@ struct app_context struct str_map buffers_by_name; ///< Excludes GLOBAL and SERVER struct buffer *global_buffer; ///< The global buffer - struct buffer *server_buffer; ///< The server buffer struct buffer *current_buffer; ///< The current buffer // TODO: So that we always output proper date change messages @@ -503,10 +568,6 @@ struct app_context } *g_ctx; -static void on_irc_ping_timeout (void *user_data); -static void on_irc_timeout (void *user_data); -static void on_irc_reconnect_timeout (void *user_data); - static void app_context_init (struct app_context *self) { @@ -516,19 +577,11 @@ app_context_init (struct app_context *self) self->config.free = free; load_config_defaults (&self->config, g_config_table); - self->irc_fd = -1; - str_init (&self->read_buffer); - self->irc_ready = false; - - str_map_init (&self->irc_users); - self->irc_users.key_xfrm = irc_strxfrm; - str_map_init (&self->irc_channels); - self->irc_channels.key_xfrm = irc_strxfrm; - str_map_init (&self->irc_buffer_map); - self->irc_buffer_map.key_xfrm = irc_strxfrm; - poller_init (&self->poller); + server_init (&self->server, &self->poller); + self->server.ctx = self; + str_map_init (&self->buffers_by_name); self->buffers_by_name.key_xfrm = irc_strxfrm; @@ -559,32 +612,13 @@ app_context_free (struct app_context *self) str_map_free (&self->config); for (size_t i = 0; i < ATTR_COUNT; i++) free (self->attrs[i]); - str_free (&self->read_buffer); - - if (self->irc_fd != -1) - { - xclose (self->irc_fd); - poller_fd_reset (&self->irc_event); - } - if (self->ssl) - SSL_free (self->ssl); - if (self->ssl_ctx) - SSL_CTX_free (self->ssl_ctx); - - if (self->irc_user) - user_unref (self->irc_user); - free (self->irc_user_mode); - free (self->irc_user_host); // FIXME: this doesn't free the history state LIST_FOR_EACH (struct buffer, iter, self->buffers) buffer_destroy (iter); str_map_free (&self->buffers_by_name); - str_map_free (&self->irc_users); - str_map_free (&self->irc_channels); - str_map_free (&self->irc_buffer_map); - + server_free (&self->server); poller_free (&self->poller); iconv_close (self->latin1_to_utf8); @@ -1342,7 +1376,8 @@ buffer_send_internal (struct app_context *ctx, struct buffer *buffer, if (buffer == ctx->current_buffer) buffer_line_display (ctx, line, false); else if (!ctx->isolate_buffers && - (buffer == ctx->global_buffer || buffer == ctx->server_buffer)) + (buffer == ctx->global_buffer || + buffer == ctx->current_buffer->server->buffer)) buffer_line_display (ctx, line, true); else { @@ -1408,10 +1443,11 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer) #endif // RL_READLINE_VERSION // And make sure to unlink the buffer from "irc_buffer_map" + struct server *s = buffer->server; if (buffer->channel) - str_map_set (&ctx->irc_buffer_map, buffer->channel->name, NULL); + str_map_set (&s->irc_buffer_map, buffer->channel->name, NULL); if (buffer->user) - str_map_set (&ctx->irc_buffer_map, buffer->user->nickname, NULL); + str_map_set (&s->irc_buffer_map, buffer->user->nickname, NULL); str_map_set (&ctx->buffers_by_name, buffer->name, NULL); LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer); @@ -1424,8 +1460,8 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer) // one to leave the pointers point to invalid memory if (buffer == ctx->global_buffer) ctx->global_buffer = NULL; - if (buffer == ctx->server_buffer) - ctx->server_buffer = NULL; + if (buffer == ctx->server.buffer) + ctx->server.buffer = NULL; refresh_prompt (ctx); } @@ -1530,7 +1566,7 @@ buffer_rename (struct app_context *ctx, hard_assert (buffer->type == BUFFER_PM); struct buffer *collision = - str_map_find (&ctx->irc_buffer_map, new_name); + str_map_find (&buffer->server->irc_buffer_map, new_name); if (collision) { // TODO: use full weechat-style buffer names @@ -1618,13 +1654,14 @@ init_buffers (struct app_context *ctx) { // At the moment we have only two global everpresent buffers struct buffer *global = ctx->global_buffer = buffer_new (); - struct buffer *server = ctx->server_buffer = buffer_new (); + struct buffer *server = ctx->server.buffer = buffer_new (); global->type = BUFFER_GLOBAL; global->name = xstrdup (PROGRAM_NAME); server->type = BUFFER_SERVER; server->name = xstrdup (str_map_find (&ctx->config, "irc_host")); + server->server = &ctx->server; LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, global); LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, server); @@ -1636,33 +1673,33 @@ static void irc_user_on_destroy (void *object, void *user_data) { struct user *user = object; - struct app_context *ctx = user_data; - str_map_set (&ctx->irc_users, user->nickname, NULL); + struct server *s = user_data; + str_map_set (&s->irc_users, user->nickname, NULL); } static struct user * -irc_make_user (struct app_context *ctx, char *nickname) +irc_make_user (struct server *s, char *nickname) { - hard_assert (!str_map_find (&ctx->irc_users, nickname)); + hard_assert (!str_map_find (&s->irc_users, nickname)); struct user *user = user_new (); user->on_destroy = irc_user_on_destroy; - user->user_data = ctx; + user->user_data = s; user->nickname = nickname; - str_map_set (&ctx->irc_users, user->nickname, user); + str_map_set (&s->irc_users, user->nickname, user); return user; } static struct buffer * -irc_get_or_make_user_buffer (struct app_context *ctx, const char *nickname) +irc_get_or_make_user_buffer (struct server *s, const char *nickname) { - struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, nickname); + struct buffer *buffer = str_map_find (&s->irc_buffer_map, nickname); if (buffer) return buffer; - struct user *user = str_map_find (&ctx->irc_users, nickname); + struct user *user = str_map_find (&s->irc_users, nickname); if (!user) - user = irc_make_user (ctx, xstrdup (nickname)); + user = irc_make_user (s, xstrdup (nickname)); else user = user_ref (user); @@ -1670,9 +1707,10 @@ irc_get_or_make_user_buffer (struct app_context *ctx, const char *nickname) buffer = buffer_new (); buffer->type = BUFFER_PM; buffer->name = xstrdup (nickname); + buffer->server = s; buffer->user = user; - LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer); - str_map_set (&ctx->irc_buffer_map, user->nickname, buffer); + LIST_APPEND_WITH_TAIL (s->ctx->buffers, s->ctx->buffers_tail, buffer); + str_map_set (&s->irc_buffer_map, user->nickname, buffer); return buffer; } @@ -1698,24 +1736,24 @@ static void irc_channel_on_destroy (void *object, void *user_data) { struct channel *channel = object; - struct app_context *ctx = user_data; + struct server *s = user_data; LIST_FOR_EACH (struct channel_user, iter, channel->users) irc_channel_unlink_user (channel, iter); - str_map_set (&ctx->irc_channels, channel->name, NULL); + str_map_set (&s->irc_channels, channel->name, NULL); } static struct channel * -irc_make_channel (struct app_context *ctx, char *name) +irc_make_channel (struct server *s, char *name) { - hard_assert (!str_map_find (&ctx->irc_channels, name)); + hard_assert (!str_map_find (&s->irc_channels, name)); struct channel *channel = channel_new (); channel->on_destroy = irc_channel_on_destroy; - channel->user_data = ctx; + channel->user_data = s; channel->name = name; channel->mode = xstrdup (""); channel->topic = NULL; - str_map_set (&ctx->irc_channels, channel->name, channel); + str_map_set (&s->irc_channels, channel->name, channel); return channel; } @@ -1743,37 +1781,38 @@ irc_find_userhost (const char *prefix) } static bool -irc_is_this_us (struct app_context *ctx, const char *prefix) +irc_is_this_us (struct server *s, const char *prefix) { char *nick = irc_cut_nickname (prefix); - bool result = !irc_strcmp (nick, ctx->irc_user->nickname); + bool result = !irc_strcmp (nick, s->irc_user->nickname); free (nick); return result; } static bool -irc_is_channel (struct app_context *ctx, const char *ident) +irc_is_channel (struct server *s, const char *ident) { - (void) ctx; // TODO: parse prefixes from server features + (void) s; // TODO: parse prefixes from server features return *ident && !!strchr ("#&+!", *ident); } static void -irc_shutdown (struct app_context *ctx) +irc_shutdown (struct server *s) { // TODO: set a timer after which we cut the connection? // Generally non-critical - if (ctx->ssl) - soft_assert (SSL_shutdown (ctx->ssl) != -1); + if (s->ssl) + soft_assert (SSL_shutdown (s->ssl) != -1); else - soft_assert (shutdown (ctx->irc_fd, SHUT_WR) == 0); + soft_assert (shutdown (s->irc_fd, SHUT_WR) == 0); } static void try_finish_quit (struct app_context *ctx) { - if (ctx->quitting && ctx->irc_fd == -1) + // TODO: multiserver + if (ctx->quitting && ctx->server.irc_fd == -1) ctx->polling = false; } @@ -1790,11 +1829,14 @@ initiate_quit (struct app_context *ctx) // This is okay as long as we're not called from within readline rl_callback_handler_remove (); - // Initiate a connection close buffer_send_status (ctx, ctx->global_buffer, "Shutting down"); - if (ctx->irc_fd != -1) + + // Initiate a connection close + // TODO: multiserver + struct server *s = &ctx->server; + if (s->irc_fd != -1) // XXX: when we go async, we'll have to flush output buffers first - irc_shutdown (ctx); + irc_shutdown (s); ctx->quitting = true; try_finish_quit (ctx); @@ -1823,13 +1865,13 @@ irc_to_term (struct app_context *ctx, const char *text) return term; } -static bool irc_send (struct app_context *ctx, +static bool irc_send (struct server *s, const char *format, ...) ATTRIBUTE_PRINTF (2, 3); static bool -irc_send (struct app_context *ctx, const char *format, ...) +irc_send (struct server *s, const char *format, ...) { - if (!soft_assert (ctx->irc_fd != -1)) + if (!soft_assert (s->irc_fd != -1)) { print_debug ("tried sending a message to a dead server connection"); return false; @@ -1845,30 +1887,30 @@ irc_send (struct app_context *ctx, const char *format, ...) if (g_debug_mode) { struct app_readline_state state; - if (ctx->readline_prompt_shown) + if (s->ctx->readline_prompt_shown) app_readline_hide (&state); - char *term = irc_to_term (ctx, str.str); + char *term = irc_to_term (s->ctx, str.str); fprintf (stderr, "[IRC] <== \"%s\"\n", term); free (term); - if (ctx->readline_prompt_shown) - app_readline_restore (&state, ctx->readline_prompt); + if (s->ctx->readline_prompt_shown) + app_readline_restore (&state, s->ctx->readline_prompt); } str_append (&str, "\r\n"); bool result = true; - if (ctx->ssl) + if (s->ssl) { // TODO: call SSL_get_error() to detect if a clean shutdown has occured - if (SSL_write (ctx->ssl, str.str, str.len) != (int) str.len) + if (SSL_write (s->ssl, str.str, str.len) != (int) str.len) { LOG_FUNC_FAILURE ("SSL_write", ERR_error_string (ERR_get_error (), NULL)); result = false; } } - else if (write (ctx->irc_fd, str.str, str.len) != (ssize_t) str.len) + else if (write (s->irc_fd, str.str, str.len) != (ssize_t) str.len) { LOG_LIBC_FAILURE ("write"); result = false; @@ -1893,24 +1935,24 @@ irc_get_boolean_from_config } static bool -irc_initialize_ssl_ctx (struct app_context *ctx, struct error **e) +irc_initialize_ssl_ctx (struct server *s, struct error **e) { // XXX: maybe we should call SSL_CTX_set_options() for some workarounds bool verify; - if (!irc_get_boolean_from_config (ctx, "ssl_verify", &verify, e)) + if (!irc_get_boolean_from_config (s->ctx, "ssl_verify", &verify, e)) return false; if (!verify) - SSL_CTX_set_verify (ctx->ssl_ctx, SSL_VERIFY_NONE, NULL); + SSL_CTX_set_verify (s->ssl_ctx, SSL_VERIFY_NONE, NULL); - const char *ca_file = str_map_find (&ctx->config, "ca_file"); - const char *ca_path = str_map_find (&ctx->config, "ca_path"); + const char *ca_file = str_map_find (&s->ctx->config, "ca_file"); + const char *ca_path = str_map_find (&s->ctx->config, "ca_path"); struct error *error = NULL; if (ca_file || ca_path) { - if (SSL_CTX_load_verify_locations (ctx->ssl_ctx, ca_file, ca_path)) + if (SSL_CTX_load_verify_locations (s->ssl_ctx, ca_file, ca_path)) return true; error_set (&error, "%s: %s", @@ -1919,7 +1961,7 @@ irc_initialize_ssl_ctx (struct app_context *ctx, struct error **e) goto ca_error; } - if (!SSL_CTX_set_default_verify_paths (ctx->ssl_ctx)) + if (!SSL_CTX_set_default_verify_paths (s->ssl_ctx)) { error_set (&error, "%s: %s", "Couldn't load the default CA certificate bundle", @@ -1936,48 +1978,48 @@ ca_error: } // Only inform the user if we're not actually verifying - buffer_send_error (ctx, ctx->server_buffer, "%s", error->message); + buffer_send_error (s->ctx, s->buffer, "%s", error->message); error_free (error); return true; } static bool -irc_initialize_ssl (struct app_context *ctx, struct error **e) +irc_initialize_ssl (struct server *s, struct error **e) { const char *error_info = NULL; - ctx->ssl_ctx = SSL_CTX_new (SSLv23_client_method ()); - if (!ctx->ssl_ctx) + s->ssl_ctx = SSL_CTX_new (SSLv23_client_method ()); + if (!s->ssl_ctx) goto error_ssl_1; - if (!irc_initialize_ssl_ctx (ctx, e)) + if (!irc_initialize_ssl_ctx (s, e)) goto error_ssl_2; - ctx->ssl = SSL_new (ctx->ssl_ctx); - if (!ctx->ssl) + s->ssl = SSL_new (s->ssl_ctx); + if (!s->ssl) goto error_ssl_2; - const char *ssl_cert = str_map_find (&ctx->config, "ssl_cert"); + const char *ssl_cert = str_map_find (&s->ctx->config, "ssl_cert"); if (ssl_cert) { char *path = resolve_config_filename (ssl_cert); if (!path) - buffer_send_error (ctx, ctx->global_buffer, + buffer_send_error (s->ctx, s->ctx->global_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 (ctx->ssl, path, SSL_FILETYPE_PEM) - || !SSL_use_PrivateKey_file (ctx->ssl, path, SSL_FILETYPE_PEM)) - buffer_send_error (ctx, ctx->global_buffer, + 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, s->ctx->global_buffer, "%s: %s", "Setting the SSL client certificate failed", ERR_error_string (ERR_get_error (), NULL)); free (path); } - SSL_set_connect_state (ctx->ssl); - if (!SSL_set_fd (ctx->ssl, ctx->irc_fd)) + SSL_set_connect_state (s->ssl); + if (!SSL_set_fd (s->ssl, s->irc_fd)) goto error_ssl_3; // Avoid SSL_write() returning SSL_ERROR_WANT_READ - SSL_set_mode (ctx->ssl, SSL_MODE_AUTO_RETRY); + SSL_set_mode (s->ssl, SSL_MODE_AUTO_RETRY); - switch (xssl_get_error (ctx->ssl, SSL_connect (ctx->ssl), &error_info)) + switch (xssl_get_error (s->ssl, SSL_connect (s->ssl), &error_info)) { case SSL_ERROR_NONE: return true; @@ -1988,11 +2030,11 @@ irc_initialize_ssl (struct app_context *ctx, struct error **e) } error_ssl_3: - SSL_free (ctx->ssl); - ctx->ssl = NULL; + SSL_free (s->ssl); + s->ssl = NULL; error_ssl_2: - SSL_CTX_free (ctx->ssl_ctx); - ctx->ssl_ctx = NULL; + SSL_CTX_free (s->ssl_ctx); + s->ssl_ctx = NULL; error_ssl_1: // XXX: these error strings are really nasty; also there could be // multiple errors on the OpenSSL stack. @@ -2003,7 +2045,7 @@ error_ssl_1: } static bool -irc_establish_connection (struct app_context *ctx, +irc_establish_connection (struct server *s, const char *host, const char *port, struct error **e) { struct addrinfo gai_hints, *gai_result, *gai_iter; @@ -2044,8 +2086,7 @@ irc_establish_connection (struct app_context *ctx, real_host = buf; char *address = format_host_port_pair (real_host, port); - buffer_send_status (ctx, ctx->server_buffer, - "Connecting to %s...", address); + buffer_send_status (s->ctx, s->buffer, "Connecting to %s...", address); free (address); if (!connect (sockfd, gai_iter->ai_addr, gai_iter->ai_addrlen)) @@ -2062,7 +2103,7 @@ irc_establish_connection (struct app_context *ctx, return false; } - ctx->irc_fd = sockfd; + s->irc_fd = sockfd; return true; } @@ -2114,14 +2155,15 @@ make_prompt (struct app_context *ctx, struct str *output) if (buffer != ctx->global_buffer) { + struct server *s = buffer->server; str_append_c (output, ' '); - if (ctx->irc_fd == -1) + if (s->irc_fd == -1) str_append (output, "(disconnected)"); else { - str_append (output, ctx->irc_user->nickname); - if (*ctx->irc_user_mode) - str_append_printf (output, "(%s)", ctx->irc_user_mode); + str_append (output, s->irc_user->nickname); + if (*s->irc_user_mode) + str_append_printf (output, "(%s)", s->irc_user_mode); } } @@ -2440,13 +2482,13 @@ ctcp_destroy (struct ctcp_chunk *list) // TODO: we alse definitely need to parse server capability messages static struct buffer * -irc_get_buffer_for_message (struct app_context *ctx, +irc_get_buffer_for_message (struct server *s, const struct irc_message *msg, const char *target) { - struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, target); - if (irc_is_channel (ctx, target)) + struct buffer *buffer = str_map_find (&s->irc_buffer_map, target); + if (irc_is_channel (s, target)) { - struct channel *channel = str_map_find (&ctx->irc_channels, target); + struct channel *channel = str_map_find (&s->irc_channels, target); hard_assert ((channel && buffer) || (channel && !buffer) || (!channel && !buffer)); @@ -2460,17 +2502,17 @@ irc_get_buffer_for_message (struct app_context *ctx, // Don't make user buffers for servers (they can send NOTICEs) if (!irc_find_userhost (msg->prefix)) - return ctx->server_buffer; + return s->buffer; char *nickname = irc_cut_nickname (msg->prefix); - buffer = irc_get_or_make_user_buffer (ctx, nickname); + buffer = irc_get_or_make_user_buffer (s, nickname); free (nickname); } return buffer; } static bool -irc_is_highlight (struct app_context *ctx, const char *message) +irc_is_highlight (struct server *s, const char *message) { // Well, this is rather crude but it should make most users happy. // Ideally we could do this at least in proper Unicode. @@ -2478,7 +2520,7 @@ irc_is_highlight (struct app_context *ctx, const char *message) for (char *p = copy; *p; p++) *p = irc_tolower (*p); - char *nick = xstrdup (ctx->irc_user->nickname); + char *nick = xstrdup (s->irc_user->nickname); for (char *p = nick; *p; p++) *p = irc_tolower (*p); @@ -2504,32 +2546,33 @@ irc_is_highlight (struct app_context *ctx, const char *message) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static void -irc_handle_join (struct app_context *ctx, const struct irc_message *msg) +irc_handle_join (struct server *s, const struct irc_message *msg) { if (!msg->prefix || msg->params.len < 1) return; const char *channel_name = msg->params.vector[0]; - if (!irc_is_channel (ctx, channel_name)) + if (!irc_is_channel (s, channel_name)) return; - struct channel *channel = str_map_find (&ctx->irc_channels, channel_name); - struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, channel_name); + struct channel *channel = str_map_find (&s->irc_channels, channel_name); + struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name); hard_assert ((channel && buffer) || (channel && !buffer) || (!channel && !buffer)); // We've joined a new channel - if (!channel && irc_is_this_us (ctx, msg->prefix)) + if (!channel && irc_is_this_us (s, msg->prefix)) { buffer = buffer_new (); buffer->type = BUFFER_CHANNEL; buffer->name = xstrdup (channel_name); + buffer->server = s; buffer->channel = channel = - irc_make_channel (ctx, xstrdup (channel_name)); - LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer); - str_map_set (&ctx->irc_buffer_map, channel->name, buffer); + irc_make_channel (s, xstrdup (channel_name)); + LIST_APPEND_WITH_TAIL (s->ctx->buffers, s->ctx->buffers_tail, buffer); + str_map_set (&s->irc_buffer_map, channel->name, buffer); - buffer_activate (ctx, buffer); + buffer_activate (s->ctx, buffer); } // This is weird, ignoring @@ -2538,9 +2581,9 @@ irc_handle_join (struct app_context *ctx, const struct irc_message *msg) // Get or make a user object char *nickname = irc_cut_nickname (msg->prefix); - struct user *user = str_map_find (&ctx->irc_users, nickname); + struct user *user = str_map_find (&s->irc_users, nickname); if (!user) - user = irc_make_user (ctx, nickname); + user = irc_make_user (s, nickname); else { user = user_ref (user); @@ -2560,31 +2603,31 @@ irc_handle_join (struct app_context *ctx, const struct irc_message *msg) // Finally log the message if (buffer) { - buffer_send (ctx, buffer, BUFFER_LINE_JOIN, 0, - .who = irc_to_utf8 (ctx, msg->prefix), - .object = irc_to_utf8 (ctx, channel_name)); + 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)); } } static void -irc_handle_kick (struct app_context *ctx, const struct irc_message *msg) +irc_handle_kick (struct server *s, const struct irc_message *msg) { if (!msg->prefix || msg->params.len < 2) return; const char *channel_name = msg->params.vector[0]; const char *target = msg->params.vector[1]; - if (!irc_is_channel (ctx, channel_name) - || irc_is_channel (ctx, target)) + if (!irc_is_channel (s, channel_name) + || irc_is_channel (s, target)) return; const char *message = ""; if (msg->params.len > 2) message = msg->params.vector[2]; - struct user *user = str_map_find (&ctx->irc_users, target); - struct channel *channel = str_map_find (&ctx->irc_channels, channel_name); - struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, channel_name); + struct user *user = str_map_find (&s->irc_users, target); + struct channel *channel = str_map_find (&s->irc_channels, channel_name); + struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name); hard_assert ((channel && buffer) || (channel && !buffer) || (!channel && !buffer)); @@ -2594,22 +2637,22 @@ irc_handle_kick (struct app_context *ctx, const struct irc_message *msg) if (buffer) { - buffer_send (ctx, buffer, BUFFER_LINE_KICK, 0, - .who = irc_to_utf8 (ctx, msg->prefix), - .object = irc_to_utf8 (ctx, target), - .reason = irc_to_utf8 (ctx, message)); + 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)); } } static void -irc_handle_mode (struct app_context *ctx, const struct irc_message *msg) +irc_handle_mode (struct server *s, const struct irc_message *msg) { // TODO: parse the mode change and apply it // TODO: log a message } static void -irc_handle_nick (struct app_context *ctx, const struct irc_message *msg) +irc_handle_nick (struct server *s, const struct irc_message *msg) { if (!msg->prefix || msg->params.len < 1) return; @@ -2617,40 +2660,40 @@ irc_handle_nick (struct app_context *ctx, const struct irc_message *msg) const char *new_nickname = msg->params.vector[0]; char *nickname = irc_cut_nickname (msg->prefix); - struct user *user = str_map_find (&ctx->irc_users, nickname); + struct user *user = str_map_find (&s->irc_users, nickname); free (nickname); if (!user) return; // What the fuck // TODO: probably log a message and force a reconnect - if (str_map_find (&ctx->irc_users, new_nickname)) + if (str_map_find (&s->irc_users, new_nickname)) return; // Log a message in any PM buffer and rename it; // we may even have one for ourselves struct buffer *pm_buffer = - str_map_find (&ctx->irc_buffer_map, user->nickname); + str_map_find (&s->irc_buffer_map, user->nickname); if (pm_buffer) { - str_map_set (&ctx->irc_buffer_map, new_nickname, pm_buffer); - str_map_set (&ctx->irc_buffer_map, user->nickname, NULL); + str_map_set (&s->irc_buffer_map, new_nickname, pm_buffer); + str_map_set (&s->irc_buffer_map, user->nickname, NULL); - char *who = irc_is_this_us (ctx, msg->prefix) - ? irc_to_utf8 (ctx, msg->prefix) + char *who = irc_is_this_us (s, msg->prefix) + ? irc_to_utf8 (s->ctx, msg->prefix) : NULL; - buffer_send (ctx, pm_buffer, BUFFER_LINE_NICK, 0, + buffer_send (s->ctx, pm_buffer, BUFFER_LINE_NICK, 0, .who = who, - .object = irc_to_utf8 (ctx, new_nickname)); + .object = irc_to_utf8 (s->ctx, new_nickname)); // TODO: use a full weechat-style buffer name here - buffer_rename (ctx, pm_buffer, new_nickname); + buffer_rename (s->ctx, pm_buffer, new_nickname); } - if (irc_is_this_us (ctx, msg->prefix)) + if (irc_is_this_us (s, msg->prefix)) { // Log a message in all open buffers on this server struct str_map_iter iter; - str_map_iter_init (&iter, &ctx->irc_buffer_map); + str_map_iter_init (&iter, &s->irc_buffer_map); struct buffer *buffer; while ((buffer = str_map_iter_next (&iter))) { @@ -2658,8 +2701,8 @@ irc_handle_nick (struct app_context *ctx, const struct irc_message *msg) if (buffer == pm_buffer) continue; - buffer_send (ctx, buffer, BUFFER_LINE_NICK, 0, - .object = irc_to_utf8 (ctx, new_nickname)); + buffer_send (s->ctx, buffer, BUFFER_LINE_NICK, 0, + .object = irc_to_utf8 (s->ctx, new_nickname)); } } else @@ -2668,35 +2711,35 @@ irc_handle_nick (struct app_context *ctx, const struct irc_message *msg) LIST_FOR_EACH (struct user_channel, iter, user->channels) { struct buffer *buffer = - str_map_find (&ctx->irc_buffer_map, iter->channel->name); + str_map_find (&s->irc_buffer_map, iter->channel->name); hard_assert (buffer != NULL); - buffer_send (ctx, buffer, BUFFER_LINE_NICK, 0, - .who = irc_to_utf8 (ctx, msg->prefix), - .object = irc_to_utf8 (ctx, new_nickname)); + 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)); } } // Finally rename the user - str_map_set (&ctx->irc_users, new_nickname, user_ref (user)); - str_map_set (&ctx->irc_users, user->nickname, NULL); + str_map_set (&s->irc_users, new_nickname, user_ref (user)); + str_map_set (&s->irc_users, user->nickname, NULL); free (user->nickname); user->nickname = xstrdup (new_nickname); // We might have renamed ourselves - refresh_prompt (ctx); + refresh_prompt (s->ctx); } static void -irc_handle_ctcp_reply (struct app_context *ctx, +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 (ctx, nickname); - char *tag_utf8 = irc_to_utf8 (ctx, chunk->tag.str); - char *text_utf8 = irc_to_utf8 (ctx, chunk->text.str); + 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 (ctx, ctx->server_buffer, + buffer_send_status (s->ctx, s->buffer, "CTCP reply from %s: %s %s", nickname_utf8, tag_utf8, text_utf8); free (nickname); @@ -2706,26 +2749,26 @@ irc_handle_ctcp_reply (struct app_context *ctx, } static void -irc_handle_notice_text (struct app_context *ctx, +irc_handle_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 (ctx, msg, target); + struct buffer *buffer = irc_get_buffer_for_message (s, msg, target); if (buffer) { // TODO: some more obvious indication of highlights - int flags = irc_is_highlight (ctx, text->str) + int flags = irc_is_highlight (s, text->str) ? BUFFER_LINE_HIGHLIGHT : 0; - buffer_send (ctx, buffer, BUFFER_LINE_NOTICE, flags, - .who = irc_to_utf8 (ctx, msg->prefix), - .text = irc_to_utf8 (ctx, text->str)); + 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)); } } static void -irc_handle_notice (struct app_context *ctx, const struct irc_message *msg) +irc_handle_notice (struct server *s, const struct irc_message *msg) { if (!msg->prefix || msg->params.len < 2) return; @@ -2734,20 +2777,20 @@ irc_handle_notice (struct app_context *ctx, const struct irc_message *msg) struct ctcp_chunk *chunks = ctcp_parse (msg->params.vector[1]); LIST_FOR_EACH (struct ctcp_chunk, iter, chunks) if (!iter->is_extended) - irc_handle_notice_text (ctx, msg, &iter->text); + irc_handle_notice_text (s, msg, &iter->text); else - irc_handle_ctcp_reply (ctx, msg, iter); + irc_handle_ctcp_reply (s, msg, iter); ctcp_destroy (chunks); } static void -irc_handle_part (struct app_context *ctx, const struct irc_message *msg) +irc_handle_part (struct server *s, const struct irc_message *msg) { if (!msg->prefix || msg->params.len < 1) return; const char *channel_name = msg->params.vector[0]; - if (!irc_is_channel (ctx, channel_name)) + if (!irc_is_channel (s, channel_name)) return; const char *message = ""; @@ -2755,11 +2798,11 @@ irc_handle_part (struct app_context *ctx, const struct irc_message *msg) message = msg->params.vector[1]; char *nickname = irc_cut_nickname (msg->prefix); - struct user *user = str_map_find (&ctx->irc_users, nickname); + struct user *user = str_map_find (&s->irc_users, nickname); free (nickname); - struct channel *channel = str_map_find (&ctx->irc_channels, channel_name); - struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, channel_name); + struct channel *channel = str_map_find (&s->irc_channels, channel_name); + struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name); hard_assert ((channel && buffer) || (channel && !buffer) || (!channel && !buffer)); @@ -2769,20 +2812,20 @@ irc_handle_part (struct app_context *ctx, const struct irc_message *msg) if (buffer) { - buffer_send (ctx, buffer, BUFFER_LINE_PART, 0, - .who = irc_to_utf8 (ctx, msg->prefix), - .object = irc_to_utf8 (ctx, channel_name), - .reason = irc_to_utf8 (ctx, message)); + 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)); } } static void -irc_handle_ping (struct app_context *ctx, const struct irc_message *msg) +irc_handle_ping (struct server *s, const struct irc_message *msg) { if (msg->params.len) - irc_send (ctx, "PONG :%s", msg->params.vector[0]); + irc_send (s, "PONG :%s", msg->params.vector[0]); else - irc_send (ctx, "PONG"); + irc_send (s, "PONG"); } static char * @@ -2798,11 +2841,11 @@ ctime_now (char buf[26]) return buf; } -static void irc_send_ctcp_reply (struct app_context *ctx, const char *recipient, +static void irc_send_ctcp_reply (struct server *s, const char *recipient, const char *format, ...) ATTRIBUTE_PRINTF (3, 4); static void -irc_send_ctcp_reply (struct app_context *ctx, +irc_send_ctcp_reply (struct server *s, const char *recipient, const char *format, ...) { struct str m; @@ -2813,46 +2856,46 @@ irc_send_ctcp_reply (struct app_context *ctx, str_append_vprintf (&m, format, ap); va_end (ap); - irc_send (ctx, "NOTICE %s :\x01%s\x01", recipient, m.str); + irc_send (s, "NOTICE %s :\x01%s\x01", recipient, m.str); - char *text_utf8 = irc_to_utf8 (ctx, m.str); - char *recipient_utf8 = irc_to_utf8 (ctx, recipient); + 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 (ctx, ctx->server_buffer, + buffer_send_status (s->ctx, s->buffer, "CTCP reply to %s: %s", recipient_utf8, text_utf8); free (text_utf8); free (recipient_utf8); } static void -irc_handle_ctcp_request (struct app_context *ctx, +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 (ctx, nickname); - char *tag_utf8 = irc_to_utf8 (ctx, chunk->tag.str); + 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); - buffer_send_status (ctx, ctx->server_buffer, + buffer_send_status (s->ctx, s->buffer, "CTCP requested by %s: %s", nickname_utf8, tag_utf8); const char *target = msg->params.vector[0]; const char *recipient = nickname; - if (irc_is_channel (ctx, target)) + if (irc_is_channel (s, target)) recipient = target; if (!strcmp (chunk->tag.str, "CLIENTINFO")) - irc_send_ctcp_reply (ctx, recipient, "CLIENTINFO %s %s %s %s", + irc_send_ctcp_reply (s, recipient, "CLIENTINFO %s %s %s %s", "PING", "VERSION", "TIME", "CLIENTINFO"); else if (!strcmp (chunk->tag.str, "PING")) - irc_send_ctcp_reply (ctx, recipient, "PING %s", chunk->text.str); + irc_send_ctcp_reply (s, recipient, "PING %s", chunk->text.str); else if (!strcmp (chunk->tag.str, "VERSION")) { struct utsname info; if (uname (&info)) LOG_LIBC_FAILURE ("uname"); else - irc_send_ctcp_reply (ctx, recipient, "VERSION %s %s on %s %s", + irc_send_ctcp_reply (s, recipient, "VERSION %s %s on %s %s", PROGRAM_NAME, PROGRAM_VERSION, info.sysname, info.machine); } else if (!strcmp (chunk->tag.str, "TIME")) @@ -2861,7 +2904,7 @@ irc_handle_ctcp_request (struct app_context *ctx, if (!ctime_now (buf)) LOG_LIBC_FAILURE ("asctime_r"); else - irc_send_ctcp_reply (ctx, recipient, "TIME %s", buf); + irc_send_ctcp_reply (s, recipient, "TIME %s", buf); } free (nickname); @@ -2870,29 +2913,29 @@ irc_handle_ctcp_request (struct app_context *ctx, } static void -irc_handle_privmsg_text (struct app_context *ctx, +irc_handle_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 (ctx, msg, target); + struct buffer *buffer = irc_get_buffer_for_message (s, msg, target); if (buffer) { // TODO: some more obvious indication of highlights - int flags = irc_is_highlight (ctx, text->str) + 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 (ctx, buffer, type, flags, - .who = irc_to_utf8 (ctx, msg->prefix), - .text = irc_to_utf8 (ctx, text->str)); + buffer_send (s->ctx, buffer, type, flags, + .who = irc_to_utf8 (s->ctx, msg->prefix), + .text = irc_to_utf8 (s->ctx, text->str)); } } static void -irc_handle_privmsg (struct app_context *ctx, const struct irc_message *msg) +irc_handle_privmsg (struct server *s, const struct irc_message *msg) { if (!msg->prefix || msg->params.len < 2) return; @@ -2901,26 +2944,26 @@ irc_handle_privmsg (struct app_context *ctx, const struct irc_message *msg) struct ctcp_chunk *chunks = ctcp_parse (msg->params.vector[1]); LIST_FOR_EACH (struct ctcp_chunk, iter, chunks) if (!iter->is_extended) - irc_handle_privmsg_text (ctx, msg, &iter->text, false); + irc_handle_privmsg_text (s, msg, &iter->text, false); else if (!strcmp (iter->tag.str, "ACTION")) - irc_handle_privmsg_text (ctx, msg, &iter->text, true); + irc_handle_privmsg_text (s, msg, &iter->text, true); else - irc_handle_ctcp_request (ctx, msg, iter); + irc_handle_ctcp_request (s, msg, iter); ctcp_destroy (chunks); } static void -irc_handle_quit (struct app_context *ctx, const struct irc_message *msg) +irc_handle_quit (struct server *s, const struct irc_message *msg) { if (!msg->prefix) return; // What the fuck - if (irc_is_this_us (ctx, msg->prefix)) + if (irc_is_this_us (s, msg->prefix)) return; char *nickname = irc_cut_nickname (msg->prefix); - struct user *user = str_map_find (&ctx->irc_users, nickname); + struct user *user = str_map_find (&s->irc_users, nickname); free (nickname); if (!user) return; @@ -2931,12 +2974,12 @@ irc_handle_quit (struct app_context *ctx, const struct irc_message *msg) // Log a message in any PM buffer struct buffer *buffer = - str_map_find (&ctx->irc_buffer_map, user->nickname); + str_map_find (&s->irc_buffer_map, user->nickname); if (buffer) { - buffer_send (ctx, buffer, BUFFER_LINE_QUIT, 0, - .who = irc_to_utf8 (ctx, msg->prefix), - .reason = irc_to_utf8 (ctx, message)); + buffer_send (s->ctx, buffer, BUFFER_LINE_QUIT, 0, + .who = irc_to_utf8 (s->ctx, msg->prefix), + .reason = irc_to_utf8 (s->ctx, message)); // TODO: set some kind of a flag in the buffer and when the user // reappers on a channel (JOIN), log a "is back online" message. @@ -2947,11 +2990,11 @@ irc_handle_quit (struct app_context *ctx, 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 (&ctx->irc_buffer_map, iter->channel->name); + buffer = str_map_find (&s->irc_buffer_map, iter->channel->name); if (buffer) - buffer_send (ctx, buffer, BUFFER_LINE_QUIT, 0, - .who = irc_to_utf8 (ctx, msg->prefix), - .reason = irc_to_utf8 (ctx, message)); + buffer_send (s->ctx, buffer, BUFFER_LINE_QUIT, 0, + .who = irc_to_utf8 (s->ctx, msg->prefix), + .reason = irc_to_utf8 (s->ctx, message)); // This destroys "iter" which doesn't matter to us irc_remove_user_from_channel (user, iter->channel); @@ -2959,18 +3002,18 @@ irc_handle_quit (struct app_context *ctx, const struct irc_message *msg) } static void -irc_handle_topic (struct app_context *ctx, const struct irc_message *msg) +irc_handle_topic (struct server *s, const struct irc_message *msg) { if (!msg->prefix || msg->params.len < 2) return; const char *channel_name = msg->params.vector[0]; const char *topic = msg->params.vector[1]; - if (!irc_is_channel (ctx, channel_name)) + if (!irc_is_channel (s, channel_name)) return; - struct channel *channel = str_map_find (&ctx->irc_channels, channel_name); - struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, channel_name); + struct channel *channel = str_map_find (&s->irc_channels, channel_name); + struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name); hard_assert ((channel && buffer) || (channel && !buffer) || (!channel && !buffer)); @@ -2983,16 +3026,16 @@ irc_handle_topic (struct app_context *ctx, const struct irc_message *msg) if (buffer) { - buffer_send (ctx, buffer, BUFFER_LINE_TOPIC, 0, - .who = irc_to_utf8 (ctx, msg->prefix), - .text = irc_to_utf8 (ctx, topic)); + buffer_send (s->ctx, buffer, BUFFER_LINE_TOPIC, 0, + .who = irc_to_utf8 (s->ctx, msg->prefix), + .text = irc_to_utf8 (s->ctx, topic)); } } static struct irc_handler { char *name; - void (*handler) (struct app_context *ctx, const struct irc_message *msg); + void (*handler) (struct server *s, const struct irc_message *msg); } g_irc_handlers[] = { @@ -3018,7 +3061,7 @@ irc_handler_cmp_by_name (const void *a, const void *b) } static bool -irc_try_parse_word_for_userhost (struct app_context *ctx, const char *word) +irc_try_parse_word_for_userhost (struct server *s, const char *word) { regex_t re; int err = regcomp (&re, "^[^!@]+!([^!@]+@[^!@]+)$", REG_EXTENDED); @@ -3029,8 +3072,8 @@ irc_try_parse_word_for_userhost (struct app_context *ctx, const char *word) bool result = false; if (!regexec (&re, word, 2, matches, 0)) { - free (ctx->irc_user_host); - ctx->irc_user_host = xstrndup (word + matches[1].rm_so, + free (s->irc_user_host); + s->irc_user_host = xstrndup (word + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); result = true; } @@ -3039,19 +3082,19 @@ irc_try_parse_word_for_userhost (struct app_context *ctx, const char *word) } static void -irc_try_parse_welcome_for_userhost (struct app_context *ctx, const char *m) +irc_try_parse_welcome_for_userhost (struct server *s, const char *m) { struct str_vector v; str_vector_init (&v); split_str_ignore_empty (m, ' ', &v); for (size_t i = 0; i < v.len; i++) - if (irc_try_parse_word_for_userhost (ctx, v.vector[i])) + if (irc_try_parse_word_for_userhost (s, v.vector[i])) break; str_vector_free (&v); } static void -irc_process_numeric (struct app_context *ctx, +irc_process_numeric (struct server *s, const struct irc_message *msg, unsigned long numeric) { // Numerics typically have human-readable information @@ -3067,8 +3110,8 @@ irc_process_numeric (struct app_context *ctx, // and send it to the server buffer char *reconstructed = join_str_vector (©, ' '); str_vector_free (©); - buffer_send (ctx, ctx->server_buffer, BUFFER_LINE_STATUS, 0, - .text = irc_to_utf8 (ctx, reconstructed)); + buffer_send (s->ctx, s->buffer, BUFFER_LINE_STATUS, 0, + .text = irc_to_utf8 (s->ctx, reconstructed)); free (reconstructed); switch (numeric) @@ -3076,7 +3119,7 @@ irc_process_numeric (struct app_context *ctx, case IRC_RPL_WELCOME: // We still issue a USERHOST anyway as this is in general unreliable if (msg->params.len == 2) - irc_try_parse_welcome_for_userhost (ctx, msg->params.vector[1]); + irc_try_parse_welcome_for_userhost (s, msg->params.vector[1]); break; case IRC_RPL_ISUPPORT: // TODO: parse this, mainly PREFIX; see @@ -3100,51 +3143,51 @@ static void irc_process_message (const struct irc_message *msg, const char *raw, void *user_data) { - struct app_context *ctx = user_data; + struct server *s = user_data; if (g_debug_mode) { struct app_readline_state state; - if (ctx->readline_prompt_shown) + if (s->ctx->readline_prompt_shown) app_readline_hide (&state); - char *term = irc_to_term (ctx, raw); + char *term = irc_to_term (s->ctx, raw); fprintf (stderr, "[IRC] ==> \"%s\"\n", term); free (term); - if (ctx->readline_prompt_shown) - app_readline_restore (&state, ctx->readline_prompt); + if (s->ctx->readline_prompt_shown) + app_readline_restore (&state, s->ctx->readline_prompt); } // XXX: or is the 001 numeric enough? For what? - if (!ctx->irc_ready && (!strcasecmp (msg->command, "MODE") + if (!s->irc_ready && (!strcasecmp (msg->command, "MODE") || !strcasecmp (msg->command, "376") // RPL_ENDOFMOTD || !strcasecmp (msg->command, "422"))) // ERR_NOMOTD { // XXX: should we really print this? - buffer_send_status (ctx, ctx->server_buffer, "Successfully connected"); - ctx->irc_ready = true; - refresh_prompt (ctx); + buffer_send_status (s->ctx, s->buffer, "Successfully connected"); + s->irc_ready = true; + refresh_prompt (s->ctx); // TODO: parse any response and store the result for us in app_context; // this enables proper message splitting on output; // we can also use WHOIS if it's not supported (optional by RFC 2812) - irc_send (ctx, "USERHOST %s", ctx->irc_user->nickname); + irc_send (s, "USERHOST %s", s->irc_user->nickname); - const char *autojoin = str_map_find (&ctx->config, "autojoin"); + const char *autojoin = str_map_find (&s->ctx->config, "autojoin"); if (autojoin) - irc_send (ctx, "JOIN :%s", autojoin); + irc_send (s, "JOIN :%s", autojoin); } struct irc_handler key = { .name = msg->command }; struct irc_handler *handler = bsearch (&key, g_irc_handlers, N_ELEMENTS (g_irc_handlers), sizeof key, irc_handler_cmp_by_name); if (handler) - handler->handler (ctx, msg); + handler->handler (s, msg); unsigned long numeric; if (xstrtoul (&numeric, msg->command, 10)) - irc_process_numeric (ctx, msg, numeric); + irc_process_numeric (s, msg, numeric); } // --- Message autosplitting magic --------------------------------------------- @@ -3241,15 +3284,15 @@ error: /// Automatically splits messages that arrive at other clients with our prefix /// so that they don't arrive cut off by the server static bool -irc_autosplit_message (struct app_context *ctx, const char *message, +irc_autosplit_message (struct server *s, const char *message, int fixed_part, struct str_vector *output, struct error **e) { // :!@ int space_in_one_message = 0; - if (ctx->irc_user_host) + if (s->irc_user_host) space_in_one_message = 510 - - 1 - (int) strlen (ctx->irc_user->nickname) - - 1 - (int) strlen (ctx->irc_user_host) + - 1 - (int) strlen (s->irc_user->nickname) + - 1 - (int) strlen (s->irc_user_host) - 1 - fixed_part; // However we don't always have the full info for message splitting @@ -3262,7 +3305,7 @@ irc_autosplit_message (struct app_context *ctx, const char *message, struct send_autosplit_args; -typedef void (*send_autosplit_logger_fn) (struct app_context *ctx, +typedef void (*send_autosplit_logger_fn) (struct server *s, struct send_autosplit_args *args, struct buffer *buffer, const char *line); struct send_autosplit_args @@ -3276,86 +3319,86 @@ struct send_autosplit_args }; static void -send_autosplit_message (struct app_context *ctx, struct send_autosplit_args a) +send_autosplit_message (struct server *s, struct send_autosplit_args a) { - struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, a.target); + 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 str_vector lines; str_vector_init (&lines); struct error *e = NULL; - if (!irc_autosplit_message (ctx, a.message, fixed_part, &lines, &e)) + if (!irc_autosplit_message (s, a.message, fixed_part, &lines, &e)) { - buffer_send_error (ctx, - buffer ? buffer : ctx->server_buffer, "%s", e->message); + buffer_send_error (s->ctx, + buffer ? buffer : s->buffer, "%s", e->message); error_free (e); goto end; } for (size_t i = 0; i < lines.len; i++) { - irc_send (ctx, "%s %s :%s%s%s", a.command, a.target, + irc_send (s, "%s %s :%s%s%s", a.command, a.target, a.prefix, lines.vector[i], a.suffix); - a.logger (ctx, &a, buffer, lines.vector[i]); + a.logger (s, &a, buffer, lines.vector[i]); } end: str_vector_free (&lines); } static void -log_outcoming_action (struct app_context *ctx, +log_outcoming_action (struct server *s, struct send_autosplit_args *a, struct buffer *buffer, const char *line) { (void) a; if (buffer) - buffer_send (ctx, buffer, BUFFER_LINE_ACTION, 0, - .who = irc_to_utf8 (ctx, ctx->irc_user->nickname), - .text = irc_to_utf8 (ctx, line)); + 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)); // This can only be sent from a user or channel buffer } -#define SEND_AUTOSPLIT_ACTION(ctx, target, message) \ - send_autosplit_message ((ctx), (struct send_autosplit_args) \ +#define SEND_AUTOSPLIT_ACTION(s, target, message) \ + send_autosplit_message ((s), (struct send_autosplit_args) \ { "PRIVMSG", (target), (message), log_outcoming_action, \ "\x01" "ACTION ", "\x01" }) static void -log_outcoming_privmsg (struct app_context *ctx, +log_outcoming_privmsg (struct server *s, struct send_autosplit_args *a, struct buffer *buffer, const char *line) { if (buffer) - buffer_send (ctx, buffer, BUFFER_LINE_PRIVMSG, 0, - .who = irc_to_utf8 (ctx, ctx->irc_user->nickname), - .text = irc_to_utf8 (ctx, line)); + 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)); else // TODO: fix logging and encoding - buffer_send (ctx, ctx->server_buffer, BUFFER_LINE_STATUS, 0, + buffer_send (s->ctx, s->buffer, BUFFER_LINE_STATUS, 0, .text = xstrdup_printf ("MSG(%s): %s", a->target, line)); } -#define SEND_AUTOSPLIT_PRIVMSG(ctx, target, message) \ - send_autosplit_message ((ctx), (struct send_autosplit_args) \ +#define SEND_AUTOSPLIT_PRIVMSG(s, target, message) \ + send_autosplit_message ((s), (struct send_autosplit_args) \ { "PRIVMSG", (target), (message), log_outcoming_privmsg, "", "" }) static void -log_outcoming_notice (struct app_context *ctx, +log_outcoming_notice (struct server *s, struct send_autosplit_args *a, struct buffer *buffer, const char *line) { if (buffer) - buffer_send (ctx, buffer, BUFFER_LINE_NOTICE, 0, - .who = irc_to_utf8 (ctx, ctx->irc_user->nickname), - .text = irc_to_utf8 (ctx, line)); + 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)); else // TODO: fix logging and encoding - buffer_send (ctx, ctx->server_buffer, BUFFER_LINE_STATUS, 0, + buffer_send (s->ctx, s->buffer, BUFFER_LINE_STATUS, 0, .text = xstrdup_printf ("Notice -> %s: %s", a->target, line)); } -#define SEND_AUTOSPLIT_NOTICE(ctx, target, message) \ - send_autosplit_message ((ctx), (struct send_autosplit_args) \ +#define SEND_AUTOSPLIT_NOTICE(s, target, message) \ + send_autosplit_message ((s), (struct send_autosplit_args) \ { "NOTICE", (target), (message), log_outcoming_notice, "", "" }) // --- User input handling ----------------------------------------------------- @@ -3406,10 +3449,14 @@ server_command_check (struct app_context *ctx, const char *action) if (ctx->current_buffer->type == BUFFER_GLOBAL) buffer_send_error (ctx, ctx->current_buffer, "Can't do this from a global buffer (%s)", action); - else if (ctx->irc_fd == -1) - buffer_send_error (ctx, ctx->server_buffer, "Not connected"); else - return true; + { + struct server *s = ctx->current_buffer->server; + if (s->irc_fd == -1) + buffer_send_error (ctx, s->buffer, "Not connected"); + else + return true; + } return false; } @@ -3441,9 +3488,9 @@ handle_buffer_close (struct app_context *ctx, char *arguments) else if (buffer == ctx->global_buffer) buffer_send_error (ctx, ctx->global_buffer, "Can't close the global buffer"); - else if (buffer == ctx->server_buffer) + else if (buffer->type == BUFFER_SERVER) buffer_send_error (ctx, ctx->global_buffer, - "Can't close the server buffer"); + "Can't close a server buffer"); else { if (buffer == ctx->current_buffer) @@ -3488,11 +3535,12 @@ handle_command_msg (struct app_context *ctx, char *arguments) if (!*arguments) return false; + struct server *s = ctx->current_buffer->server; char *target = cut_word (&arguments); if (!*arguments) - buffer_send_error (ctx, ctx->server_buffer, "No text to send"); + buffer_send_error (ctx, s->buffer, "No text to send"); else - SEND_AUTOSPLIT_PRIVMSG (ctx, target, arguments); + SEND_AUTOSPLIT_PRIVMSG (s, target, arguments); return true; } @@ -3504,15 +3552,16 @@ handle_command_query (struct app_context *ctx, char *arguments) if (!*arguments) return false; + struct server *s = ctx->current_buffer->server; char *target = cut_word (&arguments); - if (irc_is_channel (ctx, target)) - buffer_send_error (ctx, ctx->server_buffer, "Cannot query a channel"); + if (irc_is_channel (s, target)) + buffer_send_error (ctx, s->buffer, "Cannot query a channel"); else if (!*arguments) - buffer_send_error (ctx, ctx->server_buffer, "No text to send"); + buffer_send_error (ctx, s->buffer, "No text to send"); else { - buffer_activate (ctx, irc_get_or_make_user_buffer (ctx, target)); - SEND_AUTOSPLIT_PRIVMSG (ctx, target, arguments); + buffer_activate (ctx, irc_get_or_make_user_buffer (s, target)); + SEND_AUTOSPLIT_PRIVMSG (s, target, arguments); } return true; } @@ -3525,11 +3574,12 @@ handle_command_notice (struct app_context *ctx, char *arguments) if (!*arguments) return false; + struct server *s = ctx->current_buffer->server; char *target = cut_word (&arguments); if (!*arguments) - buffer_send_error (ctx, ctx->server_buffer, "No text to send"); + buffer_send_error (ctx, s->buffer, "No text to send"); else - SEND_AUTOSPLIT_NOTICE (ctx, target, arguments); + SEND_AUTOSPLIT_NOTICE (s, target, arguments); return true; } @@ -3549,12 +3599,13 @@ handle_command_ctcp (struct app_context *ctx, char *arguments) for (char *p = tag; *p; p++) *p = toupper_ascii (*p); + struct server *s = ctx->current_buffer->server; if (*arguments) - irc_send (ctx, "PRIVMSG %s :\x01%s %s\x01", target, tag, arguments); + irc_send (s, "PRIVMSG %s :\x01%s %s\x01", target, tag, arguments); else - irc_send (ctx, "PRIVMSG %s :\x01%s\x01", target, tag); + irc_send (s, "PRIVMSG %s :\x01%s\x01", target, tag); - buffer_send_status (ctx, ctx->server_buffer, + buffer_send_status (ctx, s->buffer, "CTCP query to %s: %s", target, tag); return true; } @@ -3565,14 +3616,15 @@ handle_command_me (struct app_context *ctx, char *arguments) if (!server_command_check (ctx, "send messages")) return true; + struct server *s = ctx->current_buffer->server; if (ctx->current_buffer->type == BUFFER_CHANNEL) - SEND_AUTOSPLIT_ACTION (ctx, + SEND_AUTOSPLIT_ACTION (s, ctx->current_buffer->channel->name, arguments); else if (ctx->current_buffer->type == BUFFER_PM) - SEND_AUTOSPLIT_ACTION (ctx, + SEND_AUTOSPLIT_ACTION (s, ctx->current_buffer->user->nickname, arguments); else - buffer_send_error (ctx, ctx->server_buffer, + buffer_send_error (ctx, s->buffer, "Can't do this from a server buffer (%s)", "send CTCP actions"); return true; @@ -3581,12 +3633,14 @@ handle_command_me (struct app_context *ctx, char *arguments) static bool handle_command_quit (struct app_context *ctx, char *arguments) { - if (ctx->irc_fd != -1) + // TODO: multiserver + struct server *s = &ctx->server; + if (s->irc_fd != -1) { if (*arguments) - irc_send (ctx, "QUIT :%s", arguments); + irc_send (s, "QUIT :%s", arguments); else - irc_send (ctx, "QUIT :%s", PROGRAM_NAME " " PROGRAM_VERSION); + irc_send (s, "QUIT :%s", PROGRAM_NAME " " PROGRAM_VERSION); } initiate_quit (ctx); return true; @@ -3598,10 +3652,11 @@ handle_command_join (struct app_context *ctx, char *arguments) if (!server_command_check (ctx, "join")) return true; + struct server *s = ctx->current_buffer->server; if (*arguments) // TODO: check if the arguments are in the form of // "channel(,channel)* key(,key)*" - irc_send (ctx, "JOIN %s", arguments); + irc_send (s, "JOIN %s", arguments); else { if (ctx->current_buffer->type != BUFFER_CHANNEL) @@ -3615,7 +3670,7 @@ handle_command_join (struct app_context *ctx, char *arguments) "you already are on the channel"); else // TODO: send the key if known - irc_send (ctx, "JOIN %s", ctx->current_buffer->channel->name); + irc_send (s, "JOIN %s", ctx->current_buffer->channel->name); } return true; } @@ -3626,10 +3681,11 @@ handle_command_part (struct app_context *ctx, char *arguments) if (!server_command_check (ctx, "part")) return true; + struct server *s = ctx->current_buffer->server; if (*arguments) // TODO: check if the arguments are in the form of "channel(,channel)*" // TODO: make sure to send the reason as one argument - irc_send (ctx, "PART %s", arguments); + irc_send (s, "PART %s", arguments); else { if (ctx->current_buffer->type != BUFFER_CHANNEL) @@ -3641,7 +3697,7 @@ handle_command_part (struct app_context *ctx, char *arguments) buffer_send_error (ctx, ctx->current_buffer, "%s: %s", "Can't join", "you're not on the channel"); else - irc_send (ctx, "PART %s", ctx->current_buffer->channel->name); + irc_send (s, "PART %s", ctx->current_buffer->channel->name); } return true; } @@ -3652,10 +3708,11 @@ handle_command_list (struct app_context *ctx, char *arguments) if (!server_command_check (ctx, "list channels")) return true; + struct server *s = ctx->current_buffer->server; if (*arguments) - irc_send (ctx, "LIST %s", arguments); + irc_send (s, "LIST %s", arguments); else - irc_send (ctx, "LIST"); + irc_send (s, "LIST"); return true; } @@ -3667,7 +3724,8 @@ handle_command_nick (struct app_context *ctx, char *arguments) if (!*arguments) return false; - irc_send (ctx, "NICK %s", cut_word (&arguments)); + struct server *s = ctx->current_buffer->server; + irc_send (s, "NICK %s", cut_word (&arguments)); return true; } @@ -3677,7 +3735,8 @@ handle_command_quote (struct app_context *ctx, char *arguments) if (!server_command_check (ctx, "quote")) return true; - irc_send (ctx, "%s", arguments); + struct server *s = ctx->current_buffer->server; + irc_send (s, "%s", arguments); return true; } @@ -3834,16 +3893,16 @@ process_user_command (struct app_context *ctx, char *command) } static void -send_message_to_target (struct app_context *ctx, +send_message_to_target (struct server *s, const char *target, char *message, struct buffer *buffer) { - if (ctx->irc_fd == -1) + if (s->irc_fd == -1) { - buffer_send_error (ctx, buffer, "Not connected"); + buffer_send_error (s->ctx, buffer, "Not connected"); return; } - SEND_AUTOSPLIT_PRIVMSG (ctx, target, message); + SEND_AUTOSPLIT_PRIVMSG (s, target, message); } static void @@ -3859,10 +3918,12 @@ send_message_to_current_buffer (struct app_context *ctx, char *message) buffer_send_error (ctx, buffer, "This buffer is not a channel"); break; case BUFFER_CHANNEL: - send_message_to_target (ctx, buffer->channel->name, message, buffer); + send_message_to_target (buffer->server, + buffer->channel->name, message, buffer); break; case BUFFER_PM: - send_message_to_target (ctx, buffer->user->nickname, message, buffer); + send_message_to_target (buffer->server, + buffer->user->nickname, message, buffer); break; } } @@ -3896,15 +3957,15 @@ enum irc_read_result }; static enum irc_read_result -irc_fill_read_buffer_ssl (struct app_context *ctx, struct str *buf) +irc_fill_read_buffer_ssl (struct server *s, struct str *buf) { int n_read; start: - n_read = SSL_read (ctx->ssl, buf->str + buf->len, + n_read = SSL_read (s->ssl, buf->str + buf->len, buf->alloc - buf->len - 1 /* null byte */); const char *error_info = NULL; - switch (xssl_get_error (ctx->ssl, n_read, &error_info)) + switch (xssl_get_error (s->ssl, n_read, &error_info)) { case SSL_ERROR_NONE: buf->str[buf->len += n_read] = '\0'; @@ -3917,7 +3978,7 @@ start: { // Let it finish the handshake as we don't poll for writability; // any errors are to be collected by SSL_read() in the next iteration - struct pollfd pfd = { .fd = ctx->irc_fd, .events = POLLOUT }; + struct pollfd pfd = { .fd = s->irc_fd, .events = POLLOUT }; soft_assert (poll (&pfd, 1, 0) > 0); goto start; } @@ -3930,11 +3991,11 @@ start: } static enum irc_read_result -irc_fill_read_buffer (struct app_context *ctx, struct str *buf) +irc_fill_read_buffer (struct server *s, struct str *buf) { ssize_t n_read; start: - n_read = recv (ctx->irc_fd, buf->str + buf->len, + n_read = recv (s->irc_fd, buf->str + buf->len, buf->alloc - buf->len - 1 /* null byte */, 0); if (n_read > 0) @@ -3954,133 +4015,134 @@ start: return IRC_READ_ERROR; } -static bool irc_connect (struct app_context *, struct error **); -static void irc_queue_reconnect (struct app_context *); +static bool irc_connect (struct server *s, struct error **); +static void irc_queue_reconnect (struct server *s); static void -irc_cancel_timers (struct app_context *ctx) +irc_cancel_timers (struct server *s) { - poller_timer_reset (&ctx->timeout_tmr); - poller_timer_reset (&ctx->ping_tmr); - poller_timer_reset (&ctx->reconnect_tmr); + poller_timer_reset (&s->timeout_tmr); + poller_timer_reset (&s->ping_tmr); + poller_timer_reset (&s->reconnect_tmr); } static void on_irc_reconnect_timeout (void *user_data) { - struct app_context *ctx = user_data; + struct server *s = user_data; struct error *e = NULL; - if (irc_connect (ctx, &e)) + if (irc_connect (s, &e)) return; - buffer_send_error (ctx, ctx->server_buffer, "%s", e->message); + buffer_send_error (s->ctx, s->buffer, "%s", e->message); error_free (e); - irc_queue_reconnect (ctx); + irc_queue_reconnect (s); } static void -irc_queue_reconnect (struct app_context *ctx) +irc_queue_reconnect (struct server *s) { // TODO: exponentional backoff - hard_assert (ctx->irc_fd == -1); - buffer_send_status (ctx, ctx->server_buffer, - "Trying to reconnect in %ld seconds...", ctx->reconnect_delay); - poller_timer_set (&ctx->reconnect_tmr, ctx->reconnect_delay * 1000); + hard_assert (s->irc_fd == -1); + buffer_send_status (s->ctx, s->buffer, + "Trying to reconnect in %ld seconds...", s->ctx->reconnect_delay); + poller_timer_set (&s->reconnect_tmr, s->ctx->reconnect_delay * 1000); } static void -on_irc_disconnected (struct app_context *ctx) +on_irc_disconnected (struct server *s) { // Get rid of the dead socket and related things - if (ctx->ssl) + if (s->ssl) { - SSL_free (ctx->ssl); - ctx->ssl = NULL; - SSL_CTX_free (ctx->ssl_ctx); - ctx->ssl_ctx = NULL; + SSL_free (s->ssl); + s->ssl = NULL; + SSL_CTX_free (s->ssl_ctx); + s->ssl_ctx = NULL; } - xclose (ctx->irc_fd); - ctx->irc_fd = -1; - ctx->irc_ready = false; + xclose (s->irc_fd); + s->irc_fd = -1; + s->irc_ready = false; - user_unref (ctx->irc_user); - ctx->irc_user = NULL; + user_unref (s->irc_user); + s->irc_user = NULL; - free (ctx->irc_user_mode); - ctx->irc_user_mode = NULL; - free (ctx->irc_user_host); - ctx->irc_user_host = NULL; + free (s->irc_user_mode); + s->irc_user_mode = NULL; + free (s->irc_user_host); + s->irc_user_host = NULL; - ctx->irc_event.closed = true; - poller_fd_reset (&ctx->irc_event); + s->irc_event.closed = true; + poller_fd_reset (&s->irc_event); // All of our timers have lost their meaning now - irc_cancel_timers (ctx); + irc_cancel_timers (s); - if (ctx->quitting) - try_finish_quit (ctx); - else if (!ctx->reconnect) + if (s->ctx->quitting) + try_finish_quit (s->ctx); + else if (!s->ctx->reconnect) // XXX: not sure if we want this in a client - initiate_quit (ctx); + // FIXME: no, we don't, would need to be changed for multiserver anyway + initiate_quit (s->ctx); else - irc_queue_reconnect (ctx); + irc_queue_reconnect (s); } static void on_irc_ping_timeout (void *user_data) { - struct app_context *ctx = user_data; - buffer_send_error (ctx, ctx->server_buffer, "Connection timeout"); - on_irc_disconnected (ctx); + struct server *s = user_data; + buffer_send_error (s->ctx, s->buffer, "Connection timeout"); + on_irc_disconnected (s); } static void on_irc_timeout (void *user_data) { // Provoke a response from the server - struct app_context *ctx = user_data; - irc_send (ctx, "PING :%s", - (char *) str_map_find (&ctx->config, "nickname")); + struct server *s = user_data; + irc_send (s, "PING :%s", + (char *) str_map_find (&s->ctx->config, "nickname")); } static void -irc_reset_connection_timeouts (struct app_context *ctx) +irc_reset_connection_timeouts (struct server *s) { - irc_cancel_timers (ctx); - poller_timer_set (&ctx->timeout_tmr, 3 * 60 * 1000); - poller_timer_set (&ctx->ping_tmr, (3 * 60 + 30) * 1000); + irc_cancel_timers (s); + poller_timer_set (&s->timeout_tmr, 3 * 60 * 1000); + poller_timer_set (&s->ping_tmr, (3 * 60 + 30) * 1000); } static void -on_irc_readable (const struct pollfd *fd, struct app_context *ctx) +on_irc_readable (const struct pollfd *fd, struct server *s) { if (fd->revents & ~(POLLIN | POLLHUP | POLLERR)) print_debug ("fd %d: unexpected revents: %d", fd->fd, fd->revents); - (void) set_blocking (ctx->irc_fd, false); + (void) set_blocking (s->irc_fd, false); - struct str *buf = &ctx->read_buffer; - enum irc_read_result (*fill_buffer)(struct app_context *, struct str *) - = ctx->ssl + struct str *buf = &s->read_buffer; + enum irc_read_result (*fill_buffer)(struct server *, struct str *) + = s->ssl ? irc_fill_read_buffer_ssl : irc_fill_read_buffer; bool disconnected = false; while (true) { str_ensure_space (buf, 512); - switch (fill_buffer (ctx, buf)) + switch (fill_buffer (s, buf)) { case IRC_READ_AGAIN: goto end; case IRC_READ_ERROR: - buffer_send_error (ctx, ctx->server_buffer, + buffer_send_error (s->ctx, s->buffer, "Reading from the IRC server failed"); disconnected = true; goto end; case IRC_READ_EOF: - buffer_send_error (ctx, ctx->server_buffer, + buffer_send_error (s->ctx, s->buffer, "The IRC server closed the connection"); disconnected = true; goto end; @@ -4090,25 +4152,27 @@ on_irc_readable (const struct pollfd *fd, struct app_context *ctx) if (buf->len >= (1 << 20)) { - buffer_send_error (ctx, ctx->server_buffer, + buffer_send_error (s->ctx, s->buffer, "The IRC server seems to spew out data frantically"); - irc_shutdown (ctx); + irc_shutdown (s); goto end; } } end: - (void) set_blocking (ctx->irc_fd, true); - irc_process_buffer (buf, irc_process_message, ctx); + (void) set_blocking (s->irc_fd, true); + irc_process_buffer (buf, irc_process_message, s); if (disconnected) - on_irc_disconnected (ctx); + on_irc_disconnected (s); else - irc_reset_connection_timeouts (ctx); + irc_reset_connection_timeouts (s); } static bool -irc_connect (struct app_context *ctx, struct error **e) +irc_connect (struct server *s, struct error **e) { + struct app_context *ctx = s->ctx; + const char *irc_host = str_map_find (&ctx->config, "irc_host"); const char *irc_port = str_map_find (&ctx->config, "irc_port"); @@ -4137,7 +4201,7 @@ irc_connect (struct app_context *ctx, struct error **e) { 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, ctx->server_buffer, + buffer_send_status (ctx, s->buffer, "Connecting to %s via %s...", address, socks_address); free (socks_address); free (address); @@ -4151,36 +4215,36 @@ irc_connect (struct app_context *ctx, struct error **e) error_free (error); return false; } - ctx->irc_fd = fd; + s->irc_fd = fd; } - else if (!irc_establish_connection (ctx, irc_host, irc_port, e)) + else if (!irc_establish_connection (s, irc_host, irc_port, e)) return false; - if (use_ssl && !irc_initialize_ssl (ctx, e)) + if (use_ssl && !irc_initialize_ssl (s, e)) { - xclose (ctx->irc_fd); - ctx->irc_fd = -1; + xclose (s->irc_fd); + s->irc_fd = -1; return false; } - buffer_send_status (ctx, ctx->server_buffer, "Connection established"); + buffer_send_status (ctx, s->buffer, "Connection established"); - poller_fd_init (&ctx->irc_event, &ctx->poller, ctx->irc_fd); - ctx->irc_event.dispatcher = (poller_fd_fn) on_irc_readable; - ctx->irc_event.user_data = ctx; + poller_fd_init (&s->irc_event, &ctx->poller, s->irc_fd); + s->irc_event.dispatcher = (poller_fd_fn) on_irc_readable; + s->irc_event.user_data = s; - poller_fd_set (&ctx->irc_event, POLLIN); - irc_reset_connection_timeouts (ctx); + poller_fd_set (&s->irc_event, POLLIN); + irc_reset_connection_timeouts (s); - irc_send (ctx, "NICK %s", nickname); - irc_send (ctx, "USER %s 8 * :%s", username, realname); + irc_send (s, "NICK %s", nickname); + irc_send (s, "USER %s 8 * :%s", username, realname); // XXX: maybe we should wait for the first message from the server // FIXME: the user may exist already after we've reconnected. Either // make sure that there's no reference of this nick upon disconnection, // or search in "irc_users" first... or something. - ctx->irc_user = irc_make_user (ctx, xstrdup (nickname)); - ctx->irc_user_mode = xstrdup (""); - ctx->irc_user_host = NULL; + s->irc_user = irc_make_user (s, xstrdup (nickname)); + s->irc_user_mode = xstrdup (""); + s->irc_user_host = NULL; return true; } @@ -4195,10 +4259,14 @@ on_signal_pipe_readable (const struct pollfd *fd, struct app_context *ctx) if (g_termination_requested && !ctx->quitting) { // There may be a timer set to reconnect to the server - irc_cancel_timers (ctx); + // TODO: multiserver + struct server *s = &ctx->server; + // TODO: a faster timer for quitting + irc_reset_connection_timeouts (s); - if (ctx->irc_fd != -1) - irc_send (ctx, "QUIT :Terminated by signal"); + // FIXME: use a normal quit message + if (s->irc_fd != -1) + irc_send (s, "QUIT :Terminated by signal"); initiate_quit (ctx); } @@ -4455,18 +4523,6 @@ load_config (struct app_context *ctx, struct error **e) static void init_poller_events (struct app_context *ctx) { - poller_timer_init (&ctx->timeout_tmr, &ctx->poller); - ctx->timeout_tmr.dispatcher = on_irc_timeout; - ctx->timeout_tmr.user_data = ctx; - - poller_timer_init (&ctx->ping_tmr, &ctx->poller); - ctx->ping_tmr.dispatcher = on_irc_ping_timeout; - ctx->ping_tmr.user_data = ctx; - - poller_timer_init (&ctx->reconnect_tmr, &ctx->poller); - ctx->reconnect_tmr.dispatcher = on_irc_reconnect_timeout; - ctx->reconnect_tmr.user_data = ctx; - poller_fd_init (&ctx->signal_event, &ctx->poller, g_signal_pipe[0]); ctx->signal_event.dispatcher = (poller_fd_fn) on_signal_pipe_readable; ctx->signal_event.user_data = ctx; @@ -4554,13 +4610,13 @@ main (int argc, char *argv[]) init_colors (&ctx); init_poller_events (&ctx); init_buffers (&ctx); - ctx.current_buffer = ctx.server_buffer; + ctx.current_buffer = ctx.server.buffer; refresh_prompt (&ctx); // TODO: connect asynchronously (first step towards multiple servers) - if (!irc_connect (&ctx, &e)) + if (!irc_connect (&ctx.server, &e)) { - buffer_send_error (&ctx, ctx.server_buffer, "%s", e->message); + buffer_send_error (&ctx, ctx.server.buffer, "%s", e->message); error_free (e); exit (EXIT_FAILURE); }