updates.
[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
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         server_setup_fill_reconn(conn, sserver);
167
168         signal_emit("server setup fill server", 2, conn, sserver);
169 }
170
171 static void server_setup_fill_chatnet(SERVER_CONNECT_REC *conn,
172                                       CHATNET_REC *chatnet)
173 {
174         g_return_if_fail(IS_SERVER_CONNECT(conn));
175         g_return_if_fail(IS_CHATNET(chatnet));
176
177         if (chatnet->nick != NULL) {
178                 g_free(conn->nick);
179                 conn->nick = g_strdup(chatnet->nick);;
180         }
181         if (chatnet->username != NULL) {
182                 g_free(conn->username);
183                 conn->username = g_strdup(chatnet->username);;
184         }
185         if (chatnet->realname != NULL) {
186                 g_free(conn->realname);
187                 conn->realname = g_strdup(chatnet->realname);;
188         }
189         if (chatnet->own_host != NULL) {
190                 conn_set_ip(conn, chatnet->own_host,
191                             &chatnet->own_ip4, &chatnet->own_ip6);
192         }
193
194         signal_emit("server setup fill chatnet", 2, conn, chatnet);
195 }
196
197 static SERVER_CONNECT_REC *
198 create_addr_conn(int chat_type, const char *address, int port,
199                  const char *chatnet, const char *password,
200                  const char *nick)
201 {
202         CHAT_PROTOCOL_REC *proto;
203         SERVER_CONNECT_REC *conn;
204         SERVER_SETUP_REC *sserver;
205         CHATNET_REC *chatnetrec;
206
207         g_return_val_if_fail(address != NULL, NULL);
208
209         sserver = server_setup_find(address, port, chatnet);
210         if (sserver != NULL) {
211                 if (chat_type < 0)
212                         chat_type = sserver->chat_type;
213                 else if (chat_type != sserver->chat_type)
214                         sserver = NULL;
215         }
216
217         proto = chat_type >= 0 ? chat_protocol_find_id(chat_type) :
218                 chat_protocol_get_default();
219
220         conn = proto->create_server_connect();
221         server_connect_ref(conn);
222
223         conn->chat_type = proto->id;
224         if (chatnet != NULL && *chatnet != '\0')
225                 conn->chatnet = g_strdup(chatnet);
226
227         /* fill in the defaults */
228         server_setup_fill(conn, address, port);
229
230         /* fill the rest from chat network settings */
231         chatnetrec = chatnet != NULL ? chatnet_find(chatnet) :
232                 (sserver == NULL || sserver->chatnet == NULL ? NULL :
233                  chatnet_find(sserver->chatnet));
234         if (chatnetrec != NULL)
235                 server_setup_fill_chatnet(conn, chatnetrec);
236
237         /* fill the information from setup */
238         if (sserver != NULL)
239                 server_setup_fill_server(conn, sserver);
240
241         /* nick / password given in command line overrides all settings */
242         if (password && *password) {
243                 g_free_not_null(conn->password);
244                 conn->password = g_strdup(password);
245         }
246         if (nick && *nick) {
247                 g_free_not_null(conn->nick);
248                 conn->nick = g_strdup(nick);
249         }
250
251         return conn;
252 }
253
254 /* Connect to server where last connect succeeded (or we haven't tried to
255    connect yet). If there's no such server, connect to server where we
256    haven't connected for the longest time */
257 static SERVER_CONNECT_REC *
258 create_chatnet_conn(const char *dest, int port,
259                     const char *password, const char *nick)
260 {
261         SERVER_SETUP_REC *bestrec;
262         GSList *tmp;
263         time_t now, besttime;
264
265         now = time(NULL);
266         bestrec = NULL; besttime = now;
267         for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
268                 SERVER_SETUP_REC *rec = tmp->data;
269
270                 if (rec->chatnet == NULL ||
271                     g_strcasecmp(rec->chatnet, dest) != 0)
272                         continue;
273
274                 if (!rec->last_failed) {
275                         bestrec = rec;
276                         break;
277                 }
278
279                 if (bestrec == NULL || besttime > rec->last_connect) {
280                         bestrec = rec;
281                         besttime = rec->last_connect;
282                 }
283         }
284
285         return bestrec == NULL ? NULL :
286                 create_addr_conn(bestrec->chat_type, bestrec->address, 0,
287                                  dest, NULL, nick);
288 }
289
290 /* Create server connection record. `dest' is required, rest can be NULL.
291    `dest' is either a server address or chat network */
292 SERVER_CONNECT_REC *
293 server_create_conn(int chat_type, const char *dest, int port,
294                    const char *chatnet, const char *password,
295                    const char *nick)
296 {
297         SERVER_CONNECT_REC *rec;
298         CHATNET_REC *chatrec;
299
300         g_return_val_if_fail(dest != NULL, NULL);
301
302         chatrec = chatnet_find(dest);
303         if (chatrec != NULL) {
304                 rec = create_chatnet_conn(chatrec->name, port, password, nick);
305                 if (rec != NULL)
306                         return rec;
307         }
308
309         chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
310         if (chatrec != NULL)
311                 chatnet = chatrec->name;
312
313         return create_addr_conn(chat_type, dest, port,
314                                 chatnet, password, nick);
315 }
316
317 /* Find matching server from setup. Try to find record with a same port,
318    but fallback to any server with the same address. */
319 SERVER_SETUP_REC *server_setup_find(const char *address, int port,
320                                     const char *chatnet)
321 {
322         SERVER_SETUP_REC *server;
323         GSList *tmp;
324
325         g_return_val_if_fail(address != NULL, NULL);
326
327         server = NULL;
328         for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
329                 SERVER_SETUP_REC *rec = tmp->data;
330
331                 if (g_strcasecmp(rec->address, address) == 0 &&
332                     (chatnet == NULL || rec->chatnet == NULL ||
333                      g_strcasecmp(rec->chatnet, chatnet) == 0)) {
334                         server = rec;
335                         if (rec->port == port)
336                                 break;
337                 }
338         }
339
340         return server;
341 }
342
343 /* Find matching server from setup. Ports must match or NULL is returned. */
344 SERVER_SETUP_REC *server_setup_find_port(const char *address, int port)
345 {
346         SERVER_SETUP_REC *rec;
347
348         rec = server_setup_find(address, port, NULL);
349         return rec == NULL || rec->port != port ? NULL : rec;
350 }
351
352 static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
353 {
354         SERVER_SETUP_REC *rec;
355         CHATNET_REC *chatnetrec;
356         char *server, *chatnet, *family;
357         int port;
358
359         g_return_val_if_fail(node != NULL, NULL);
360
361         server = config_node_get_str(node, "address", NULL);
362         if (server == NULL)
363                 return NULL;
364
365         port = config_node_get_int(node, "port", 0);
366         if (server_setup_find_port(server, port) != NULL) {
367                 /* already exists - don't let it get there twice or
368                    server reconnects will screw up! */
369                 return NULL;
370         }
371
372         rec = NULL;
373         chatnet = config_node_get_str(node, "chatnet", NULL);
374
375         chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
376         if (chatnetrec == NULL && chatnet != NULL) {
377                 /* chat network not found, create it. */
378                 chatnetrec = chat_protocol_get_default()->create_chatnet();
379                 chatnetrec->chat_type = chat_protocol_get_default()->id;
380                 chatnetrec->name = g_strdup(chatnet);
381                 chatnet_create(chatnetrec);
382         }
383
384         family = config_node_get_str(node, "family", "");
385
386         rec = CHAT_PROTOCOL(chatnetrec)->create_server_setup();
387         rec->type = module_get_uniq_id("SERVER SETUP", 0);
388         rec->chat_type = CHAT_PROTOCOL(chatnetrec)->id;
389         rec->chatnet = chatnetrec == NULL ? NULL : g_strdup(chatnetrec->name);
390         rec->family = g_strcasecmp(family, "inet6") == 0 ? AF_INET6 :
391                 (g_strcasecmp(family, "inet") == 0 ? AF_INET : 0);
392         rec->address = g_strdup(server);
393         rec->password = g_strdup(config_node_get_str(node, "password", NULL));
394         rec->port = port;
395         rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
396         rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE);
397         rec->own_host = g_strdup(config_node_get_str(node, "own_host", NULL));
398
399         signal_emit("server setup read", 2, rec, node);
400
401         setupservers = g_slist_append(setupservers, rec);
402         return rec;
403 }
404
405 static void server_setup_save(SERVER_SETUP_REC *rec)
406 {
407         CONFIG_NODE *parentnode, *node;
408         int index;
409
410         index = g_slist_index(setupservers, rec);
411
412         parentnode = iconfig_node_traverse("(servers", TRUE);
413         node = config_node_nth(parentnode, index);
414         if (node == NULL)
415                 node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
416
417         iconfig_node_clear(node);
418         iconfig_node_set_str(node, "address", rec->address);
419         iconfig_node_set_str(node, "chatnet", rec->chatnet);
420
421         iconfig_node_set_int(node, "port", rec->port);
422         iconfig_node_set_str(node, "password", rec->password);
423         iconfig_node_set_str(node, "own_host", rec->own_host);
424
425         iconfig_node_set_str(node, "family",
426                              rec->family == AF_INET6 ? "inet6" :
427                              rec->family == AF_INET ? "inet" : NULL);
428
429         if (rec->autoconnect)
430                 iconfig_node_set_bool(node, "autoconnect", TRUE);
431         if (rec->no_proxy)
432                 iconfig_node_set_bool(node, "no_proxy", TRUE);
433
434         signal_emit("server setup saved", 2, rec, node);
435 }
436
437 static void server_setup_remove_config(SERVER_SETUP_REC *rec)
438 {
439         CONFIG_NODE *node;
440         int index;
441
442         node = iconfig_node_traverse("servers", FALSE);
443         if (node != NULL) {
444                 index = g_slist_index(setupservers, rec);
445                 iconfig_node_list_remove(node, index);
446         }
447 }
448
449 static void server_setup_destroy(SERVER_SETUP_REC *rec)
450 {
451         setupservers = g_slist_remove(setupservers, rec);
452         signal_emit("server setup destroyed", 1, rec);
453
454         g_free_not_null(rec->own_host);
455         g_free_not_null(rec->own_ip4);
456         g_free_not_null(rec->own_ip6);
457         g_free_not_null(rec->chatnet);
458         g_free_not_null(rec->password);
459         g_free(rec->address);
460         g_free(rec);
461 }
462
463 void server_setup_add(SERVER_SETUP_REC *rec)
464 {
465         rec->type = module_get_uniq_id("SERVER SETUP", 0);
466         if (g_slist_find(setupservers, rec) == NULL)
467                 setupservers = g_slist_append(setupservers, rec);
468         server_setup_save(rec);
469
470         signal_emit("server setup updated", 1, rec);
471 }
472
473 void server_setup_remove(SERVER_SETUP_REC *rec)
474 {
475         server_setup_remove_config(rec);
476         server_setup_destroy(rec);
477 }
478
479 static void read_servers(void)
480 {
481         CONFIG_NODE *node;
482         GSList *tmp;
483
484         while (setupservers != NULL)
485                 server_setup_destroy(setupservers->data);
486
487         /* Read servers */
488         node = iconfig_node_traverse("servers", FALSE);
489         if (node != NULL) {
490                 tmp = config_node_first(node->value);
491                 for (; tmp != NULL; tmp = config_node_next(tmp))
492                         server_setup_read(tmp->data);
493         }
494 }
495
496 static void read_settings(void)
497 {
498         if (old_source_host == NULL ||
499             strcmp(old_source_host, settings_get_str("hostname")) != 0) {
500                 g_free_not_null(old_source_host);
501                 old_source_host = g_strdup(settings_get_str("hostname"));
502
503                 source_host_ok = FALSE;
504                 get_source_host_ip();
505         }
506 }
507
508 void servers_setup_init(void)
509 {
510         settings_add_str("server", "hostname", "");
511
512         settings_add_str("server", "nick", NULL);
513         settings_add_str("server", "user_name", NULL);
514         settings_add_str("server", "real_name", NULL);
515
516         settings_add_bool("proxy", "use_proxy", FALSE);
517         settings_add_str("proxy", "proxy_address", "");
518         settings_add_int("proxy", "proxy_port", 6667);
519         settings_add_str("proxy", "proxy_string", "CONNECT %s %d");
520         settings_add_str("proxy", "proxy_string_after", "");
521         settings_add_str("proxy", "proxy_password", "");
522
523         setupservers = NULL;
524         source_host_ip4 = source_host_ip6 = NULL;
525         old_source_host = NULL;
526         read_settings();
527
528         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
529         signal_add("setup reread", (SIGNAL_FUNC) read_servers);
530         signal_add("irssi init read settings", (SIGNAL_FUNC) read_servers);
531 }
532
533 void servers_setup_deinit(void)
534 {
535         g_free_not_null(source_host_ip4);
536         g_free_not_null(source_host_ip6);
537         g_free_not_null(old_source_host);
538
539         while (setupservers != NULL)
540                 server_setup_destroy(setupservers->data);
541
542         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
543         signal_remove("setup reread", (SIGNAL_FUNC) read_servers);
544         signal_remove("irssi init read settings", (SIGNAL_FUNC) read_servers);
545
546         module_uniq_destroy("SERVER SETUP");
547 }