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