Move some global-state dependent functions to src/
authorJelmer Vernooij <jelmer@samba.org>
Sun, 16 Dec 2007 14:00:52 +0000 (15:00 +0100)
committerJelmer Vernooij <jelmer@samba.org>
Sun, 16 Dec 2007 14:00:52 +0000 (15:00 +0100)
Makefile
lib/network.c
lib/network.h
src/internals.h
src/network.c [new file with mode: 0644]

index c0c81175f6ff7ec4d6ac48e5d4a786545a954bf8..84dabe1440c77786eba04773e935c16a993e0b9e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -61,6 +61,7 @@ objs = src/posix.o \
           src/log_support.o \
           src/log_custom.o \
           src/auto_away.o \
+          src/network.o \
           $(SSL_OBJS)
 
 lib_headers = \
index 46fc9a6ca30710abddabeb64cf629ff51ff887bc..a86ab44c780b1547eb554889df4ef66b699d054d 100644 (file)
@@ -27,23 +27,9 @@ static gboolean delayed_connect_server(struct irc_network *s);
 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);
@@ -125,166 +111,6 @@ gboolean network_set_charset(struct irc_network *n, const char *name)
        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;
@@ -834,16 +660,6 @@ static void reconnect(struct irc_network *server)
        }
 }
 
-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;
@@ -969,13 +785,13 @@ void clients_send(GList *clients, const struct line *l,
        }
 }
 
-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;
        }
 
@@ -986,7 +802,7 @@ static pid_t piped_child(char* const command[], int *f_in)
        pid = fork();
 
        if (pid == -1) {
-               log_global(LOG_ERROR, "fork: %s", strerror(errno));
+               network_log(LOG_ERROR, s, "fork: %s", strerror(errno));
                return -1;
        }
 
@@ -1111,7 +927,7 @@ static gboolean connect_program(struct irc_network *s)
        
        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;
 
@@ -1173,27 +989,12 @@ static gboolean delayed_connect_server(struct irc_network *s)
        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;
@@ -1203,25 +1004,9 @@ struct irc_network *load_network(struct global *global, struct network_config *s
        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
@@ -1268,30 +1053,6 @@ static void free_network(struct irc_network *s)
        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, 
@@ -1325,7 +1086,7 @@ void register_virtual_network(struct virtual_network_ops *ops)
 }
 
 /**
- * Autoconnect to all the networks in a global context.
+ * Autoconnect to all the networks in a list.
  *
  * @param networks GList with networks
  * @return TRUE
@@ -1345,34 +1106,10 @@ gboolean autoconnect_networks(GList *networks)
        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
  */
@@ -1388,80 +1125,6 @@ struct irc_network *find_network(GList *networks, const char *name)
        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.
  *
index 09d7ccee721d94513d43b09e1fee183970ecdf35..f044441befb9847c4a53fd57110608418f5065fd 100644 (file)
@@ -135,11 +135,8 @@ struct irc_network {
 
 /* 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);
@@ -152,8 +149,6 @@ G_MODULE_EXPORT struct irc_network *find_network(GList *gl, const char *);
 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 *);
index 6a71b8ca0e6aecc82c822474bdc0e42f074820ac..5d0a00a418748236e4b83d13d58c65482a3ff689 100644 (file)
@@ -63,6 +63,14 @@ gboolean init_plugins(const char *dir);
 /* 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 *);
diff --git a/src/network.c b/src/network.c
new file mode 100644 (file)
index 0000000..f4df738
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+       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);
+}