2 ctrlproxy: A modular IRC proxy
3 (c) 2002-2007 Jelmer Vernooij <jelmer@nl.linux.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "internals.h"
23 #include "transport.h"
25 #define AF_LOCAL AF_UNIX
28 static GHashTable *virtual_network_ops = NULL;
30 static gboolean delayed_connect_server(struct irc_network *s);
31 static gboolean connect_server(struct irc_network *s);
32 static gboolean close_server(struct irc_network *s);
33 static void reconnect(struct irc_network *server);
34 static gboolean server_finish_connect(GIOChannel *ioc, GIOCondition cond,
37 static void state_log_helper(enum log_level l, void *userdata, const char *msg)
39 network_log(l, (const struct irc_network *)userdata, "%s", msg);
42 static void server_send_login (struct irc_network *s)
44 struct irc_login_details *login_details = s->callbacks->get_login_details(s);
47 s->connection.state = NETWORK_CONNECTION_STATE_LOGIN_SENT;
49 network_log(LOG_TRACE, s, "Sending login details");
51 s->external_state = network_state_init(login_details->nick, login_details->username,
53 network_state_set_log_fn(s->external_state, state_log_helper, s);
54 if (s->callbacks->state_set)
55 s->callbacks->state_set(s);
56 g_assert(s->linestack != NULL);
58 if (login_details->password != NULL) {
59 network_send_args(s, "PASS", login_details->password, NULL);
61 g_assert(login_details->nick != NULL && strlen(login_details->nick) > 0);
62 network_send_args(s, "NICK", login_details->nick, NULL);
63 g_assert(login_details->username != NULL && strlen(login_details->username) > 0);
64 g_assert(login_details->mode != NULL && strlen(login_details->mode) > 0);
65 g_assert(login_details->unused != NULL && strlen(login_details->unused) > 0);
66 g_assert(login_details->realname != NULL && strlen(login_details->realname) > 0);
67 network_send_args(s, "USER", login_details->username, login_details->mode,
68 login_details->unused, login_details->realname, NULL);
70 free_login_details(login_details);
74 * Change the character set used to communicate with the server.
76 * @param n network to change the character set for
77 * @param name name of the character set to use
78 * @return true if setting the charset worked
80 gboolean network_set_charset(struct irc_network *n, const char *name)
82 if (!transport_set_charset(n->connection.transport, name)) {
83 network_log(LOG_WARNING, n, "Unable to find charset `%s'", name);
89 static void network_report_disconnect(struct irc_network *n, const char *fmt, ...)
94 tmp = g_strdup_vprintf(fmt, ap);
97 g_free(n->connection.data.tcp.last_disconnect_reason);
98 n->connection.data.tcp.last_disconnect_reason = tmp;
100 network_log(LOG_WARNING, n, "%s", tmp);
103 static void on_transport_disconnect(struct irc_transport *transport)
108 static void on_transport_hup(struct irc_transport *transport)
110 struct irc_network *network = transport->userdata;
112 network_report_disconnect(network, "Hangup from server, scheduling reconnect");
116 static gboolean on_transport_error(struct irc_transport *transport, const char *error_msg)
118 struct irc_network *network = transport->userdata;
120 network_report_disconnect(network,
121 "Error from server: %s, scheduling reconnect", error_msg);
128 static void on_transport_charset_error(struct irc_transport *transport, const char *error_msg)
130 struct irc_network *network = transport->userdata;
132 network_log(LOG_WARNING, network, "Error while sending line with charset: %s",
136 static gboolean on_transport_recv(struct irc_transport *transport,
137 const struct irc_line *line)
139 struct irc_network *server = transport->userdata;
140 return server->callbacks->process_from_server(server, line);
143 static void on_transport_log(struct irc_transport *transport, const struct irc_line *l, const GError *error)
145 struct irc_network *network = transport->userdata;
147 network_log(LOG_WARNING, network, "Error while sending line '%s': %s",
148 l->args[0], error->message);
151 static const struct irc_transport_callbacks network_callbacks = {
152 .disconnect = on_transport_disconnect,
153 .hangup = on_transport_hup,
154 .error = on_transport_error,
155 .charset_error = on_transport_charset_error,
156 .recv = on_transport_recv,
157 .log = on_transport_log,
160 static struct tcp_server_config *network_get_next_tcp_server(struct irc_network *n)
162 struct network_config *nc = n->private_data;
167 cur = g_list_find(nc->type_settings.tcp.servers, n->connection.data.tcp.current_server);
169 /* Get next available server */
170 if (cur != NULL && cur->next != NULL)
173 cur = nc->type_settings.tcp.servers;
181 static gboolean network_send_line_direct(struct irc_network *s, struct irc_client *c,
182 const struct irc_line *ol)
184 struct irc_line nl, *l;
185 struct network_config *nc = s->private_data;
187 g_assert(nc != NULL);
189 g_assert(nc->type == NETWORK_TCP ||
190 nc->type == NETWORK_PROGRAM ||
191 nc->type == NETWORK_IOCHANNEL ||
192 nc->type == NETWORK_VIRTUAL);
195 memcpy(l, ol, sizeof(struct irc_line));
198 /* origin lines should never be sent to the server */
199 g_assert(l->origin == NULL);
201 if (nc->type == NETWORK_VIRTUAL) {
202 if (s->connection.data.virtual.ops == NULL)
204 return s->connection.data.virtual.ops->to_server(s, c, l);
206 if (s->connection.transport == NULL)
208 return transport_send_line(s->connection.transport, l);
213 * Send a line to the network.
214 * @param s Network to send to.
215 * @param c Client the line was sent by originally.
216 * @param ol Line to send to the network
217 * @param is_private Whether the line should not be broadcast to other clients
219 gboolean network_send_line(struct irc_network *s, struct irc_client *c,
220 const struct irc_line *ol, gboolean is_private)
230 if (l.origin == NULL && s->external_state != NULL) {
231 tmp = l.origin = g_strdup(s->external_state->me.hostmask);
234 if (l.origin != NULL) {
235 if (!run_server_filter(s, &l, TO_SERVER)) {
240 run_log_filter(s, lc = linedup(&l), TO_SERVER); free_line(lc);
241 run_replication_filter(s, lc = linedup(&l), TO_SERVER); free_line(lc);
242 linestack_insert_line(s->linestack, ol, TO_SERVER, s->external_state);
245 g_assert(l.args[0] != NULL);
247 /* Also write this message to all other clients currently connected */
249 (!g_strcasecmp(l.args[0], "PRIVMSG") ||
250 !g_strcasecmp(l.args[0], "NOTICE"))) {
252 if (s->global->config->report_time == REPORT_TIME_ALWAYS)
253 line_prefix_time(&l, time(NULL)+s->global->config->report_time_offset);
255 clients_send(s->clients, &l, c);
260 log_network_line(s, ol, FALSE);
262 redirect_record(&s->queries, s, c, ol);
264 return network_send_line_direct(s, c, ol);
268 * Indicate that a response is received by a virtual network.
270 * @param n Network to receive data
271 * @param num Number of the response to receive
273 gboolean virtual_network_recv_response(struct irc_network *n, int num, ...)
278 struct network_config *nc;
282 nc = n->private_data;
284 g_assert(nc->type == NETWORK_VIRTUAL);
287 l = virc_parse_line(n->name, ap);
290 l->args = g_realloc(l->args, sizeof(char *) * (l->argc+4));
291 memmove(&l->args[2], &l->args[0], l->argc * sizeof(char *));
293 l->args[0] = g_strdup_printf("%03d", num);
295 if (n->external_state != NULL && n->external_state->me.nick != NULL)
296 l->args[1] = g_strdup(n->external_state->me.nick);
298 l->args[1] = g_strdup("*");
301 l->args[l->argc] = NULL;
303 ret = virtual_network_recv_line(n, l);
311 * Indicate that a line is received by a virtual network.
313 * @param s Network to send to.
314 * @param l Line to receive.
316 gboolean virtual_network_recv_line(struct irc_network *s, struct irc_line *l)
321 if (l->origin == NULL)
322 l->origin = g_strdup(get_my_hostname());
324 return s->callbacks->process_from_server(s, l);
328 * Indicate that a line has been received.
330 * @param s Network to use.
331 * @param origin Origin to make the data originate from
333 gboolean virtual_network_recv_args(struct irc_network *s, const char *origin, ...)
341 va_start(ap, origin);
342 l = virc_parse_line(origin, ap);
345 ret = virtual_network_recv_line(s, l);
353 * Send a new line to the network.
356 * @param ... Arguments terminated by NULL
358 gboolean network_send_args(struct irc_network *s, ...)
367 l = virc_parse_line(NULL, ap);
370 ret = network_send_line(s, NULL, l, TRUE);
377 static gboolean bindsock(struct irc_network *s,
378 int sock, struct addrinfo *res,
382 struct addrinfo hints_bind;
384 struct addrinfo *res_bind, *addrinfo_bind;
386 memset(&hints_bind, 0, sizeof(hints_bind));
387 hints_bind.ai_family = res->ai_family;
390 hints_bind.ai_flags = AI_ADDRCONFIG;
393 hints_bind.ai_socktype = res->ai_socktype;
394 hints_bind.ai_protocol = res->ai_protocol;
396 error = getaddrinfo(address, service, &hints_bind, &addrinfo_bind);
398 network_log(LOG_ERROR, s,
399 "Unable to lookup %s:%s %s", address, service,
400 gai_strerror(error));
404 for (res_bind = addrinfo_bind;
405 res_bind; res_bind = res_bind->ai_next) {
406 if (bind(sock, res_bind->ai_addr, res_bind->ai_addrlen) < 0) {
407 network_log(LOG_ERROR, s, "Unable to bind to %s:%s %s",
408 address, service, strerror(errno));
412 freeaddrinfo(addrinfo_bind);
414 return (res_bind != NULL);
420 * @param server network to ping
421 * @param ping_source GSource id of the ping event
423 static void ping_server(struct irc_network *server, gboolean ping_source)
425 gint silent_time = time(NULL) - server->connection.last_line_recvd;
426 if (silent_time > MAX_SILENT_TIME) {
427 network_report_disconnect(server, "Ping timeout (%d seconds)",
430 } else if (silent_time > MIN_SILENT_TIME) {
431 network_send_args(server, "PING", "ctrlproxy", NULL);
435 static gboolean connect_current_tcp_server(struct irc_network *s)
437 struct addrinfo *res;
439 struct tcp_server_config *cs;
440 GIOChannel *ioc = NULL;
441 struct addrinfo hints;
442 struct addrinfo *addrinfo = NULL;
445 gboolean connect_finished = TRUE;
446 struct network_config *nc;
450 nc = s->private_data;
452 if (!s->connection.data.tcp.current_server) {
453 s->connection.data.tcp.current_server = network_get_next_tcp_server(s);
456 network_log(LOG_TRACE, s, "connect_current_tcp_server");
458 cs = s->connection.data.tcp.current_server;
460 nc->autoconnect = FALSE;
461 network_log(LOG_WARNING, s, "No servers listed, not connecting");
465 network_log(LOG_INFO, s, "Connecting with %s:%s", cs->host, cs->port);
467 memset(&hints, 0, sizeof(hints));
468 hints.ai_family = PF_UNSPEC;
469 hints.ai_socktype = SOCK_STREAM;
472 hints.ai_flags = AI_ADDRCONFIG;
476 error = getaddrinfo(cs->host, cs->port, &hints, &addrinfo);
478 network_log(LOG_ERROR, s, "Unable to lookup %s:%s %s",
479 cs->host, cs->port, gai_strerror(error));
480 if (addrinfo != NULL)
481 freeaddrinfo(addrinfo);
487 for (res = addrinfo; res; res = res->ai_next) {
489 sock = socket(res->ai_family, res->ai_socktype,
495 if (cs->bind_address)
496 bindsock(s, sock, res, cs->bind_address, NULL);
497 else if (nc->type_settings.tcp.default_bind_address)
498 bindsock(s, sock, res, nc->type_settings.tcp.default_bind_address, NULL);
500 ioc = g_io_channel_unix_new(sock);
501 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
503 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
504 if (errno == EINPROGRESS) {
505 connect_finished = FALSE;
508 g_io_channel_unref(ioc);
516 size = sizeof(struct sockaddr_storage);
517 g_assert(s->connection.data.tcp.local_name == NULL);
518 g_assert(s->connection.data.tcp.remote_name == NULL);
521 network_log(LOG_ERROR, s, "Unable to connect: %s", strerror(errno));
525 s->connection.data.tcp.remote_name = g_memdup(res->ai_addr,
527 s->connection.data.tcp.local_name = g_malloc(size);
528 s->connection.data.tcp.namelen = getsockname(sock, s->connection.data.tcp.local_name, &size);
530 freeaddrinfo(addrinfo);
532 g_io_channel_set_close_on_unref(ioc, TRUE);
534 cs = s->connection.data.tcp.current_server;
537 g_io_channel_set_close_on_unref(ioc, TRUE);
538 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
540 ioc = ssl_wrap_iochannel (ioc, SSL_TYPE_CLIENT,
541 s->connection.data.tcp.current_server->host,
545 network_report_disconnect(s, "Couldn't connect via server %s:%s", cs->host, cs->port);
550 network_log(LOG_WARNING, s, "SSL enabled for %s:%s, but no SSL support loaded", cs->host, cs->port);
554 s->connection.state = NETWORK_CONNECTION_STATE_CONNECTING;
556 if (!connect_finished) {
557 s->connection.data.tcp.connect_id = g_io_add_watch(ioc,
558 G_IO_OUT|G_IO_ERR|G_IO_HUP,
559 server_finish_connect, s);
561 server_finish_connect(ioc, G_IO_OUT, s);
564 g_io_channel_unref(ioc);
569 static void reconnect(struct irc_network *server)
571 struct network_config *nc = server->private_data;
574 g_assert(server->connection.state != NETWORK_CONNECTION_STATE_RECONNECT_PENDING);
576 close_server(server);
580 if (nc->type == NETWORK_TCP)
581 server->connection.data.tcp.current_server = network_get_next_tcp_server(server);
583 if (nc->type == NETWORK_TCP ||
584 nc->type == NETWORK_IOCHANNEL ||
585 nc->type == NETWORK_PROGRAM) {
586 server->connection.state = NETWORK_CONNECTION_STATE_RECONNECT_PENDING;
587 network_log(LOG_INFO, server, "Reconnecting in %d seconds",
588 server->reconnect_interval);
589 server->reconnect_id = g_timeout_add(1000 *
590 server->reconnect_interval,
591 (GSourceFunc) delayed_connect_server, server);
593 connect_server(server);
597 static void free_tcp_names(struct irc_network *n)
599 g_free(n->connection.data.tcp.local_name);
600 g_free(n->connection.data.tcp.remote_name);
601 n->connection.data.tcp.local_name = NULL;
602 n->connection.data.tcp.remote_name = NULL;
605 static gboolean close_server(struct irc_network *n)
607 struct network_config *nc = n->private_data;
611 if (n->connection.state == NETWORK_CONNECTION_STATE_RECONNECT_PENDING) {
612 g_source_remove(n->reconnect_id);
614 n->connection.state = NETWORK_CONNECTION_STATE_NOT_CONNECTED;
617 if (n->connection.state == NETWORK_CONNECTION_STATE_CONNECTING) {
618 g_source_remove(n->connection.data.tcp.connect_id);
619 n->connection.data.tcp.connect_id = 0;
620 n->connection.state = NETWORK_CONNECTION_STATE_NOT_CONNECTED;
621 if (nc->type == NETWORK_TCP)
625 if (n->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED) {
629 network_send_args(n, "QUIT", NULL);
631 if (n->callbacks->disconnect != NULL)
632 n->callbacks->disconnect(n);
634 if (n->external_state) {
636 free_network_state(n->external_state);
637 n->external_state = NULL;
644 case NETWORK_PROGRAM:
645 case NETWORK_IOCHANNEL:
646 irc_transport_disconnect(n->connection.transport);
647 if (n->connection.data.tcp.ping_id > 0) {
648 g_source_remove(n->connection.data.tcp.ping_id);
649 n->connection.data.tcp.ping_id = 0;
652 free_irc_transport(n->connection.transport);
654 case NETWORK_VIRTUAL:
655 if (n->connection.data.virtual.ops &&
656 n->connection.data.virtual.ops->fini) {
657 n->connection.data.virtual.ops->fini(n);
660 default: g_assert_not_reached();
663 n->connection.state = NETWORK_CONNECTION_STATE_NOT_CONNECTED;
668 static pid_t piped_child(struct irc_network *s, char* const command[], int *f_in)
673 if (socketpair(PF_UNIX, SOCK_STREAM, AF_LOCAL, sock) == -1) {
674 network_log(LOG_ERROR, s, "socketpair: %s", strerror(errno));
680 fcntl(sock[0], F_SETFL, O_NONBLOCK);
685 network_log(LOG_ERROR, s, "fork: %s", strerror(errno));
697 execvp(command[0], command);
706 static gboolean server_finish_connect(GIOChannel *ioc, GIOCondition cond,
709 struct irc_network *s = data;
711 if (cond & G_IO_ERR) {
712 network_report_disconnect(s, "Error connecting: %s",
713 g_io_channel_unix_get_sock_error(ioc));
718 if (cond & G_IO_OUT) {
719 s->connection.state = NETWORK_CONNECTION_STATE_CONNECTED;
721 s->connection.data.tcp.connect_id = 0; /* Otherwise data will be queued */
722 network_set_iochannel(s, ioc);
724 s->connection.last_line_recvd = time(NULL);
725 s->connection.data.tcp.ping_id = g_timeout_add(5000,
726 (GSourceFunc) ping_server, s);
731 if (cond & G_IO_HUP) {
732 network_report_disconnect(s, "Server closed connection");
741 * Change the IO channel used to communicate with a network.
742 * @param s Network to set the IO channel for.
743 * @param ioc IO channel to use
745 gboolean network_set_iochannel(struct irc_network *s, GIOChannel *ioc)
747 GError *error = NULL;
748 struct network_config *nc = s->private_data;
749 g_assert(nc->type != NETWORK_VIRTUAL);
750 if (g_io_channel_set_encoding(ioc, NULL, &error) != G_IO_STATUS_NORMAL) {
751 network_log(LOG_ERROR, s, "Unable to change encoding: %s",
752 error?error->message:"unknown");
757 g_io_channel_set_close_on_unref(ioc, TRUE);
758 if (g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, &error) != G_IO_STATUS_NORMAL) {
759 network_log(LOG_ERROR, s, "Unable to change flags: %s",
760 error?error->message:"unknown");
766 s->connection.transport = irc_transport_new_iochannel(ioc);
768 irc_transport_set_callbacks(s->connection.transport,
772 transport_parse_buffer(s->connection.transport);
774 server_send_login(s);
779 static gboolean connect_program(struct irc_network *s)
781 struct network_config *nc = s->private_data;
789 g_assert(nc->type == NETWORK_PROGRAM);
791 cmd[0] = nc->type_settings.program_location;
793 pid = piped_child(s, cmd, &sock);
795 if (pid == -1) return FALSE;
797 ioc = g_io_channel_unix_new(sock);
798 network_set_iochannel(s, ioc);
800 g_io_channel_unref(ioc);
802 if (s->name == NULL) {
803 if (strchr(nc->type_settings.program_location, '/')) {
804 s->name = g_strdup(strrchr(nc->type_settings.program_location, '/')+1);
806 s->name = g_strdup(nc->type_settings.program_location);
813 static gboolean connect_virtual(struct irc_network *s)
815 struct irc_login_details *details;
816 struct network_config *nc = s->private_data;
818 if (nc->type_settings.virtual.ops == NULL)
821 details = s->callbacks->get_login_details(s);
823 s->external_state = network_state_init(details->nick, details->username,
826 free_login_details(details);
827 s->external_state->userdata = s;
828 s->external_state->log = state_log_helper;
829 if (s->callbacks->state_set)
830 s->callbacks->state_set(s);
831 s->connection.state = NETWORK_CONNECTION_STATE_MOTD_RECVD;
833 if (nc->type_settings.virtual.ops->init)
834 return nc->type_settings.virtual.ops->init(s);
839 static gboolean connect_server(struct irc_network *s)
841 struct network_config *nc = s->private_data;
848 return connect_current_tcp_server(s);
850 case NETWORK_PROGRAM:
851 return connect_program(s);
853 case NETWORK_VIRTUAL:
854 return connect_virtual(s);
855 default: g_assert_not_reached();
861 static gboolean delayed_connect_server(struct irc_network *s)
865 return (s->connection.state == NETWORK_CONNECTION_STATE_RECONNECT_PENDING);
868 struct irc_network *irc_network_new(const struct irc_network_callbacks *callbacks, void *private_data)
870 struct irc_network *s;
872 s = g_new0(struct irc_network, 1);
873 s->callbacks = callbacks;
875 s->private_data = private_data;
876 s->reconnect_interval = ((struct network_config *)private_data)->reconnect_interval == -1?DEFAULT_RECONNECT_INTERVAL:((struct network_config *)private_data)->reconnect_interval;
877 s->info = network_info_init();
878 s->name = g_strdup(((struct network_config *)private_data)->name);
879 s->info->ircd = g_strdup("ctrlproxy");
880 s->info->forced_nick_changes = TRUE; /* Forced nick changes are done by ctrlproxy */
883 s->ssl_credentials = ssl_get_client_credentials(NULL);
890 * Connect to a network, returns TRUE if connection was successful
891 * (or startup of connection was successful)
893 * @param s Network to connect to
895 gboolean connect_network(struct irc_network *s)
898 g_assert(s->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED ||
899 s->connection.state == NETWORK_CONNECTION_STATE_RECONNECT_PENDING);
901 return connect_server(s);
904 static void free_network(struct irc_network *s)
906 struct network_config *nc;
908 nc = s->private_data;
910 free_network_info(s->info);
911 if (nc->type == NETWORK_TCP)
912 g_free(s->connection.data.tcp.last_disconnect_reason);
915 ssl_free_client_credentials(s->ssl_credentials);
924 * Disconnect from a network. The network will still be kept in memory,
925 * but all socket connections associated with it will be dropped.
927 * @param s Network to disconnect from
928 * @return Whether disconnecting succeeded.
930 gboolean disconnect_network(struct irc_network *s)
933 if (s->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED) {
937 network_log(LOG_INFO, s, "Disconnecting");
938 return close_server(s);
942 * Register a new virtual network type.
944 * @param ops Callback functions for the virtual network type.
946 void register_virtual_network(struct virtual_network_ops *ops)
948 if (virtual_network_ops == NULL)
949 virtual_network_ops = g_hash_table_new(g_str_hash, g_str_equal);
951 g_hash_table_insert(virtual_network_ops, ops->name, ops);
954 struct virtual_network_ops *find_virtual_network(const char *name)
956 if (virtual_network_ops == NULL)
958 return g_hash_table_lookup(virtual_network_ops, name);
962 * Autoconnect to all the networks in a list.
964 * @param networks GList with networks
967 gboolean autoconnect_networks(GList *networks)
970 for (gl = networks; gl; gl = gl->next)
972 struct irc_network *n = gl->data;
973 struct network_config *nc = n->private_data;
984 * Find a network by name.
986 * @param networks GList with possible networks
987 * @param name Name of the network to search for.
988 * @return first network found or NULL
990 struct irc_network *find_network(GList *networks, const char *name)
993 for (gl = networks; gl; gl = gl->next) {
994 struct irc_network *n = gl->data;
995 if (n->name && !g_strcasecmp(n->name, name))
1003 * Switch to the next server listed for a network.
1007 void irc_network_select_next_server(struct irc_network *n)
1009 struct network_config *nc = n->private_data;
1014 if (nc->type != NETWORK_TCP)
1017 network_log(LOG_INFO, n, "Trying next server");
1018 n->connection.data.tcp.current_server = network_get_next_tcp_server(n);
1022 * Generate 005-response string to send to client connected to a network.
1024 * @param n Network to generate for
1025 * @return An 005 string, newly allocated
1027 char *network_generate_feature_string(struct irc_network *n)
1031 return network_info_string(n->info);
1035 * Increase the reference count for a network
1037 struct irc_network *network_ref(struct irc_network *n)
1044 void irc_network_unref(struct irc_network *n)
1049 if (n->references == 0)
1053 void network_log(enum log_level l, const struct irc_network *s,
1054 const char *fmt, ...)
1059 if (s->callbacks->log == NULL)
1066 ret = g_strdup_vprintf(fmt, ap);
1069 s->callbacks->log(l, s, ret);
1074 void unregister_virtual_networks(void)
1076 if (virtual_network_ops != NULL)
1077 g_hash_table_destroy(virtual_network_ops);
1078 virtual_network_ops = NULL;