Remove duplicate hook call.
[jelmer/ctrlproxy.git] / libirc / connection.c
1 /*
2         ctrlproxy: A modular IRC proxy
3         (c) 2002-2007 Jelmer Vernooij <jelmer@nl.linux.org>
4
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.
9
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.
14
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.
18 */
19
20 #include "internals.h"
21 #include "irc.h"
22 #include "ssl.h"
23 #include "transport.h"
24 #ifndef AF_LOCAL
25 #define AF_LOCAL AF_UNIX
26 #endif
27
28 static GHashTable *virtual_network_ops = NULL;
29
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, 
35                                                                   void *data);
36
37 static void state_log_helper(enum log_level l, void *userdata, const char *msg)
38 {
39         network_log(l, (const struct irc_network *)userdata, "%s", msg);
40 }
41
42 static void server_send_login (struct irc_network *s) 
43 {
44         struct irc_login_details *login_details = s->callbacks->get_login_details(s);
45         g_assert(s);
46
47         s->connection.state = NETWORK_CONNECTION_STATE_LOGIN_SENT;
48
49         network_log(LOG_TRACE, s, "Sending login details");
50
51         s->external_state = network_state_init(login_details->nick, login_details->username, 
52                                                                   get_my_hostname());
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);
57
58         if (login_details->password != NULL) {
59                 network_send_args(s, "PASS", login_details->password, NULL);
60         }
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);
69
70         free_login_details(login_details);
71 }
72
73 /**
74  * Change the character set used to communicate with the server.
75  *
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
79  */
80 gboolean network_set_charset(struct irc_network *n, const char *name)
81 {
82         if (!transport_set_charset(n->connection.transport, name)) {
83                 network_log(LOG_WARNING, n, "Unable to find charset `%s'", name);
84                 return FALSE;
85         }
86         return TRUE;
87 }
88
89 static void network_report_disconnect(struct irc_network *n, const char *fmt, ...)
90 {
91         va_list ap;
92         char *tmp;
93         va_start(ap, fmt);
94         tmp = g_strdup_vprintf(fmt, ap);
95         va_end(ap);
96
97         g_free(n->connection.data.tcp.last_disconnect_reason);
98         n->connection.data.tcp.last_disconnect_reason = tmp;
99
100         network_log(LOG_WARNING, n, "%s", tmp);
101 }
102
103 static void on_transport_disconnect(struct irc_transport *transport)
104 {
105
106 }
107
108 static void on_transport_hup(struct irc_transport *transport)
109 {
110         struct irc_network *network = transport->userdata;
111
112         network_report_disconnect(network, "Hangup from server, scheduling reconnect");
113         reconnect(network);
114 }
115
116 static gboolean on_transport_error(struct irc_transport *transport, const char *error_msg)
117 {
118         struct irc_network *network = transport->userdata;
119
120         network_report_disconnect(network, 
121                                                                   "Error from server: %s, scheduling reconnect", error_msg);
122         reconnect(network);
123
124         return FALSE;
125 }
126
127
128 static void on_transport_charset_error(struct irc_transport *transport, const char *error_msg)
129 {
130         struct irc_network *network = transport->userdata;
131
132         network_log(LOG_WARNING, network, "Error while sending line with charset: %s", 
133                                 error_msg);
134 }
135
136 static gboolean on_transport_recv(struct irc_transport *transport,
137                                                           const struct irc_line *line)
138 {
139         struct irc_network *server = transport->userdata;
140         return server->callbacks->process_from_server(server, line);
141 }
142
143 static void on_transport_log(struct irc_transport *transport, const struct irc_line *l, const GError *error)
144 {
145         struct irc_network *network = transport->userdata;
146
147         network_log(LOG_WARNING, network, "Error while sending line '%s': %s", 
148                                 l->args[0], error->message);
149 }
150
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,
158 };
159
160 static struct tcp_server_config *network_get_next_tcp_server(struct irc_network *n)
161 {
162         struct network_config *nc = n->private_data;
163         GList *cur;
164         
165         g_assert(n);
166         g_assert(nc);
167         cur = g_list_find(nc->type_settings.tcp.servers, n->connection.data.tcp.current_server);
168
169         /* Get next available server */
170         if (cur != NULL && cur->next != NULL) 
171                 cur = cur->next; 
172         else 
173                 cur = nc->type_settings.tcp.servers;
174
175         if (cur != NULL) 
176                 return cur->data; 
177
178         return NULL;
179 }
180
181 static gboolean network_send_line_direct(struct irc_network *s, struct irc_client *c, 
182                                                                                  const struct irc_line *ol)
183 {
184         struct irc_line nl, *l;
185         struct network_config *nc = s->private_data;
186
187         g_assert(nc != NULL);
188
189         g_assert(nc->type == NETWORK_TCP ||
190                  nc->type == NETWORK_PROGRAM ||
191                  nc->type == NETWORK_IOCHANNEL ||
192                  nc->type == NETWORK_VIRTUAL);
193
194         l = &nl;
195         memcpy(l, ol, sizeof(struct irc_line));
196         nl.origin = NULL;
197
198         /* origin lines should never be sent to the server */
199         g_assert(l->origin == NULL);
200
201         if (nc->type == NETWORK_VIRTUAL) {
202                 if (s->connection.data.virtual.ops == NULL) 
203                         return FALSE;
204                 return s->connection.data.virtual.ops->to_server(s, c, l);
205         } else {
206                 if (s->connection.transport == NULL)
207                         return FALSE;
208                 return transport_send_line(s->connection.transport, l);
209         }
210 }
211
212 /**
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
218  */
219 gboolean network_send_line(struct irc_network *s, struct irc_client *c, 
220                                                    const struct irc_line *ol, gboolean is_private)
221 {
222         struct irc_line l;
223         char *tmp = NULL;
224         struct irc_line *lc;
225
226         g_assert(ol);
227         g_assert(s);
228         l = *ol;
229
230         if (l.origin == NULL && s->external_state != NULL) {
231                 tmp = l.origin = g_strdup(s->external_state->me.hostmask);
232         }
233
234         if (l.origin != NULL) {
235                 if (!run_server_filter(s, &l, TO_SERVER)) {
236                         g_free(tmp);
237                         return TRUE;
238                 }
239
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);
243         }
244
245         g_assert(l.args[0] != NULL);
246
247         /* Also write this message to all other clients currently connected */
248         if (!is_private && 
249            (!g_strcasecmp(l.args[0], "PRIVMSG") || 
250                 !g_strcasecmp(l.args[0], "NOTICE"))) {
251                 g_assert(l.origin);
252                 if (s->global->config->report_time == REPORT_TIME_ALWAYS)
253                         line_prefix_time(&l, time(NULL)+s->global->config->report_time_offset);
254
255                 clients_send(s->clients, &l, c);
256         }
257
258         g_free(tmp);
259
260         log_network_line(s, ol, FALSE);
261
262         redirect_record(&s->queries, s, c, ol);
263
264         return network_send_line_direct(s, c, ol);
265 }
266
267 /**
268  * Indicate that a response is received by a virtual network.
269  *
270  * @param n Network to receive data
271  * @param num Number of the response to receive
272  */
273 gboolean virtual_network_recv_response(struct irc_network *n, int num, ...) 
274 {
275         va_list ap;
276         struct irc_line *l;
277         gboolean ret;
278         struct network_config *nc;
279
280         g_assert(n);
281
282         nc = n->private_data;
283
284         g_assert(nc->type == NETWORK_VIRTUAL);
285
286         va_start(ap, num);
287         l = virc_parse_line(n->name, ap);
288         va_end(ap);
289
290         l->args = g_realloc(l->args, sizeof(char *) * (l->argc+4));
291         memmove(&l->args[2], &l->args[0], l->argc * sizeof(char *));
292
293         l->args[0] = g_strdup_printf("%03d", num);
294
295         if (n->external_state != NULL && n->external_state->me.nick != NULL) 
296                 l->args[1] = g_strdup(n->external_state->me.nick);
297         else 
298                 l->args[1] = g_strdup("*");
299
300         l->argc+=2;
301         l->args[l->argc] = NULL;
302
303         ret = virtual_network_recv_line(n, l);
304
305         free_line(l);
306
307         return ret;
308 }
309
310 /**
311  * Indicate that a line is received by a virtual network.
312  *
313  * @param s Network to send to.
314  * @param l Line to receive.
315  */
316 gboolean virtual_network_recv_line(struct irc_network *s, struct irc_line *l)
317 {
318         g_assert(s != NULL);
319         g_assert(l != NULL);
320
321         if (l->origin == NULL) 
322                 l->origin = g_strdup(get_my_hostname());
323
324         return s->callbacks->process_from_server(s, l);
325 }
326
327 /**
328  * Indicate that a line has been received.
329  *
330  * @param s Network to use.
331  * @param origin Origin to make the data originate from
332  */
333 gboolean virtual_network_recv_args(struct irc_network *s, const char *origin, ...)
334 {
335         va_list ap;
336         struct irc_line *l;
337         gboolean ret;
338
339         g_assert(s);
340
341         va_start(ap, origin);
342         l = virc_parse_line(origin, ap);
343         va_end(ap);
344
345         ret = virtual_network_recv_line(s, l);
346
347         free_line(l);
348
349         return ret;
350 }
351
352 /**
353  * Send a new line to the network.
354  *
355  * @param s Network
356  * @param ... Arguments terminated by NULL
357  */
358 gboolean network_send_args(struct irc_network *s, ...)
359 {
360         va_list ap;
361         struct irc_line *l;
362         gboolean ret;
363
364         g_assert(s);
365
366         va_start(ap, s);
367         l = virc_parse_line(NULL, ap);
368         va_end(ap);
369
370         ret = network_send_line(s, NULL, l, TRUE);
371
372         free_line(l);
373
374         return ret;
375 }
376
377 static gboolean bindsock(struct irc_network *s,
378                                                  int sock, struct addrinfo *res, 
379                                                  const char *address,
380                                                  const char *service)
381 {
382         struct addrinfo hints_bind;
383         int error;
384         struct addrinfo *res_bind, *addrinfo_bind;
385
386         memset(&hints_bind, 0, sizeof(hints_bind));
387         hints_bind.ai_family = res->ai_family;
388
389 #ifdef AI_ADDRCONFIG
390         hints_bind.ai_flags = AI_ADDRCONFIG;
391 #endif
392
393         hints_bind.ai_socktype = res->ai_socktype;
394         hints_bind.ai_protocol = res->ai_protocol;
395
396         error = getaddrinfo(address, service, &hints_bind, &addrinfo_bind);
397         if (error) {
398                 network_log(LOG_ERROR, s, 
399                                         "Unable to lookup %s:%s %s", address, service, 
400                                         gai_strerror(error));
401                 return FALSE;
402         } 
403
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));
409                 } else 
410                         break;
411         }
412         freeaddrinfo(addrinfo_bind);
413
414         return (res_bind != NULL);
415 }
416
417 /**
418  * Ping the network.
419  *
420  * @param server network to ping
421  * @param ping_source GSource id of the ping event
422  */
423 static void ping_server(struct irc_network *server, gboolean ping_source)
424 {
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)", 
428                                                                   silent_time);
429                 reconnect(server);
430         } else if (silent_time > MIN_SILENT_TIME) {
431                 network_send_args(server, "PING", "ctrlproxy", NULL);
432         }
433 }
434
435 static gboolean connect_current_tcp_server(struct irc_network *s) 
436 {
437         struct addrinfo *res;
438         int sock = -1;
439         struct tcp_server_config *cs;
440         GIOChannel *ioc = NULL;
441         struct addrinfo hints;
442         struct addrinfo *addrinfo = NULL;
443         int error;
444         socklen_t size;
445         gboolean connect_finished = TRUE;
446         struct network_config *nc;
447
448         g_assert(s != NULL);
449         
450         nc = s->private_data;
451
452         if (!s->connection.data.tcp.current_server) {
453                 s->connection.data.tcp.current_server = network_get_next_tcp_server(s);
454         }
455
456         network_log(LOG_TRACE, s, "connect_current_tcp_server");
457         
458         cs = s->connection.data.tcp.current_server;
459         if (cs == NULL) {
460                 nc->autoconnect = FALSE;
461                 network_log(LOG_WARNING, s, "No servers listed, not connecting");
462                 return FALSE;
463         }
464
465         network_log(LOG_INFO, s, "Connecting with %s:%s", cs->host, cs->port);
466
467         memset(&hints, 0, sizeof(hints));
468         hints.ai_family = PF_UNSPEC;
469         hints.ai_socktype = SOCK_STREAM;
470
471 #ifdef AI_ADDRCONFIG
472         hints.ai_flags = AI_ADDRCONFIG;
473 #endif
474
475         /* Lookup */
476         error = getaddrinfo(cs->host, cs->port, &hints, &addrinfo);
477         if (error) {
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);
482                 return FALSE;
483         }
484
485         /* Connect */
486
487         for (res = addrinfo; res; res = res->ai_next) {
488
489                 sock = socket(res->ai_family, res->ai_socktype,
490                                           res->ai_protocol);
491                 if (sock < 0) {
492                         continue;
493                 }
494
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);
499
500                 ioc = g_io_channel_unix_new(sock);
501                 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
502
503                 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
504                         if (errno == EINPROGRESS) {
505                                 connect_finished = FALSE;
506                                 break;
507                         }
508                         g_io_channel_unref(ioc);
509                         ioc = NULL;
510                         continue;
511                 }
512
513                 break; 
514         }
515
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);
519
520         if (!res || !ioc) {
521                 network_log(LOG_ERROR, s, "Unable to connect: %s", strerror(errno));
522                 return FALSE;
523         }
524
525         s->connection.data.tcp.remote_name = g_memdup(res->ai_addr, 
526                                                                                                   res->ai_addrlen);
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);
529
530         freeaddrinfo(addrinfo);
531
532         g_io_channel_set_close_on_unref(ioc, TRUE);
533
534         cs = s->connection.data.tcp.current_server;
535         if (cs->ssl) {
536 #ifdef HAVE_GNUTLS
537                 g_io_channel_set_close_on_unref(ioc, TRUE);
538                 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
539
540                 ioc = ssl_wrap_iochannel (ioc, SSL_TYPE_CLIENT, 
541                                                                  s->connection.data.tcp.current_server->host,
542                                                                  s->ssl_credentials
543                                                                  );
544                 if (!ioc) {
545                         network_report_disconnect(s, "Couldn't connect via server %s:%s", cs->host, cs->port);
546                         reconnect(s);
547                         return FALSE;
548                 }
549 #else
550                 network_log(LOG_WARNING, s, "SSL enabled for %s:%s, but no SSL support loaded", cs->host, cs->port);
551 #endif
552         }
553
554         s->connection.state = NETWORK_CONNECTION_STATE_CONNECTING;
555
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);
560         } else {
561                 server_finish_connect(ioc, G_IO_OUT, s);
562         }
563
564         g_io_channel_unref(ioc);
565
566         return TRUE;
567 }
568
569 static void reconnect(struct irc_network *server)
570 {
571         struct network_config *nc = server->private_data;
572         g_assert(server);
573
574         g_assert(server->connection.state != NETWORK_CONNECTION_STATE_RECONNECT_PENDING);
575
576         close_server(server);
577
578         g_assert(nc);
579
580         if (nc->type == NETWORK_TCP)
581                 server->connection.data.tcp.current_server = network_get_next_tcp_server(server);
582
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);
592         } else {
593                 connect_server(server); 
594         }
595 }
596
597 static void free_tcp_names(struct irc_network *n)
598 {
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;
603 }
604
605 static gboolean close_server(struct irc_network *n) 
606 {
607         struct network_config *nc = n->private_data;
608
609         g_assert(n);
610
611         if (n->connection.state == NETWORK_CONNECTION_STATE_RECONNECT_PENDING) {
612                 g_source_remove(n->reconnect_id);
613                 n->reconnect_id = 0;
614                 n->connection.state = NETWORK_CONNECTION_STATE_NOT_CONNECTED;
615         }
616
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)
622                         free_tcp_names(n);
623         }
624
625         if (n->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED) {
626                 return FALSE;
627         } 
628
629         network_send_args(n, "QUIT", NULL);
630
631         if (n->callbacks->disconnect != NULL)
632                 n->callbacks->disconnect(n);
633
634         if (n->external_state) {
635                 n->linestack = NULL;
636                 free_network_state(n->external_state); 
637                 n->external_state = NULL;
638         }
639
640         g_assert(nc);
641
642         switch (nc->type) {
643         case NETWORK_TCP: 
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;
650                 }
651                 free_tcp_names(n);
652                 free_irc_transport(n->connection.transport);
653                 break;
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);
658                 }
659                 break;
660                 default: g_assert_not_reached();
661         }
662
663         n->connection.state = NETWORK_CONNECTION_STATE_NOT_CONNECTED;
664
665         return TRUE;
666 }
667
668 static pid_t piped_child(struct irc_network *s, char* const command[], int *f_in)
669 {
670         pid_t pid;
671         int sock[2];
672
673         if (socketpair(PF_UNIX, SOCK_STREAM, AF_LOCAL, sock) == -1) {
674                 network_log(LOG_ERROR, s, "socketpair: %s", strerror(errno));
675                 return -1;
676         }
677
678         *f_in = sock[0];
679
680         fcntl(sock[0], F_SETFL, O_NONBLOCK);
681
682         pid = fork();
683
684         if (pid == -1) {
685                 network_log(LOG_ERROR, s, "fork: %s", strerror(errno));
686                 return -1;
687         }
688
689         if (pid == 0) {
690                 close(0);
691                 close(1);
692                 close(2);
693                 close(sock[0]);
694
695                 dup2(sock[1], 0);
696                 dup2(sock[1], 1);
697                 execvp(command[0], command);
698                 exit(-1);
699         }
700
701         close(sock[1]);
702
703         return pid;
704 }
705
706 static gboolean server_finish_connect(GIOChannel *ioc, GIOCondition cond, 
707                                                                   void *data)
708 {
709         struct irc_network *s = data;
710
711         if (cond & G_IO_ERR) {
712                 network_report_disconnect(s, "Error connecting: %s", 
713                                                                   g_io_channel_unix_get_sock_error(ioc));
714                 reconnect(s);
715                 return FALSE;
716         }
717
718         if (cond & G_IO_OUT) {
719                 s->connection.state = NETWORK_CONNECTION_STATE_CONNECTED;
720
721                 s->connection.data.tcp.connect_id = 0; /* Otherwise data will be queued */
722                 network_set_iochannel(s, ioc);
723
724                 s->connection.last_line_recvd = time(NULL);
725                 s->connection.data.tcp.ping_id = g_timeout_add(5000, 
726                                                                            (GSourceFunc) ping_server, s);
727
728                 return FALSE;
729         }
730
731         if (cond & G_IO_HUP) {
732                 network_report_disconnect(s, "Server closed connection");
733                 reconnect(s);
734                 return FALSE;
735         }
736
737         return FALSE;
738 }
739
740 /**
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
744  */
745 gboolean network_set_iochannel(struct irc_network *s, GIOChannel *ioc)
746 {
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");
753                 if (error != NULL)
754                         g_error_free(error);
755                 return FALSE;
756         }
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");
761                 if (error != NULL)
762                         g_error_free(error);
763                 return FALSE;
764         }
765
766         s->connection.transport = irc_transport_new_iochannel(ioc);
767
768         irc_transport_set_callbacks(s->connection.transport, 
769                                                                 &network_callbacks, 
770                                                                 s);
771
772         transport_parse_buffer(s->connection.transport);
773
774         server_send_login(s);
775
776         return TRUE;
777 }
778
779 static gboolean connect_program(struct irc_network *s)
780 {
781         struct network_config *nc = s->private_data;
782         int sock;
783         char *cmd[2];
784         pid_t pid;
785         GIOChannel *ioc;
786         
787         g_assert(s);
788         g_assert(nc);
789         g_assert(nc->type == NETWORK_PROGRAM);
790         
791         cmd[0] = nc->type_settings.program_location;
792         cmd[1] = NULL;
793         pid = piped_child(s, cmd, &sock);
794
795         if (pid == -1) return FALSE;
796
797         ioc = g_io_channel_unix_new(sock);
798         network_set_iochannel(s, ioc);
799
800         g_io_channel_unref(ioc);
801
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);
805                 } else {
806                         s->name = g_strdup(nc->type_settings.program_location);
807                 }
808         }
809
810         return TRUE;
811 }
812
813 static gboolean connect_virtual(struct irc_network *s)
814 {
815         struct irc_login_details *details;
816         struct network_config *nc = s->private_data;
817
818         if (nc->type_settings.virtual.ops == NULL)
819                 return FALSE;
820
821         details = s->callbacks->get_login_details(s);
822
823         s->external_state = network_state_init(details->nick, details->username, 
824                                                                   get_my_hostname());
825
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;
832
833         if (nc->type_settings.virtual.ops->init)
834                 return nc->type_settings.virtual.ops->init(s);
835
836         return TRUE;
837 }
838
839 static gboolean connect_server(struct irc_network *s)
840 {
841         struct network_config *nc = s->private_data;
842
843         g_assert(s);
844         g_assert(nc);
845
846         switch (nc->type) {
847         case NETWORK_TCP:
848                 return connect_current_tcp_server(s);
849
850         case NETWORK_PROGRAM:
851                 return connect_program(s);
852
853         case NETWORK_VIRTUAL:
854                 return connect_virtual(s);
855         default: g_assert_not_reached();
856         }
857
858         return TRUE;
859 }
860
861 static gboolean delayed_connect_server(struct irc_network *s)
862 {
863         g_assert(s);
864         connect_server(s);
865         return (s->connection.state == NETWORK_CONNECTION_STATE_RECONNECT_PENDING);
866 }
867
868 struct irc_network *irc_network_new(const struct irc_network_callbacks *callbacks, void *private_data)
869 {
870         struct irc_network *s;
871
872         s = g_new0(struct irc_network, 1);
873         s->callbacks = callbacks;
874         s->references = 1;
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 */
881
882 #ifdef HAVE_GNUTLS
883         s->ssl_credentials = ssl_get_client_credentials(NULL);
884 #endif
885
886         return s;
887 }
888
889 /**
890  * Connect to a network, returns TRUE if connection was successful 
891  * (or startup of connection was successful) 
892  *
893  * @param s Network to connect to
894  */
895 gboolean connect_network(struct irc_network *s) 
896 {
897         g_assert(s);
898         g_assert(s->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED ||
899                          s->connection.state == NETWORK_CONNECTION_STATE_RECONNECT_PENDING);
900
901         return connect_server(s);
902 }
903
904 static void free_network(struct irc_network *s)
905 {
906         struct network_config *nc;
907
908         nc = s->private_data;
909
910         free_network_info(s->info);
911         if (nc->type == NETWORK_TCP)
912                 g_free(s->connection.data.tcp.last_disconnect_reason);
913
914 #ifdef HAVE_GNUTLS
915         ssl_free_client_credentials(s->ssl_credentials);
916 #endif
917
918         g_free(s->name);
919         g_free(s);
920 }
921
922
923 /** 
924  * Disconnect from a network. The network will still be kept in memory, 
925  * but all socket connections associated with it will be dropped.
926  *
927  * @param s Network to disconnect from
928  * @return Whether disconnecting succeeded.
929  */
930 gboolean disconnect_network(struct irc_network *s)
931 {
932         g_assert(s);
933         if (s->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED) {
934                 return FALSE;
935         }
936
937         network_log(LOG_INFO, s, "Disconnecting");
938         return close_server(s);
939 }
940
941 /**
942  * Register a new virtual network type.
943  *
944  * @param ops Callback functions for the virtual network type.
945  */
946 void register_virtual_network(struct virtual_network_ops *ops)
947 {
948         if (virtual_network_ops == NULL)
949                 virtual_network_ops = g_hash_table_new(g_str_hash, g_str_equal);
950         g_assert(ops);
951         g_hash_table_insert(virtual_network_ops, ops->name, ops);
952 }
953
954 struct virtual_network_ops *find_virtual_network(const char *name)
955 {
956         if (virtual_network_ops == NULL)
957                 return NULL;
958         return g_hash_table_lookup(virtual_network_ops, name);
959 }
960
961 /**
962  * Autoconnect to all the networks in a list.
963  *
964  * @param networks GList with networks
965  * @return TRUE
966  */
967 gboolean autoconnect_networks(GList *networks)
968 {
969         GList *gl;
970         for (gl = networks; gl; gl = gl->next)
971         {
972                 struct irc_network *n = gl->data;
973                 struct network_config *nc = n->private_data;
974                 g_assert(n);
975                 g_assert(nc);
976                 if (nc->autoconnect)
977                         connect_network(n);
978         }
979
980         return TRUE;
981 }
982
983 /**
984  * Find a network by name.
985  *
986  * @param networks GList with possible networks
987  * @param name Name of the network to search for.
988  * @return first network found or NULL
989  */
990 struct irc_network *find_network(GList *networks, const char *name)
991 {
992         GList *gl;
993         for (gl = networks; gl; gl = gl->next) {
994                 struct irc_network *n = gl->data;
995                 if (n->name && !g_strcasecmp(n->name, name)) 
996                         return n;
997         }
998
999         return NULL;
1000 }
1001
1002 /**
1003  * Switch to the next server listed for a network.
1004  *
1005  * @param n Network
1006  */
1007 void irc_network_select_next_server(struct irc_network *n)
1008 {
1009         struct network_config *nc = n->private_data;
1010
1011         g_assert(n);
1012         g_assert(nc);
1013
1014         if (nc->type != NETWORK_TCP) 
1015                 return;
1016
1017         network_log(LOG_INFO, n, "Trying next server");
1018         n->connection.data.tcp.current_server = network_get_next_tcp_server(n);
1019 }
1020
1021 /**
1022  * Generate 005-response string to send to client connected to a network.
1023  *
1024  * @param n Network to generate for
1025  * @return An 005 string, newly allocated
1026  */
1027 char *network_generate_feature_string(struct irc_network *n)
1028 {
1029         g_assert(n);
1030
1031         return network_info_string(n->info);
1032 }
1033
1034 /**
1035  * Increase the reference count for a network
1036  */
1037 struct irc_network *network_ref(struct irc_network *n)
1038 {
1039         if (n != NULL)
1040                 n->references++;
1041         return n;
1042 }
1043
1044 void irc_network_unref(struct irc_network *n)
1045 {
1046         if (n == NULL)
1047                 return;
1048         n->references--;
1049         if (n->references == 0) 
1050                 free_network(n);
1051 }
1052
1053 void network_log(enum log_level l, const struct irc_network *s, 
1054                                  const char *fmt, ...)
1055 {
1056         char *ret;
1057         va_list ap;
1058
1059         if (s->callbacks->log == NULL)
1060                 return;
1061
1062         g_assert(s);
1063         g_assert(fmt);
1064
1065         va_start(ap, fmt);
1066         ret = g_strdup_vprintf(fmt, ap);
1067         va_end(ap);
1068
1069         s->callbacks->log(l, s, ret);
1070
1071         g_free(ret);
1072 }
1073
1074 void unregister_virtual_networks(void)
1075 {
1076         if (virtual_network_ops != NULL)
1077                 g_hash_table_destroy(virtual_network_ops);
1078         virtual_network_ops = NULL;
1079 }