Added SILC Thread Queue API
[runtime.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
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 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 /* Find matching server from setup. Ports must match or NULL is returned. */
356 SERVER_SETUP_REC *server_setup_find_port(const char *address, int port)
357 {
358         SERVER_SETUP_REC *rec;
359
360         rec = server_setup_find(address, port, NULL);
361         return rec == NULL || rec->port != port ? NULL : rec;
362 }
363
364 static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
365 {
366         SERVER_SETUP_REC *rec;
367         CHATNET_REC *chatnetrec;
368         char *server, *chatnet, *family;
369         int port;
370
371         g_return_val_if_fail(node != NULL, NULL);
372
373         server = config_node_get_str(node, "address", NULL);
374         if (server == NULL)
375                 return NULL;
376
377         port = config_node_get_int(node, "port", 0);
378         if (server_setup_find_port(server, port) != NULL) {
379                 /* already exists - don't let it get there twice or
380                    server reconnects will screw up! */
381                 return NULL;
382         }
383
384         rec = NULL;
385         chatnet = config_node_get_str(node, "chatnet", NULL);
386
387         chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
388         if (chatnetrec == NULL && chatnet != NULL) {
389                 /* chat network not found, create it. */
390                 chatnetrec = chat_protocol_get_default()->create_chatnet();
391                 chatnetrec->chat_type = chat_protocol_get_default()->id;
392                 chatnetrec->name = g_strdup(chatnet);
393                 chatnet_create(chatnetrec);
394         }
395
396         family = config_node_get_str(node, "family", "");
397
398         rec = CHAT_PROTOCOL(chatnetrec)->create_server_setup();
399         rec->type = module_get_uniq_id("SERVER SETUP", 0);
400         rec->chat_type = CHAT_PROTOCOL(chatnetrec)->id;
401         rec->chatnet = chatnetrec == NULL ? NULL : g_strdup(chatnetrec->name);
402         rec->family = g_strcasecmp(family, "inet6") == 0 ? AF_INET6 :
403                 (g_strcasecmp(family, "inet") == 0 ? AF_INET : 0);
404         rec->address = g_strdup(server);
405         rec->password = g_strdup(config_node_get_str(node, "password", NULL));
406         rec->use_ssl = config_node_get_bool(node, "use_ssl", FALSE);
407         rec->ssl_cert = g_strdup(config_node_get_str(node, "ssl_cert", NULL));
408         rec->ssl_pkey = g_strdup(config_node_get_str(node, "ssl_pkey", NULL));
409         rec->ssl_verify = config_node_get_bool(node, "ssl_verify", FALSE);
410         rec->ssl_cafile = g_strdup(config_node_get_str(node, "ssl_cafile", NULL));
411         rec->ssl_capath = g_strdup(config_node_get_str(node, "ssl_capath", NULL));
412         if (rec->ssl_cafile || rec->ssl_capath)
413                 rec->ssl_verify = TRUE;
414         if (rec->ssl_cert != NULL || rec->ssl_verify)
415                 rec->use_ssl = TRUE;
416         rec->port = port;
417         rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
418         rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE);
419         rec->own_host = g_strdup(config_node_get_str(node, "own_host", NULL));
420
421         signal_emit("server setup read", 2, rec, node);
422
423         setupservers = g_slist_append(setupservers, rec);
424         return rec;
425 }
426
427 static void server_setup_save(SERVER_SETUP_REC *rec)
428 {
429         CONFIG_NODE *parentnode, *node;
430         int index;
431
432         index = g_slist_index(setupservers, rec);
433
434         parentnode = iconfig_node_traverse("(servers", TRUE);
435         node = config_node_nth(parentnode, index);
436         if (node == NULL)
437                 node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
438
439         iconfig_node_clear(node);
440         iconfig_node_set_str(node, "address", rec->address);
441         iconfig_node_set_str(node, "chatnet", rec->chatnet);
442
443         iconfig_node_set_int(node, "port", rec->port);
444         iconfig_node_set_str(node, "password", rec->password);
445         iconfig_node_set_bool(node, "use_ssl", rec->use_ssl);
446         iconfig_node_set_str(node, "ssl_cert", rec->ssl_cert);
447         iconfig_node_set_str(node, "ssl_pkey", rec->ssl_pkey);
448         iconfig_node_set_bool(node, "ssl_verify", rec->ssl_verify);
449         iconfig_node_set_str(node, "ssl_cafile", rec->ssl_cafile);
450         iconfig_node_set_str(node, "ssl_capath", rec->ssl_capath);
451         iconfig_node_set_str(node, "own_host", rec->own_host);
452
453         iconfig_node_set_str(node, "family",
454                              rec->family == AF_INET6 ? "inet6" :
455                              rec->family == AF_INET ? "inet" : NULL);
456
457         if (rec->autoconnect)
458                 iconfig_node_set_bool(node, "autoconnect", TRUE);
459         if (rec->no_proxy)
460                 iconfig_node_set_bool(node, "no_proxy", TRUE);
461
462         signal_emit("server setup saved", 2, rec, node);
463 }
464
465 static void server_setup_remove_config(SERVER_SETUP_REC *rec)
466 {
467         CONFIG_NODE *node;
468         int index;
469
470         node = iconfig_node_traverse("servers", FALSE);
471         if (node != NULL) {
472                 index = g_slist_index(setupservers, rec);
473                 iconfig_node_list_remove(node, index);
474         }
475 }
476
477 static void server_setup_destroy(SERVER_SETUP_REC *rec)
478 {
479         setupservers = g_slist_remove(setupservers, rec);
480         signal_emit("server setup destroyed", 1, rec);
481
482         g_free_not_null(rec->own_host);
483         g_free_not_null(rec->own_ip4);
484         g_free_not_null(rec->own_ip6);
485         g_free_not_null(rec->chatnet);
486         g_free_not_null(rec->password);
487         g_free_not_null(rec->ssl_cert);
488         g_free_not_null(rec->ssl_pkey);
489         g_free_not_null(rec->ssl_cafile);
490         g_free_not_null(rec->ssl_capath);
491         g_free(rec->address);
492         g_free(rec);
493 }
494
495 void server_setup_add(SERVER_SETUP_REC *rec)
496 {
497         rec->type = module_get_uniq_id("SERVER SETUP", 0);
498         if (g_slist_find(setupservers, rec) == NULL)
499                 setupservers = g_slist_append(setupservers, rec);
500         server_setup_save(rec);
501
502         signal_emit("server setup updated", 1, rec);
503 }
504
505 void server_setup_remove(SERVER_SETUP_REC *rec)
506 {
507         server_setup_remove_config(rec);
508         server_setup_destroy(rec);
509 }
510
511 static void read_servers(void)
512 {
513         CONFIG_NODE *node;
514         GSList *tmp;
515
516         while (setupservers != NULL)
517                 server_setup_destroy(setupservers->data);
518
519         /* Read servers */
520         node = iconfig_node_traverse("servers", FALSE);
521         if (node != NULL) {
522                 tmp = config_node_first(node->value);
523                 for (; tmp != NULL; tmp = config_node_next(tmp))
524                         server_setup_read(tmp->data);
525         }
526 }
527
528 static void read_settings(void)
529 {
530         if (old_source_host == NULL ||
531             strcmp(old_source_host, settings_get_str("hostname")) != 0) {
532                 g_free_not_null(old_source_host);
533                 old_source_host = g_strdup(settings_get_str("hostname"));
534
535                 source_host_ok = FALSE;
536                 get_source_host_ip();
537         }
538 }
539
540 void servers_setup_init(void)
541 {
542         settings_add_str("server", "hostname", "");
543
544         settings_add_str("server", "nick", NULL);
545         settings_add_str("server", "user_name", NULL);
546         settings_add_str("server", "real_name", NULL);
547
548         settings_add_bool("server", "use_ssl", FALSE);
549         settings_add_str("server", "ssl_cert", NULL);
550         settings_add_str("server", "ssl_pkey", NULL);
551         settings_add_bool("server", "ssl_verify", FALSE);
552         settings_add_str("server", "ssl_cafile", NULL);
553         settings_add_str("server", "ssl_cacert", NULL);
554
555         settings_add_bool("proxy", "use_proxy", FALSE);
556         settings_add_str("proxy", "proxy_address", "");
557         settings_add_int("proxy", "proxy_port", 6667);
558         settings_add_str("proxy", "proxy_string", "CONNECT %s %d");
559         settings_add_str("proxy", "proxy_string_after", "");
560         settings_add_str("proxy", "proxy_password", "");
561
562         setupservers = NULL;
563         source_host_ip4 = source_host_ip6 = NULL;
564         old_source_host = NULL;
565         read_settings();
566
567         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
568         signal_add("setup reread", (SIGNAL_FUNC) read_servers);
569         signal_add("irssi init read settings", (SIGNAL_FUNC) read_servers);
570 }
571
572 void servers_setup_deinit(void)
573 {
574         g_free_not_null(source_host_ip4);
575         g_free_not_null(source_host_ip6);
576         g_free_not_null(old_source_host);
577
578         while (setupservers != NULL)
579                 server_setup_destroy(setupservers->data);
580
581         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
582         signal_remove("setup reread", (SIGNAL_FUNC) read_servers);
583         signal_remove("irssi init read settings", (SIGNAL_FUNC) read_servers);
584
585         module_uniq_destroy("SERVER SETUP");
586 }