aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--degesch.c282
1 files changed, 188 insertions, 94 deletions
diff --git a/degesch.c b/degesch.c
index d1faae4..de093c6 100644
--- a/degesch.c
+++ b/degesch.c
@@ -1013,10 +1013,18 @@ struct server
{
struct app_context *ctx; ///< Application context
+ char *name; ///< Server identifier
+ struct buffer *buffer; ///< The buffer for this server
+
+ // Configuration:
+
+ struct config_item_ *config; ///< Configuration root
bool reconnect; ///< Whether to reconnect on conn. fail.
unsigned long reconnect_delay; ///< Reconnect delay in seconds
bool manual_disconnect; ///< Don't reconnect
+ // Connection:
+
enum server_state state; ///< Connection state
struct connector *connector; ///< Connection establisher
@@ -1030,8 +1038,7 @@ struct server
// TODO: an output queue to prevent excess floods (this will be needed
// especially for away status polling)
- char *name; ///< Server identifier
- struct buffer *buffer; ///< The buffer for this server
+ // IRC:
struct str_map irc_users; ///< IRC user data
struct str_map irc_channels; ///< IRC channel data
@@ -1055,6 +1062,8 @@ static void irc_initiate_connect (struct server *s);
static void
server_init (struct server *self, struct poller *poller)
{
+ memset (self, 0, sizeof *self);
+
self->socket = -1;
str_init (&self->read_buffer);
self->state = IRC_DISCONNECTED;
@@ -1111,6 +1120,13 @@ server_free (struct server *self)
str_map_free (&self->irc_buffer_map);
}
+static void
+server_destroy (void *self)
+{
+ server_free (self);
+ free (self);
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct app_context
@@ -1122,7 +1138,7 @@ struct app_context
bool no_colors; ///< Colour output mode
bool isolate_buffers; ///< Isolate global/server buffers
- struct server server; ///< Our only server so far
+ struct str_map servers; ///< Our servers
// Events:
@@ -1138,14 +1154,13 @@ struct app_context
struct buffer *buffers; ///< All our buffers in order
struct buffer *buffers_tail; ///< The tail of our buffers
+ struct buffer *global_buffer; ///< The global buffer
+ struct buffer *current_buffer; ///< The current buffer
struct buffer *last_buffer; ///< Last used buffer
// TODO: make buffer names fully unique like weechat does
struct str_map buffers_by_name; ///< Buffers by name
- struct buffer *global_buffer; ///< The global buffer
- struct buffer *current_buffer; ///< The current buffer
-
// TODO: So that we always output proper date change messages
time_t last_displayed_msg_time; ///< Time of last displayed message
@@ -1171,9 +1186,9 @@ app_context_init (struct app_context *self)
config_init (&self->config);
poller_init (&self->poller);
- server_init (&self->server, &self->poller);
- self->server.ctx = self;
- self->server.name = xstrdup ("server");
+ str_map_init (&self->servers);
+ self->servers.free = server_destroy;
+ self->servers.key_xfrm = irc_strxfrm;
str_map_init (&self->buffers_by_name);
self->buffers_by_name.key_xfrm = irc_strxfrm;
@@ -1213,7 +1228,7 @@ app_context_free (struct app_context *self)
buffer_destroy (iter);
str_map_free (&self->buffers_by_name);
- server_free (&self->server);
+ str_map_free (&self->servers);
poller_free (&self->poller);
iconv_close (self->latin1_to_utf8);
@@ -1415,9 +1430,9 @@ register_config_modules (struct app_context *ctx)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const char *
-get_config_string (struct app_context *ctx, const char *key)
+get_config_string (struct config_item_ *root, const char *key)
{
- struct config_item_ *item = config_item_get (ctx->config.root, key, NULL);
+ struct config_item_ *item = config_item_get (root, key, NULL);
hard_assert (item);
if (item->type == CONFIG_ITEM_NULL)
return NULL;
@@ -1426,9 +1441,10 @@ get_config_string (struct app_context *ctx, const char *key)
}
static bool
-set_config_string (struct app_context *ctx, const char *key, const char *value)
+set_config_string
+ (struct config_item_ *root, const char *key, const char *value)
{
- struct config_item_ *item = config_item_get (ctx->config.root, key, NULL);
+ struct config_item_ *item = config_item_get (root, key, NULL);
hard_assert (item);
struct str s;
@@ -1448,17 +1464,17 @@ set_config_string (struct app_context *ctx, const char *key, const char *value)
}
static int64_t
-get_config_integer (struct app_context *ctx, const char *key)
+get_config_integer (struct config_item_ *root, const char *key)
{
- struct config_item_ *item = config_item_get (ctx->config.root, key, NULL);
+ struct config_item_ *item = config_item_get (root, key, NULL);
hard_assert (item && item->type == CONFIG_ITEM_INTEGER);
return item->value.integer;
}
static bool
-get_config_boolean (struct app_context *ctx, const char *key)
+get_config_boolean (struct config_item_ *root, const char *key)
{
- struct config_item_ *item = config_item_get (ctx->config.root, key, NULL);
+ struct config_item_ *item = config_item_get (root, key, NULL);
hard_assert (item && item->type == CONFIG_ITEM_BOOLEAN);
return item->value.boolean;
}
@@ -1687,7 +1703,7 @@ init_attribute (struct app_context *ctx, int id, const char *default_)
#undef XX
};
- const char *user = get_config_string (ctx, table[id]);
+ const char *user = get_config_string (ctx->config.root, table[id]);
if (user)
ctx->attrs[id] = xstrdup (user);
else
@@ -2483,19 +2499,19 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer)
if (buffer->user)
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);
- buffer_destroy (buffer);
-
- if (buffer == ctx->last_buffer)
- ctx->last_buffer = NULL;
-
// It's not a good idea to remove these buffers, but it's even a worse
// 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->type == BUFFER_SERVER)
+ buffer->server->buffer = NULL;
+
+ if (buffer == ctx->last_buffer)
+ ctx->last_buffer = NULL;
+
+ str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
+ LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
+ buffer_destroy (buffer);
refresh_prompt (ctx);
}
@@ -2635,19 +2651,11 @@ buffer_get_index (struct app_context *ctx, struct buffer *buffer)
static void
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 ();
-
global->type = BUFFER_GLOBAL;
global->name = xstrdup (PROGRAM_NAME);
- server->type = BUFFER_SERVER;
- server->name = xstrdup ("server");
- server->server = &ctx->server;
-
buffer_add (ctx, global);
- buffer_add (ctx, server);
}
// --- Users, channels ---------------------------------------------------------
@@ -2811,12 +2819,12 @@ irc_initialize_ssl_ctx (struct server *s, struct error **e)
{
// XXX: maybe we should call SSL_CTX_set_options() for some workarounds
- bool verify = get_config_boolean (s->ctx, "server.ssl_verify");
+ bool verify = get_config_boolean (s->config, "ssl_verify");
if (!verify)
SSL_CTX_set_verify (s->ssl_ctx, SSL_VERIFY_NONE, NULL);
- const char *ca_file = get_config_string (s->ctx, "server.ca_file");
- const char *ca_path = get_config_string (s->ctx, "server.ca_path");
+ const char *ca_file = get_config_string (s->config, "ssl_ca_file");
+ const char *ca_path = get_config_string (s->config, "ssl_ca_path");
struct error *error = NULL;
if (ca_file || ca_path)
@@ -2866,7 +2874,7 @@ irc_initialize_ssl (struct server *s, struct error **e)
if (!s->ssl)
goto error_ssl_2;
- const char *ssl_cert = get_config_string (s->ctx, "server.ssl_cert");
+ const char *ssl_cert = get_config_string (s->config, "ssl_cert");
if (ssl_cert)
{
char *path = resolve_config_filename (ssl_cert);
@@ -3011,8 +3019,19 @@ irc_destroy_connector (struct server *s)
static void
try_finish_quit (struct app_context *ctx)
{
- // TODO: multiserver
- if (ctx->quitting && !irc_is_connected (&ctx->server))
+ if (!ctx->quitting)
+ return;
+
+ struct str_map_iter iter;
+ str_map_iter_init (&iter, &ctx->servers);
+
+ bool disconnected_all = true;
+ struct server *s;
+ while ((s = str_map_iter_next (&iter)))
+ if (irc_is_connected (s))
+ disconnected_all = false;
+
+ if (disconnected_all)
ctx->polling = false;
}
@@ -3025,13 +3044,18 @@ initiate_quit (struct app_context *ctx)
buffer_send_status (ctx, ctx->global_buffer, "Shutting down");
// Initiate a connection close
- // TODO: multiserver
- struct server *s = &ctx->server;
- if (irc_is_connected (s))
- // XXX: when we go async, we'll have to flush output buffers first
- irc_shutdown (s);
- else if (s->state == IRC_CONNECTING)
- irc_destroy_connector (s);
+ struct str_map_iter iter;
+ str_map_iter_init (&iter, &ctx->servers);
+
+ struct server *s;
+ while ((s = str_map_iter_next (&iter)))
+ {
+ if (irc_is_connected (s))
+ // XXX: when we go async, we'll have to flush output buffers first
+ irc_shutdown (s);
+ else if (s->state == IRC_CONNECTING)
+ irc_destroy_connector (s);
+ }
ctx->quitting = true;
try_finish_quit (ctx);
@@ -3248,9 +3272,9 @@ end:
static void
irc_register (struct server *s)
{
- const char *nickname = get_config_string (s->ctx, "server.nickname");
- const char *username = get_config_string (s->ctx, "server.username");
- const char *realname = get_config_string (s->ctx, "server.realname");
+ const char *nickname = get_config_string (s->config, "nickname");
+ const char *username = get_config_string (s->config, "username");
+ const char *realname = get_config_string (s->config, "realname");
// These are filled automatically if needed
hard_assert (nickname && username && realname);
@@ -3268,7 +3292,7 @@ irc_finish_connection (struct server *s, int socket)
s->socket = socket;
struct error *e = NULL;
- bool use_ssl = get_config_boolean (ctx, "server.ssl");
+ 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);
@@ -3378,13 +3402,13 @@ irc_initiate_connect_socks (struct server *s,
{
struct app_context *ctx = s->ctx;
- const char *socks_host = get_config_string (ctx, "server.socks_host");
- int64_t socks_port_int = get_config_integer (ctx, "server.socks_port");
+ const char *socks_host = get_config_string (s->config, "socks_host");
+ int64_t socks_port_int = get_config_integer (s->config, "socks_port");
const char *socks_username =
- get_config_string (ctx, "server.socks_username");
+ get_config_string (s->config, "socks_username");
const char *socks_password =
- get_config_string (ctx, "server.socks_password");
+ get_config_string (s->config, "socks_password");
if (!socks_host)
return false;
@@ -3427,7 +3451,7 @@ irc_initiate_connect (struct server *s)
hard_assert (s->state == IRC_DISCONNECTED);
struct app_context *ctx = s->ctx;
- const char *addresses = get_config_string (ctx, "server.addresses");
+ const char *addresses = get_config_string (s->config, "addresses");
if (!addresses || !addresses[strspn (addresses, ",")])
{
// No sense in trying to reconnect
@@ -4231,7 +4255,7 @@ irc_on_registered (struct server *s, const char *nickname)
// we can also use WHOIS if it's not supported (optional by RFC 2812)
irc_send (s, "USERHOST %s", s->irc_user->nickname);
- const char *autojoin = get_config_string (s->ctx, "server.autojoin");
+ const char *autojoin = get_config_string (s->config, "autojoin");
if (autojoin)
irc_send (s, "JOIN :%s", autojoin);
@@ -5098,10 +5122,16 @@ handle_command_me (struct app_context *ctx, char *arguments)
static bool
handle_command_quit (struct app_context *ctx, char *arguments)
{
- // TODO: multiserver
- struct server *s = &ctx->server;
- if (irc_is_connected (s))
- irc_initiate_disconnect (s, *arguments ? arguments : NULL);
+ struct str_map_iter iter;
+ str_map_iter_init (&iter, &ctx->servers);
+
+ struct server *s;
+ while ((s = str_map_iter_next (&iter)))
+ {
+ if (irc_is_connected (s))
+ irc_initiate_disconnect (s, *arguments ? arguments : NULL);
+ }
+
initiate_quit (ctx);
return true;
}
@@ -5159,8 +5189,24 @@ handle_command_part (struct app_context *ctx, char *arguments)
static bool
handle_command_connect (struct app_context *ctx, char *arguments)
{
- // TODO: multiserver
- struct server *s = &ctx->server;
+ struct server *s = NULL;
+ if (*arguments)
+ {
+ char *name = cut_word (&arguments);
+ if (!(s = str_map_find (&ctx->servers, name)))
+ buffer_send_error (ctx, ctx->global_buffer,
+ "%s: %s: %s", "Can't connect", "no such server", name);
+ }
+ else if (ctx->current_buffer->type == BUFFER_GLOBAL)
+ buffer_send_error (ctx, ctx->current_buffer,
+ "%s: %s", "Can't connect",
+ "no server name given and this buffer is global");
+ else
+ s = ctx->current_buffer->server;
+
+ if (!s)
+ return true;
+
if (irc_is_connected (s))
{
buffer_send_error (ctx, s->buffer, "Already connected");
@@ -5177,8 +5223,17 @@ handle_command_connect (struct app_context *ctx, char *arguments)
static bool
handle_command_disconnect (struct app_context *ctx, char *arguments)
{
- // TODO: multiserver
- struct server *s = &ctx->server;
+ // TODO: try to take server name from arguments
+ struct server *s = NULL;
+ if (ctx->current_buffer->type == BUFFER_GLOBAL)
+ buffer_send_error (ctx, ctx->current_buffer,
+ "%s: %s", "Can't disconnect", "this buffer is global");
+ else
+ s = ctx->current_buffer->server;
+
+ if (!s)
+ return true;
+
if (s->state == IRC_CONNECTING)
{
buffer_send_status (ctx, s->buffer, "Connecting aborted");
@@ -5292,7 +5347,7 @@ g_command_handlers[] =
NOT_IMPLEMENTED (invite)
{ "connect", "Connect to the server",
- NULL,
+ "[server]",
handle_command_connect },
{ "disconnect", "Disconnect from the server",
"[reason]",
@@ -6264,11 +6319,11 @@ app_editline_init (struct input *self)
// --- Configuration loading ---------------------------------------------------
static bool
-autofill_user_info (struct app_context *ctx, struct error **e)
+autofill_user_info (struct server *s, struct error **e)
{
- const char *nickname = get_config_string (ctx, "server.nickname");
- const char *username = get_config_string (ctx, "server.username");
- const char *realname = get_config_string (ctx, "server.realname");
+ const char *nickname = get_config_string (s->config, "nickname");
+ const char *username = get_config_string (s->config, "username");
+ const char *realname = get_config_string (s->config, "realname");
if (nickname && username && realname)
return true;
@@ -6280,9 +6335,9 @@ autofill_user_info (struct app_context *ctx, struct error **e)
// FIXME: set_config_strings() writes errors on its own
if (!nickname)
- set_config_string (ctx, "server.nickname", pwd->pw_name);
+ set_config_string (s->config, "nickname", pwd->pw_name);
if (!username)
- set_config_string (ctx, "server.username", pwd->pw_name);
+ set_config_string (s->config, "username", pwd->pw_name);
// Not all systems have the GECOS field but the vast majority does
if (!realname)
@@ -6294,7 +6349,7 @@ autofill_user_info (struct app_context *ctx, struct error **e)
if (comma)
*comma = '\0';
- set_config_string (ctx, "server.realname", gecos);
+ set_config_string (s->config, "realname", gecos);
}
return true;
@@ -6371,18 +6426,9 @@ load_configuration (struct app_context *ctx)
}
config_load (&ctx->config, root ? root : config_item_object ());
- if (!autofill_user_info (ctx, &e))
- {
- print_error ("%s: %s", "failed to fill in user details", e->message);
- error_free (e);
- }
ctx->isolate_buffers =
- get_config_boolean (ctx, "behaviour.isolate_buffers");
- ctx->server.reconnect =
- get_config_boolean (ctx, "server.reconnect");
- ctx->server.reconnect_delay =
- get_config_integer (ctx, "server.reconnect_delay");
+ get_config_boolean (ctx->config.root, "behaviour.isolate_buffers");
}
// --- Signals -----------------------------------------------------------------
@@ -6453,23 +6499,35 @@ setup_signal_handlers (void)
// --- I/O event handlers ------------------------------------------------------
+// FIXME: merge this with initiate_quit()
static void
-on_signal_pipe_readable (const struct pollfd *fd, struct app_context *ctx)
+preinitiate_quit (struct app_context *ctx)
{
- char dummy;
- (void) read (fd->fd, &dummy, 1);
+ struct str_map_iter iter;
+ str_map_iter_init (&iter, &ctx->servers);
- if (g_termination_requested && !ctx->quitting)
+ struct server *s;
+ while ((s = str_map_iter_next (&iter)))
{
// There may be a timer set to reconnect to the server
- // TODO: multiserver
- struct server *s = &ctx->server;
// TODO: a faster timer for quitting
// XXX: why do we do this? Just to reset the reconnect timer?
irc_reset_connection_timeouts (s);
if (irc_is_connected (s))
irc_initiate_disconnect (s, NULL);
+ }
+}
+
+static void
+on_signal_pipe_readable (const struct pollfd *fd, struct app_context *ctx)
+{
+ char dummy;
+ (void) read (fd->fd, &dummy, 1);
+
+ if (g_termination_requested && !ctx->quitting)
+ {
+ preinitiate_quit (ctx);
initiate_quit (ctx);
}
@@ -6588,6 +6646,43 @@ display_logo (void)
str_vector_free (&v);
}
+static void
+create_server (struct app_context *ctx)
+{
+ struct server *s = xmalloc (sizeof *s);
+ server_init (s, &ctx->poller);
+
+ s->ctx = ctx;
+ s->name = xstrdup ("server");
+ str_map_set (&ctx->servers, s->name, s);
+
+ // Load configuration
+ s->config = config_item_get (ctx->config.root, "server", NULL);
+ hard_assert (s->config != NULL);
+
+ s->reconnect = get_config_boolean (s->config, "reconnect");
+ s->reconnect_delay = get_config_integer (s->config, "reconnect_delay");
+
+ struct error *e = NULL;
+ if (!autofill_user_info (s, &e))
+ {
+ print_error ("%s: %s", "failed to fill in user details", e->message);
+ error_free (e);
+ }
+
+ // Add a buffer and activate it
+ struct buffer *buffer = s->buffer = buffer_new ();
+ buffer->type = BUFFER_SERVER;
+ buffer->name = xstrdup (s->name);
+ buffer->server = s;
+
+ buffer_add (ctx, buffer);
+ buffer_activate (ctx, buffer);
+
+ // Connect to the server ASAP
+ poller_timer_set (&s->reconnect_tmr, 0);
+}
+
int
main (int argc, char *argv[])
{
@@ -6650,10 +6745,9 @@ main (int argc, char *argv[])
refresh_prompt (&ctx);
input_start (&ctx.input, argv[0]);
- buffer_activate (&ctx, ctx.server.buffer);
- // Connect to the server ASAP
- poller_timer_set (&ctx.server.reconnect_tmr, 0);
+ // TODO: finish multi-server
+ create_server (&ctx);
ctx.polling = true;
while (ctx.polling)