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