src/log_support.o \
src/log_custom.o \
src/auto_away.o \
+ src/network.o \
$(SSL_OBJS)
lib_headers = \
static gboolean connect_server(struct irc_network *s);
static gboolean close_server(struct irc_network *s);
static void reconnect(struct irc_network *server);
-static void clients_send_state(GList *clients, struct network_state *s);
static gboolean server_finish_connect(GIOChannel *ioc, GIOCondition cond,
void *data);
-struct new_network_notify_data {
- new_network_notify_fn fn;
- void *data;
-};
-
-void register_new_network_notify(struct global *global, new_network_notify_fn fn, void *userdata)
-{
- struct new_network_notify_data *p = g_new0(struct new_network_notify_data, 1);
- p->fn = fn;
- p->data = userdata;
- global->new_network_notifiers = g_list_append(global->new_network_notifiers, p);
-}
-
static void state_log_helper(enum log_level l, void *userdata, const char *msg)
{
network_log(l, (const struct irc_network *)userdata, "%s", msg);
return TRUE;
}
-/**
- * Update the isupport settings for a local network based on the
- * ISUPPORT info sent by a server.
- *
- * @param net_info Current information
- * @param remote_info Remote information
- * @return Whether updating went ok.
- */
-static gboolean network_update_isupport(struct network_info *net_info,
- struct network_info *remote_info)
-{
- if (remote_info->name != NULL) {
- g_free(net_info->name);
- net_info->name = g_strdup(remote_info->name);
- }
-
- if (remote_info->prefix != NULL) {
- g_free(net_info->prefix);
- net_info->prefix = g_strdup(remote_info->prefix);
- }
-
- if (remote_info->chantypes != NULL) {
- g_free(net_info->chantypes);
- net_info->chantypes = g_strdup(remote_info->chantypes);
- }
-
- if (remote_info->casemapping != CASEMAP_UNKNOWN) {
- net_info->casemapping = remote_info->casemapping;
- }
-
- if (remote_info->supported_user_modes != NULL) {
- g_free(net_info->supported_user_modes);
- net_info->supported_user_modes = g_strdup(remote_info->supported_user_modes);
- }
-
- if (remote_info->supported_channel_modes != NULL) {
- g_free(net_info->supported_channel_modes);
- net_info->supported_channel_modes = g_strdup(remote_info->supported_channel_modes);
- }
-
- return TRUE;
-}
-
-/**
- * Process a line received from the server
- *
- * @param n Network the line is received at
- * @param l Line received
- * @return Whether the message was received ok
- */
-static gboolean process_from_server(struct irc_network *n, const struct line *l)
-{
- struct line *lc;
- GError *error = NULL;
-
- g_assert(n);
- g_assert(l);
-
- log_network_line(n, l, TRUE);
-
- /* Silently drop empty messages, as allowed by RFC */
- if (l->argc == 0) {
- return TRUE;
- }
-
- n->connection.last_line_recvd = time(NULL);
-
- if (n->state == NULL) {
- network_log(LOG_WARNING, n,
- "Dropping message '%s' because network is disconnected.",
- l->args[0]);
- return FALSE;
- }
-
- run_log_filter(n, lc = linedup(l), FROM_SERVER); free_line(lc);
- run_replication_filter(n, lc = linedup(l), FROM_SERVER); free_line(lc);
-
- g_assert(n->state);
-
- state_handle_data(n->state, l);
-
- g_assert(l->args[0]);
-
- if (!g_strcasecmp(l->args[0], "PING")){
- network_send_args(n, "PONG", l->args[1], NULL);
- return TRUE;
- } else if (!g_strcasecmp(l->args[0], "PONG")){
- return TRUE;
- } else if (!g_strcasecmp(l->args[0], "ERROR")) {
- network_log(LOG_ERROR, n, "error: %s", l->args[1]);
- } else if (!g_strcasecmp(l->args[0], "433") &&
- n->connection.state == NETWORK_CONNECTION_STATE_LOGIN_SENT){
- char *tmp = g_strdup_printf("%s_", l->args[2]);
- network_send_args(n, "NICK", tmp, NULL);
- network_log(LOG_WARNING, n, "%s was already in use, trying %s",
- l->args[2], tmp);
- g_free(tmp);
- } else if (atoi(l->args[0]) == RPL_ENDOFMOTD ||
- atoi(l->args[0]) == ERR_NOMOTD) {
- GList *gl;
- n->connection.state = NETWORK_CONNECTION_STATE_MOTD_RECVD;
-
- network_set_charset(n, get_charset(&n->info));
-
- if (error != NULL)
- network_log(LOG_WARNING, n, "Error setting charset %s: %s",
- get_charset(&n->info), error->message);
-
- network_log(LOG_INFO, n, "Successfully logged in");
-
- network_update_isupport(&n->info, &n->state->info);
-
- nickserv_identify_me(n, n->state->me.nick);
-
- clients_send_state(n->clients, n->state);
-
- server_connected_hook_execute(n);
-
- network_send_args(n, "USERHOST", n->state->me.nick, NULL);
-
- /* Rejoin channels */
- for (gl = n->config->channels; gl; gl = gl->next) {
- struct channel_config *c = gl->data;
-
- if (c->autojoin) {
- network_send_args(n, "JOIN", c->name, c->key, NULL);
- }
- }
- }
-
- if ( n->connection.state == NETWORK_CONNECTION_STATE_MOTD_RECVD) {
- gboolean linestack_store = TRUE;
- if (atoi(l->args[0])) {
- linestack_store &= (!redirect_response(n, l));
- } else {
- if (n->clients == NULL) {
- if (!g_strcasecmp(l->args[0], "PRIVMSG") && l->argc > 2 &&
- l->args[2][0] == '\001' &&
- g_strncasecmp(l->args[2], "\001ACTION", 7) != 0) {
- ctcp_process_network_request(n, l);
- } else if (!g_strcasecmp(l->args[0], "NOTICE") && l->argc > 2 &&
- l->args[2][0] == '\001') {
- ctcp_process_network_reply(n, l);
- }
- } else if (run_server_filter(n, l, FROM_SERVER)) {
- if (!strcmp(l->args[0], "PRIVMSG") &&
- n->global->config->report_time == REPORT_TIME_ALWAYS)
- l = line_prefix_time(l, time(NULL)+n->global->config->report_time_offset);
-
- clients_send(n->clients, l, NULL);
- }
- }
-
- if (linestack_store)
- linestack_insert_line(n->linestack, l, FROM_SERVER, n->state);
- }
-
- return TRUE;
-}
-
static void network_report_disconnect(struct irc_network *n, const char *fmt, ...)
{
va_list ap;
}
}
-static void clients_send_state(GList *clients, struct network_state *s)
-{
- GList *gl;
-
- for (gl = clients; gl; gl = gl->next) {
- struct client *c = gl->data;
- client_send_state(c, s);
- }
-}
-
static void clients_invalidate_state(GList *clients, struct network_state *s)
{
GList *gl;
}
}
-static pid_t piped_child(char* const command[], int *f_in)
+static pid_t piped_child(struct irc_network *s, char* const command[], int *f_in)
{
pid_t pid;
int sock[2];
if (socketpair(PF_UNIX, SOCK_STREAM, AF_LOCAL, sock) == -1) {
- log_global(LOG_ERROR, "socketpair: %s", strerror(errno));
+ network_log(LOG_ERROR, s, "socketpair: %s", strerror(errno));
return -1;
}
pid = fork();
if (pid == -1) {
- log_global(LOG_ERROR, "fork: %s", strerror(errno));
+ network_log(LOG_ERROR, s, "fork: %s", strerror(errno));
return -1;
}
cmd[0] = s->config->type_settings.program_location;
cmd[1] = NULL;
- pid = piped_child(cmd, &sock);
+ pid = piped_child(s, cmd, &sock);
if (pid == -1) return FALSE;
return (s->connection.state == NETWORK_CONNECTION_STATE_RECONNECT_PENDING);
}
-/**
- * Load a network from a configuration file specification.
- *
- * @param global Global context to use.
- * @param sc Network configuration to load form
- * @return A new network instance, already added to the global context.
- */
-struct irc_network *load_network(struct global *global, struct network_config *sc)
+struct irc_network *irc_network_new(gboolean (*process_from_server) (struct irc_network *, const struct line *), struct network_config *sc)
{
struct irc_network *s;
- GList *gl;
g_assert(sc);
- if (global != NULL) {
- /* Don't connect to the same network twice */
- s = find_network(global->networks, sc->name);
- if (s)
- return s;
- }
-
s = g_new0(struct irc_network, 1);
s->process_from_server = process_from_server;
s->references = 1;
s->info.name = g_strdup(s->config->name);
s->info.ircd = g_strdup("ctrlproxy");
s->connection.pending_lines = g_queue_new();
- s->global = global;
s->info.forced_nick_changes = TRUE; /* Forced nick changes are done by ctrlproxy */
s->connection.outgoing_iconv = s->connection.incoming_iconv = (GIConv)-1;
- if (global != NULL) {
- g_free(s->info.charset);
- if (s->global->config->client_charset != NULL) {
- s->info.charset = g_strdup(s->global->config->client_charset);
- }
-
- global->networks = g_list_append(global->networks, s);
-
- for (gl = global->new_network_notifiers; gl; gl = gl->next) {
- struct new_network_notify_data *p = gl->data;
-
- p->fn(s, p->data);
- }
- }
-
#ifdef HAVE_GNUTLS
s->ssl_credentials = ssl_get_client_credentials(NULL);
#endif
g_free(s);
}
-/**
- * Unload a network from a global context.
- *
- * @param s Network to unload.
- */
-void unload_network(struct irc_network *s)
-{
- GList *l;
-
- g_assert(s);
- l = s->clients;
-
- while(l) {
- struct client *c = l->data;
- l = l->next;
- disconnect_client(c, "Server exiting");
- }
-
- if (s->global != NULL) {
- s->global->networks = g_list_remove(s->global->networks, s);
- }
-
- network_unref(s);
-}
/**
* Disconnect from a network. The network will still be kept in memory,
}
/**
- * Autoconnect to all the networks in a global context.
+ * Autoconnect to all the networks in a list.
*
* @param networks GList with networks
* @return TRUE
return TRUE;
}
-/**
- * Load all the networks in a configuration file.
- *
- * @param global Global context to load networks into.
- * @param cfg Configuration to read from
- * @return TRUE
- */
-gboolean load_networks(struct global *global, struct ctrlproxy_config *cfg,
- network_log_fn fn)
-{
- GList *gl;
- g_assert(cfg);
- for (gl = cfg->networks; gl; gl = gl->next)
- {
- struct network_config *nc = gl->data;
- struct irc_network *n;
- n = load_network(global, nc);
- if (n != NULL)
- network_set_log_fn(n, fn, n);
- }
-
- return TRUE;
-}
-
/**
* Find a network by name.
*
- * @param global Global context to search in.
+ * @param networks GList with possible networks
* @param name Name of the network to search for.
* @return first network found or NULL
*/
return NULL;
}
-/**
- * Find a network by host name and port or name.
- *
- * @param global Context to search in
- * @param hostname Hostname to search for
- * @param port Port to search from.
- * @param create Whether to create the network if it wasn't found.
- * @return the network found or created or NULL
- */
-struct irc_network *find_network_by_hostname(struct global *global,
- const char *hostname, guint16 port,
- gboolean create)
-{
- GList *gl;
- char *portname = g_strdup_printf("%d", port);
- g_assert(portname != NULL);
- g_assert(hostname != NULL);
-
- for (gl = global->networks; gl; gl = gl->next) {
- GList *sv;
- struct irc_network *n = gl->data;
- g_assert(n);
-
- if (n->info.name && !g_strcasecmp(n->info.name, hostname)) {
- g_free(portname);
- return n;
- }
-
- g_assert(n->config);
- if (n->config->type == NETWORK_TCP)
- {
- for (sv = n->config->type_settings.tcp_servers; sv; sv = sv->next)
- {
- struct tcp_server_config *server = sv->data;
-
- if (!g_strcasecmp(server->host, hostname) &&
- !g_strcasecmp(server->port, portname)) {
- g_free(portname);
- return n;
- }
- }
- }
-
- if (n->info.name && !g_strcasecmp(n->info.name, hostname)) {
- g_free(portname);
- return n;
- }
- }
-
- /* Create a new server */
- if (create)
- {
- struct tcp_server_config *s = g_new0(struct tcp_server_config, 1);
- struct network_config *nc;
- struct irc_network *n;
- nc = network_config_init(global->config);
-
- nc->name = g_strdup(hostname);
- nc->type = NETWORK_TCP;
- s->host = g_strdup(hostname);
- s->port = portname;
-
- nc->type_settings.tcp_servers = g_list_append(nc->type_settings.tcp_servers, s);
-
- n = load_network(global, nc);
- network_set_log_fn(n, (network_log_fn)handle_network_log, n);
- return n;
- }
-
- g_free(portname);
-
- return NULL;
-}
-
/**
* Disconnect from and unload all networks.
*
/* server.c */
G_MODULE_EXPORT gboolean network_set_charset(struct irc_network *n, const char *name);
-G_MODULE_EXPORT struct irc_network *find_network_by_hostname(struct global *global, const char *host, guint16 port, gboolean create);
-G_MODULE_EXPORT gboolean load_networks(struct global *, struct ctrlproxy_config *cfg, network_log_fn);
G_MODULE_EXPORT gboolean autoconnect_networks(GList *);
-G_MODULE_EXPORT struct irc_network *load_network(struct global *, struct network_config *);
-G_MODULE_EXPORT void unload_network(struct irc_network *);
+G_MODULE_EXPORT struct irc_network *irc_network_new(gboolean (*process_from_server) (struct irc_network *, const struct line *), struct network_config *sc);
G_MODULE_EXPORT gboolean connect_network(struct irc_network *);
G_MODULE_EXPORT void network_select_next_server(struct irc_network *n);
G_MODULE_EXPORT gboolean disconnect_network(struct irc_network *s);
G_MODULE_EXPORT gboolean virtual_network_recv_line(struct irc_network *l, struct line *);
G_MODULE_EXPORT gboolean virtual_network_recv_args(struct irc_network *l, const char *origin, ...);
G_MODULE_EXPORT gboolean virtual_network_recv_response(struct irc_network *n, int num, ...);
-typedef void (*new_network_notify_fn) (struct irc_network *, void *);
-G_MODULE_EXPORT void register_new_network_notify(struct global *, new_network_notify_fn, void *userdata);
G_MODULE_EXPORT G_GNUC_MALLOC struct linestack_context *new_linestack(struct irc_network *network);
G_MODULE_EXPORT G_GNUC_MALLOC char *network_generate_feature_string(struct irc_network *n);
G_MODULE_EXPORT struct irc_network *network_ref(struct irc_network *);
/* motd.c */
char **get_motd_lines(struct client *);
+/* network.c */
+G_MODULE_EXPORT struct irc_network *load_network(struct global *global, struct network_config *sc);
+G_MODULE_EXPORT gboolean load_networks(struct global *, struct ctrlproxy_config *cfg, network_log_fn);
+G_MODULE_EXPORT void unload_network(struct irc_network *);
+typedef void (*new_network_notify_fn) (struct irc_network *, void *);
+G_MODULE_EXPORT struct irc_network *find_network_by_hostname(struct global *global, const char *host, guint16 port, gboolean create);
+G_MODULE_EXPORT void register_new_network_notify(struct global *, new_network_notify_fn, void *userdata);
+
/* hooks.c */
void server_disconnected_hook_execute(struct irc_network *);
void server_connected_hook_execute(struct irc_network *);
--- /dev/null
+/*
+ ctrlproxy: A modular IRC proxy
+ (c) 2002-2007 Jelmer Vernooij <jelmer@nl.linux.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "internals.h"
+#include "irc.h"
+#include "ssl.h"
+
+/**
+ * Update the isupport settings for a local network based on the
+ * ISUPPORT info sent by a server.
+ *
+ * @param net_info Current information
+ * @param remote_info Remote information
+ * @return Whether updating went ok.
+ */
+static gboolean network_update_isupport(struct network_info *net_info,
+ struct network_info *remote_info)
+{
+ if (remote_info->name != NULL) {
+ g_free(net_info->name);
+ net_info->name = g_strdup(remote_info->name);
+ }
+
+ if (remote_info->prefix != NULL) {
+ g_free(net_info->prefix);
+ net_info->prefix = g_strdup(remote_info->prefix);
+ }
+
+ if (remote_info->chantypes != NULL) {
+ g_free(net_info->chantypes);
+ net_info->chantypes = g_strdup(remote_info->chantypes);
+ }
+
+ if (remote_info->casemapping != CASEMAP_UNKNOWN) {
+ net_info->casemapping = remote_info->casemapping;
+ }
+
+ if (remote_info->supported_user_modes != NULL) {
+ g_free(net_info->supported_user_modes);
+ net_info->supported_user_modes = g_strdup(remote_info->supported_user_modes);
+ }
+
+ if (remote_info->supported_channel_modes != NULL) {
+ g_free(net_info->supported_channel_modes);
+ net_info->supported_channel_modes = g_strdup(remote_info->supported_channel_modes);
+ }
+
+ return TRUE;
+}
+
+static void clients_send_state(GList *clients, struct network_state *s)
+{
+ GList *gl;
+
+ for (gl = clients; gl; gl = gl->next) {
+ struct client *c = gl->data;
+ client_send_state(c, s);
+ }
+}
+
+
+
+/**
+ * Process a line received from the server
+ *
+ * @param n Network the line is received at
+ * @param l Line received
+ * @return Whether the message was received ok
+ */
+static gboolean process_from_server(struct irc_network *n, const struct line *l)
+{
+ struct line *lc;
+ GError *error = NULL;
+
+ g_assert(n);
+ g_assert(l);
+
+ log_network_line(n, l, TRUE);
+
+ /* Silently drop empty messages, as allowed by RFC */
+ if (l->argc == 0) {
+ return TRUE;
+ }
+
+ n->connection.last_line_recvd = time(NULL);
+
+ if (n->state == NULL) {
+ network_log(LOG_WARNING, n,
+ "Dropping message '%s' because network is disconnected.",
+ l->args[0]);
+ return FALSE;
+ }
+
+ run_log_filter(n, lc = linedup(l), FROM_SERVER); free_line(lc);
+ run_replication_filter(n, lc = linedup(l), FROM_SERVER); free_line(lc);
+
+ g_assert(n->state);
+
+ state_handle_data(n->state, l);
+
+ g_assert(l->args[0]);
+
+ if (!g_strcasecmp(l->args[0], "PING")){
+ network_send_args(n, "PONG", l->args[1], NULL);
+ return TRUE;
+ } else if (!g_strcasecmp(l->args[0], "PONG")){
+ return TRUE;
+ } else if (!g_strcasecmp(l->args[0], "ERROR")) {
+ network_log(LOG_ERROR, n, "error: %s", l->args[1]);
+ } else if (!g_strcasecmp(l->args[0], "433") &&
+ n->connection.state == NETWORK_CONNECTION_STATE_LOGIN_SENT){
+ char *tmp = g_strdup_printf("%s_", l->args[2]);
+ network_send_args(n, "NICK", tmp, NULL);
+ network_log(LOG_WARNING, n, "%s was already in use, trying %s",
+ l->args[2], tmp);
+ g_free(tmp);
+ } else if (atoi(l->args[0]) == RPL_ENDOFMOTD ||
+ atoi(l->args[0]) == ERR_NOMOTD) {
+ GList *gl;
+ n->connection.state = NETWORK_CONNECTION_STATE_MOTD_RECVD;
+
+ network_set_charset(n, get_charset(&n->info));
+
+ if (error != NULL)
+ network_log(LOG_WARNING, n, "Error setting charset %s: %s",
+ get_charset(&n->info), error->message);
+
+ network_log(LOG_INFO, n, "Successfully logged in");
+
+ network_update_isupport(&n->info, &n->state->info);
+
+ nickserv_identify_me(n, n->state->me.nick);
+
+ clients_send_state(n->clients, n->state);
+
+ server_connected_hook_execute(n);
+
+ network_send_args(n, "USERHOST", n->state->me.nick, NULL);
+
+ /* Rejoin channels */
+ for (gl = n->config->channels; gl; gl = gl->next) {
+ struct channel_config *c = gl->data;
+
+ if (c->autojoin) {
+ network_send_args(n, "JOIN", c->name, c->key, NULL);
+ }
+ }
+ }
+
+ if ( n->connection.state == NETWORK_CONNECTION_STATE_MOTD_RECVD) {
+ gboolean linestack_store = TRUE;
+ if (atoi(l->args[0])) {
+ linestack_store &= (!redirect_response(n, l));
+ } else {
+ if (n->clients == NULL) {
+ if (!g_strcasecmp(l->args[0], "PRIVMSG") && l->argc > 2 &&
+ l->args[2][0] == '\001' &&
+ g_strncasecmp(l->args[2], "\001ACTION", 7) != 0) {
+ ctcp_process_network_request(n, l);
+ } else if (!g_strcasecmp(l->args[0], "NOTICE") && l->argc > 2 &&
+ l->args[2][0] == '\001') {
+ ctcp_process_network_reply(n, l);
+ }
+ } else if (run_server_filter(n, l, FROM_SERVER)) {
+ if (!strcmp(l->args[0], "PRIVMSG") &&
+ n->global->config->report_time == REPORT_TIME_ALWAYS)
+ l = line_prefix_time(l, time(NULL)+n->global->config->report_time_offset);
+
+ clients_send(n->clients, l, NULL);
+ }
+ }
+
+ if (linestack_store)
+ linestack_insert_line(n->linestack, l, FROM_SERVER, n->state);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Find a network by host name and port or name.
+ *
+ * @param global Context to search in
+ * @param hostname Hostname to search for
+ * @param port Port to search from.
+ * @param create Whether to create the network if it wasn't found.
+ * @return the network found or created or NULL
+ */
+struct irc_network *find_network_by_hostname(struct global *global,
+ const char *hostname, guint16 port,
+ gboolean create)
+{
+ GList *gl;
+ char *portname = g_strdup_printf("%d", port);
+ g_assert(portname != NULL);
+ g_assert(hostname != NULL);
+
+ for (gl = global->networks; gl; gl = gl->next) {
+ GList *sv;
+ struct irc_network *n = gl->data;
+ g_assert(n);
+
+ if (n->info.name && !g_strcasecmp(n->info.name, hostname)) {
+ g_free(portname);
+ return n;
+ }
+
+ g_assert(n->config);
+ if (n->config->type == NETWORK_TCP)
+ {
+ for (sv = n->config->type_settings.tcp_servers; sv; sv = sv->next)
+ {
+ struct tcp_server_config *server = sv->data;
+
+ if (!g_strcasecmp(server->host, hostname) &&
+ !g_strcasecmp(server->port, portname)) {
+ g_free(portname);
+ return n;
+ }
+ }
+ }
+
+ if (n->info.name && !g_strcasecmp(n->info.name, hostname)) {
+ g_free(portname);
+ return n;
+ }
+ }
+
+ /* Create a new server */
+ if (create)
+ {
+ struct tcp_server_config *s = g_new0(struct tcp_server_config, 1);
+ struct network_config *nc;
+ struct irc_network *n;
+ nc = network_config_init(global->config);
+
+ nc->name = g_strdup(hostname);
+ nc->type = NETWORK_TCP;
+ s->host = g_strdup(hostname);
+ s->port = portname;
+
+ nc->type_settings.tcp_servers = g_list_append(nc->type_settings.tcp_servers, s);
+
+ n = load_network(global, nc);
+ network_set_log_fn(n, (network_log_fn)handle_network_log, n);
+ return n;
+ }
+
+ g_free(portname);
+
+ return NULL;
+}
+
+struct new_network_notify_data {
+ new_network_notify_fn fn;
+ void *data;
+};
+
+void register_new_network_notify(struct global *global, new_network_notify_fn fn, void *userdata)
+{
+ struct new_network_notify_data *p = g_new0(struct new_network_notify_data, 1);
+ p->fn = fn;
+ p->data = userdata;
+ global->new_network_notifiers = g_list_append(global->new_network_notifiers, p);
+}
+
+/**
+ * Load a network from a configuration file specification.
+ *
+ * @param global Global context to use.
+ * @param sc Network configuration to load form
+ * @param process_from_server Function to handle lines received from server
+ * @return A new network instance, already added to the global context.
+ */
+struct irc_network *load_network(struct global *global, struct network_config *sc)
+{
+ struct irc_network *net;
+
+ if (global != NULL) {
+ /* Don't connect to the same network twice */
+ net = find_network(global->networks, sc->name);
+ if (net)
+ return net;
+ }
+
+ net = irc_network_new(process_from_server, sc);
+
+ net->global = global;
+
+ if (global != NULL) {
+ GList *gl;
+ g_free(net->info.charset);
+ if (net->global->config->client_charset != NULL) {
+ net->info.charset = g_strdup(net->global->config->client_charset);
+ }
+
+ global->networks = g_list_append(global->networks, net);
+
+ for (gl = global->new_network_notifiers; gl; gl = gl->next) {
+ struct new_network_notify_data *p = gl->data;
+
+ p->fn(net, p->data);
+ }
+ }
+
+ return net;
+}
+
+/**
+ * Load all the networks in a configuration file.
+ *
+ * @param global Global context to load networks into.
+ * @param cfg Configuration to read from
+ * @return TRUE
+ */
+gboolean load_networks(struct global *global, struct ctrlproxy_config *cfg,
+ network_log_fn fn)
+{
+ GList *gl;
+ g_assert(cfg);
+ for (gl = cfg->networks; gl; gl = gl->next)
+ {
+ struct network_config *nc = gl->data;
+ struct irc_network *n;
+ n = load_network(global, nc);
+ if (n != NULL)
+ network_set_log_fn(n, fn, n);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Unload a network from a global context.
+ *
+ * @param s Network to unload.
+ */
+void unload_network(struct irc_network *s)
+{
+ GList *l;
+
+ g_assert(s);
+ l = s->clients;
+
+ while(l) {
+ struct client *c = l->data;
+ l = l->next;
+ disconnect_client(c, "Server exiting");
+ }
+
+ if (s->global != NULL) {
+ s->global->networks = g_list_remove(s->global->networks, s);
+ }
+
+ network_unref(s);
+}