1 /* X-Chat
2  * Copyright (C) 1998 Peter Zelezny.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 
25 #ifndef WIN32
26 #include <unistd.h>
27 #endif
28 
29 #include "hexchat.h"
30 #include "cfgfiles.h"
31 #include "fe.h"
32 #include "server.h"
33 #include "text.h"
34 #include "util.h" /* token_foreach */
35 #include "hexchatc.h"
36 
37 #include "servlist.h"
38 
39 
40 struct defaultserver
41 {
42 	char *network;
43 	char *host;
44 	char *channel;
45 	char *charset;
46 	int loginmode;		/* default authentication type */
47 	char *connectcmd;	/* default connect command - should only be used for rare login types, paired with LOGIN_CUSTOM */
48 	gboolean ssl;
49 };
50 
51 static const struct defaultserver def[] =
52 {
53 	{"2600net",	0},
54 	/* Invalid hostname in cert */
55 	{0,			"irc.2600.net"},
56 
57 	{"ACN", 0, 0, 0, LOGIN_SASL, 0, TRUE},
58 	{0,			"global.acn.gr"},
59 
60 	{"AfterNET", 0, 0, 0, LOGIN_SASL, 0, TRUE},
61 	{0,			"irc.afternet.org"},
62 
63 	{"Aitvaras",	0},
64 #ifdef USE_OPENSSL
65 	{0,			"irc.data.lt/+6668"},
66 	{0,			"irc.omicron.lt/+6668"},
67 	{0,			"irc.vub.lt/+6668"},
68 #endif
69 	{0,			"irc.data.lt"},
70 	{0,			"irc.omicron.lt"},
71 	{0,			"irc.vub.lt"},
72 
73 	{"Anthrochat", 0, 0, 0, 0, 0, TRUE},
74 	{0,			"irc.anthrochat.net"},
75 
76 	{"ARCNet",	0},
77 	{0,			"arcnet-irc.org"},
78 
79 	{"AustNet",	0},
80 	{0,			"irc.austnet.org"},
81 
82 	{"AzzurraNet",	0},
83 	{0,			"irc.azzurra.org"},
84 
85 	{"Canternet", 0, 0, 0, LOGIN_SASL, 0, TRUE},
86 	{0,			"irc.canternet.org"},
87 
88 	{"Chat4all", 0, 0, 0, 0, 0, TRUE},
89 	{0,			"irc.chat4all.org"},
90 
91 	{"ChatJunkies",	0},
92 	{0,			"irc.chatjunkies.org"},
93 
94 	{"chatpat", 0, 0, "CP1251", LOGIN_CUSTOM, "MSG NS IDENTIFY %p"},
95 	{0,			"irc.unibg.net"},
96 	{0,			"irc.chatpat.bg"},
97 
98 	{"ChatSpike", 0, 0, 0, LOGIN_SASL},
99 	{0,			"irc.chatspike.net"},
100 
101 	{"DaIRC", 0},
102 	{0,			"irc.dairc.net"},
103 
104 	{"DALnet", 0, 0, 0, LOGIN_NICKSERV},
105 	/* Self signed */
106 	{0,			"us.dal.net"},
107 
108 	{"DarkMyst", 0, 0, 0, LOGIN_SASL, 0, TRUE},
109 	{0,			"irc.darkmyst.org"},
110 
111 #ifdef USE_OPENSSL
112 	{"darkscience", 0, 0, 0, LOGIN_SASL, 0, TRUE},
113 	{0,			"irc.darkscience.net"},
114 	{0,			"irc.drk.sc"},
115 	{0,			"irc.darkscience.ws"},
116 #endif
117 
118 	{"Dark-Tou-Net",	0},
119 	{0,			"irc.d-t-net.de"},
120 
121 	{"DigitalIRC", 0, 0, 0, LOGIN_SASL, 0, TRUE},
122 	{0,			"irc.digitalirc.org"},
123 
124 #ifdef USE_OPENSSL
125 	{"DosersNET", 0, 0, 0, LOGIN_SASL, 0, TRUE},
126 	{0,			"irc.dosers.net/+6697"},
127 #endif
128 
129 	{"EFnet",	0},
130 	{0,			"irc.choopa.net"},
131 	{0,			"efnet.port80.se"},
132 	{0,			"irc.underworld.no"},
133 	{0,			"efnet.deic.eu"},
134 
135 	{"EnterTheGame",	0},
136 	{0,			"irc.enterthegame.com"},
137 
138 	{"EntropyNet",	0, 0, 0, LOGIN_SASL, 0, TRUE},
139 	{0,			"irc.entropynet.net"},
140 
141 	{"EsperNet", 0, 0, 0, LOGIN_SASL, 0, TRUE},
142 	{0,			"irc.esper.net"},
143 
144 	{"EUIrc",	0},
145 	{0,			"irc.euirc.net"},
146 
147 	{"EuropNet", 0},
148 	/* Self signed */
149 	{0,			"irc.europnet.org"},
150 
151 	{"FDFNet",	0},
152 	/* Self signed */
153 	{0,			"irc.fdfnet.net"},
154 
155 	{"GameSurge", 0},
156 	{0,			"irc.gamesurge.net"},
157 
158 	{"GeekShed", 0, 0, 0, 0, 0, TRUE},
159 	{0,			"irc.geekshed.net"},
160 
161 	{"German-Elite", 0, 0, "CP1252"},
162 	{0,			"irc.german-elite.net"},
163 
164 	{"GIMPNet",		0},
165 	/* Invalid hostname in cert */
166 	{0,			"irc.gimp.org"},
167 	{0,			"irc.gnome.org"},
168 
169 	{"GlobalGamers", 0},
170 #ifdef USE_OPENSSL
171 	{0,			"irc.globalgamers.net/+6660"},
172 #endif
173 	{0,			"irc.globalgamers.net"},
174 
175 #ifdef USE_OPENSSL
176 	{"hackint", 0, 0, 0, LOGIN_SASL, 0, TRUE},
177 	{0,			"irc.hackint.org"},
178 	{0,			"irc.eu.hackint.org"},
179 #endif
180 
181 	{"Hashmark",	0},
182 	{0,			"irc.hashmark.net"},
183 
184 	{"ICQ-Chat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
185 	{0,			"irc.icq-chat.com"},
186 
187 	{"Interlinked", 0, 0, 0, LOGIN_SASL, 0, TRUE},
188 	{0,			"irc.interlinked.me"},
189 
190 	{"IRC4Fun", 0, 0, 0, LOGIN_SASL, 0, TRUE},
191 	{0,				"irc.irc4fun.net"},
192 
193 	{"IRCHighWay", 0, 0, 0, 0, 0, TRUE},
194 	{0,				"irc.irchighway.net"},
195 
196 	{"IRCNet",		0},
197 	{0,				"open.ircnet.net"},
198 
199 	{"IRCtoo",	0},
200 	{0,			"irc.irctoo.net"},
201 
202 	{"Keyboard-Failure", 0},
203 	/* SSL is self-signed */
204 	{0,			"irc.kbfail.net"},
205 
206 	{"Libera.Chat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
207 	{0,			"irc.libera.chat"},
208 
209 #ifdef USE_OPENSSL
210 	{"LibertaCasa", 0, 0, 0, LOGIN_SASL, 0, TRUE},
211 	{0,			"irc.liberta.casa"},
212 #endif
213 
214 	{"LibraIRC", 0},
215 	/* Self signed */
216 	{0,			"irc.librairc.net"},
217 
218 #ifdef USE_OPENSSL
219 	{"LinkNet",	0},
220 	{0,			"irc.link-net.org/+7000"},
221 #endif
222 
223 	{"MindForge", 0, 0, 0, LOGIN_SASL},
224 	{0,			"irc.mindforge.org"},
225 
226 	{"MIXXnet",		0},
227 	{0,			"irc.mixxnet.net"},
228 
229 	{"Oceanius", 0, 0, 0, LOGIN_SASL},
230 	/* Self signed */
231 	{0,			"irc.oceanius.com"},
232 
233 	{"OFTC", 0, 0, 0, 0, 0, TRUE},
234 	{0,			"irc.oftc.net"},
235 
236 	{"OtherNet",	0},
237 	{0,			"irc.othernet.org"},
238 
239 	{"OzOrg",	0},
240 	{0,			"irc.oz.org"},
241 
242 	{"PIK", 0},
243 	{0,			"irc.krstarica.com"},
244 
245 	{"pirc.pl",	0, 0, 0, 0, 0, TRUE},
246 	{0,			"irc.pirc.pl"},
247 
248 	{"PTNet",	0},
249 	{0,			"irc.ptnet.org"},
250 	{0,			"uevora.ptnet.org"},
251 	{0,			"claranet.ptnet.org"},
252 	{0,			"sonaquela.ptnet.org"},
253 	{0,			"uc.ptnet.org"},
254 	{0,			"ipg.ptnet.org"},
255 
256 	{"QuakeNet", 0, 0, 0, LOGIN_CHALLENGEAUTH},
257 	{0,			"irc.quakenet.org"},
258 
259 	{"Rizon", 0, 0, 0, 0, 0, TRUE},
260 	{0,			"irc.rizon.net"},
261 
262 	{"RusNet", 0, 0, "KOI8-R (Cyrillic)"},
263 	/* Self signed */
264 	{0,			"irc.tomsk.net"},
265 	{0,			"irc.run.net"},
266 	{0,			"irc.ru"},
267 	{0,			"irc.lucky.net"},
268 
269 	{"Serenity-IRC",	0},
270 	{0,			"irc.serenity-irc.net"},
271 
272 	{"SimosNap", 0, 0, 0, LOGIN_SASL, 0, TRUE},
273 	{0,            "irc.simosnap.com"},
274 
275 	{"SlashNET",	0},
276 	/* Self signed */
277 	{0,			"irc.slashnet.org"},
278 
279 	{"Snoonet", 0, 0, 0, LOGIN_SASL, 0, TRUE},
280 	{0,			"irc.snoonet.org"},
281 
282 	{"Sohbet.Net", 0, 0, "CP1254"},
283 	{0,			"irc.sohbet.net"},
284 
285 	{"SorceryNet", 0, 0, 0, LOGIN_SASL},
286 	/* Self signed */
287 	{0,			"irc.sorcery.net"},
288 
289 	{"SpotChat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
290 	{0,			"irc.spotchat.org"},
291 
292 	{"Station51", 0},
293 	/* Self signed */
294 	{0,			"irc.station51.net"},
295 
296 	{"StormBit", 0, 0, 0, LOGIN_SASL, 0, TRUE},
297 	{0,			"irc.stormbit.net"},
298 
299 	{"SwiftIRC", 0},
300 	/* Expired cert */
301 	{0,			"irc.swiftirc.net"},
302 
303 	{"synIRC", 0},
304 	/* Self signed */
305 	{0, "irc.synirc.net"},
306 
307 	{"Techtronix",	0, 0, 0, LOGIN_SASL, 0, TRUE},
308 	{0,			"irc.techtronix.net"},
309 
310 	{"tilde.chat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
311 	{0,			"irc.tilde.chat"},
312 
313 	{"TURLINet", 0, 0, 0, 0, 0, TRUE},
314 	/* all servers use UTF-8 and valid certs */
315 	{0,			"irc.servx.org"},
316 	{0,			"i.valware.uk"},
317 
318 
319 #ifdef USE_OPENSSL
320 	{"TripSit", 0, 0, 0, LOGIN_SASL, 0, TRUE},
321 	{0,			"irc.tripsit.me"},
322 	{0,			"newirc.tripsit.me"},
323 	{0,			"coconut.tripsit.me"},
324 	{0,			"innsbruck.tripsit.me"},
325 #endif
326 
327 	{"UnderNet", 0, 0, 0, LOGIN_CUSTOM, "MSG x@channels.undernet.org login %u %p"},
328 	{0,			"us.undernet.org"},
329 
330 	{"Xertion", 0, 0, 0, LOGIN_SASL, 0, TRUE},
331 	{0,			"irc.xertion.org"},
332 
333 	{0,0}
334 };
335 
336 GSList *network_list = 0;
337 
338 favchannel *
servlist_favchan_copy(favchannel * fav)339 servlist_favchan_copy (favchannel *fav)
340 {
341 	favchannel *newfav;
342 
343 	newfav = g_new (favchannel, 1);
344 	newfav->name = g_strdup (fav->name);
345 	newfav->key = g_strdup (fav->key);		/* g_strdup() can handle NULLs so no need to check it */
346 
347 	return newfav;
348 }
349 
350 void
servlist_connect(session * sess,ircnet * net,gboolean join)351 servlist_connect (session *sess, ircnet *net, gboolean join)
352 {
353 	ircserver *ircserv;
354 	GSList *list;
355 	char *port;
356 	server *serv;
357 
358 	if (!sess)
359 		sess = new_ircwindow (NULL, NULL, SESS_SERVER, TRUE);
360 
361 	serv = sess->server;
362 
363 	/* connect to the currently selected Server-row */
364 	list = g_slist_nth (net->servlist, net->selected);
365 	if (!list)
366 		list = net->servlist;
367 	if (!list)
368 		return;
369 	ircserv = list->data;
370 
371 	/* in case a protocol switch is added to the servlist gui */
372 	server_fill_her_up (sess->server);
373 
374 	if (join)
375 	{
376 		sess->willjoinchannel[0] = 0;
377 
378 		if (net->favchanlist)
379 		{
380 			if (serv->favlist)
381 			{
382 				g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free);
383 			}
384 			serv->favlist = g_slist_copy_deep (net->favchanlist, (GCopyFunc) servlist_favchan_copy, NULL);
385 		}
386 	}
387 
388 	if (net->logintype)
389 	{
390 		serv->loginmethod = net->logintype;
391 	}
392 	else
393 	{
394 		serv->loginmethod = LOGIN_DEFAULT_REAL;
395 	}
396 
397 	serv->password[0] = 0;
398 
399 	if (net->pass)
400 	{
401 		safe_strcpy (serv->password, net->pass, sizeof (serv->password));
402 	}
403 
404 	if (net->flags & FLAG_USE_GLOBAL)
405 	{
406 		strcpy (serv->nick, prefs.hex_irc_nick1);
407 	}
408 	else
409 	{
410 		if (net->nick)
411 			strcpy (serv->nick, net->nick);
412 	}
413 
414 	serv->dont_use_proxy = (net->flags & FLAG_USE_PROXY) ? FALSE : TRUE;
415 
416 #ifdef USE_OPENSSL
417 	serv->use_ssl = (net->flags & FLAG_USE_SSL) ? TRUE : FALSE;
418 	serv->accept_invalid_cert =
419 		(net->flags & FLAG_ALLOW_INVALID) ? TRUE : FALSE;
420 #endif
421 
422 	serv->network = net;
423 
424 	port = strrchr (ircserv->hostname, '/');
425 	if (port)
426 	{
427 		*port = 0;
428 
429 		/* support "+port" to indicate SSL (like mIRC does) */
430 		if (port[1] == '+')
431 		{
432 #ifdef USE_OPENSSL
433 			serv->use_ssl = TRUE;
434 #endif
435 			serv->connect (serv, ircserv->hostname, atoi (port + 2), FALSE);
436 		} else
437 		{
438 			serv->connect (serv, ircserv->hostname, atoi (port + 1), FALSE);
439 		}
440 
441 		*port = '/';
442 	} else
443 		serv->connect (serv, ircserv->hostname, -1, FALSE);
444 
445 	server_set_encoding (serv, net->encoding);
446 }
447 
448 int
servlist_connect_by_netname(session * sess,char * network,gboolean join)449 servlist_connect_by_netname (session *sess, char *network, gboolean join)
450 {
451 	ircnet *net;
452 	GSList *list = network_list;
453 
454 	while (list)
455 	{
456 		net = list->data;
457 
458 		if (g_ascii_strcasecmp (net->name, network) == 0)
459 		{
460 			servlist_connect (sess, net, join);
461 			return 1;
462 		}
463 
464 		list = list->next;
465 	}
466 
467 	return 0;
468 }
469 
470 int
servlist_have_auto(void)471 servlist_have_auto (void)
472 {
473 	GSList *list = network_list;
474 	ircnet *net;
475 
476 	while (list)
477 	{
478 		net = list->data;
479 
480 		if (net->flags & FLAG_AUTO_CONNECT)
481 			return 1;
482 
483 		list = list->next;
484 	}
485 
486 	return 0;
487 }
488 
489 int
servlist_auto_connect(session * sess)490 servlist_auto_connect (session *sess)
491 {
492 	GSList *list = network_list;
493 	ircnet *net;
494 	int ret = 0;
495 
496 	while (list)
497 	{
498 		net = list->data;
499 
500 		if (net->flags & FLAG_AUTO_CONNECT)
501 		{
502 			servlist_connect (sess, net, TRUE);
503 			ret = 1;
504 		}
505 
506 		list = list->next;
507 	}
508 
509 	return ret;
510 }
511 
512 static gint
servlist_cycle_cb(server * serv)513 servlist_cycle_cb (server *serv)
514 {
515 	if (serv->network)
516 	{
517 		PrintTextf (serv->server_session,
518 			_("Cycling to next server in %s...\n"), ((ircnet *)serv->network)->name);
519 		servlist_connect (serv->server_session, serv->network, TRUE);
520 	}
521 
522 	return 0;
523 }
524 
525 int
servlist_cycle(server * serv)526 servlist_cycle (server *serv)
527 {
528 	ircnet *net;
529 	int max, del;
530 
531 	net = serv->network;
532 	if (net)
533 	{
534 		max = g_slist_length (net->servlist);
535 		if (max > 0)
536 		{
537 			/* try the next server, if that option is on */
538 			if (net->flags & FLAG_CYCLE)
539 			{
540 				net->selected++;
541 				if (net->selected >= max)
542 					net->selected = 0;
543 			}
544 
545 			del = prefs.hex_net_reconnect_delay * 1000;
546 			if (del < 1000)
547 				del = 500;				  /* so it doesn't block the gui */
548 
549 			if (del)
550 				serv->recondelay_tag = fe_timeout_add (del, servlist_cycle_cb, serv);
551 			else
552 				servlist_connect (serv->server_session, net, TRUE);
553 
554 			return TRUE;
555 		}
556 	}
557 
558 	return FALSE;
559 }
560 
561 ircserver *
servlist_server_find(ircnet * net,char * name,int * pos)562 servlist_server_find (ircnet *net, char *name, int *pos)
563 {
564 	GSList *list = net->servlist;
565 	ircserver *serv;
566 	int i = 0;
567 
568 	while (list)
569 	{
570 		serv = list->data;
571 		if (strcmp (serv->hostname, name) == 0)
572 		{
573 			if (pos)
574 			{
575 				*pos = i;
576 			}
577 			return serv;
578 		}
579 		i++;
580 		list = list->next;
581 	}
582 
583 	return NULL;
584 }
585 
586 favchannel *
servlist_favchan_find(ircnet * net,char * channel,int * pos)587 servlist_favchan_find (ircnet *net, char *channel, int *pos)
588 {
589 	GSList *list;
590 	favchannel *favchan;
591 	int i = 0;
592 
593 	if (net == NULL)
594 		return NULL;
595 
596 	list = net->favchanlist;
597 
598 	while (list)
599 	{
600 		favchan = list->data;
601 		if (g_ascii_strcasecmp (favchan->name, channel) == 0)
602 		{
603 			if (pos)
604 			{
605 				*pos = i;
606 			}
607 			return favchan;
608 		}
609 		i++;
610 		list = list->next;
611 	}
612 
613 	return NULL;
614 }
615 
616 commandentry *
servlist_command_find(ircnet * net,char * cmd,int * pos)617 servlist_command_find (ircnet *net, char *cmd, int *pos)
618 {
619 	GSList *list = net->commandlist;
620 	commandentry *entry;
621 	int i = 0;
622 
623 	while (list)
624 	{
625 		entry = list->data;
626 		if (strcmp (entry->command, cmd) == 0)
627 		{
628 			if (pos)
629 			{
630 				*pos = i;
631 			}
632 			return entry;
633 		}
634 		i++;
635 		list = list->next;
636 	}
637 
638 	return NULL;
639 }
640 
641 /* find a network (e.g. (ircnet *) to "FreeNode") from a hostname
642    (e.g. "irc.eu.freenode.net") */
643 
644 ircnet *
servlist_net_find_from_server(char * server_name)645 servlist_net_find_from_server (char *server_name)
646 {
647 	GSList *list = network_list;
648 	GSList *slist;
649 	ircnet *net;
650 	ircserver *serv;
651 
652 	while (list)
653 	{
654 		net = list->data;
655 
656 		slist = net->servlist;
657 		while (slist)
658 		{
659 			gsize hostname_len;
660 			const char *hostname, *p;
661 
662 			serv = slist->data;
663 			hostname = serv->hostname;
664 
665 			/* Ignore port when comparing */
666 			if ((p = strchr (hostname, '/')))
667 				hostname_len = p - hostname;
668 			else
669 				hostname_len = strlen (hostname);
670 
671 			if (g_ascii_strncasecmp (hostname, server_name, hostname_len) == 0)
672 				return net;
673 			slist = slist->next;
674 		}
675 
676 		list = list->next;
677 	}
678 
679 	return NULL;
680 }
681 
682 ircnet *
servlist_net_find(char * name,int * pos,int (* cmpfunc)(const char *,const char *))683 servlist_net_find (char *name, int *pos, int (*cmpfunc) (const char *, const char *))
684 {
685 	GSList *list = network_list;
686 	ircnet *net;
687 	int i = 0;
688 
689 	while (list)
690 	{
691 		net = list->data;
692 		if (cmpfunc (net->name, name) == 0)
693 		{
694 			if (pos)
695 				*pos = i;
696 			return net;
697 		}
698 		i++;
699 		list = list->next;
700 	}
701 
702 	return NULL;
703 }
704 
705 ircserver *
servlist_server_add(ircnet * net,char * name)706 servlist_server_add (ircnet *net, char *name)
707 {
708 	ircserver *serv;
709 
710 	serv = g_new (ircserver, 1);
711 	serv->hostname = g_strdup (name);
712 
713 	net->servlist = g_slist_append (net->servlist, serv);
714 
715 	return serv;
716 }
717 
718 commandentry *
servlist_command_add(ircnet * net,char * cmd)719 servlist_command_add (ircnet *net, char *cmd)
720 {
721 	commandentry *entry;
722 
723 	entry = g_new (commandentry, 1);
724 	entry->command = g_strdup (cmd);
725 
726 	net->commandlist = g_slist_append (net->commandlist, entry);
727 
728 	return entry;
729 }
730 
731 GSList *
servlist_favchan_listadd(GSList * chanlist,char * channel,char * key)732 servlist_favchan_listadd (GSList *chanlist, char *channel, char *key)
733 {
734 	favchannel *chan;
735 
736 	chan = g_new (favchannel, 1);
737 	chan->name = g_strdup (channel);
738 	chan->key = g_strdup (key);
739 	chanlist = g_slist_append (chanlist, chan);
740 
741 	return chanlist;
742 }
743 
744 void
servlist_favchan_add(ircnet * net,char * channel)745 servlist_favchan_add (ircnet *net, char *channel)
746 {
747 	int pos;
748 	char *name;
749 	char *key;
750 
751 	if (strchr (channel, ',') != NULL)
752 	{
753 		pos = (int) (strchr (channel, ',') - channel);
754 		name = g_strndup (channel, pos);
755 		key = g_strdup (channel + pos + 1);
756 	}
757 	else
758 	{
759 		name = g_strdup (channel);
760 		key = NULL;
761 	}
762 
763 	net->favchanlist = servlist_favchan_listadd (net->favchanlist, name, key);
764 
765 	g_free (name);
766 	g_free (key);
767 }
768 
769 void
servlist_server_remove(ircnet * net,ircserver * serv)770 servlist_server_remove (ircnet *net, ircserver *serv)
771 {
772 	g_free (serv->hostname);
773 	g_free (serv);
774 	net->servlist = g_slist_remove (net->servlist, serv);
775 }
776 
777 static void
servlist_server_remove_all(ircnet * net)778 servlist_server_remove_all (ircnet *net)
779 {
780 	ircserver *serv;
781 
782 	while (net->servlist)
783 	{
784 		serv = net->servlist->data;
785 		servlist_server_remove (net, serv);
786 	}
787 }
788 
789 void
servlist_command_free(commandentry * entry)790 servlist_command_free (commandentry *entry)
791 {
792 	g_free (entry->command);
793 	g_free (entry);
794 }
795 
796 void
servlist_command_remove(ircnet * net,commandentry * entry)797 servlist_command_remove (ircnet *net, commandentry *entry)
798 {
799 	servlist_command_free (entry);
800 	net->commandlist = g_slist_remove (net->commandlist, entry);
801 }
802 
803 void
servlist_favchan_free(favchannel * channel)804 servlist_favchan_free (favchannel *channel)
805 {
806 	g_free (channel->name);
807 	g_free (channel->key);
808 	g_free (channel);
809 }
810 
811 void
servlist_favchan_remove(ircnet * net,favchannel * channel)812 servlist_favchan_remove (ircnet *net, favchannel *channel)
813 {
814 	servlist_favchan_free (channel);
815 	net->favchanlist = g_slist_remove (net->favchanlist, channel);
816 }
817 
818 static void
free_and_clear(char * str)819 free_and_clear (char *str)
820 {
821 	if (str)
822 	{
823 		char *orig = str;
824 		while (*str)
825 			*str++ = 0;
826 		g_free (orig);
827 	}
828 }
829 
830 /* executed on exit: Clear any password strings */
831 
832 void
servlist_cleanup(void)833 servlist_cleanup (void)
834 {
835 	GSList *list;
836 	ircnet *net;
837 
838 	for (list = network_list; list; list = list->next)
839 	{
840 		net = list->data;
841 		free_and_clear (net->pass);
842 	}
843 }
844 
845 void
servlist_net_remove(ircnet * net)846 servlist_net_remove (ircnet *net)
847 {
848 	GSList *list;
849 	server *serv;
850 
851 	servlist_server_remove_all (net);
852 	network_list = g_slist_remove (network_list, net);
853 
854 	g_free (net->nick);
855 	g_free (net->nick2);
856 	g_free (net->user);
857 	g_free (net->real);
858 	free_and_clear (net->pass);
859 	if (net->favchanlist)
860 		g_slist_free_full (net->favchanlist, (GDestroyNotify) servlist_favchan_free);
861 	if (net->commandlist)
862 		g_slist_free_full (net->commandlist, (GDestroyNotify) servlist_command_free);
863 	g_free (net->encoding);
864 	g_free (net->name);
865 	g_free (net);
866 
867 	/* for safety */
868 	list = serv_list;
869 	while (list)
870 	{
871 		serv = list->data;
872 		if (serv->network == net)
873 		{
874 			serv->network = NULL;
875 		}
876 		list = list->next;
877 	}
878 }
879 
880 ircnet *
servlist_net_add(char * name,char * comment,int prepend)881 servlist_net_add (char *name, char *comment, int prepend)
882 {
883 	ircnet *net;
884 
885 	net = g_new0 (ircnet, 1);
886 	net->name = g_strdup (name);
887 	net->flags = FLAG_CYCLE | FLAG_USE_GLOBAL | FLAG_USE_PROXY;
888 #ifdef USE_OPENSSL
889 	net->flags |= FLAG_USE_SSL;
890 #endif
891 
892 	if (prepend)
893 		network_list = g_slist_prepend (network_list, net);
894 	else
895 		network_list = g_slist_append (network_list, net);
896 
897 	return net;
898 }
899 
900 static void
servlist_load_defaults(void)901 servlist_load_defaults (void)
902 {
903 	int i = 0, j = 0;
904 	ircnet *net = NULL;
905 	guint def_hash = g_str_hash ("Libera.Chat");
906 
907 	while (1)
908 	{
909 		if (def[i].network)
910 		{
911 			net = servlist_net_add (def[i].network, def[i].host, FALSE);
912 			if (def[i].channel)
913 			{
914 				servlist_favchan_add (net, def[i].channel);
915 			}
916 			if (def[i].charset)
917 			{
918 				net->encoding = g_strdup (def[i].charset);
919 			}
920 			else
921 			{
922 				net->encoding = g_strdup (IRC_DEFAULT_CHARSET);
923 			}
924 			if (def[i].loginmode)
925 			{
926 				net->logintype = def[i].loginmode;
927 			}
928 			if (def[i].connectcmd)
929 			{
930 				servlist_command_add (net, def[i].connectcmd);
931 			}
932 			if (def[i].ssl)
933 			{
934 				net->flags |= FLAG_USE_SSL;
935 			}
936 
937 			if (g_str_hash (def[i].network) == def_hash)
938 			{
939 				prefs.hex_gui_slist_select = j;
940 			}
941 
942 			j++;
943 		}
944 		else
945 		{
946 			servlist_server_add (net, def[i].host);
947 			if (!def[i+1].host && !def[i+1].network)
948 			{
949 				break;
950 			}
951 		}
952 		i++;
953 	}
954 }
955 
956 static int
servlist_load(void)957 servlist_load (void)
958 {
959 	FILE *fp;
960 	char buf[2048];
961 	int len;
962 	ircnet *net = NULL;
963 
964 	/* simple migration we will keep for a short while */
965 	char *oldfile = g_build_filename (get_xdir (), "servlist_.conf", NULL);
966 	char *newfile = g_build_filename (get_xdir (), "servlist.conf", NULL);
967 
968 	if (g_file_test (oldfile, G_FILE_TEST_EXISTS) && !g_file_test (newfile, G_FILE_TEST_EXISTS))
969 	{
970 		g_rename (oldfile, newfile);
971 	}
972 
973 	g_free (oldfile);
974 	g_free (newfile);
975 
976 	fp = hexchat_fopen_file ("servlist.conf", "r", 0);
977 	if (!fp)
978 		return FALSE;
979 
980 	while (fgets (buf, sizeof (buf) - 2, fp))
981 	{
982 		len = strlen (buf);
983 		if (!len)
984 			continue;
985 		buf[len] = 0;
986 		buf[len-1] = 0;	/* remove the trailing \n */
987 		if (net)
988 		{
989 			switch (buf[0])
990 			{
991 			case 'I':
992 				net->nick = g_strdup (buf + 2);
993 				break;
994 			case 'i':
995 				net->nick2 = g_strdup (buf + 2);
996 				break;
997 			case 'U':
998 				net->user = g_strdup (buf + 2);
999 				break;
1000 			case 'R':
1001 				net->real = g_strdup (buf + 2);
1002 				break;
1003 			case 'P':
1004 				net->pass = g_strdup (buf + 2);
1005 				break;
1006 			case 'L':
1007 				net->logintype = atoi (buf + 2);
1008 				break;
1009 			case 'E':
1010 				net->encoding = servlist_check_encoding (buf + 2) ? g_strdup (buf + 2) : g_strdup ("UTF-8");
1011 				break;
1012 			case 'F':
1013 				net->flags = atoi (buf + 2);
1014 				break;
1015 			case 'S':	/* new server/hostname for this network */
1016 				servlist_server_add (net, buf + 2);
1017 				break;
1018 			case 'C':
1019 				servlist_command_add (net, buf + 2);
1020 				break;
1021 			case 'J':
1022 				servlist_favchan_add (net, buf + 2);
1023 				break;
1024 			case 'D':
1025 				net->selected = atoi (buf + 2);
1026 				break;
1027 			/* FIXME Migration code. In 2.9.5 the order was:
1028 			 *
1029 			 * P=serverpass, A=saslpass, B=nickservpass
1030 			 *
1031 			 * So if server password was unset, we can safely use SASL
1032 			 * password for our new universal password, or if that's also
1033 			 * unset, use NickServ password.
1034 			 *
1035 			 * Should be removed at some point.
1036 			 */
1037 			case 'A':
1038 				if (!net->pass)
1039 				{
1040 					net->pass = g_strdup (buf + 2);
1041 					if (!net->logintype)
1042 					{
1043 						net->logintype = LOGIN_SASL;
1044 					}
1045 				}
1046 			case 'B':
1047 				if (!net->pass)
1048 				{
1049 					net->pass = g_strdup (buf + 2);
1050 					if (!net->logintype)
1051 					{
1052 						net->logintype = LOGIN_NICKSERV;
1053 					}
1054 				}
1055 			}
1056 		}
1057 		if (buf[0] == 'N')
1058 			net = servlist_net_add (buf + 2, /* comment */ NULL, FALSE);
1059 	}
1060 	fclose (fp);
1061 
1062 	return TRUE;
1063 }
1064 
1065 void
servlist_init(void)1066 servlist_init (void)
1067 {
1068 	if (!network_list)
1069 		if (!servlist_load ())
1070 			servlist_load_defaults ();
1071 }
1072 
1073 /* check if a charset is known by Iconv */
1074 int
servlist_check_encoding(char * charset)1075 servlist_check_encoding (char *charset)
1076 {
1077 	GIConv gic;
1078 	char *c;
1079 
1080 	c = strchr (charset, ' ');
1081 	if (c)
1082 		c[0] = 0;
1083 
1084 	gic = g_iconv_open (charset, "UTF-8");
1085 
1086 	if (c)
1087 		c[0] = ' ';
1088 
1089 	if (gic != (GIConv)-1)
1090 	{
1091 		g_iconv_close (gic);
1092 		return TRUE;
1093 	}
1094 
1095 	return FALSE;
1096 }
1097 
1098 int
servlist_save(void)1099 servlist_save (void)
1100 {
1101 	FILE *fp;
1102 	char *buf;
1103 	ircnet *net;
1104 	ircserver *serv;
1105 	commandentry *cmd;
1106 	favchannel *favchan;
1107 	GSList *list;
1108 	GSList *netlist;
1109 	GSList *cmdlist;
1110 	GSList *favlist;
1111 #ifndef WIN32
1112 	int first = FALSE;
1113 
1114 	buf = g_build_filename (get_xdir (), "servlist.conf", NULL);
1115 	if (g_access (buf, F_OK) != 0)
1116 		first = TRUE;
1117 #endif
1118 
1119 	fp = hexchat_fopen_file ("servlist.conf", "w", 0);
1120 	if (!fp)
1121 	{
1122 #ifndef WIN32
1123 		g_free (buf);
1124 #endif
1125 		return FALSE;
1126 	}
1127 
1128 #ifndef WIN32
1129 	if (first)
1130 		g_chmod (buf, 0600);
1131 
1132 	g_free (buf);
1133 #endif
1134 	fprintf (fp, "v=" PACKAGE_VERSION "\n\n");
1135 
1136 	list = network_list;
1137 	while (list)
1138 	{
1139 		net = list->data;
1140 
1141 		fprintf (fp, "N=%s\n", net->name);
1142 		if (net->nick)
1143 			fprintf (fp, "I=%s\n", net->nick);
1144 		if (net->nick2)
1145 			fprintf (fp, "i=%s\n", net->nick2);
1146 		if (net->user)
1147 			fprintf (fp, "U=%s\n", net->user);
1148 		if (net->real)
1149 			fprintf (fp, "R=%s\n", net->real);
1150 		if (net->pass)
1151 			fprintf (fp, "P=%s\n", net->pass);
1152 		if (net->logintype)
1153 			fprintf (fp, "L=%d\n", net->logintype);
1154 		if (net->encoding)
1155 		{
1156 			fprintf (fp, "E=%s\n", net->encoding);
1157 			if (!servlist_check_encoding (net->encoding))
1158 			{
1159 				buf = g_strdup_printf (_("Warning: \"%s\" character set is unknown. No conversion will be applied for network %s."),
1160 							 net->encoding, net->name);
1161 				fe_message (buf, FE_MSG_WARN);
1162 				g_free (buf);
1163 			}
1164 		}
1165 
1166 		fprintf (fp, "F=%d\nD=%d\n", net->flags, net->selected);
1167 
1168 		netlist = net->servlist;
1169 		while (netlist)
1170 		{
1171 			serv = netlist->data;
1172 			fprintf (fp, "S=%s\n", serv->hostname);
1173 			netlist = netlist->next;
1174 		}
1175 
1176 		cmdlist = net->commandlist;
1177 		while (cmdlist)
1178 		{
1179 			cmd = cmdlist->data;
1180 			fprintf (fp, "C=%s\n", cmd->command);
1181 			cmdlist = cmdlist->next;
1182 		}
1183 
1184 		favlist = net->favchanlist;
1185 		while (favlist)
1186 		{
1187 			favchan = favlist->data;
1188 
1189 			if (favchan->key)
1190 			{
1191 				fprintf (fp, "J=%s,%s\n", favchan->name, favchan->key);
1192 			}
1193 			else
1194 			{
1195 				fprintf (fp, "J=%s\n", favchan->name);
1196 			}
1197 
1198 			favlist = favlist->next;
1199 		}
1200 
1201 		if (fprintf (fp, "\n") < 1)
1202 		{
1203 			fclose (fp);
1204 			return FALSE;
1205 		}
1206 
1207 		list = list->next;
1208 	}
1209 
1210 	fclose (fp);
1211 	return TRUE;
1212 }
1213 
1214 static int
joinlist_find_chan(favchannel * curr_item,const char * channel)1215 joinlist_find_chan (favchannel *curr_item, const char *channel)
1216 {
1217 	if (!g_ascii_strcasecmp (curr_item->name, channel))
1218 	{
1219 		return 0;
1220 	}
1221 	else
1222 	{
1223 		return 1;
1224 	}
1225 }
1226 
1227 gboolean
joinlist_is_in_list(server * serv,char * channel)1228 joinlist_is_in_list (server *serv, char *channel)
1229 {
1230 	if (!serv->network || !((ircnet *)serv->network)->favchanlist)
1231 	{
1232 		return FALSE;
1233 	}
1234 
1235 	if (g_slist_find_custom (((ircnet *)serv->network)->favchanlist, channel, (GCompareFunc) joinlist_find_chan))
1236 	{
1237 		return TRUE;
1238 	}
1239 	else
1240 	{
1241 		return FALSE;
1242 	}
1243 }
1244