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