/* X-Chat * Copyright (C) 1998 Peter Zelezny. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #ifndef WIN32 #include #endif #include "hexchat.h" #include "cfgfiles.h" #include "fe.h" #include "server.h" #include "text.h" #include "util.h" /* token_foreach */ #include "hexchatc.h" #include "servlist.h" struct defaultserver { char *network; char *host; char *channel; char *charset; int loginmode; /* default authentication type */ char *connectcmd; /* default connect command - should only be used for rare login types, paired with LOGIN_CUSTOM */ }; static const struct defaultserver def[] = { {"2600net", 0}, {0, "irc.2600.net"}, {"2ch", 0, 0, "iso-2022-jp", 0, 0}, {0, "irc.2ch.net"}, {0, "irc2.2ch.net"}, {0, "irc.juggler.jp"}, {"AccessIRC", 0}, {0, "irc.accessirc.net"}, {0, "eu.accessirc.net"}, {"AfterNET", 0}, {0, "irc.afternet.org"}, {0, "us.afternet.org"}, {0, "eu.afternet.org"}, {"Aitvaras", 0}, #ifdef USE_IPV6 #ifdef USE_OPENSSL {0, "irc6.ktu.lt/+7668"}, #endif {0, "irc6.ktu.lt/7666"}, #endif #ifdef USE_OPENSSL {0, "irc.data.lt/+6668"}, {0, "irc-ssl.omnitel.net/+6668"}, {0, "irc-ssl.le.lt/+9999"}, #endif {0, "irc.data.lt"}, {0, "irc.omnitel.net"}, {0, "irc.ktu.lt"}, {0, "irc.le.lt"}, {0, "irc.takas.lt"}, {0, "irc.5ci.net"}, {0, "irc.kis.lt"}, {"AlphaChat", 0, 0, 0, LOGIN_SASL}, {0, "irc.alphachat.net"}, {0, "na.alphachat.net"}, {0, "eu.alphachat.net"}, {0, "au.alphachat.net"}, {0, "za.alphachat.net"}, {"ARCNet", 0}, {0, "se1.arcnet.vapor.com"}, {0, "us1.arcnet.vapor.com"}, {0, "us2.arcnet.vapor.com"}, {0, "us3.arcnet.vapor.com"}, {0, "ca1.arcnet.vapor.com"}, {0, "de1.arcnet.vapor.com"}, {0, "de3.arcnet.vapor.com"}, {0, "ch1.arcnet.vapor.com"}, {0, "be1.arcnet.vapor.com"}, {0, "nl3.arcnet.vapor.com"}, {0, "uk1.arcnet.vapor.com"}, {0, "uk2.arcnet.vapor.com"}, {0, "fr1.arcnet.vapor.com"}, {"AustNet", 0}, {0, "au.austnet.org"}, {0, "us.austnet.org"}, {0, "ca.austnet.org"}, {"AzzurraNet", 0}, {0, "irc.azzurra.org"}, {0, "crypto.azzurra.org"}, {"Beirut", 0}, {0, "irc.beirut.com"}, {"Chat4all", 0}, #ifdef USE_OPENSSL {0, "irc.chat4all.org/+7001"}, #endif {0, "irc.chat4all.org"}, {"ChattingAway", 0}, {0, "irc.chattingaway.com"}, {"ChatJunkies", 0}, {0, "irc.chatjunkies.org"}, {0, "nl.chatjunkies.org"}, {"ChatNet", 0}, {0, "US.ChatNet.Org"}, {0, "EU.ChatNet.Org"}, {"ChatSpike", 0}, {0, "irc.chatspike.net"}, {"Criten", 0}, {0, "irc.criten.net"}, {0, "irc.eu.criten.net"}, {"DALnet", 0}, {0, "irc.dal.net"}, {0, "irc.eu.dal.net"}, {"Dark-Tou-Net", 0}, {0, "irc.d-t-net.de"}, {0, "bw.d-t-net.de"}, {0, "nc.d-t-net.de"}, {0, "wakka.d-t-net.de"}, {"DarkMyst", 0, 0, 0, LOGIN_SASL}, {0, "irc.darkmyst.org"}, {"DeepIRC", 0}, {0, "irc.deepirc.net"}, {"DeltaAnime", 0}, {0, "irc.deltaanime.net"}, {"EFnet", 0}, {0, "irc.blackened.com"}, {0, "irc.Prison.NET"}, {0, "irc.Qeast.net"}, {0, "irc.efnet.pl"}, {0, "efnet.demon.co.uk"}, {0, "irc.lightning.net"}, {0, "irc.mindspring.com"}, {0, "irc.easynews.com"}, {0, "irc.servercentral.net"}, {"ElectronIRC", 0}, #ifdef USE_OPENSSL {0, "irc.electronirc.tk/+6697"}, #endif {0, "irc.electronirc.tk"}, {"EnterTheGame", 0}, {0, "IRC.EnterTheGame.Com"}, {"EntropyNet", 0, 0, 0, LOGIN_SASL}, #ifdef USE_OPENSSL {0, "irc.entropynet.net/+6697"}, #endif {0, "irc.entropynet.net"}, #ifdef USE_IPV6 #ifdef USE_OPENSSL {0, "irc6.entropynet.net/+6697"}, #endif {0, "irc6.entropynet.net"}, #endif {"EsperNet", 0, 0, 0, LOGIN_SASL}, #ifdef USE_OPENSSL {0, "irc.esper.net/+6697"}, #endif {0, "irc.esper.net"}, {"EUIrc", 0}, {0, "irc.euirc.net"}, {0, "irc.ham.de.euirc.net"}, {0, "irc.ber.de.euirc.net"}, {0, "irc.ffm.de.euirc.net"}, {0, "irc.bre.de.euirc.net"}, {0, "irc.hes.de.euirc.net"}, {0, "irc.vie.at.euirc.net"}, {0, "irc.inn.at.euirc.net"}, {0, "irc.bas.ch.euirc.net"}, {"EuropNet", 0}, {0, "irc.europnet.org"}, {"FDFNet", 0}, {0, "irc.fdfnet.net"}, {0, "irc.eu.fdfnet.net"}, {"FEFNet", 0, 0, 0, LOGIN_SASL}, {0, "irc.fef.net"}, {0, "irc.ggn.net"}, {0, "irc.vendetta.com"}, {"fr
/*

  Copyright (c) 2010 Samuel Lidén Borell <samuel@kodafritt.se>

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.

*/

#ifndef MISC_H
#define MISC_H

void secure_erase(void *ptr, size_t size);

#ifdef __G_LIB_H__
char *import_glib_string(gchar *gstr);
#endif

#endif
s, sizeof (serv->password)); } if (net->flags & FLAG_USE_GLOBAL) { strcpy (serv->nick, prefs.hex_irc_nick1); } else { if (net->nick) strcpy (serv->nick, net->nick); } serv->dont_use_proxy = (net->flags & FLAG_USE_PROXY) ? FALSE : TRUE; #ifdef USE_OPENSSL serv->use_ssl = (net->flags & FLAG_USE_SSL) ? TRUE : FALSE; serv->accept_invalid_cert = (net->flags & FLAG_ALLOW_INVALID) ? TRUE : FALSE; #endif serv->network = net; port = strrchr (ircserv->hostname, '/'); if (port) { *port = 0; /* support "+port" to indicate SSL (like mIRC does) */ if (port[1] == '+') { #ifdef USE_OPENSSL serv->use_ssl = TRUE; #endif serv->connect (serv, ircserv->hostname, atoi (port + 2), FALSE); } else { serv->connect (serv, ircserv->hostname, atoi (port + 1), FALSE); } *port = '/'; } else serv->connect (serv, ircserv->hostname, -1, FALSE); server_set_encoding (serv, net->encoding); } int servlist_connect_by_netname (session *sess, char *network, gboolean join) { ircnet *net; GSList *list = network_list; while (list) { net = list->data; if (g_ascii_strcasecmp (net->name, network) == 0) { servlist_connect (sess, net, join); return 1; } list = list->next; } return 0; } int servlist_have_auto (void) { GSList *list = network_list; ircnet *net; while (list) { net = list->data; if (net->flags & FLAG_AUTO_CONNECT) return 1; list = list->next; } return 0; } int servlist_auto_connect (session *sess) { GSList *list = network_list; ircnet *net; int ret = 0; while (list) { net = list->data; if (net->flags & FLAG_AUTO_CONNECT) { servlist_connect (sess, net, TRUE); ret = 1; } list = list->next; } return ret; } static gint servlist_cycle_cb (server *serv) { if (serv->network) { PrintTextf (serv->server_session, _("Cycling to next server in %s...\n"), ((ircnet *)serv->network)->name); servlist_connect (serv->server_session, serv->network, TRUE); } return 0; } int servlist_cycle (server *serv) { ircnet *net; int max, del; net = serv->network; if (net) { max = g_slist_length (net->servlist); if (max > 0) { /* try the next server, if that option is on */ if (net->flags & FLAG_CYCLE) { net->selected++; if (net->selected >= max) net->selected = 0; } del = prefs.hex_net_reconnect_delay * 1000; if (del < 1000) del = 500; /* so it doesn't block the gui */ if (del) serv->recondelay_tag = fe_timeout_add (del, servlist_cycle_cb, serv); else servlist_connect (serv->server_session, net, TRUE); return TRUE; } } return FALSE; } ircserver * servlist_server_find (ircnet *net, char *name, int *pos) { GSList *list = net->servlist; ircserver *serv; int i = 0; while (list) { serv = list->data; if (strcmp (serv->hostname, name) == 0) { if (pos) { *pos = i; } return serv; } i++; list = list->next; } return NULL; } favchannel * servlist_favchan_find (ircnet *net, char *channel, int *pos) { GSList *list; favchannel *favchan; int i = 0; if (net == NULL) return NULL; list = net->favchanlist; while (list) { favchan = list->data; if (strcmp (favchan->name, channel) == 0) { if (pos) { *pos = i; } return favchan; } i++; list = list->next; } return NULL; } commandentry * servlist_command_find (ircnet *net, char *cmd, int *pos) { GSList *list = net->commandlist; commandentry *entry; int i = 0; while (list) { entry = list->data; if (strcmp (entry->command, cmd) == 0) { if (pos) { *pos = i; } return entry; } i++; list = list->next; } return NULL; } /* find a network (e.g. (ircnet *) to "FreeNode") from a hostname (e.g. "irc.eu.freenode.net") */ ircnet * servlist_net_find_from_server (char *server_name) { GSList *list = network_list; GSList *slist; ircnet *net; ircserver *serv; while (list) { net = list->data; slist = net->servlist; while (slist) { serv = slist->data; if (g_ascii_strcasecmp (serv->hostname, server_name) == 0) return net; slist = slist->next; } list = list->next; } return NULL; } ircnet * servlist_net_find (char *name, int *pos, int (*cmpfunc) (const char *, const char *)) { GSList *list = network_list; ircnet *net; int i = 0; while (list) { net = list->data; if (cmpfunc (net->name, name) == 0) { if (pos) *pos = i; return net; } i++; list = list->next; } return NULL; } ircserver * servlist_server_add (ircnet *net, char *name) { ircserver *serv; serv = malloc (sizeof (ircserver)); memset (serv, 0, sizeof (ircserver)); serv->hostname = strdup (name); net->servlist = g_slist_append (net->servlist, serv); return serv; } commandentry * servlist_command_add (ircnet *net, char *cmd) { commandentry *entry; entry = malloc (sizeof (commandentry)); memset (entry, 0, sizeof (commandentry)); entry->command = strdup (cmd); net->commandlist = g_slist_append (net->commandlist, entry); return entry; } GSList * servlist_favchan_listadd (GSList *chanlist, char *channel, char *key) { favchannel *chan; chan = malloc (sizeof (favchannel)); memset (chan, 0, sizeof (favchannel)); chan->name = g_strdup (channel); chan->key = g_strdup (key); chanlist = g_slist_append (chanlist, chan); return chanlist; } void servlist_favchan_add (ircnet *net, char *channel) { int pos; char *name; char *key; if (strchr (channel, ',') != NULL) { pos = (int) (strchr (channel, ',') - channel); name = g_strndup (channel, pos); key = g_strdup (channel + pos + 1); } else { name = g_strdup (channel); key = NULL; } net->favchanlist = servlist_favchan_listadd (net->favchanlist, name, key); g_free (name); g_free (key); } void servlist_server_remove (ircnet *net, ircserver *serv) { free (serv->hostname); free (serv); net->servlist = g_slist_remove (net->servlist, serv); } static void servlist_server_remove_all (ircnet *net) { ircserver *serv; while (net->servlist) { serv = net->servlist->data; servlist_server_remove (net, serv); } } void servlist_command_free (commandentry *entry) { g_free (entry->command); g_free (entry); } void servlist_command_remove (ircnet *net, commandentry *entry) { servlist_command_free (entry); net->commandlist = g_slist_remove (net->commandlist, entry); } void servlist_favchan_free (favchannel *channel) { g_free (channel->name); g_free (channel->key); g_free (channel); } void servlist_favchan_remove (ircnet *net, favchannel *channel) { servlist_favchan_free (channel); net->favchanlist = g_slist_remove (net->favchanlist, channel); } static void free_and_clear (char *str) { if (str) { char *orig = str; while (*str) *str++ = 0; free (orig); } } /* executed on exit: Clear any password strings */ void servlist_cleanup (void) { GSList *list; ircnet *net; for (list = network_list; list; list = list->next) { net = list->data; free_and_clear (net->pass); } } void servlist_net_remove (ircnet *net) { GSList *list; server *serv; servlist_server_remove_all (net); network_list = g_slist_remove (network_list, net); if (net->nick) free (net->nick); if (net->nick2) free (net->nick2); if (net->user) free (net->user); if (net->real) free (net->real); free_and_clear (net->pass); if (net->favchanlist) g_slist_free_full (net->favchanlist, (GDestroyNotify) servlist_favchan_free); if (net->commandlist) g_slist_free_full (net->commandlist, (GDestroyNotify) servlist_command_free); if (net->comment) free (net->comment); if (net->encoding) free (net->encoding); free (net->name); free (net); /* for safety */ list = serv_list; while (list) { serv = list->data; if (serv->network == net) { serv->network = NULL; } list = list->next; } } ircnet * servlist_net_add (char *name, char *comment, int prepend) { ircnet *net; net = malloc (sizeof (ircnet)); memset (net, 0, sizeof (ircnet)); net->name = strdup (name); /* net->comment = strdup (comment);*/ net->flags = FLAG_CYCLE | FLAG_USE_GLOBAL | FLAG_USE_PROXY; if (prepend) network_list = g_slist_prepend (network_list, net); else network_list = g_slist_append (network_list, net); return net; } static void servlist_load_defaults (void) { int i = 0, j = 0; ircnet *net = NULL; guint def_hash = g_str_hash ("freenode"); while (1) { if (def[i].network) { net = servlist_net_add (def[i].network, def[i].host, FALSE); if (def[i].channel) { servlist_favchan_add (net, def[i].channel); } if (def[i].charset) { net->encoding = g_strdup (def[i].charset); } else { net->encoding = g_strdup (IRC_DEFAULT_CHARSET); } if (def[i].loginmode) { net->logintype = def[i].loginmode; } if (def[i].connectcmd) { servlist_command_add (net, def[i].connectcmd); } if (g_str_hash (def[i].network) == def_hash) { prefs.hex_gui_slist_select = j; } j++; } else { servlist_server_add (net, def[i].host); if (!def[i+1].host && !def[i+1].network) { break; } } i++; } } static int servlist_load (void) { FILE *fp; char buf[2048]; int len; ircnet *net = NULL; /* simple migration we will keep for a short while */ char *oldfile = g_build_filename (get_xdir (), "servlist_.conf", NULL); char *newfile = g_build_filename (get_xdir (), "servlist.conf", NULL); if (g_file_test (oldfile, G_FILE_TEST_EXISTS) && !g_file_test (newfile, G_FILE_TEST_EXISTS)) { g_rename (oldfile, newfile); } g_free (oldfile); g_free (newfile); fp = hexchat_fopen_file ("servlist.conf", "r", 0); if (!fp) return FALSE; while (fgets (buf, sizeof (buf) - 2, fp)) { len = strlen (buf); buf[len] = 0; buf[len-1] = 0; /* remove the trailing \n */ if (net) { switch (buf[0]) { case 'I': net->nick = strdup (buf + 2); break; case 'i': net->nick2 = strdup (buf + 2); break; case 'U': net->user = strdup (buf + 2); break; case 'R': net->real = strdup (buf + 2); break; case 'P': net->pass = strdup (buf + 2); break; case 'L': net->logintype = atoi (buf + 2); break; case 'E': net->encoding = strdup (buf + 2); break; case 'F': net->flags = atoi (buf + 2); break; case 'S': /* new server/hostname for this network */ servlist_server_add (net, buf + 2); break; case 'C': servlist_command_add (net, buf + 2); break; case 'J': servlist_favchan_add (net, buf + 2); break; case 'D': net->selected = atoi (buf + 2); break; /* FIXME Migration code. In 2.9.5 the order was: * * P=serverpass, A=saslpass, B=nickservpass * * So if server password was unset, we can safely use SASL * password for our new universal password, or if that's also * unset, use NickServ password. * * Should be removed at some point. */ case 'A': if (!net->pass) { net->pass = strdup (buf + 2); if (!net->logintype) { net->logintype = LOGIN_SASL; } } case 'B': if (!net->pass) { net->pass = strdup (buf + 2); if (!net->logintype) { net->logintype = LOGIN_NICKSERV; } } } } if (buf[0] == 'N') net = servlist_net_add (buf + 2, /* comment */ NULL, FALSE); } fclose (fp); return TRUE; } void servlist_init (void) { if (!network_list) if (!servlist_load ()) servlist_load_defaults (); } /* check if a charset is known by Iconv */ int servlist_check_encoding (char *charset) { GIConv gic; char *c; c = strchr (charset, ' '); if (c) c[0] = 0; if (!g_ascii_strcasecmp (charset, "IRC")) /* special case */ { if (c) c[0] = ' '; return TRUE; } gic = g_iconv_open (charset, "UTF-8"); if (c) c[0] = ' '; if (gic != (GIConv)-1) { g_iconv_close (gic); return TRUE; } return FALSE; } int servlist_save (void) { FILE *fp; char *buf; ircnet *net; ircserver *serv; commandentry *cmd; favchannel *favchan; GSList *list; GSList *netlist; GSList *cmdlist; GSList *favlist; #ifndef WIN32 int first = FALSE; buf = g_build_filename (get_xdir (), "servlist.conf", NULL); if (g_access (buf, F_OK) != 0) first = TRUE; #endif fp = hexchat_fopen_file ("servlist.conf", "w", 0); if (!fp) { #ifndef WIN32 g_free (buf); #endif return FALSE; } #ifndef WIN32 if (first) g_chmod (buf, 0600); g_free (buf); #endif fprintf (fp, "v=" PACKAGE_VERSION "\n\n"); list = network_list; while (list) { net = list->data; fprintf (fp, "N=%s\n", net->name); if (net->nick) fprintf (fp, "I=%s\n", net->nick); if (net->nick2) fprintf (fp, "i=%s\n", net->nick2); if (net->user) fprintf (fp, "U=%s\n", net->user); if (net->real) fprintf (fp, "R=%s\n", net->real); if (net->pass) fprintf (fp, "P=%s\n", net->pass); if (net->logintype) fprintf (fp, "L=%d\n", net->logintype); if (net->encoding && g_ascii_strcasecmp (net->encoding, "System") && g_ascii_strcasecmp (net->encoding, "System default")) { fprintf (fp, "E=%s\n", net->encoding); if (!servlist_check_encoding (net->encoding)) { buf = g_strdup_printf (_("Warning: \"%s\" character set is unknown. No conversion will be applied for network %s."), net->encoding, net->name); fe_message (buf, FE_MSG_WARN); g_free (buf); } } fprintf (fp, "F=%d\nD=%d\n", net->flags, net->selected); netlist = net->servlist; while (netlist) { serv = netlist->data; fprintf (fp, "S=%s\n", serv->hostname); netlist = netlist->next; } cmdlist = net->commandlist; while (cmdlist) { cmd = cmdlist->data; fprintf (fp, "C=%s\n", cmd->command); cmdlist = cmdlist->next; } favlist = net->favchanlist; while (favlist) { favchan = favlist->data; if (favchan->key) { fprintf (fp, "J=%s,%s\n", favchan->name, favchan->key); } else { fprintf (fp, "J=%s\n", favchan->name); } favlist = favlist->next; } if (fprintf (fp, "\n") < 1) { fclose (fp); return FALSE; } list = list->next; } fclose (fp); return TRUE; } static int joinlist_find_chan (favchannel *curr_item, const char *channel) { if (!g_ascii_strcasecmp (curr_item->name, channel)) { return 0; } else { return 1; } } gboolean joinlist_is_in_list (server *serv, char *channel) { if (!serv->network || !((ircnet *)serv->network)->favchanlist) { return FALSE; } if (g_slist_find_custom (((ircnet *)serv->network)->favchanlist, channel, (GCompareFunc) joinlist_find_chan)) { return TRUE; } else { return FALSE; } }