degesch: implement /server add

This commit is contained in:
Přemysl Eric Janouch 2015-07-05 20:32:48 +02:00
parent 15d3129ea3
commit 096a179e09

168
degesch.c
View File

@ -6291,6 +6291,94 @@ dump_matching_options
}
}
// --- Server management -------------------------------------------------------
static struct str_map *
get_servers_config (struct app_context *ctx)
{
return &config_item_get (ctx->config.root, "servers", NULL)->value.object;
}
static bool
validate_server_name (const char *name)
{
for (const unsigned char *p = (const unsigned char *) name; *p; p++)
if (*p < 32 || *p == '.')
return false;
return true;
}
static bool
check_server_name_for_addition (struct app_context *ctx, const char *name)
{
if (!strcasecmp_ascii (name, ctx->global_buffer->name))
log_global_error (ctx, "Cannot create server `#s': #s",
name, "name collides with the global buffer");
else if (str_map_find (&ctx->servers, name))
log_global_error (ctx, "Cannot create server `#s': #s",
name, "server already exists");
else if (!validate_server_name (name))
log_global_error (ctx, "Cannot create server `#s': #s",
name, "invalid server name");
else
return true;
return false;
}
static struct server *
server_add (struct app_context *ctx,
const char *name, struct config_item_ *subtree)
{
hard_assert (!str_map_find (&ctx->servers, name));
struct server *s = xmalloc (sizeof *s);
server_init (s, &ctx->poller);
s->ctx = ctx;
s->name = xstrdup (name);
str_map_set (&ctx->servers, s->name, s);
s->config = subtree;
// 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);
config_schema_apply_to_object (g_config_server, subtree, s);
config_schema_call_changed (subtree);
// Connect to the server ASAP
// TODO: make this configurable ("autoconnect")
poller_timer_set (&s->reconnect_tmr, 0);
return s;
}
static void
server_add_new (struct app_context *ctx, const char *name)
{
// Note that there may already be something in the configuration under
// that key that we've ignored earlier, and there may also be
// a case-insensitive conflict. Those things may only happen as a result
// of manual edits to the configuration, though, and they're not really
// going to break anything. They only cause surprises when loading.
struct str_map *servers = get_servers_config (ctx);
struct config_item_ *subtree = config_item_object ();
str_map_set (servers, name, subtree);
struct server *s = server_add (ctx, name, subtree);
struct error *e = NULL;
if (!irc_autofill_user_info (s, &e))
{
log_server_error (s, s->buffer,
"#s: #s", "Failed to fill in user details", e->message);
error_free (e);
}
}
// --- User input handling -----------------------------------------------------
// HANDLER_NEEDS_REG is primarily for message sending commands,
@ -7144,6 +7232,19 @@ show_servers_list (struct app_context *ctx)
log_global_indent (ctx, " #s", s->name);
}
static bool
handle_server_add (struct handler_args *a)
{
char *name = cut_word (&a->arguments);
if (*a->arguments)
return false;
struct app_context *ctx = a->ctx;
if (check_server_name_for_addition (ctx, name))
server_add_new (ctx, name);
return true;
}
static bool
handle_command_server (struct handler_args *a)
{
@ -7153,7 +7254,7 @@ handle_command_server (struct handler_args *a)
if (!strcasecmp_ascii (action, "list"))
show_servers_list (ctx);
else if (!strcasecmp_ascii (action, "add"))
; // TODO: <name>
result = handle_server_add (a);
else if (!strcasecmp_ascii (action, "remove"))
; // TODO: <name>
else if (!strcasecmp_ascii (action, "rename"))
@ -8438,70 +8539,21 @@ load_configuration (struct app_context *ctx)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
load_server_from_config (struct app_context *ctx,
const char *name, struct config_item_ *subtree)
{
// They're case-insensitive in the application but not so in the config
if (str_map_find (&ctx->servers, name))
{
log_global_error (ctx, "Error in configuration: "
"ignoring server `#s' as it collides with another one", name);
return;
}
struct server *s = xmalloc (sizeof *s);
server_init (s, &ctx->poller);
s->ctx = ctx;
s->name = xstrdup (name);
str_map_set (&ctx->servers, s->name, s);
s->config = subtree;
// 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);
config_schema_apply_to_object (g_config_server, subtree, s);
config_schema_call_changed (subtree);
// XXX: is this desirable in here? I think we should do it only when
// actually creating a new server instead of every time we load them.
struct error *e = NULL;
if (!irc_autofill_user_info (s, &e))
{
log_server_error (s, s->buffer,
"#s: #s", "Failed to fill in user details", e->message);
error_free (e);
}
// Connect to the server ASAP
// TODO: make this configurable ("autoconnect")
poller_timer_set (&s->reconnect_tmr, 0);
}
static void
load_servers (struct app_context *ctx)
{
struct config_item_ *servers =
config_item_get (ctx->config.root, "servers", NULL);
struct str_map_iter iter;
str_map_iter_init (&iter, &servers->value.object);
str_map_iter_init (&iter, get_servers_config (ctx));
struct config_item_ *s;
while ((s = str_map_iter_next (&iter)))
struct config_item_ *subtree;
while ((subtree = str_map_iter_next (&iter)))
{
if (s->type != CONFIG_ITEM_OBJECT)
const char *name = iter.link->key;
if (subtree->type != CONFIG_ITEM_OBJECT)
log_global_error (ctx, "Error in configuration: "
"ignoring server `#s' as it's not an object", iter.link->key);
else
load_server_from_config (ctx, iter.link->key, s);
"ignoring server `#s' as it's not an object", name);
else if (check_server_name_for_addition (ctx, name))
server_add (ctx, name, subtree);
}
}