Move special line-handling to src/
[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  */
218 gboolean network_send_line(struct irc_network *s, struct irc_client *c, 
219                                                    const struct irc_line *ol)
220 {
221         struct irc_line l;
222
223         g_assert(ol);
224         g_assert(s);
225         l = *ol;
226
227         if (l.origin == NULL && s->external_state != NULL) {
228                 l.origin = s->external_state->me.hostmask;
229         }
230
231         if (l.origin != NULL) {
232                 if (!s->callbacks->process_to_server(s, &l)) {
233                         return TRUE;
234                 }
235         }
236
237         return network_send_line_direct(s, c, ol);
238 }
239
240 /**
241  * Indicate that a response is received by a virtual network.
242  *
243  * @param n Network to receive data
244  * @param num Number of the response to receive
245  */
246 gboolean virtual_network_recv_response(struct irc_network *n, int num, ...) 
247 {
248         va_list ap;
249         struct irc_line *l;
250         gboolean ret;
251         struct network_config *nc;
252
253         g_assert(n);
254
255         nc = n->private_data;
256
257         g_assert(nc->type == NETWORK_VIRTUAL);
258
259         va_start(ap, num);
260         l = virc_parse_line(n->name, ap);
261         va_end(ap);
262
263         l->args = g_realloc(l->args, sizeof(char *) * (l->argc+4));
264         memmove(&l->args[2], &l->args[0], l->argc * sizeof(char *));
265
266         l->args[0] = g_strdup_printf("%03d", num);
267
268         if (n->external_state != NULL && n->external_state->me.nick != NULL) 
269                 l->args[1] = g_strdup(n->external_state->me.nick);
270         else 
271                 l->args[1] = g_strdup("*");
272
273         l->argc+=2;
274         l->args[l->argc] = NULL;
275
276         ret = virtual_network_recv_line(n, l);
277
278         free_line(l);
279
280         return ret;
281 }
282
283 /**
284  * Indicate that a line is received by a virtual network.
285  *
286  * @param s Network to send to.
287  * @param l Line to receive.
288  */
289 gboolean virtual_network_recv_line(struct irc_network *s, struct irc_line *l)
290 {
291         g_assert(s != NULL);
292         g_assert(l != NULL);
293
294         if (l->origin == NULL) 
295                 l->origin = g_strdup(get_my_hostname());
296
297         return s->callbacks->process_from_server(s, l);
298 }
299
300 /**
301  * Indicate that a line has been received.
302  *
303  * @param s Network to use.
304  * @param origin Origin to make the data originate from
305  */
306 gboolean virtual_network_recv_args(struct irc_network *s, const char *origin, ...)
307 {
308         va_list ap;
309         struct irc_line *l;
310         gboolean ret;
311
312         g_assert(s);
313
314         va_start(ap, origin);
315         l = virc_parse_line(origin, ap);
316         va_end(ap);
317
318         ret = virtual_network_recv_line(s, l);
319
320         free_line(l);
321
322         return ret;
323 }
324
325 /**
326  * Send a new line to the network.
327  *
328  * @param s Network
329  * @param ... Arguments terminated by NULL
330  */
331 gboolean network_send_args(struct irc_network *s, ...)
332 {
333         va_list ap;
334         struct irc_line *l;
335         gboolean ret;
336
337         g_assert(s);
338
339         va_start(ap, s);
340         l = virc_parse_line(NULL, ap);
341         va_end(ap);
342
343         ret = network_send_line(s, NULL, l);
344
345         free_line(l);
346
347         return ret;
348 }
349
350 static gboolean bindsock(struct irc_network *s,
351                                                  int sock, struct addrinfo *res, 
352                                                  const char *address,
353                                                  const char *service)
354 {
355         struct addrinfo hints_bind;
356         int error;
357         struct addrinfo *res_bind, *addrinfo_bind;
358
359         memset(&hints_bind, 0, sizeof(hints_bind));
360         hints_bind.ai_family = res->ai_family;
361
362 #ifdef AI_ADDRCONFIG
363         hints_bind.ai_flags = AI_ADDRCONFIG;
364 #endif
365
366         hints_bind.ai_socktype = res->ai_socktype;
367         hints_bind.ai_protocol = res->ai_protocol;
368
369         error = getaddrinfo(address, service, &hints_bind, &addrinfo_bind);
370         if (error) {
371                 network_log(LOG_ERROR, s, 
372                                         "Unable to lookup %s:%s %s", address, service, 
373                                         gai_strerror(error));
374                 return FALSE;
375         } 
376
377         for (res_bind = addrinfo_bind; 
378                  res_bind; res_bind = res_bind->ai_next) {
379                 if (bind(sock, res_bind->ai_addr, res_bind->ai_addrlen) < 0) {
380                         network_log(LOG_ERROR, s, "Unable to bind to %s:%s %s", 
381                                                 address, service, strerror(errno));
382                 } else 
383                         break;
384         }
385         freeaddrinfo(addrinfo_bind);
386
387         return (res_bind != NULL);
388 }
389
390 /**
391  * Ping the network.
392  *
393  * @param server network to ping
394  * @param ping_source GSource id of the ping event
395  */
396 static void ping_server(struct irc_network *server, gboolean ping_source)
397 {
398         gint silent_time = time(NULL) - server->connection.last_line_recvd;
399         if (silent_time > MAX_SILENT_TIME) {
400                 network_report_disconnect(server, "Ping timeout (%d seconds)", 
401                                                                   silent_time);
402                 reconnect(server);
403         } else if (silent_time > MIN_SILENT_TIME) {
404                 network_send_args(server, "PING", "ctrlproxy", NULL);
405         }
406 }
407
408 static gboolean connect_current_tcp_server(struct irc_network *s) 
409 {
410         struct addrinfo *res;
411         int sock = -1;
412         struct tcp_server_config *cs;
413         GIOChannel *ioc = NULL;
414         struct addrinfo hints;
415         struct addrinfo *addrinfo = NULL;
416         int error;
417         socklen_t size;
418         gboolean connect_finished = TRUE;
419         struct network_config *nc;
420
421         g_assert(s != NULL);
422         
423         nc = s->private_data;
424
425         if (!s->connection.data.tcp.current_server) {
426                 s->connection.data.tcp.current_server = network_get_next_tcp_server(s);
427         }
428
429         network_log(LOG_TRACE, s, "connect_current_tcp_server");
430         
431         cs = s->connection.data.tcp.current_server;
432         if (cs == NULL) {
433                 nc->autoconnect = FALSE;
434                 network_log(LOG_WARNING, s, "No servers listed, not connecting");
435                 return FALSE;
436         }
437
438         network_log(LOG_INFO, s, "Connecting with %s:%s", cs->host, cs->port);
439
440         memset(&hints, 0, sizeof(hints));
441         hints.ai_family = PF_UNSPEC;
442         hints.ai_socktype = SOCK_STREAM;
443
444 #ifdef AI_ADDRCONFIG
445         hints.ai_flags = AI_ADDRCONFIG;
446 #endif
447
448         /* Lookup */
449         error = getaddrinfo(cs->host, cs->port, &hints, &addrinfo);
450         if (error) {
451                 network_log(LOG_ERROR, s, "Unable to lookup %s:%s %s", 
452                                         cs->host, cs->port, gai_strerror(error));
453                 if (addrinfo != NULL)
454                         freeaddrinfo(addrinfo);
455                 return FALSE;
456         }
457
458         /* Connect */
459
460         for (res = addrinfo; res; res = res->ai_next) {
461
462                 sock = socket(res->ai_family, res->ai_socktype,
463                                           res->ai_protocol);
464                 if (sock < 0) {
465                         continue;
466                 }
467
468                 if (cs->bind_address)
469                         bindsock(s, sock, res, cs->bind_address, NULL);
470                 else if (nc->type_settings.tcp.default_bind_address)
471                         bindsock(s, sock, res, nc->type_settings.tcp.default_bind_address, NULL);
472
473                 ioc = g_io_channel_unix_new(sock);
474                 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
475
476                 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
477                         if (errno == EINPROGRESS) {
478                                 connect_finished = FALSE;
479                                 break;
480                         }
481                         g_io_channel_unref(ioc);
482                         ioc = NULL;
483                         continue;
484                 }
485
486                 break; 
487         }
488
489         size = sizeof(struct sockaddr_storage);
490         g_assert(s->connection.data.tcp.local_name == NULL);
491         g_assert(s->connection.data.tcp.remote_name == NULL);
492
493         if (!res || !ioc) {
494                 network_log(LOG_ERROR, s, "Unable to connect: %s", strerror(errno));
495                 return FALSE;
496         }
497
498         s->connection.data.tcp.remote_name = g_memdup(res->ai_addr, 
499                                                                                                   res->ai_addrlen);
500         s->connection.data.tcp.local_name = g_malloc(size);
501         s->connection.data.tcp.namelen = getsockname(sock, s->connection.data.tcp.local_name, &size);
502
503         freeaddrinfo(addrinfo);
504
505         g_io_channel_set_close_on_unref(ioc, TRUE);
506
507         cs = s->connection.data.tcp.current_server;
508         if (cs->ssl) {
509 #ifdef HAVE_GNUTLS
510                 g_io_channel_set_close_on_unref(ioc, TRUE);
511                 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
512
513                 ioc = ssl_wrap_iochannel (ioc, SSL_TYPE_CLIENT, 
514                                                                  s->connection.data.tcp.current_server->host,
515                                                                  s->ssl_credentials
516                                                                  );
517                 if (!ioc) {
518                         network_report_disconnect(s, "Couldn't connect via server %s:%s", cs->host, cs->port);
519                         reconnect(s);
520                         return FALSE;
521                 }
522 #else
523                 network_log(LOG_WARNING, s, "SSL enabled for %s:%s, but no SSL support loaded", cs->host, cs->port);
524 #endif
525         }
526
527         s->connection.state = NETWORK_CONNECTION_STATE_CONNECTING;
528
529         if (!connect_finished) {
530                 s->connection.data.tcp.connect_id = g_io_add_watch(ioc, 
531                                                                                                    G_IO_OUT|G_IO_ERR|G_IO_HUP, 
532                                                                                                    server_finish_connect, s);
533         } else {
534                 server_finish_connect(ioc, G_IO_OUT, s);
535         }
536
537         g_io_channel_unref(ioc);
538
539         return TRUE;
540 }
541
542 static void reconnect(struct irc_network *server)
543 {
544         struct network_config *nc = server->private_data;
545         g_assert(server);
546
547         g_assert(server->connection.state != NETWORK_CONNECTION_STATE_RECONNECT_PENDING);
548
549         close_server(server);
550
551         g_assert(nc);
552
553         if (nc->type == NETWORK_TCP)
554                 server->connection.data.tcp.current_server = network_get_next_tcp_server(server);
555
556         if (nc->type == NETWORK_TCP ||
557                 nc->type == NETWORK_IOCHANNEL ||
558                 nc->type == NETWORK_PROGRAM) {
559                 server->connection.state = NETWORK_CONNECTION_STATE_RECONNECT_PENDING;
560                 network_log(LOG_INFO, server, "Reconnecting in %d seconds", 
561                                         server->reconnect_interval);
562                 server->reconnect_id = g_timeout_add(1000 * 
563                                                                 server->reconnect_interval, 
564                                                                 (GSourceFunc) delayed_connect_server, server);
565         } else {
566                 connect_server(server); 
567         }
568 }
569
570 static void free_tcp_names(struct irc_network *n)
571 {
572         g_free(n->connection.data.tcp.local_name);
573         g_free(n->connection.data.tcp.remote_name);
574         n->connection.data.tcp.local_name = NULL;
575         n->connection.data.tcp.remote_name = NULL;
576 }
577
578 static gboolean close_server(struct irc_network *n) 
579 {
580         struct network_config *nc = n->private_data;
581
582         g_assert(n);
583
584         if (n->connection.state == NETWORK_CONNECTION_STATE_RECONNECT_PENDING) {
585                 g_source_remove(n->reconnect_id);
586                 n->reconnect_id = 0;
587                 n->connection.state = NETWORK_CONNECTION_STATE_NOT_CONNECTED;
588         }
589
590         if (n->connection.state == NETWORK_CONNECTION_STATE_CONNECTING) {
591                 g_source_remove(n->connection.data.tcp.connect_id);
592                 n->connection.data.tcp.connect_id = 0;
593                 n->connection.state = NETWORK_CONNECTION_STATE_NOT_CONNECTED;
594                 if (nc->type == NETWORK_TCP)
595                         free_tcp_names(n);
596         }
597
598         if (n->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED) {
599                 return FALSE;
600         } 
601
602         network_send_args(n, "QUIT", NULL);
603
604         if (n->callbacks->disconnect != NULL)
605                 n->callbacks->disconnect(n);
606
607         if (n->external_state) {
608                 n->linestack = NULL;
609                 free_network_state(n->external_state); 
610                 n->external_state = NULL;
611         }
612
613         g_assert(nc);
614
615         switch (nc->type) {
616         case NETWORK_TCP: 
617         case NETWORK_PROGRAM: 
618         case NETWORK_IOCHANNEL:
619                 irc_transport_disconnect(n->connection.transport);
620                 if (n->connection.data.tcp.ping_id > 0) {
621                         g_source_remove(n->connection.data.tcp.ping_id);
622                         n->connection.data.tcp.ping_id = 0;
623                 }
624                 free_tcp_names(n);
625                 free_irc_transport(n->connection.transport);
626                 break;
627         case NETWORK_VIRTUAL:
628                 if (n->connection.data.virtual.ops && 
629                         n->connection.data.virtual.ops->fini) {
630                         n->connection.data.virtual.ops->fini(n);
631                 }
632                 break;
633                 default: g_assert_not_reached();
634         }
635
636         n->connection.state = NETWORK_CONNECTION_STATE_NOT_CONNECTED;
637
638         return TRUE;
639 }
640
641 static pid_t piped_child(struct irc_network *s, char* const command[], int *f_in)
642 {
643         pid_t pid;
644         int sock[2];
645
646         if (socketpair(PF_UNIX, SOCK_STREAM, AF_LOCAL, sock) == -1) {
647                 network_log(LOG_ERROR, s, "socketpair: %s", strerror(errno));
648                 return -1;
649         }
650
651         *f_in = sock[0];
652
653         fcntl(sock[0], F_SETFL, O_NONBLOCK);
654
655         pid = fork();
656
657         if (pid == -1) {
658                 network_log(LOG_ERROR, s, "fork: %s", strerror(errno));
659                 return -1;
660         }
661
662         if (pid == 0) {
663                 close(0);
664                 close(1);
665                 close(2);
666                 close(sock[0]);
667
668                 dup2(sock[1], 0);
669                 dup2(sock[1], 1);
670                 execvp(command[0], command);
671                 exit(-1);
672         }
673
674         close(sock[1]);
675
676         return pid;
677 }
678
679 static gboolean server_finish_connect(GIOChannel *ioc, GIOCondition cond, 
680                                                                   void *data)
681 {
682         struct irc_network *s = data;
683
684         if (cond & G_IO_ERR) {
685                 network_report_disconnect(s, "Error connecting: %s", 
686                                                                   g_io_channel_unix_get_sock_error(ioc));
687                 reconnect(s);
688                 return FALSE;
689         }
690
691         if (cond & G_IO_OUT) {
692                 s->connection.state = NETWORK_CONNECTION_STATE_CONNECTED;
693
694                 s->connection.data.tcp.connect_id = 0; /* Otherwise data will be queued */
695                 network_set_iochannel(s, ioc);
696
697                 s->connection.last_line_recvd = time(NULL);
698                 s->connection.data.tcp.ping_id = g_timeout_add(5000, 
699                                                                            (GSourceFunc) ping_server, s);
700
701                 return FALSE;
702         }
703
704         if (cond & G_IO_HUP) {
705                 network_report_disconnect(s, "Server closed connection");
706                 reconnect(s);
707                 return FALSE;
708         }
709
710         return FALSE;
711 }
712
713 /**
714  * Change the IO channel used to communicate with a network.
715  * @param s Network to set the IO channel for.
716  * @param ioc IO channel to use
717  */
718 gboolean network_set_iochannel(struct irc_network *s, GIOChannel *ioc)
719 {
720         GError *error = NULL;
721         struct network_config *nc = s->private_data;
722         g_assert(nc->type != NETWORK_VIRTUAL);
723         if (g_io_channel_set_encoding(ioc, NULL, &error) != G_IO_STATUS_NORMAL) {
724                 network_log(LOG_ERROR, s, "Unable to change encoding: %s", 
725                                         error?error->message:"unknown");
726                 if (error != NULL)
727                         g_error_free(error);
728                 return FALSE;
729         }
730         g_io_channel_set_close_on_unref(ioc, TRUE);
731         if (g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, &error) != G_IO_STATUS_NORMAL) {
732                 network_log(LOG_ERROR, s, "Unable to change flags: %s", 
733                                         error?error->message:"unknown");
734                 if (error != NULL)
735                         g_error_free(error);
736                 return FALSE;
737         }
738
739         s->connection.transport = irc_transport_new_iochannel(ioc);
740
741         irc_transport_set_callbacks(s->connection.transport, 
742                                                                 &network_callbacks, 
743                                                                 s);
744
745         transport_parse_buffer(s->connection.transport);
746
747         server_send_login(s);
748
749         return TRUE;
750 }
751
752 static gboolean connect_program(struct irc_network *s)
753 {
754         struct network_config *nc = s->private_data;
755         int sock;
756         char *cmd[2];
757         pid_t pid;
758         GIOChannel *ioc;
759         
760         g_assert(s);
761         g_assert(nc);
762         g_assert(nc->type == NETWORK_PROGRAM);
763         
764         cmd[0] = nc->type_settings.program_location;
765         cmd[1] = NULL;
766         pid = piped_child(s, cmd, &sock);
767
768         if (pid == -1) return FALSE;
769
770         ioc = g_io_channel_unix_new(sock);
771         network_set_iochannel(s, ioc);
772
773         g_io_channel_unref(ioc);
774
775         if (s->name == NULL) {
776                 if (strchr(nc->type_settings.program_location, '/')) {
777                         s->name = g_strdup(strrchr(nc->type_settings.program_location, '/')+1);
778                 } else {
779                         s->name = g_strdup(nc->type_settings.program_location);
780                 }
781         }
782
783         return TRUE;
784 }
785
786 static gboolean connect_virtual(struct irc_network *s)
787 {
788         struct irc_login_details *details;
789         struct network_config *nc = s->private_data;
790
791         if (nc->type_settings.virtual.ops == NULL)
792                 return FALSE;
793
794         details = s->callbacks->get_login_details(s);
795
796         s->external_state = network_state_init(details->nick, details->username, 
797                                                                   get_my_hostname());
798
799         free_login_details(details);
800         s->external_state->userdata = s;
801         s->external_state->log = state_log_helper;
802         if (s->callbacks->state_set)
803                 s->callbacks->state_set(s);
804         s->connection.state = NETWORK_CONNECTION_STATE_MOTD_RECVD;
805
806         if (nc->type_settings.virtual.ops->init)
807                 return nc->type_settings.virtual.ops->init(s);
808
809         return TRUE;
810 }
811
812 static gboolean connect_server(struct irc_network *s)
813 {
814         struct network_config *nc = s->private_data;
815
816         g_assert(s);
817         g_assert(nc);
818
819         switch (nc->type) {
820         case NETWORK_TCP:
821                 return connect_current_tcp_server(s);
822
823         case NETWORK_PROGRAM:
824                 return connect_program(s);
825
826         case NETWORK_VIRTUAL:
827                 return connect_virtual(s);
828         default: g_assert_not_reached();
829         }
830
831         return TRUE;
832 }
833
834 static gboolean delayed_connect_server(struct irc_network *s)
835 {
836         g_assert(s);
837         connect_server(s);
838         return (s->connection.state == NETWORK_CONNECTION_STATE_RECONNECT_PENDING);
839 }
840
841 struct irc_network *irc_network_new(const struct irc_network_callbacks *callbacks, void *private_data)
842 {
843         struct irc_network *s;
844
845         s = g_new0(struct irc_network, 1);
846         s->callbacks = callbacks;
847         s->references = 1;
848         s->private_data = private_data;
849         s->reconnect_interval = ((struct network_config *)private_data)->reconnect_interval == -1?DEFAULT_RECONNECT_INTERVAL:((struct network_config *)private_data)->reconnect_interval;
850         s->info = network_info_init();
851         s->name = g_strdup(((struct network_config *)private_data)->name);
852         s->info->ircd = g_strdup("ctrlproxy");
853         s->info->forced_nick_changes = TRUE; /* Forced nick changes are done by ctrlproxy */
854
855 #ifdef HAVE_GNUTLS
856         s->ssl_credentials = ssl_get_client_credentials(NULL);
857 #endif
858
859         return s;
860 }
861
862 /**
863  * Connect to a network, returns TRUE if connection was successful 
864  * (or startup of connection was successful) 
865  *
866  * @param s Network to connect to
867  */
868 gboolean connect_network(struct irc_network *s) 
869 {
870         g_assert(s);
871         g_assert(s->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED ||
872                          s->connection.state == NETWORK_CONNECTION_STATE_RECONNECT_PENDING);
873
874         return connect_server(s);
875 }
876
877 static void free_network(struct irc_network *s)
878 {
879         struct network_config *nc;
880
881         nc = s->private_data;
882
883         free_network_info(s->info);
884         if (nc->type == NETWORK_TCP)
885                 g_free(s->connection.data.tcp.last_disconnect_reason);
886
887 #ifdef HAVE_GNUTLS
888         ssl_free_client_credentials(s->ssl_credentials);
889 #endif
890
891         g_free(s->name);
892         g_free(s);
893 }
894
895
896 /** 
897  * Disconnect from a network. The network will still be kept in memory, 
898  * but all socket connections associated with it will be dropped.
899  *
900  * @param s Network to disconnect from
901  * @return Whether disconnecting succeeded.
902  */
903 gboolean disconnect_network(struct irc_network *s)
904 {
905         g_assert(s);
906         if (s->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED) {
907                 return FALSE;
908         }
909
910         network_log(LOG_INFO, s, "Disconnecting");
911         return close_server(s);
912 }
913
914 /**
915  * Register a new virtual network type.
916  *
917  * @param ops Callback functions for the virtual network type.
918  */
919 void register_virtual_network(struct virtual_network_ops *ops)
920 {
921         if (virtual_network_ops == NULL)
922                 virtual_network_ops = g_hash_table_new(g_str_hash, g_str_equal);
923         g_assert(ops);
924         g_hash_table_insert(virtual_network_ops, ops->name, ops);
925 }
926
927 struct virtual_network_ops *find_virtual_network(const char *name)
928 {
929         if (virtual_network_ops == NULL)
930                 return NULL;
931         return g_hash_table_lookup(virtual_network_ops, name);
932 }
933
934 /**
935  * Autoconnect to all the networks in a list.
936  *
937  * @param networks GList with networks
938  * @return TRUE
939  */
940 gboolean autoconnect_networks(GList *networks)
941 {
942         GList *gl;
943         for (gl = networks; gl; gl = gl->next)
944         {
945                 struct irc_network *n = gl->data;
946                 struct network_config *nc = n->private_data;
947                 g_assert(n);
948                 g_assert(nc);
949                 if (nc->autoconnect)
950                         connect_network(n);
951         }
952
953         return TRUE;
954 }
955
956 /**
957  * Find a network by name.
958  *
959  * @param networks GList with possible networks
960  * @param name Name of the network to search for.
961  * @return first network found or NULL
962  */
963 struct irc_network *find_network(GList *networks, const char *name)
964 {
965         GList *gl;
966         for (gl = networks; gl; gl = gl->next) {
967                 struct irc_network *n = gl->data;
968                 if (n->name && !g_strcasecmp(n->name, name)) 
969                         return n;
970         }
971
972         return NULL;
973 }
974
975 /**
976  * Switch to the next server listed for a network.
977  *
978  * @param n Network
979  */
980 void irc_network_select_next_server(struct irc_network *n)
981 {
982         struct network_config *nc = n->private_data;
983
984         g_assert(n);
985         g_assert(nc);
986
987         if (nc->type != NETWORK_TCP) 
988                 return;
989
990         network_log(LOG_INFO, n, "Trying next server");
991         n->connection.data.tcp.current_server = network_get_next_tcp_server(n);
992 }
993
994 /**
995  * Generate 005-response string to send to client connected to a network.
996  *
997  * @param n Network to generate for
998  * @return An 005 string, newly allocated
999  */
1000 char *network_generate_feature_string(struct irc_network *n)
1001 {
1002         g_assert(n);
1003
1004         return network_info_string(n->info);
1005 }
1006
1007 /**
1008  * Increase the reference count for a network
1009  */
1010 struct irc_network *network_ref(struct irc_network *n)
1011 {
1012         if (n != NULL)
1013                 n->references++;
1014         return n;
1015 }
1016
1017 void irc_network_unref(struct irc_network *n)
1018 {
1019         if (n == NULL)
1020                 return;
1021         n->references--;
1022         if (n->references == 0) 
1023                 free_network(n);
1024 }
1025
1026 void network_log(enum log_level l, const struct irc_network *s, 
1027                                  const char *fmt, ...)
1028 {
1029         char *ret;
1030         va_list ap;
1031
1032         if (s->callbacks->log == NULL)
1033                 return;
1034
1035         g_assert(s);
1036         g_assert(fmt);
1037
1038         va_start(ap, fmt);
1039         ret = g_strdup_vprintf(fmt, ap);
1040         va_end(ap);
1041
1042         s->callbacks->log(l, s, ret);
1043
1044         g_free(ret);
1045 }
1046
1047 void unregister_virtual_networks(void)
1048 {
1049         if (virtual_network_ops != NULL)
1050                 g_hash_table_destroy(virtual_network_ops);
1051         virtual_network_ops = NULL;
1052 }