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