1 /*
2 server.c : irssi
3
4 Copyright (C) 1999-2000 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 "commands.h"
24 #include "net-disconnect.h"
25 #include "net-nonblock.h"
26 #include "net-sendbuffer.h"
27 #include "misc.h"
28 #include "rawlog.h"
29 #include "settings.h"
30
31 #include "chat-protocols.h"
32 #include "servers.h"
33 #include "servers-reconnect.h"
34 #include "servers-setup.h"
35 #include "channels.h"
36 #include "queries.h"
37
38 GSList *servers, *lookup_servers;
39
40 /* connection to server failed */
server_connect_failed(SERVER_REC * server,const char * msg)41 void server_connect_failed(SERVER_REC *server, const char *msg)
42 {
43 g_return_if_fail(IS_SERVER(server));
44
45 lookup_servers = g_slist_remove(lookup_servers, server);
46
47 signal_emit("server connect failed", 2, server, msg);
48
49 if (server->connect_tag != -1) {
50 g_source_remove(server->connect_tag);
51 server->connect_tag = -1;
52 }
53 if (server->handle != NULL) {
54 net_sendbuffer_destroy(server->handle, TRUE);
55 server->handle = NULL;
56 }
57
58 if (server->connect_pipe[0] != NULL) {
59 g_io_channel_shutdown(server->connect_pipe[0], TRUE, NULL);
60 g_io_channel_unref(server->connect_pipe[0]);
61 g_io_channel_shutdown(server->connect_pipe[1], TRUE, NULL);
62 g_io_channel_unref(server->connect_pipe[1]);
63 server->connect_pipe[0] = NULL;
64 server->connect_pipe[1] = NULL;
65 }
66
67 server_unref(server);
68 }
69
70 /* generate tag from server's address */
server_create_address_tag(const char * address)71 static char *server_create_address_tag(const char *address)
72 {
73 const char *start, *end;
74
75 g_return_val_if_fail(address != NULL, NULL);
76
77 /* try to generate a reasonable server tag */
78 if (strchr(address, '.') == NULL) {
79 start = end = NULL;
80 } else if (g_ascii_strncasecmp(address, "irc", 3) == 0 ||
81 g_ascii_strncasecmp(address, "chat", 4) == 0) {
82 /* irc-2.cs.hut.fi -> hut, chat.bt.net -> bt */
83 end = strrchr(address, '.');
84 start = end-1;
85 while (start > address && *start != '.') start--;
86 } else {
87 /* efnet.cs.hut.fi -> efnet */
88 end = strchr(address, '.');
89 start = end;
90 }
91
92 if (start == end) start = address; else start++;
93 if (end == NULL) end = address + strlen(address);
94
95 return g_strndup(start, (int) (end-start));
96 }
97
98 /* create unique tag for server. prefer ircnet's name or
99 generate it from server's address */
server_create_tag(SERVER_CONNECT_REC * conn)100 static char *server_create_tag(SERVER_CONNECT_REC *conn)
101 {
102 GString *str;
103 char *tag;
104 int num;
105
106 g_return_val_if_fail(IS_SERVER_CONNECT(conn), NULL);
107
108 tag = conn->chatnet != NULL && *conn->chatnet != '\0' ?
109 g_strdup(conn->chatnet) :
110 server_create_address_tag(conn->address);
111
112 if (conn->tag != NULL && server_find_tag(conn->tag) == NULL &&
113 server_find_lookup_tag(conn->tag) == NULL &&
114 strncmp(conn->tag, tag, strlen(tag)) == 0) {
115 /* use the existing tag if it begins with the same ID -
116 this is useful when you have several connections to
117 same server and you want to keep the same tags with
118 the servers (or it would cause problems when rejoining
119 /LAYOUT SAVEd channels). */
120 g_free(tag);
121 return g_strdup(conn->tag);
122 }
123
124
125 /* then just append numbers after tag until unused is found.. */
126 str = g_string_new(tag);
127
128 num = 2;
129 while (server_find_tag(str->str) != NULL ||
130 server_find_lookup_tag(str->str) != NULL) {
131 g_string_printf(str, "%s%d", tag, num);
132 num++;
133 }
134 g_free(tag);
135
136 tag = str->str;
137 g_string_free(str, FALSE);
138 return tag;
139 }
140
141 /* Connection to server finished, fill the rest of the fields */
server_connect_finished(SERVER_REC * server)142 void server_connect_finished(SERVER_REC *server)
143 {
144 server->connect_time = time(NULL);
145
146 servers = g_slist_append(servers, server);
147 signal_emit("server connected", 1, server);
148 }
149
server_connect_callback_init(SERVER_REC * server,GIOChannel * handle)150 static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle)
151 {
152 int error;
153
154 g_return_if_fail(IS_SERVER(server));
155
156 error = net_geterror(handle);
157 if (error != 0) {
158 server->connection_lost = TRUE;
159 server_connect_failed(server, g_strerror(error));
160 return;
161 }
162
163 lookup_servers = g_slist_remove(lookup_servers, server);
164 g_source_remove(server->connect_tag);
165 server->connect_tag = -1;
166
167 server_connect_finished(server);
168 }
169
server_connect_callback_init_ssl(SERVER_REC * server,GIOChannel * handle)170 static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *handle)
171 {
172 int error;
173
174 g_return_if_fail(IS_SERVER(server));
175
176 error = irssi_ssl_handshake(handle);
177 if (error == -1) {
178 server->connection_lost = TRUE;
179 server_connect_failed(server, NULL);
180 return;
181 }
182 if (error & 1) {
183 if (server->connect_tag != -1)
184 g_source_remove(server->connect_tag);
185 server->connect_tag = g_input_add(handle, error == 1 ? G_INPUT_READ : G_INPUT_WRITE,
186 (GInputFunction)
187 server_connect_callback_init_ssl,
188 server);
189 return;
190 }
191
192 lookup_servers = g_slist_remove(lookup_servers, server);
193 if (server->connect_tag != -1) {
194 g_source_remove(server->connect_tag);
195 server->connect_tag = -1;
196 }
197
198 server_connect_finished(server);
199 }
200
server_real_connect(SERVER_REC * server,IPADDR * ip,const char * unix_socket)201 static void server_real_connect(SERVER_REC *server, IPADDR *ip,
202 const char *unix_socket)
203 {
204 GIOChannel *handle;
205 IPADDR *own_ip = NULL;
206 const char *errmsg;
207 char *errmsg2;
208 char ipaddr[MAX_IP_LEN];
209 int port = 0;
210
211 g_return_if_fail(ip != NULL || unix_socket != NULL);
212
213 signal_emit("server connecting", 2, server, ip);
214
215 if (server->connrec->no_connect)
216 return;
217
218 if (ip != NULL) {
219 own_ip = IPADDR_IS_V6(ip) ? server->connrec->own_ip6 : server->connrec->own_ip4;
220 port = server->connrec->proxy != NULL ?
221 server->connrec->proxy_port : server->connrec->port;
222 handle = net_connect_ip(ip, port, own_ip);
223 } else {
224 handle = net_connect_unix(unix_socket);
225 }
226
227 if (server->connrec->use_tls && handle != NULL) {
228 server->handle = net_sendbuffer_create(handle, 0);
229 handle = net_start_ssl(server);
230 if (handle == NULL) {
231 net_sendbuffer_destroy(server->handle, TRUE);
232 server->handle = NULL;
233 } else {
234 server->handle->handle = handle;
235 }
236 }
237
238 if (handle == NULL) {
239 /* failed */
240 errmsg = g_strerror(errno);
241 errmsg2 = NULL;
242 if (errno == EADDRNOTAVAIL) {
243 if (own_ip != NULL) {
244 /* show the IP which is causing the error */
245 net_ip2host(own_ip, ipaddr);
246 errmsg2 = g_strconcat(errmsg, ": ", ipaddr, NULL);
247 }
248 server->no_reconnect = TRUE;
249 }
250 if (server->connrec->use_tls && errno == ENOSYS)
251 server->no_reconnect = TRUE;
252
253 server->connection_lost = TRUE;
254 server_connect_failed(server, errmsg2 ? errmsg2 : errmsg);
255 g_free(errmsg2);
256 } else {
257 if (!server->connrec->use_tls)
258 server->handle = net_sendbuffer_create(handle, 0);
259 if (server->connrec->use_tls)
260 server_connect_callback_init_ssl(server, handle);
261 else
262 server->connect_tag =
263 g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
264 (GInputFunction)
265 server_connect_callback_init,
266 server);
267 }
268 }
269
server_connect_callback_readpipe(SERVER_REC * server)270 static void server_connect_callback_readpipe(SERVER_REC *server)
271 {
272 RESOLVED_IP_REC iprec;
273 IPADDR *ip;
274 const char *errormsg;
275 char *servername = NULL;
276
277 g_source_remove(server->connect_tag);
278 server->connect_tag = -1;
279
280 net_gethostbyname_return(server->connect_pipe[0], &iprec);
281
282 g_io_channel_shutdown(server->connect_pipe[0], TRUE, NULL);
283 g_io_channel_unref(server->connect_pipe[0]);
284 g_io_channel_shutdown(server->connect_pipe[1], TRUE, NULL);
285 g_io_channel_unref(server->connect_pipe[1]);
286
287 server->connect_pipe[0] = NULL;
288 server->connect_pipe[1] = NULL;
289
290 /* figure out if we should use IPv4 or v6 address */
291 if (iprec.error != 0) {
292 /* error */
293 ip = NULL;
294 } else if (server->connrec->family == AF_INET) {
295 /* force IPv4 connection */
296 ip = iprec.ip4.family == 0 ? NULL : &iprec.ip4;
297 servername = iprec.host4;
298 } else if (server->connrec->family == AF_INET6) {
299 /* force IPv6 connection */
300 ip = iprec.ip6.family == 0 ? NULL : &iprec.ip6;
301 servername = iprec.host6;
302 } else {
303 /* pick the one that was found, or if both do it like
304 /SET resolve_prefer_ipv6 says. */
305 if (iprec.ip4.family == 0 ||
306 (iprec.ip6.family != 0 &&
307 settings_get_bool("resolve_prefer_ipv6"))) {
308 ip = &iprec.ip6;
309 servername = iprec.host6;
310 } else {
311 ip = &iprec.ip4;
312 servername = iprec.host4;
313 }
314 }
315
316 if (ip != NULL) {
317 /* host lookup ok */
318 if (servername) {
319 g_free(server->connrec->address);
320 server->connrec->address = g_strdup(servername);
321 }
322 server_real_connect(server, ip, NULL);
323 errormsg = NULL;
324 } else {
325 if (iprec.error == 0 || net_hosterror_notfound(iprec.error)) {
326 /* IP wasn't found for the host, don't try to
327 reconnect back to this server */
328 server->dns_error = TRUE;
329 }
330
331 if (iprec.error == 0) {
332 /* forced IPv4 or IPv6 address but it wasn't found */
333 errormsg = server->connrec->family == AF_INET ?
334 "IPv4 address not found for host" :
335 "IPv6 address not found for host";
336 } else {
337 /* gethostbyname() failed */
338 errormsg = iprec.errorstr != NULL ? iprec.errorstr :
339 "Host lookup failed";
340 }
341
342 server->connection_lost = TRUE;
343 server_connect_failed(server, errormsg);
344 }
345
346 g_free(iprec.errorstr);
347 g_free(iprec.host4);
348 g_free(iprec.host6);
349 }
350
server_connect(SERVER_CONNECT_REC * conn)351 SERVER_REC *server_connect(SERVER_CONNECT_REC *conn)
352 {
353 CHAT_PROTOCOL_REC *proto;
354 SERVER_REC *server;
355
356 proto = CHAT_PROTOCOL(conn);
357 server = proto->server_init_connect(conn);
358 proto->server_connect(server);
359
360 return server;
361 }
362
363 /* initializes server record but doesn't start connecting */
server_connect_init(SERVER_REC * server)364 void server_connect_init(SERVER_REC *server)
365 {
366 const char *str;
367
368 g_return_if_fail(server != NULL);
369
370 MODULE_DATA_INIT(server);
371 server->type = module_get_uniq_id("SERVER", 0);
372 server_ref(server);
373
374 server->nick = g_strdup(server->connrec->nick);
375 if (server->connrec->username == NULL || *server->connrec->username == '\0') {
376 g_free_not_null(server->connrec->username);
377
378 str = g_get_user_name();
379 if (*str == '\0') str = "unknown";
380 server->connrec->username = g_strdup(str);
381 }
382 if (server->connrec->realname == NULL || *server->connrec->realname == '\0') {
383 g_free_not_null(server->connrec->realname);
384
385 str = g_get_real_name();
386 if (*str == '\0') str = server->connrec->username;
387 server->connrec->realname = g_strdup(str);
388 }
389
390 server->tag = server_create_tag(server->connrec);
391 server->connect_tag = -1;
392 }
393
394 /* starts connecting to server */
server_start_connect(SERVER_REC * server)395 int server_start_connect(SERVER_REC *server)
396 {
397 const char *connect_address;
398 int fd[2];
399
400 g_return_val_if_fail(server != NULL, FALSE);
401 if (!server->connrec->unix_socket && server->connrec->port <= 0)
402 return FALSE;
403
404 server->rawlog = rawlog_create();
405
406 if (server->connrec->connect_handle != NULL) {
407 /* already connected */
408 GIOChannel *handle = server->connrec->connect_handle;
409
410 server->connrec->connect_handle = NULL;
411 server->handle = net_sendbuffer_create(handle, 0);
412 server_connect_finished(server);
413 } else if (server->connrec->unix_socket) {
414 /* connect with unix socket */
415 server_real_connect(server, NULL, server->connrec->address);
416 } else {
417 /* resolve host name */
418 if (pipe(fd) != 0) {
419 g_warning("server_connect(): pipe() failed.");
420 g_free(server->tag);
421 g_free(server->nick);
422 return FALSE;
423 }
424
425 server->connect_pipe[0] = g_io_channel_new(fd[0]);
426 server->connect_pipe[1] = g_io_channel_new(fd[1]);
427
428 connect_address = server->connrec->proxy != NULL ?
429 server->connrec->proxy : server->connrec->address;
430 server->connect_pid =
431 net_gethostbyname_nonblock(connect_address,
432 server->connect_pipe[1],
433 settings_get_bool("resolve_reverse_lookup"));
434 server->connect_tag =
435 g_input_add(server->connect_pipe[0], G_INPUT_READ,
436 (GInputFunction)
437 server_connect_callback_readpipe,
438 server);
439
440 server->connect_time = time(NULL);
441 lookup_servers = g_slist_append(lookup_servers, server);
442
443 signal_emit("server looking", 1, server);
444 }
445 return TRUE;
446 }
447
server_remove_channels(SERVER_REC * server)448 static int server_remove_channels(SERVER_REC *server)
449 {
450 GSList *tmp, *next;
451 int found;
452
453 g_return_val_if_fail(server != NULL, FALSE);
454
455 found = FALSE;
456 for (tmp = server->channels; tmp != NULL; tmp = next) {
457 CHANNEL_REC *channel = tmp->data;
458
459 next = tmp->next;
460 channel_destroy(channel);
461 found = TRUE;
462 }
463
464 while (server->queries != NULL)
465 query_change_server(server->queries->data, NULL);
466
467 g_slist_free(server->channels);
468 g_slist_free(server->queries);
469
470 return found;
471 }
472
server_disconnect(SERVER_REC * server)473 void server_disconnect(SERVER_REC *server)
474 {
475 g_return_if_fail(IS_SERVER(server));
476
477 if (server->disconnected)
478 return;
479
480 if (server->connect_tag != -1) {
481 /* still connecting to server.. */
482 if (server->connect_pid != -1)
483 net_disconnect_nonblock(server->connect_pid);
484 server_connect_failed(server, NULL);
485 return;
486 }
487
488 servers = g_slist_remove(servers, server);
489
490 server->disconnected = TRUE;
491 signal_emit("server disconnected", 1, server);
492
493 /* we used to destroy the handle here but it may be still in
494 use during signal processing, so destroy it on unref
495 instead */
496
497 if (server->readtag > 0) {
498 g_source_remove(server->readtag);
499 server->readtag = -1;
500 }
501
502 server_unref(server);
503 }
504
server_ref(SERVER_REC * server)505 void server_ref(SERVER_REC *server)
506 {
507 g_return_if_fail(IS_SERVER(server));
508
509 server->refcount++;
510 }
511
server_unref(SERVER_REC * server)512 int server_unref(SERVER_REC *server)
513 {
514 int chans;
515
516 g_return_val_if_fail(IS_SERVER(server), FALSE);
517
518 if (--server->refcount > 0)
519 return TRUE;
520
521 if (g_slist_find(servers, server) != NULL) {
522 g_warning("Non-referenced server wasn't disconnected");
523 server_disconnect(server);
524 return TRUE;
525 }
526
527 /* close all channels */
528 chans = server_remove_channels(server);
529
530 /* since module initialisation uses server connected, only let
531 them know that the object got destroyed if the server was
532 disconnected */
533 if (server->disconnected) {
534 signal_emit("server destroyed", 1, server);
535 }
536
537 if (server->handle != NULL) {
538 if (!chans || server->connection_lost)
539 net_sendbuffer_destroy(server->handle, TRUE);
540 else {
541 /* we were on some channels, try to let the server
542 disconnect so that our quit message is guaranteed
543 to get displayed */
544 net_disconnect_later(net_sendbuffer_handle(server->handle));
545 net_sendbuffer_destroy(server->handle, FALSE);
546 }
547 server->handle = NULL;
548 }
549
550 MODULE_DATA_DEINIT(server);
551 server_connect_unref(server->connrec);
552 if (server->rawlog != NULL) rawlog_destroy(server->rawlog);
553 g_free(server->version);
554 g_free(server->away_reason);
555 g_free(server->nick);
556 g_free(server->tag);
557
558 server->type = 0;
559 g_free(server);
560 return FALSE;
561 }
562
server_find_tag(const char * tag)563 SERVER_REC *server_find_tag(const char *tag)
564 {
565 GSList *tmp;
566
567 g_return_val_if_fail(tag != NULL, NULL);
568 if (*tag == '\0') return NULL;
569
570 for (tmp = servers; tmp != NULL; tmp = tmp->next) {
571 SERVER_REC *server = tmp->data;
572
573 if (g_ascii_strcasecmp(server->tag, tag) == 0)
574 return server;
575 }
576
577 return NULL;
578 }
579
server_find_lookup_tag(const char * tag)580 SERVER_REC *server_find_lookup_tag(const char *tag)
581 {
582 GSList *tmp;
583
584 g_return_val_if_fail(tag != NULL, NULL);
585 if (*tag == '\0') return NULL;
586
587 for (tmp = lookup_servers; tmp != NULL; tmp = tmp->next) {
588 SERVER_REC *server = tmp->data;
589
590 if (g_ascii_strcasecmp(server->tag, tag) == 0)
591 return server;
592 }
593
594 return NULL;
595 }
596
server_find_chatnet(const char * chatnet)597 SERVER_REC *server_find_chatnet(const char *chatnet)
598 {
599 GSList *tmp;
600
601 g_return_val_if_fail(chatnet != NULL, NULL);
602 if (*chatnet == '\0') return NULL;
603
604 for (tmp = servers; tmp != NULL; tmp = tmp->next) {
605 SERVER_REC *server = tmp->data;
606
607 if (server->connrec->chatnet != NULL &&
608 g_ascii_strcasecmp(server->connrec->chatnet, chatnet) == 0)
609 return server;
610 }
611
612 return NULL;
613 }
614
server_connect_ref(SERVER_CONNECT_REC * conn)615 void server_connect_ref(SERVER_CONNECT_REC *conn)
616 {
617 conn->refcount++;
618 }
619
server_connect_unref(SERVER_CONNECT_REC * conn)620 void server_connect_unref(SERVER_CONNECT_REC *conn)
621 {
622 g_return_if_fail(IS_SERVER_CONNECT(conn));
623
624 if (--conn->refcount > 0)
625 return;
626 if (conn->refcount < 0) {
627 g_warning("Connection '%s' refcount = %d",
628 conn->tag, conn->refcount);
629 }
630
631 CHAT_PROTOCOL(conn)->destroy_server_connect(conn);
632
633 if (conn->connect_handle != NULL)
634 net_disconnect(conn->connect_handle);
635
636 g_free_not_null(conn->proxy);
637 g_free_not_null(conn->proxy_string);
638 g_free_not_null(conn->proxy_string_after);
639 g_free_not_null(conn->proxy_password);
640
641 g_free_not_null(conn->tag);
642 g_free_not_null(conn->address);
643 g_free_not_null(conn->chatnet);
644
645 g_free_not_null(conn->own_ip4);
646 g_free_not_null(conn->own_ip6);
647
648 g_free_not_null(conn->password);
649 g_free_not_null(conn->nick);
650 g_free_not_null(conn->username);
651 g_free_not_null(conn->realname);
652
653 g_free_not_null(conn->tls_cert);
654 g_free_not_null(conn->tls_pkey);
655 g_free_not_null(conn->tls_pass);
656 g_free_not_null(conn->tls_cafile);
657 g_free_not_null(conn->tls_capath);
658 g_free_not_null(conn->tls_ciphers);
659 g_free_not_null(conn->tls_pinned_cert);
660 g_free_not_null(conn->tls_pinned_pubkey);
661
662 g_free_not_null(conn->channels);
663 g_free_not_null(conn->away_reason);
664
665 conn->type = 0;
666 g_free(conn);
667 }
668
server_change_nick(SERVER_REC * server,const char * nick)669 void server_change_nick(SERVER_REC *server, const char *nick)
670 {
671 g_free(server->nick);
672 server->nick = g_strdup(nick);
673
674 signal_emit("server nick changed", 1, server);
675 }
676
677 /* Update own IPv4 and IPv6 records */
server_connect_own_ip_save(SERVER_CONNECT_REC * conn,IPADDR * ip4,IPADDR * ip6)678 void server_connect_own_ip_save(SERVER_CONNECT_REC *conn,
679 IPADDR *ip4, IPADDR *ip6)
680 {
681 if (ip4 == NULL || ip4->family == 0)
682 g_free_and_null(conn->own_ip4);
683 if (ip6 == NULL || ip6->family == 0)
684 g_free_and_null(conn->own_ip6);
685
686 if (ip4 != NULL && ip4->family != 0) {
687 /* IPv4 address was found */
688 if (conn->own_ip4 == NULL)
689 conn->own_ip4 = g_new0(IPADDR, 1);
690 memcpy(conn->own_ip4, ip4, sizeof(IPADDR));
691 }
692
693 if (ip6 != NULL && ip6->family != 0) {
694 /* IPv6 address was found */
695 if (conn->own_ip6 == NULL)
696 conn->own_ip6 = g_new0(IPADDR, 1);
697 memcpy(conn->own_ip6, ip6, sizeof(IPADDR));
698 }
699 }
700
701 /* `optlist' should contain only one unknown key - the server tag.
702 returns NULL if there was unknown -option */
cmd_options_get_server(const char * cmd,GHashTable * optlist,SERVER_REC * defserver)703 SERVER_REC *cmd_options_get_server(const char *cmd,
704 GHashTable *optlist,
705 SERVER_REC *defserver)
706 {
707 SERVER_REC *server;
708 GList *list;
709
710 /* get all the options, then remove the known ones. there should
711 be only one left - the server tag. */
712 list = optlist_remove_known(cmd, optlist);
713 if (list == NULL)
714 return defserver;
715
716 server = server_find_tag(list->data);
717 if (server == NULL || list->next != NULL) {
718 /* unknown option (not server tag) */
719 signal_emit("error command", 2,
720 GINT_TO_POINTER(CMDERR_OPTION_UNKNOWN),
721 server == NULL ? list->data : list->next->data);
722 signal_stop();
723
724 server = NULL;
725 }
726
727 g_list_free(list);
728 return server;
729 }
730
disconnect_servers(GSList * servers,int chat_type)731 static void disconnect_servers(GSList *servers, int chat_type)
732 {
733 GSList *tmp, *next;
734
735 for (tmp = servers; tmp != NULL; tmp = next) {
736 SERVER_REC *rec = tmp->data;
737
738 next = tmp->next;
739 if (rec->chat_type == chat_type)
740 server_disconnect(rec);
741 }
742 }
743
sig_chat_protocol_deinit(CHAT_PROTOCOL_REC * proto)744 static void sig_chat_protocol_deinit(CHAT_PROTOCOL_REC *proto)
745 {
746 disconnect_servers(servers, proto->id);
747 disconnect_servers(lookup_servers, proto->id);
748 }
749
servers_init(void)750 void servers_init(void)
751 {
752 settings_add_bool("server", "resolve_prefer_ipv6", FALSE);
753 settings_add_bool("server", "resolve_reverse_lookup", FALSE);
754 lookup_servers = servers = NULL;
755
756 signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
757
758 servers_reconnect_init();
759 servers_setup_init();
760 }
761
servers_deinit(void)762 void servers_deinit(void)
763 {
764 signal_remove("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
765
766 servers_setup_deinit();
767 servers_reconnect_deinit();
768
769 module_uniq_destroy("SERVER");
770 module_uniq_destroy("SERVER CONNECT");
771 }
772