Merge Irssi 0.8.16-rc1
[silc.git] / apps / irssi / src / core / servers-setup.c
1 /*
2  servers-setup.c : irssi
3
4     Copyright (C) 1999-2001 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "module.h"
22 #include "signals.h"
23 #include "network.h"
24 #include "lib-config/iconfig.h"
25 #include "settings.h"
26
27 #include "chat-protocols.h"
28 #include "chatnets.h"
29 #include "servers.h"
30 #include "servers-setup.h"
31
32 GSList *setupservers;
33
34 static char *old_source_host;
35 int source_host_ok; /* Use source_host_ip .. */
36 IPADDR *source_host_ip4, *source_host_ip6; /* Resolved address */
37
38 static void save_ips(IPADDR *ip4, IPADDR *ip6,
39                      IPADDR **save_ip4, IPADDR **save_ip6)
40 {
41         if (ip4->family == 0)
42                 g_free_and_null(*save_ip4);
43         else {
44                 if (*save_ip4 == NULL)
45                         *save_ip4 = g_new(IPADDR, 1);
46                 memcpy(*save_ip4, ip4, sizeof(IPADDR));
47         }
48
49         if (ip6->family == 0)
50                 g_free_and_null(*save_ip6);
51         else {
52                 if (*save_ip6 == NULL)
53                         *save_ip6 = g_new(IPADDR, 1);
54                 memcpy(*save_ip6, ip6, sizeof(IPADDR));
55         }
56 }
57
58 static void get_source_host_ip(void)
59 {
60         const char *hostname;
61         IPADDR ip4, ip6;
62
63         if (source_host_ok)
64                 return;
65
66         /* FIXME: This will block! */
67         hostname = settings_get_str("hostname");
68         source_host_ok = *hostname != '\0' &&
69                 net_gethostbyname(hostname, &ip4, &ip6) == 0;
70
71         if (source_host_ok)
72                 save_ips(&ip4, &ip6, &source_host_ip4, &source_host_ip6);
73         else {
74                 g_free_and_null(source_host_ip4);
75                 g_free_and_null(source_host_ip6);
76         }
77 }
78
79 static void conn_set_ip(SERVER_CONNECT_REC *conn, const char *own_host,
80                         IPADDR **own_ip4, IPADDR **own_ip6)
81 {
82         IPADDR ip4, ip6;
83
84         if (*own_ip4 == NULL && *own_ip6 == NULL) {
85                 /* resolve the IP */
86                 if (net_gethostbyname(own_host, &ip4, &ip6) == 0)
87                         save_ips(&ip4, &ip6, own_ip4, own_ip6);
88         }
89
90         server_connect_own_ip_save(conn, *own_ip4, *own_ip6);
91 }
92
93 /* Fill information to connection from server setup record */
94 void server_setup_fill_reconn(SERVER_CONNECT_REC *conn,
95                               SERVER_SETUP_REC *sserver)
96 {
97         g_return_if_fail(IS_SERVER_CONNECT(conn));
98         g_return_if_fail(IS_SERVER_SETUP(sserver));
99
100         if (sserver->own_host != NULL) {
101                 conn_set_ip(conn, sserver->own_host,
102                             &sserver->own_ip4, &sserver->own_ip6);
103         }
104
105         if (sserver->chatnet != NULL && conn->chatnet == NULL)
106                 conn->chatnet = g_strdup(sserver->chatnet);
107
108         if (sserver->password != NULL && conn->password == NULL)
109                 conn->password = g_strdup(sserver->password);
110
111         signal_emit("server setup fill reconn", 2, conn, sserver);
112 }
113
114 static void server_setup_fill(SERVER_CONNECT_REC *conn,
115                               const char *address, int port)
116 {
117         g_return_if_fail(conn != NULL);
118         g_return_if_fail(address != NULL);
119
120         conn->type = module_get_uniq_id("SERVER CONNECT", 0);
121
122         conn->address = g_strdup(address);
123         if (port > 0) conn->port = port;
124
125         if (!conn->nick) conn->nick = g_strdup(settings_get_str("nick"));
126         conn->username = g_strdup(settings_get_str("user_name"));
127         conn->realname = g_strdup(settings_get_str("real_name"));
128
129         /* proxy settings */
130         if (settings_get_bool("use_proxy")) {
131                 conn->proxy = g_strdup(settings_get_str("proxy_address"));
132                 conn->proxy_port = settings_get_int("proxy_port");
133                 conn->proxy_string = g_strdup(settings_get_str("proxy_string"));
134                 conn->proxy_string_after = g_strdup(settings_get_str("proxy_string_after"));
135                 conn->proxy_password = g_strdup(settings_get_str("proxy_password"));
136         }
137
138         /* source IP */
139         if (source_host_ip4 != NULL) {
140                 conn->own_ip4 = g_new(IPADDR, 1);
141                 memcpy(conn->own_ip4, source_host_ip4, sizeof(IPADDR));
142         }
143         if (source_host_ip6 != NULL) {
144                 conn->own_ip6 = g_new(IPADDR, 1);
145                 memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR));
146         }
147
148         signal_emit("server setup fill connect", 1, conn);
149 }
150
151 static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
152                                      SERVER_SETUP_REC *sserver)
153 {
154         g_return_if_fail(IS_SERVER_CONNECT(conn));
155         g_return_if_fail(IS_SERVER_SETUP(sserver));
156
157         sserver->last_connect = time(NULL);
158
159         if (sserver->no_proxy)
160                 g_free_and_null(conn->proxy);
161
162         if (sserver->family != 0 && conn->family == 0)
163                 conn->family = sserver->family;
164         if (sserver->port > 0 && conn->port <= 0)
165                 conn->port = sserver->port;
166
167         conn->use_ssl = sserver->use_ssl;
168         if (conn->ssl_cert == NULL && sserver->ssl_cert != NULL && sserver->ssl_cert[0] != '\0')
169                 conn->ssl_cert = g_strdup(sserver->ssl_cert);
170         if (conn->ssl_pkey == NULL && sserver->ssl_pkey != NULL && sserver->ssl_pkey[0] != '\0')
171                 conn->ssl_pkey = g_strdup(sserver->ssl_pkey);
172         conn->ssl_verify = sserver->ssl_verify;
173         if (conn->ssl_cafile == NULL && sserver->ssl_cafile != NULL && sserver->ssl_cafile[0] != '\0')
174                 conn->ssl_cafile = g_strdup(sserver->ssl_cafile);
175         if (conn->ssl_capath == NULL && sserver->ssl_capath != NULL && sserver->ssl_capath[0] != '\0')
176                 conn->ssl_capath = g_strdup(sserver->ssl_capath);
177
178         server_setup_fill_reconn(conn, sserver);
179
180         signal_emit("server setup fill server", 2, conn, sserver);
181 }
182
183 static void server_setup_fill_chatnet(SERVER_CONNECT_REC *conn,
184                                       CHATNET_REC *chatnet)
185 {
186         g_return_if_fail(IS_SERVER_CONNECT(conn));
187         g_return_if_fail(IS_CHATNET(chatnet));
188
189         if (chatnet->nick != NULL) {
190                 g_free(conn->nick);
191                 conn->nick = g_strdup(chatnet->nick);;
192         }
193         if (chatnet->username != NULL) {
194                 g_free(conn->username);
195                 conn->username = g_strdup(chatnet->username);;
196         }
197         if (chatnet->realname != NULL) {
198                 g_free(conn->realname);
199                 conn->realname = g_strdup(chatnet->realname);;
200         }
201         if (chatnet->own_host != NULL) {
202                 conn_set_ip(conn, chatnet->own_host,
203                             &chatnet->own_ip4, &chatnet->own_ip6);
204         }
205
206         signal_emit("server setup fill chatnet", 2, conn, chatnet);
207 }
208
209 static SERVER_CONNECT_REC *
210 create_addr_conn(int chat_type, const char *address, int port,
211                  const char *chatnet, const char *password,
212                  const char *nick)
213 {
214         CHAT_PROTOCOL_REC *proto;
215         SERVER_CONNECT_REC *conn;
216         SERVER_SETUP_REC *sserver;
217         CHATNET_REC *chatnetrec;
218
219         g_return_val_if_fail(address != NULL, NULL);
220
221         sserver = server_setup_find(address, port, chatnet);
222         if (sserver != NULL) {
223                 if (chat_type < 0)
224                         chat_type = sserver->chat_type;
225                 else if (chat_type != sserver->chat_type)
226                         sserver = NULL;
227         }
228
229         proto = chat_type >= 0 ? chat_protocol_find_id(chat_type) :
230                 chat_protocol_get_default();
231
232         conn = proto->create_server_connect();
233         server_connect_ref(conn);
234
235         conn->chat_type = proto->id;
236         if (chatnet != NULL && *chatnet != '\0')
237                 conn->chatnet = g_strdup(chatnet);
238
239         /* fill in the defaults */
240         server_setup_fill(conn, address, port);
241
242         /* fill the rest from chat network settings */
243         chatnetrec = chatnet != NULL ? chatnet_find(chatnet) :
244                 (sserver == NULL || sserver->chatnet == NULL ? NULL :
245                  chatnet_find(sserver->chatnet));
246         if (chatnetrec != NULL)
247                 server_setup_fill_chatnet(conn, chatnetrec);
248
249         /* fill the information from setup */
250         if (sserver != NULL)
251                 server_setup_fill_server(conn, sserver);
252
253         /* nick / password given in command line overrides all settings */
254         if (password && *password) {
255                 g_free_not_null(conn->password);
256                 conn->password = g_strdup(password);
257         }
258         if (nick && *nick) {
259                 g_free_not_null(conn->nick);
260                 conn->nick = g_strdup(nick);
261         }
262
263         return conn;
264 }
265
266 /* Connect to server where last connect succeeded (or we haven't tried to
267    connect yet). If there's no such server, connect to server where we
268    haven't connected for the longest time */
269 static SERVER_CONNECT_REC *
270 create_chatnet_conn(const char *dest, int port,
271                     const char *password, const char *nick)
272 {
273         SERVER_SETUP_REC *bestrec;
274         GSList *tmp;
275         time_t now, besttime;
276
277         now = time(NULL);
278         bestrec = NULL; besttime = now;
279         for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
280                 SERVER_SETUP_REC *rec = tmp->data;
281
282                 if (rec->chatnet == NULL ||
283                     g_strcasecmp(rec->chatnet, dest) != 0)
284                         continue;
285
286                 if (!rec->last_failed) {
287                         bestrec = rec;
288                         break;
289                 }
290
291                 if (bestrec == NULL || besttime > rec->last_connect) {
292                         bestrec = rec;
293                         besttime = rec->last_connect;
294                 }
295         }
296
297         return bestrec == NULL ? NULL :
298                 create_addr_conn(bestrec->chat_type, bestrec->address, 0,
299                                  dest, NULL, nick);
300 }
301
302 /* Create server connection record. `dest' is required, rest can be NULL.
303    `dest' is either a server address or chat network */
304 SERVER_CONNECT_REC *
305 server_create_conn(int chat_type, const char *dest, int port,
306                    const char *chatnet, const char *password,
307                    const char *nick)
308 {
309         SERVER_CONNECT_REC *rec;
310         CHATNET_REC *chatrec;
311
312         g_return_val_if_fail(dest != NULL, NULL);
313
314         chatrec = chatnet_find(dest);
315         if (chatrec != NULL) {
316                 rec = create_chatnet_conn(chatrec->name, port, password, nick);
317                 if (rec != NULL)
318                         return rec;
319         }
320
321         chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
322         if (chatrec != NULL)
323                 chatnet = chatrec->name;
324
325         return create_addr_conn(chat_type, dest, port,
326                                 chatnet, password, nick);
327 }
328
329 /* Find matching server from setup. Try to find record with a same port,
330    but fallback to any server with the same address. */
331 SERVER_SETUP_REC *server_setup_find(const char *address, int port,
332                                     const char *chatnet)
333 {
334         SERVER_SETUP_REC *server;
335         GSList *tmp;
336
337         g_return_val_if_fail(address != NULL, NULL);
338
339         server = NULL;
340         for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
341                 SERVER_SETUP_REC *rec = tmp->data;
342
343                 if (g_strcasecmp(rec->address, address) == 0 &&
344                     (chatnet == NULL || rec->chatnet == NULL ||
345                      g_strcasecmp(rec->chatnet, chatnet) == 0)) {
346                         server = rec;
347                         if (rec->port == port)
348                                 break;
349                 }
350         }
351
352         return server;
353 }
354
355 static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
356 {
357         SERVER_SETUP_REC *rec;
358         CHATNET_REC *chatnetrec;
359         char *server, *chatnet, *family;
360         int port;
361
362         g_return_val_if_fail(node != NULL, NULL);
363
364         server = config_node_get_str(node, "address", NULL);
365         if (server == NULL)
366                 return NULL;
367
368         port = config_node_get_int(node, "port", 0);
369         chatnet = config_node_get_str(node, "chatnet", NULL);
370
371         if (server_setup_find(server, port, chatnet) != NULL) {
372                 return NULL;
373         }
374
375         rec = NULL;
376
377         chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
378         if (chatnetrec == NULL && chatnet != NULL) {
379                 /* chat network not found, create it. */
380                 chatnetrec = chat_protocol_get_default()->create_chatnet();
381                 chatnetrec->chat_type = chat_protocol_get_default()->id;
382                 chatnetrec->name = g_strdup(chatnet);
383                 chatnet_create(chatnetrec);
384         }
385
386         family = config_node_get_str(node, "family", "");
387
388         rec = CHAT_PROTOCOL(chatnetrec)->create_server_setup();
389         rec->type = module_get_uniq_id("SERVER SETUP", 0);
390         rec->chat_type = CHAT_PROTOCOL(chatnetrec)->id;
391         rec->chatnet = chatnetrec == NULL ? NULL : g_strdup(chatnetrec->name);
392         rec->family = g_ascii_strcasecmp(family, "inet6") == 0 ? AF_INET6 :
393                 (g_ascii_strcasecmp(family, "inet") == 0 ? AF_INET : 0);
394         rec->address = g_strdup(server);
395         rec->password = g_strdup(config_node_get_str(node, "password", NULL));
396         rec->use_ssl = config_node_get_bool(node, "use_ssl", FALSE);
397         rec->ssl_cert = g_strdup(config_node_get_str(node, "ssl_cert", NULL));
398         rec->ssl_pkey = g_strdup(config_node_get_str(node, "ssl_pkey", NULL));
399         rec->ssl_verify = config_node_get_bool(node, "ssl_verify", FALSE);
400         rec->ssl_cafile = g_strdup(config_node_get_str(node, "ssl_cafile", NULL));
401         rec->ssl_capath = g_strdup(config_node_get_str(node, "ssl_capath", NULL));
402         if (rec->ssl_cafile || rec->ssl_capath)
403                 rec->ssl_verify = TRUE;
404         if (rec->ssl_cert != NULL || rec->ssl_verify)
405                 rec->use_ssl = TRUE;
406         rec->port = port;
407         rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
408         rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE);
409         rec->own_host = g_strdup(config_node_get_str(node, "own_host", NULL));
410
411         signal_emit("server setup read", 2, rec, node);
412
413         setupservers = g_slist_append(setupservers, rec);
414         return rec;
415 }
416
417 static void server_setup_save(SERVER_SETUP_REC *rec)
418 {
419         CONFIG_NODE *parentnode, *node;
420         int index;
421
422         index = g_slist_index(setupservers, rec);
423
424         parentnode = iconfig_node_traverse("(servers", TRUE);
425         node = config_node_nth(parentnode, index);
426         if (node == NULL)
427                 node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
428
429         iconfig_node_clear(node);
430         iconfig_node_set_str(node, "address", rec->address);
431         iconfig_node_set_str(node, "chatnet", rec->chatnet);
432
433         iconfig_node_set_int(node, "port", rec->port);
434         iconfig_node_set_str(node, "password", rec->password);
435         iconfig_node_set_bool(node, "use_ssl", rec->use_ssl);
436         iconfig_node_set_str(node, "ssl_cert", rec->ssl_cert);
437         iconfig_node_set_str(node, "ssl_pkey", rec->ssl_pkey);
438         iconfig_node_set_bool(node, "ssl_verify", rec->ssl_verify);
439         iconfig_node_set_str(node, "ssl_cafile", rec->ssl_cafile);
440         iconfig_node_set_str(node, "ssl_capath", rec->ssl_capath);
441         iconfig_node_set_str(node, "own_host", rec->own_host);
442
443         iconfig_node_set_str(node, "family",
444                              rec->family == AF_INET6 ? "inet6" :
445                              rec->family == AF_INET ? "inet" : NULL);
446
447         if (rec->autoconnect)
448                 iconfig_node_set_bool(node, "autoconnect", TRUE);
449         if (rec->no_proxy)
450                 iconfig_node_set_bool(node, "no_proxy", TRUE);
451
452         signal_emit("server setup saved", 2, rec, node);
453 }
454
455 static void server_setup_remove_config(SERVER_SETUP_REC *rec)
456 {
457         CONFIG_NODE *node;
458         int index;
459
460         node = iconfig_node_traverse("servers", FALSE);
461         if (node != NULL) {
462                 index = g_slist_index(setupservers, rec);
463                 iconfig_node_list_remove(node, index);
464         }
465 }
466
467 static void server_setup_destroy(SERVER_SETUP_REC *rec)
468 {
469         setupservers = g_slist_remove(setupservers, rec);
470         signal_emit("server setup destroyed", 1, rec);
471
472         g_free_not_null(rec->own_host);
473         g_free_not_null(rec->own_ip4);
474         g_free_not_null(rec->own_ip6);
475         g_free_not_null(rec->chatnet);
476         g_free_not_null(rec->password);
477         g_free_not_null(rec->ssl_cert);
478         g_free_not_null(rec->ssl_pkey);
479         g_free_not_null(rec->ssl_cafile);
480         g_free_not_null(rec->ssl_capath);
481         g_free(rec->address);
482         g_free(rec);
483 }
484
485 void server_setup_add(SERVER_SETUP_REC *rec)
486 {
487         rec->type = module_get_uniq_id("SERVER SETUP", 0);
488         if (g_slist_find(setupservers, rec) == NULL)
489                 setupservers = g_slist_append(setupservers, rec);
490         server_setup_save(rec);
491
492         signal_emit("server setup updated", 1, rec);
493 }
494
495 void server_setup_remove(SERVER_SETUP_REC *rec)
496 {
497         server_setup_remove_config(rec);
498         server_setup_destroy(rec);
499 }
500
501 static void read_servers(void)
502 {
503         CONFIG_NODE *node;
504         GSList *tmp;
505
506         while (setupservers != NULL)
507                 server_setup_destroy(setupservers->data);
508
509         /* Read servers */
510         node = iconfig_node_traverse("servers", FALSE);
511         if (node != NULL) {
512                 tmp = config_node_first(node->value);
513                 for (; tmp != NULL; tmp = config_node_next(tmp))
514                         server_setup_read(tmp->data);
515         }
516 }
517
518 static void read_settings(void)
519 {
520         if (old_source_host == NULL ||
521             strcmp(old_source_host, settings_get_str("hostname")) != 0) {
522                 g_free_not_null(old_source_host);
523                 old_source_host = g_strdup(settings_get_str("hostname"));
524
525                 source_host_ok = FALSE;
526                 get_source_host_ip();
527         }
528 }
529
530 void servers_setup_init(void)
531 {
532         settings_add_str("server", "hostname", "");
533
534         settings_add_str("server", "nick", NULL);
535         settings_add_str("server", "user_name", NULL);
536         settings_add_str("server", "real_name", NULL);
537
538         settings_add_bool("proxy", "use_proxy", FALSE);
539         settings_add_str("proxy", "proxy_address", "");
540         settings_add_int("proxy", "proxy_port", 6667);
541         settings_add_str("proxy", "proxy_string", "CONNECT %s %d");
542         settings_add_str("proxy", "proxy_string_after", "");
543         settings_add_str("proxy", "proxy_password", "");
544
545         setupservers = NULL;
546         source_host_ip4 = source_host_ip6 = NULL;
547         old_source_host = NULL;
548         read_settings();
549
550         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
551         signal_add("setup reread", (SIGNAL_FUNC) read_servers);
552         signal_add("irssi init read settings", (SIGNAL_FUNC) read_servers);
553 }
554
555 void servers_setup_deinit(void)
556 {
557         g_free_not_null(source_host_ip4);
558         g_free_not_null(source_host_ip6);
559         g_free_not_null(old_source_host);
560
561         while (setupservers != NULL)
562                 server_setup_destroy(setupservers->data);
563
564         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
565         signal_remove("setup reread", (SIGNAL_FUNC) read_servers);
566         signal_remove("irssi init read settings", (SIGNAL_FUNC) read_servers);
567
568         module_uniq_destroy("SERVER SETUP");
569 }