diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/cfgfiles.c | 4 | ||||
-rw-r--r-- | src/common/dcc.c | 9 | ||||
-rw-r--r-- | src/common/hexchat.c | 1 | ||||
-rw-r--r-- | src/common/hexchat.h | 8 | ||||
-rw-r--r-- | src/common/inbound.c | 159 | ||||
-rw-r--r-- | src/common/inbound.h | 2 | ||||
-rw-r--r-- | src/common/outbound.c | 141 | ||||
-rw-r--r-- | src/common/plugin.c | 5 | ||||
-rw-r--r-- | src/common/proto-irc.c | 276 | ||||
-rw-r--r-- | src/common/server.c | 4 | ||||
-rw-r--r-- | src/common/servlist.c | 574 | ||||
-rw-r--r-- | src/common/servlist.h | 64 | ||||
-rw-r--r-- | src/common/textevents.in | 10 | ||||
-rw-r--r-- | src/common/util.c | 94 | ||||
-rw-r--r-- | src/common/util.h | 1 |
15 files changed, 867 insertions, 485 deletions
diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c index a7949d3f..c3cad2aa 100644 --- a/src/common/cfgfiles.c +++ b/src/common/cfgfiles.c @@ -536,7 +536,9 @@ const struct prefs vars[] = {"irc_whois_front", P_OFFINT (hex_irc_whois_front), TYPE_BOOL}, {"net_auto_reconnect", P_OFFINT (hex_net_auto_reconnect), TYPE_BOOL}, +#ifndef WIN32 /* FIXME fix reconnect crashes and remove this ifdef! */ {"net_auto_reconnectonfail", P_OFFINT (hex_net_auto_reconnectonfail), TYPE_BOOL}, +#endif {"net_bind_host", P_OFFSET (hex_net_bind_host), TYPE_STR}, {"net_ping_timeout", P_OFFINT (hex_net_ping_timeout), TYPE_INT}, {"net_proxy_auth", P_OFFINT (hex_net_proxy_auth), TYPE_BOOL}, @@ -729,7 +731,7 @@ load_config (void) prefs.hex_gui_win_width = 640; prefs.hex_input_balloon_time = 20; prefs.hex_irc_ban_type = 2; - prefs.hex_irc_join_delay = 3; + prefs.hex_irc_join_delay = 5; prefs.hex_net_reconnect_delay = 10; prefs.hex_notify_timeout = 15; prefs.hex_text_max_indent = 256; diff --git a/src/common/dcc.c b/src/common/dcc.c index 9014296e..4980cabc 100644 --- a/src/common/dcc.c +++ b/src/common/dcc.c @@ -66,15 +66,6 @@ #define BIG_STR_TO_INT(x) strtoul(x,NULL,10) #endif -/* This is practically copy-paste from gstdio.h. - * GStatBuf was added in 2.26. On Win32 we already use that, - * so we only gotta check this on Unix */ -#ifndef WIN32 -#if !GLIB_CHECK_VERSION(2,26,0) -typedef struct stat GStatBuf; -#endif -#endif - static char *dcctypes[] = { "SEND", "RECV", "CHAT", "CHAT" }; struct dccstat_info dccstat[] = { diff --git a/src/common/hexchat.c b/src/common/hexchat.c index 72b97e3a..c879af9d 100644 --- a/src/common/hexchat.c +++ b/src/common/hexchat.c @@ -706,6 +706,7 @@ static char defaultconf_commands[] = "NAME KILL\n" "CMD quote KILL %2 :&3\n\n"\ "NAME LEAVE\n" "CMD part &2\n\n"\ "NAME M\n" "CMD msg &2\n\n"\ + "NAME OMSG\n" "CMD msg @%c &2\n\n"\ "NAME ONOTICE\n" "CMD notice @%c &2\n\n"\ "NAME RAW\n" "CMD quote &2\n\n"\ "NAME SERVHELP\n" "CMD quote HELP\n\n"\ diff --git a/src/common/hexchat.h b/src/common/hexchat.h index dd30dce6..5fdbfc45 100644 --- a/src/common/hexchat.h +++ b/src/common/hexchat.h @@ -479,7 +479,7 @@ typedef struct server void (*p_ns_identify)(struct server *, char *pass); void (*p_ns_ghost)(struct server *, char *usname, char *pass); void (*p_join)(struct server *, char *channel, char *key); - void (*p_join_list)(struct server *, GSList *channels, GSList *keys); + void (*p_join_list)(struct server *, GSList *favorites); void (*p_login)(struct server *, char *user, char *realname); void (*p_join_info)(struct server *, char *channel); void (*p_mode)(struct server *, char *target, char *mode); @@ -527,14 +527,12 @@ typedef struct server char hostname[128]; /* real ip number */ char servername[128]; /* what the server says is its name */ char password[86]; - char sasluser[32]; /* this is just a buffer for network->user */ - char saslpassword[86]; /* we could reuse password but then we couldn't guarantee NickServ doesn't register first */ char nick[NICKLEN]; char linebuf[2048]; /* RFC says 512 chars including \r\n */ char *last_away_reason; int pos; /* current position in linebuf */ int nickcount; - int nickservtype; /* 0=/MSG nickserv 1=/NICKSERV 2=/NS */ + int loginmethod; /* see login_types[] */ char *chantypes; /* for 005 numeric - free me */ char *chanmodes; /* for 005 numeric - free me */ @@ -568,7 +566,7 @@ typedef struct server time_t away_time; /* when we were marked away */ char *encoding; /* NULL for system */ - char *autojoin; /* list of channels & keys to join */ + GSList *favlist; /* list of channels & keys to join */ unsigned int motd_skipped:1; unsigned int connected:1; diff --git a/src/common/inbound.c b/src/common/inbound.c index 29eaf754..da2cb34c 100644 --- a/src/common/inbound.c +++ b/src/common/inbound.c @@ -1069,35 +1069,23 @@ inbound_nameslist_end (server *serv, char *chan) static gboolean check_autojoin_channels (server *serv) { - char *po; + int i = 0; session *sess; GSList *list = sess_list; - int i = 0; - GSList *channels, *keys; + GSList *sess_channels = NULL; /* joined channels that are not in the favorites list */ + favchannel *fav; - /* shouldnt really happen, the io tag is destroyed in server.c */ + /* shouldn't really happen, the io tag is destroyed in server.c */ if (!is_server (serv)) - return FALSE; - - /* send auto join list */ - if (serv->autojoin) { - joinlist_split (serv->autojoin, &channels, &keys); - serv->p_join_list (serv, channels, keys); - joinlist_free (channels, keys); - - free (serv->autojoin); - serv->autojoin = NULL; - i++; + return FALSE; } - /* this is really only for re-connects when you - * join channels not in the auto-join list. */ - channels = NULL; - keys = NULL; + /* If there's a session (i.e. this is a reconnect), autojoin to everything that was open previously. */ while (list) { sess = list->data; + if (sess->server == serv) { if (sess->willjoinchannel[0] != 0) @@ -1105,34 +1093,50 @@ check_autojoin_channels (server *serv) strcpy (sess->waitchannel, sess->willjoinchannel); sess->willjoinchannel[0] = 0; - po = strchr (sess->waitchannel, ','); - if (po) - *po = 0; - po = strchr (sess->waitchannel, ' '); - if (po) - *po = 0; + fav = servlist_favchan_find (serv->network, sess->waitchannel, NULL); /* Is this channel in our favorites? */ - /* There can be no gap between keys, list keyed chans first. */ - if (sess->channelkey[0] != 0) + /* session->channelkey is initially unset for channels joined from the favorites. You have to fill them up manually from favorites settings. */ + if (fav) { - channels = g_slist_prepend (channels, g_strdup (sess->waitchannel)); - keys = g_slist_prepend (keys, g_strdup (sess->channelkey)); + /* session->channelkey is set if there was a key change during the session. In that case, use the session key, not the one from favorites. */ + if (fav->key && !strlen (sess->channelkey)) + { + safe_strcpy (sess->channelkey, fav->key, sizeof (sess->channelkey)); + } + } + + /* for easier checks, ensure that favchannel->key is just NULL when session->channelkey is empty i.e. '' */ + if (strlen (sess->channelkey)) + { + sess_channels = servlist_favchan_listadd (sess_channels, sess->waitchannel, sess->channelkey); } else { - channels = g_slist_append (channels, g_strdup (sess->waitchannel)); - keys = g_slist_append (keys, g_strdup (sess->channelkey)); + sess_channels = servlist_favchan_listadd (sess_channels, sess->waitchannel, NULL); } i++; } } + list = list->next; } - if (channels) + if (sess_channels) { - serv->p_join_list (serv, channels, keys); - joinlist_free (channels, keys); + serv->p_join_list (serv, sess_channels); + g_slist_free_full (sess_channels, (GDestroyNotify) servlist_favchan_free); + } + else + { + /* If there's no session, just autojoin to favorites. */ + if (serv->favlist) + { + serv->p_join_list (serv, serv->favlist); + i++; + + /* FIXME this is not going to work and is not needed either. server_free() does the job already. */ + /* g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free); */ + } } serv->joindelay_tag = 0; @@ -1141,7 +1145,7 @@ check_autojoin_channels (server *serv) } void -inbound_next_nick (session *sess, char *nick) +inbound_next_nick (session *sess, char *nick, int error) { char *newnick; server *serv = sess->server; @@ -1156,14 +1160,30 @@ inbound_next_nick (session *sess, char *nick) net = serv->network; /* use network specific "Second choice"? */ if (net && !(net->flags & FLAG_USE_GLOBAL) && net->nick2) + { newnick = net->nick2; + } serv->p_change_nick (serv, newnick); - EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, newnick, NULL, NULL, 0); + if (error) + { + EMIT_SIGNAL (XP_TE_NICKERROR, sess, nick, newnick, NULL, NULL, 0); + } + else + { + EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, newnick, NULL, NULL, 0); + } break; case 3: serv->p_change_nick (serv, prefs.hex_irc_nick3); - EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, prefs.hex_irc_nick3, NULL, NULL, 0); + if (error) + { + EMIT_SIGNAL (XP_TE_NICKERROR, sess, nick, prefs.hex_irc_nick3, NULL, NULL, 0); + } + else + { + EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, prefs.hex_irc_nick3, NULL, NULL, 0); + } break; default: @@ -1367,9 +1387,31 @@ inbound_exec_eom_cmd (char *str, void *sess) return 1; } +static int +inbound_nickserv_login (server *serv) +{ + /* this could grow ugly, but let's hope there won't be new NickServ types */ + switch (serv->loginmethod) + { + case LOGIN_MSG_NICKSERV: + case LOGIN_NICKSERV: + case LOGIN_CHALLENGEAUTH: +#if 0 + case LOGIN_NS: + case LOGIN_MSG_NS: + case LOGIN_AUTH: +#endif + return 1; + default: + return 0; + } +} + void inbound_login_end (session *sess, char *text) { + GSList *cmdlist; + commandentry *cmd; server *serv = sess->server; if (!serv->end_of_motd) @@ -1384,35 +1426,50 @@ inbound_login_end (session *sess, char *text) if (serv->network) { /* there may be more than 1, separated by \n */ - if (((ircnet *)serv->network)->command) - token_foreach (((ircnet *)serv->network)->command, '\n', - inbound_exec_eom_cmd, sess); + + cmdlist = ((ircnet *)serv->network)->commandlist; + while (cmdlist) + { + cmd = cmdlist->data; + inbound_exec_eom_cmd (cmd->command, sess); + cmdlist = cmdlist->next; + } /* send nickserv password */ - if (((ircnet *)serv->network)->nickserv) - serv->p_ns_identify (serv, ((ircnet *)serv->network)->nickserv); + if (((ircnet *)serv->network)->pass && inbound_nickserv_login (serv)) + { + serv->p_ns_identify (serv, ((ircnet *)serv->network)->pass); + } } - /* send JOIN now or wait? */ - if (serv->network && ((ircnet *)serv->network)->nickserv && - prefs.hex_irc_join_delay) - serv->joindelay_tag = fe_timeout_add (prefs.hex_irc_join_delay * 1000, - check_autojoin_channels, serv); + /* wait for join if command or nickserv set */ + if (serv->network && prefs.hex_irc_join_delay + && ((((ircnet *)serv->network)->pass && inbound_nickserv_login (serv)) + || ((ircnet *)serv->network)->commandlist)) + { + serv->joindelay_tag = fe_timeout_add (prefs.hex_irc_join_delay * 1000, check_autojoin_channels, serv); + } else + { check_autojoin_channels (serv); + } + if (serv->supports_watch || serv->supports_monitor) + { notify_send_watches (serv); + } + serv->end_of_motd = TRUE; } + if (prefs.hex_irc_skip_motd && !serv->motd_skipped) { serv->motd_skipped = TRUE; - EMIT_SIGNAL (XP_TE_MOTDSKIP, serv->server_session, NULL, NULL, - NULL, NULL, 0); + EMIT_SIGNAL (XP_TE_MOTDSKIP, serv->server_session, NULL, NULL, NULL, NULL, 0); return; } - EMIT_SIGNAL (XP_TE_MOTD, serv->server_session, text, NULL, - NULL, NULL, 0); + + EMIT_SIGNAL (XP_TE_MOTD, serv->server_session, text, NULL, NULL, NULL, 0); } void diff --git a/src/common/inbound.h b/src/common/inbound.h index e90ef8c3..32368cc1 100644 --- a/src/common/inbound.h +++ b/src/common/inbound.h @@ -20,7 +20,7 @@ #ifndef HEXCHAT_INBOUND_H #define HEXCHAT_INBOUND_H -void inbound_next_nick (session *sess, char *nick); +void inbound_next_nick (session *sess, char *nick, int error); void inbound_uback (server *serv); void inbound_uaway (server *serv); void inbound_account (server *serv, char *nick, char *account); diff --git a/src/common/outbound.c b/src/common/outbound.c index c8e0ff64..255e809c 100644 --- a/src/common/outbound.c +++ b/src/common/outbound.c @@ -4356,6 +4356,85 @@ xit: free (newcmd); } +char * +command_insert_vars (session *sess, char *cmd) +{ + int pos; + GString *expanded; + ircnet *mynet = (ircnet *) sess->server->network; + + if (!mynet) /* shouldn't really happen */ + { + return g_strdup (cmd); /* the return value will be freed so we must srtdup() it */ + } + + expanded = g_string_new (NULL); + + while (strchr (cmd, '%') != NULL) + { + pos = (int) (strchr (cmd, '%') - cmd); /* offset to the first '%' */ + g_string_append_len (expanded, cmd, pos); /* copy contents till the '%' */ + cmd += pos + 1; /* jump to the char after the '%' */ + + switch (cmd[0]) + { + case 'n': + if (mynet->nick) + { + g_string_append (expanded, mynet->nick); + } + else + { + g_string_append (expanded, prefs.hex_irc_nick1); + } + cmd++; + break; + + case 'p': + if (mynet->pass) + { + g_string_append (expanded, mynet->pass); + } + cmd++; + break; + + case 'r': + if (mynet->real) + { + g_string_append (expanded, mynet->real); + } + else + { + g_string_append (expanded, prefs.hex_irc_real_name); + } + cmd++; + break; + + case 'u': + if (mynet->user) + { + g_string_append (expanded, mynet->user); + } + else + { + g_string_append (expanded, prefs.hex_irc_user_name); + } + cmd++; + break; + + default: /* unsupported character? copy it along with the '%'! */ + cmd--; + g_string_append_len (expanded, cmd, 2); + cmd += 2; + break; + } + } + + g_string_append (expanded, cmd); /* copy any remaining string after the last '%' */ + + return g_string_free (expanded, FALSE); +} + /* handle a command, without the '/' prefix */ int @@ -4372,6 +4451,7 @@ handle_command (session *sess, char *cmd, int check_spch) char tbuf_static[TBUFSIZE]; char *pdibuf; char *tbuf; + char *cmd_vars; int len; int ret = TRUE; @@ -4383,19 +4463,29 @@ handle_command (session *sess, char *cmd, int check_spch) command_level++; /* anything below MUST DEC command_level before returning */ - len = strlen (cmd); + cmd_vars = command_insert_vars (sess, cmd); + + len = strlen (cmd_vars); if (len >= sizeof (pdibuf_static)) + { pdibuf = malloc (len + 1); + } else + { pdibuf = pdibuf_static; + } if ((len * 2) >= sizeof (tbuf_static)) + { tbuf = malloc ((len * 2) + 1); + } else + { tbuf = tbuf_static; + } /* split the text into words and word_eol */ - process_data_init (pdibuf, cmd, word, word_eol, TRUE, TRUE); + process_data_init (pdibuf, cmd_vars, word, word_eol, TRUE, TRUE); /* ensure an empty string at index 32 for cmd_deop etc */ /* (internal use only, plugins can still only read 1-31). */ @@ -4405,17 +4495,25 @@ handle_command (session *sess, char *cmd, int check_spch) int_cmd = find_internal_command (word[1]); /* redo it without quotes processing, for some commands like /JOIN */ if (int_cmd && !int_cmd->handle_quotes) - process_data_init (pdibuf, cmd, word, word_eol, FALSE, FALSE); + { + process_data_init (pdibuf, cmd_vars, word, word_eol, FALSE, FALSE); + } if (check_spch && prefs.hex_input_perc_color) - check_special_chars (cmd, prefs.hex_input_perc_ascii); + { + check_special_chars (cmd_vars, prefs.hex_input_perc_ascii); + } if (plugin_emit_command (sess, word[1], word, word_eol)) + { goto xit; + } /* incase a plugin did /close */ if (!is_session (sess)) + { goto xit; + } /* first see if it's a userCommand */ list = command_list; @@ -4431,7 +4529,9 @@ handle_command (session *sess, char *cmd, int check_spch) } if (user_cmd) + { goto xit; + } /* now check internal commands */ int_cmd = find_internal_command (word[1]); @@ -4441,38 +4541,51 @@ handle_command (session *sess, char *cmd, int check_spch) if (int_cmd->needserver && !sess->server->connected) { notc_msg (sess); - } else if (int_cmd->needchannel && !sess->channel[0]) + } + else if (int_cmd->needchannel && !sess->channel[0]) { notj_msg (sess); - } else + } + else { switch (int_cmd->callback (sess, tbuf, word, word_eol)) { - case FALSE: - help (sess, tbuf, int_cmd->name, TRUE); - break; - case 2: - ret = FALSE; - goto xit; + case FALSE: + help (sess, tbuf, int_cmd->name, TRUE); + break; + case 2: + ret = FALSE; + goto xit; } } - } else + } + else { /* unknown command, just send it to the server and hope */ if (!sess->server->connected) + { PrintText (sess, _("Unknown Command. Try /help\n")); + } else - sess->server->p_raw (sess->server, cmd); + { + sess->server->p_raw (sess->server, cmd_vars); + } } xit: command_level--; if (pdibuf != pdibuf_static) + { free (pdibuf); + } if (tbuf != tbuf_static) + { free (tbuf); + } + + g_free (cmd_vars); return ret; } diff --git a/src/common/plugin.c b/src/common/plugin.c index 686f9749..61d5cb40 100644 --- a/src/common/plugin.c +++ b/src/common/plugin.c @@ -1111,11 +1111,6 @@ hexchat_get_info (hexchat_plugin *ph, const char *id) case 0x339763: /* nick */ return sess->server->nick; - case 0x438fdf9: /* nickserv */ - if (sess->server->network) - return ((ircnet *)sess->server->network)->nickserv; - return NULL; - case 0xca022f43: /* server */ if (!sess->server->connected) return NULL; diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c index 984f7f20..cb4db0cd 100644 --- a/src/common/proto-irc.c +++ b/src/common/proto-irc.c @@ -42,6 +42,7 @@ #include "util.h" #include "hexchatc.h" #include "url.h" +#include "servlist.h" static void @@ -49,7 +50,7 @@ irc_login (server *serv, char *user, char *realname) { tcp_sendf (serv, "CAP LS\r\n"); /* start with CAP LS as Charybdis sasl.txt suggests */ - if (serv->password[0]) + if (serv->password[0] && serv->loginmethod == LOGIN_PASS) { tcp_sendf (serv, "PASS %s\r\n", serv->password); } @@ -64,44 +65,54 @@ static void irc_nickserv (server *serv, char *cmd, char *arg1, char *arg2, char *arg3) { /* are all ircd authors idiots? */ - switch (serv->nickservtype) + switch (serv->loginmethod) { - case 0: - tcp_sendf (serv, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd, arg1, arg2, arg3); - break; - case 1: - tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3); - break; - case 2: - tcp_sendf (serv, "NS %s %s%s%s\r\n", cmd, arg1, arg2, arg3); - break; - case 3: - tcp_sendf (serv, "PRIVMSG NS :%s %s%s%s\r\n", cmd, arg1, arg2, arg3); - break; - case 4: - /* why couldn't QuakeNet implement one of the existing ones? */ - tcp_sendf (serv, "AUTH %s %s\r\n", arg1, arg2); + case LOGIN_MSG_NICKSERV: + tcp_sendf (serv, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd, arg1, arg2, arg3); + break; + case LOGIN_NICKSERV: + tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3); + break; +#if 0 + case LOGIN_MSG_NS: + tcp_sendf (serv, "PRIVMSG NS :%s %s%s%s\r\n", cmd, arg1, arg2, arg3); + break; + case LOGIN_NS: + tcp_sendf (serv, "NS %s %s%s%s\r\n", cmd, arg1, arg2, arg3); + break; + case LOGIN_AUTH: + /* why couldn't QuakeNet implement one of the existing ones? */ + tcp_sendf (serv, "AUTH %s %s\r\n", arg1, arg2); + break; +#endif } } static void irc_ns_identify (server *serv, char *pass) { - if (serv->nickservtype == 4) /* QuakeNet needs to do everything in its own ways... */ - { - irc_nickserv (serv, "", serv->nick, pass, ""); - } - else + switch (serv->loginmethod) { - irc_nickserv (serv, "IDENTIFY", pass, "", ""); + case LOGIN_CHALLENGEAUTH: + tcp_sendf (serv, "PRIVMSG %s :CHALLENGE\r\n", CHALLENGEAUTH_NICK); /* request a challenge from Q */ + break; +#if 0 + case LOGIN_AUTH: + irc_nickserv (serv, "", serv->nick, pass, ""); + break; +#endif + default: + irc_nickserv (serv, "IDENTIFY", pass, "", ""); } } static void irc_ns_ghost (server *serv, char *usname, char *pass) { - if (serv->nickservtype != 4) + if (serv->loginmethod != LOGIN_CHALLENGEAUTH) + { irc_nickserv (serv, "GHOST", usname, " ", pass); + } } static void @@ -114,118 +125,97 @@ irc_join (server *serv, char *channel, char *key) } static void -irc_join_list_flush (server *serv, GString *c, GString *k) +irc_join_list_flush (server *serv, GString *channels, GString *keys, int send_keys) { - char *chanstr, *keystr; + char *chanstr; + char *keystr; + + chanstr = g_string_free (channels, FALSE); /* convert our strings to char arrays */ + keystr = g_string_free (keys, FALSE); - chanstr = g_string_free (c, FALSE); - keystr = g_string_free (k, FALSE); - if (chanstr[0]) + if (send_keys) { - if (keystr[0]) - tcp_sendf (serv, "JOIN %s %s\r\n", chanstr, keystr); - else - tcp_sendf (serv, "JOIN %s\r\n", chanstr); + tcp_sendf (serv, "JOIN %s %s\r\n", chanstr, keystr); /* send the actual command */ + } + else + { + tcp_sendf (serv, "JOIN %s\r\n", chanstr); /* send the actual command */ } + g_free (chanstr); g_free (keystr); } -/* join a whole list of channels & keys, split to multiple lines - * to get around 512 limit */ +/* Join a whole list of channels & keys, split to multiple lines + * to get around the 512 limit. + */ static void -irc_join_list (server *serv, GSList *channels, GSList *keys) +irc_join_list (server *serv, GSList *favorites) { - GSList *clist; - GSList *klist; - GString *c = g_string_new (NULL); - GString *k = g_string_new (NULL); - int len; - int add; - int i, j; + int first_item = 1; /* determine whether we add commas or not */ + int send_keys = 0; /* if none of our channels have keys, we can omit the 'x' fillers altogether */ + int len = 9; /* JOIN<space>channels<space>keys\r\n\0 */ + favchannel *fav; + GString *chanlist = g_string_new (NULL); + GString *keylist = g_string_new (NULL); + GSList *favlist; - i = j = 0; - len = 9; /* "JOIN<space><space>\r\n" */ - clist = channels; - klist = keys; + favlist = favorites; - while (clist) + while (favlist) { - /* measure how many bytes this channel would add... */ - if (1) - { - add = strlen (clist->data); - if (i != 0) - add++; /* comma */ - } + fav = favlist->data; - if (klist->data) - { - add += strlen (klist->data); - } - else + len += strlen (fav->name); + if (fav->key) { - add++; /* 'x' filler */ + len += strlen (fav->key); } - if (j != 0) - add++; /* comma */ - - /* too big? dump buffer and start a fresh one */ - if (len + add > 512) + if (len >= 512) /* command length exceeds the IRC hard limit, flush it and start from scratch */ { - irc_join_list_flush (serv, c, k); + irc_join_list_flush (serv, chanlist, keylist, send_keys); + + chanlist = g_string_new (NULL); + keylist = g_string_new (NULL); - c = g_string_new (NULL); - k = g_string_new (NULL); - i = j = 0; len = 9; + first_item = 1; /* list dumped, omit commas once again */ + send_keys = 0; /* also omit keys until we actually find one */ } - /* now actually add it to our GStrings */ - if (1) + if (!first_item) { - add = strlen (clist->data); - if (i != 0) - { - add++; - g_string_append_c (c, ','); - } - g_string_append (c, clist->data); - i++; + /* This should be done before the length check, but channel names + * are already at least 2 characters long so it would trigger the + * flush anyway. + */ + len += 2; + + /* add separators but only if it's not the 1st element */ + g_string_append_c (chanlist, ','); + g_string_append_c (keylist, ','); } - if (klist->data) + g_string_append (chanlist, fav->name); + + if (fav->key) { - add += strlen (klist->data); - if (j != 0) - { - add++; - g_string_append_c (k, ','); - } - g_string_append (k, klist->data); - j++; + g_string_append (keylist, fav->key); + send_keys = 1; } else { - add++; - if (j != 0) - { - add++; - g_string_append_c (k, ','); - } - g_string_append_c (k, 'x'); - j++; + g_string_append_c (keylist, 'x'); /* 'x' filler for keyless channels so that our JOIN command is always well-formatted */ } - len += add; - - klist = klist->next; - clist = clist->next; + first_item = 0; + favlist = favlist->next; } - irc_join_list_flush (serv, c, k); + irc_join_list_flush (serv, chanlist, keylist, send_keys); + g_slist_free (favlist); } static void @@ -845,17 +835,26 @@ process_numeric (session * sess, int n, inbound_login_end (sess, text); break; - case 433: /* nickname in use */ case 432: /* erroneous nickname */ if (serv->end_of_motd) + { + goto def; + } + inbound_next_nick (sess, word[4], 1); + break; + + case 433: /* nickname in use */ + if (serv->end_of_motd) + { goto def; - inbound_next_nick (sess, word[4]); + } + inbound_next_nick (sess, word[4], 0); break; case 437: if (serv->end_of_motd || is_channel (serv, word[4])) goto def; - inbound_next_nick (sess, word[4]); + inbound_next_nick (sess, word[4], 0); break; case 471: @@ -938,16 +937,21 @@ process_numeric (session * sess, int n, } def: - if (is_channel (serv, word[4])) - { - session *realsess = find_channel (serv, word[4]); - if (!realsess) - realsess = serv->server_session; - EMIT_SIGNAL (XP_TE_SERVTEXT, realsess, text, word[1], word[2], NULL, 0); - } else { - EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text, word[1], - word[2], NULL, 0); + session *sess; + + if (is_channel (serv, word[4])) + { + sess = find_channel (serv, word[4]); + if (!sess) + sess = serv->server_session; + } + else if ((sess=find_dialog (serv,word[4]))) /* user with an open dialog */ + ; + else + sess=serv->server_session; + + EMIT_SIGNAL (XP_TE_SERVTEXT, sess, text, word[1], word[2], NULL, 0); } } } @@ -1091,11 +1095,28 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[]) case WORDL('N','O','T','I'): { - int id = FALSE; /* identified */ + int id = FALSE; /* identified */ + char *response; text = word_eol[4]; if (*text == ':') + { text++; + } + + if (!strncmp (text, "CHALLENGE ", 10)) /* QuakeNet CHALLENGE upon our request */ + { + response = challengeauth_response (((ircnet *)serv->network)->user ? ((ircnet *)serv->network)->user : prefs.hex_irc_user_name, serv->password, word[5]); + + tcp_sendf (serv, "PRIVMSG %s :CHALLENGEAUTH %s %s %s\r\n", + CHALLENGEAUTH_NICK, + ((ircnet *)serv->network)->user ? ((ircnet *)serv->network)->user : prefs.hex_irc_user_name, + response, + CHALLENGEAUTH_ALGO); + + g_free (response); + return; /* omit the CHALLENGE <hash> ALGOS message */ + } if (serv->have_idmsg) { @@ -1119,6 +1140,10 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[]) int id = FALSE; /* identified */ if (*to) { + /* Handle limited channel messages, for now no special event */ + if (strchr (serv->nick_prefixes, to[0]) != NULL) + to++; + text = word_eol[4]; if (*text == ':') text++; @@ -1218,10 +1243,23 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[]) if (strstr (word_eol[5], "sasl") != 0) { serv->have_sasl = TRUE; - EMIT_SIGNAL (XP_TE_SASLAUTH, serv->server_session, sess->server->sasluser, NULL, NULL, NULL, 0); + EMIT_SIGNAL + ( + XP_TE_SASLAUTH, + serv->server_session, + (((ircnet *)sess->server->network)->user) ? (((ircnet *)sess->server->network)->user) : prefs.hex_irc_user_name, + NULL, + NULL, + NULL, + 0 + ); tcp_send_len (serv, "AUTHENTICATE PLAIN\r\n", 20); - pass = encode_sasl_pass (sess->server->sasluser, sess->server->saslpassword); + pass = encode_sasl_pass + ( + (((ircnet *)sess->server->network)->user) ? (((ircnet *)sess->server->network)->user) : prefs.hex_irc_user_name, + sess->server->password + ); tcp_sendf (sess->server, "AUTHENTICATE %s\r\n", pass); free (pass); } @@ -1259,8 +1297,8 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[]) strcat (buffer, "extended-join "); want_cap = 1; } - /* if the SASL password is set, request SASL auth */ - if (strstr (word_eol[5], "sasl") != 0 && strlen (sess->server->saslpassword) != 0) + /* if the SASL password is set AND auth mode is set to SASL, request SASL auth */ + if (strstr (word_eol[5], "sasl") != 0 && strlen (sess->server->password) != 0 && serv->loginmethod == LOGIN_SASL) { strcat (buffer, "sasl "); want_cap = 1; diff --git a/src/common/server.c b/src/common/server.c index 45472e05..3f7027e2 100644 --- a/src/common/server.c +++ b/src/common/server.c @@ -2031,8 +2031,8 @@ server_free (server *serv) free (serv->last_away_reason); if (serv->encoding) free (serv->encoding); - if (serv->autojoin) - free (serv->autojoin); + if (serv->favlist) + g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free); fe_server_callback (serv); diff --git a/src/common/servlist.c b/src/common/servlist.c index 4b04820b..d121dd47 100644 --- a/src/common/servlist.c +++ b/src/common/servlist.c @@ -43,7 +43,8 @@ struct defaultserver char *host; char *channel; char *charset; - int nsmode; /* default NickServ type */ + 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[] = @@ -167,7 +168,7 @@ static const struct defaultserver def[] = {0, "irc.criten.net"}, {0, "irc.eu.criten.net"}, - {"DALnet", 0, 0, 0, 2}, + {"DALnet", 0}, {0, "irc.dal.net"}, {0, "irc.eu.dal.net"}, @@ -245,7 +246,7 @@ static const struct defaultserver def[] = {0, "irc.ggn.net"}, {0, "irc.vendetta.com"}, - {"freenode", 0, "#hexchat"}, + {"freenode", 0, "#hexchat", 0, LOGIN_SASL}, #ifdef USE_OPENSSL {0, "irc.freenode.net/+6697"}, #endif @@ -255,6 +256,12 @@ static const struct defaultserver def[] = {0, "kabel.freeworld.nu"}, {0, "irc.freeworld.nu"},*/ + {"FurryLand", 0}, +#ifdef USE_OPENSSL + {0, "irc.furryland.net/+6697"}, +#endif + {0, "irc.furryland.net"}, + {"Fusion Latina", 0}, {0, "irc.fusionlatina.org/2012"}, @@ -263,7 +270,7 @@ static const struct defaultserver def[] = /* {0, "sprynet.us.galaxynet.org"}, {0, "atlanta.ga.us.galaxynet.org"},*/ - {"GameSurge", 0, 0, 0, 2}, + {"GameSurge", 0}, {0, "irc.gamesurge.net"}, /* {"GamesNET", 0}, @@ -426,7 +433,7 @@ static const struct defaultserver def[] = {0, "nfsi.ptnet.org"}, {0, "fctunl.ptnet.org"}, - {"QuakeNet", 0, 0, 0, 5}, + {"QuakeNet", 0, 0, 0, LOGIN_CHALLENGEAUTH}, {0, "irc.quakenet.org"}, {0, "irc.se.quakenet.org"}, {0, "irc.dk.quakenet.org"}, @@ -460,7 +467,7 @@ static const struct defaultserver def[] = {"Rizon", 0}, {0, "irc.rizon.net"}, - {"RusNet", 0, 0, "KOI8-R (Cyrillic)", 2}, + {"RusNet", 0, 0, "KOI8-R (Cyrillic)"}, {0, "irc.tomsk.net"}, {0, "irc.rinet.ru"}, {0, "irc.run.net"}, @@ -541,11 +548,11 @@ static const struct defaultserver def[] = {0, "irc.servx.ru"}, {0, "irc.gavnos.ru"}, - {"UnderNet", 0}, + {"UnderNet", 0, 0, 0, LOGIN_CUSTOM, "MSG x@channels.undernet.org login %u %p"}, {0, "us.undernet.org"}, {0, "eu.undernet.org"}, - {"UniBG", 0, 0, 0, 4}, + {"UniBG", 0, 0, 0, LOGIN_CUSTOM, "MSG NS IDENTIFY %p"}, {0, "irc.lirex.com"}, {0, "irc.naturella.com"}, {0, "irc.spnet.net"}, @@ -581,6 +588,55 @@ static const struct defaultserver def[] = GSList *network_list = 0; +#if !GLIB_CHECK_VERSION(2,34,0) +#define g_slist_copy_deep servlist_slist_copy_deep +/* FIXME copy-paste from gslist.c, should be dumped sometime */ +static GSList* +servlist_slist_copy_deep (GSList *list, GCopyFunc func, gpointer user_data) +{ + GSList *new_list = NULL; + + if (list) + { + GSList *last; + + new_list = g_slice_new (GSList); + if (func) + new_list->data = func (list->data, user_data); + else + new_list->data = list->data; + last = new_list; + list = list->next; + while (list) + { + last->next = g_slice_new (GSList); + last = last->next; + if (func) + last->data = func (list->data, user_data); + else + last->data = list->data; + list = list->next; + } + last->next = NULL; + } + + return new_list; +} +#endif + +favchannel * +servlist_favchan_copy (favchannel *fav) +{ + favchannel *newfav; + + newfav = malloc (sizeof (favchannel)); + memset (newfav, 0, sizeof (favchannel)); + + newfav->name = g_strdup (fav->name); + newfav->key = g_strdup (fav->key); /* g_strdup() can handle NULLs so no need to check it */ + + return newfav; +} void servlist_connect (session *sess, ircnet *net, gboolean join) @@ -603,53 +659,39 @@ servlist_connect (session *sess, ircnet *net, gboolean join) return; ircserv = list->data; - /* incase a protocol switch is added to the servlist gui */ + /* in case a protocol switch is added to the servlist gui */ server_fill_her_up (sess->server); if (join) { sess->willjoinchannel[0] = 0; - if (net->autojoin) + if (net->favchanlist) { - if (serv->autojoin) - free (serv->autojoin); - serv->autojoin = strdup (net->autojoin); + if (serv->favlist) + { + g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free); + } + serv->favlist = g_slist_copy_deep (net->favchanlist, (GCopyFunc) servlist_favchan_copy, NULL); } } - if (net->nstype >= 1) /* once again, make sure gtk_combo_box_get_active() is not bugging us, just in case */ + if (net->logintype) { - serv->nickservtype = net->nstype - 1; /* ircnet->nstype starts at 1, server->nickservtype starts at 0! */ + serv->loginmethod = net->logintype; } else { - serv->nickservtype = 1; /* use /NickServ by default */ + serv->loginmethod = LOGIN_DEFAULT_REAL; } serv->password[0] = 0; - serv->sasluser[0] = 0; - serv->saslpassword[0] = 0; if (net->pass) { safe_strcpy (serv->password, net->pass, sizeof (serv->password)); } - if (net->flags & FLAG_USE_GLOBAL || net->user == NULL) - { - strcpy (serv->sasluser, prefs.hex_irc_user_name); - } - else - { - safe_strcpy (serv->sasluser, net->user, sizeof (serv->sasluser)); - } - - if (net->saslpass) - { - safe_strcpy (serv->saslpassword, net->saslpass, sizeof (serv->saslpassword)); - } - if (net->flags & FLAG_USE_GLOBAL) { strcpy (serv->nick, prefs.hex_irc_nick1); @@ -820,7 +862,9 @@ servlist_server_find (ircnet *net, char *name, int *pos) if (strcmp (serv->hostname, name) == 0) { if (pos) + { *pos = i; + } return serv; } i++; @@ -830,6 +874,56 @@ servlist_server_find (ircnet *net, char *name, int *pos) return NULL; } +favchannel * +servlist_favchan_find (ircnet *net, char *channel, int *pos) +{ + GSList *list = net->favchanlist; + favchannel *favchan; + int i = 0; + + 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") */ @@ -897,6 +991,60 @@ servlist_server_add (ircnet *net, char *name) 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) { @@ -917,6 +1065,35 @@ servlist_server_remove_all (ircnet *net) } } +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) { @@ -941,8 +1118,6 @@ servlist_cleanup (void) { net = list->data; free_and_clear (net->pass); - free_and_clear (net->saslpass); - free_and_clear (net->nickserv); } } @@ -964,12 +1139,10 @@ servlist_net_remove (ircnet *net) if (net->real) free (net->real); free_and_clear (net->pass); - free_and_clear (net->saslpass); - if (net->autojoin) - free (net->autojoin); - if (net->command) - free (net->command); - free_and_clear (net->nickserv); + 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) @@ -983,7 +1156,9 @@ servlist_net_remove (ircnet *net) { serv = list->data; if (serv->network == net) + { serv->network = NULL; + } list = list->next; } } @@ -1019,24 +1194,32 @@ servlist_load_defaults (void) if (def[i].network) { net = servlist_net_add (def[i].network, def[i].host, FALSE); - net->encoding = strdup (IRC_DEFAULT_CHARSET); if (def[i].channel) { - net->autojoin = strdup (def[i].channel); + servlist_favchan_add (net, def[i].channel); } if (def[i].charset) { - free (net->encoding); - net->encoding = strdup (def[i].charset); + net->encoding = g_strdup (def[i].charset); } - if (def[i].nsmode) + else { - net->nstype = def[i].nsmode; + 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 @@ -1057,7 +1240,6 @@ servlist_load (void) FILE *fp; char buf[2048]; int len; - char *tmp; ircnet *net = NULL; /* simple migration we will keep for a short while */ @@ -1100,43 +1282,55 @@ servlist_load (void) case 'P': net->pass = strdup (buf + 2); break; - case 'A': - net->saslpass = strdup (buf + 2); - break; - case 'J': - net->autojoin = strdup (buf + 2); + case 'L': + net->logintype = atoi (buf + 2); break; - case 'C': - if (net->command) - { - /* concat extra commands with a \n separator */ - tmp = net->command; - net->command = malloc (strlen (tmp) + strlen (buf + 2) + 2); - strcpy (net->command, tmp); - strcat (net->command, "\n"); - strcat (net->command, buf + 2); - free (tmp); - } else - net->command = strdup (buf + 2); + case 'E': + net->encoding = strdup (buf + 2); break; case 'F': net->flags = atoi (buf + 2); break; - case 'D': - net->selected = atoi (buf + 2); - break; - case 'E': - net->encoding = strdup (buf + 2); - break; case 'S': /* new server/hostname for this network */ servlist_server_add (net, buf + 2); break; - case 'B': - net->nickserv = strdup (buf + 2); + case 'C': + servlist_command_add (net, buf + 2); break; - case 'T': - net->nstype = atoi (buf + 2); + 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') @@ -1187,13 +1381,6 @@ servlist_check_encoding (char *charset) return FALSE; } -static int -servlist_write_ccmd (char *str, void *fp) -{ - return fprintf (fp, "C=%s\n", (str[0] == '/') ? str + 1 : str); -} - - int servlist_save (void) { @@ -1201,8 +1388,12 @@ servlist_save (void) char *buf; ircnet *net; ircserver *serv; + commandentry *cmd; + favchannel *favchan; GSList *list; - GSList *hlist; + GSList *netlist; + GSList *cmdlist; + GSList *favlist; #ifndef WIN32 int first = FALSE; @@ -1244,26 +1435,8 @@ servlist_save (void) fprintf (fp, "R=%s\n", net->real); if (net->pass) fprintf (fp, "P=%s\n", net->pass); - if (net->saslpass) - fprintf (fp, "A=%s\n", net->saslpass); - if (net->autojoin) - fprintf (fp, "J=%s\n", net->autojoin); - if (net->nickserv) - fprintf (fp, "B=%s\n", net->nickserv); - if (net->nstype) - { - if (net->nstype == -1) /* gtk_combo_box_get_active() returns -1 for invalid indices */ - { - net->nstype = 0; /* avoid further crashes for the current session */ - buf = g_strdup_printf (_("Warning: invalid NickServ type. Falling back to default type for network %s."), net->name); - fe_message (buf, FE_MSG_WARN); - g_free (buf); - } - else /* the selection was fine, save it */ - { - fprintf (fp, "T=%d\n", net->nstype); - } - } + 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")) { @@ -1277,17 +1450,39 @@ servlist_save (void) } } - if (net->command) - token_foreach (net->command, '\n', servlist_write_ccmd, fp); - fprintf (fp, "F=%d\nD=%d\n", net->flags, net->selected); - hlist = net->servlist; - while (hlist) + netlist = net->servlist; + while (netlist) { - serv = hlist->data; + serv = netlist->data; fprintf (fp, "S=%s\n", serv->hostname); - hlist = hlist->next; + 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) @@ -1303,162 +1498,33 @@ servlist_save (void) return TRUE; } -static void -joinlist_free1 (GSList *list) -{ - GSList *head = list; - - for (; list; list = list->next) - g_free (list->data); - g_slist_free (head); -} - -void -joinlist_free (GSList *channels, GSList *keys) -{ - joinlist_free1 (channels); - joinlist_free1 (keys); -} - -gboolean -joinlist_is_in_list (server *serv, char *channel) -{ - GSList *channels, *keys; - GSList *list; - - if (!serv->network || !((ircnet *)serv->network)->autojoin) - return FALSE; - - joinlist_split (((ircnet *)serv->network)->autojoin, &channels, &keys); - - for (list = channels; list; list = list->next) - { - if (serv->p_cmp (list->data, channel) == 0) - return TRUE; - } - - joinlist_free (channels, keys); - - return FALSE; -} - -gchar * -joinlist_merge (GSList *channels, GSList *keys) +static int +joinlist_find_chan (favchannel *curr_item, const char *channel) { - GString *out = g_string_new (NULL); - GSList *list; - int i, j; - - for (; channels; channels = channels->next) + if (!g_ascii_strcasecmp (curr_item->name, channel)) { - g_string_append (out, channels->data); - - if (channels->next) - g_string_append_c (out, ','); + return 0; } - - /* count number of REAL keys */ - for (i = 0, list = keys; list; list = list->next) - if (list->data) - i++; - - if (i > 0) + else { - g_string_append_c (out, ' '); - - for (j = 0; keys; keys = keys->next) - { - if (keys->data) - { - g_string_append (out, keys->data); - j++; - if (j == i) - break; - } - - if (keys->next) - g_string_append_c (out, ','); - } + return 1; } - - return g_string_free (out, FALSE); } -void -joinlist_split (char *autojoin, GSList **channels, GSList **keys) +gboolean +joinlist_is_in_list (server *serv, char *channel) { - char *parta, *partb; - char *chan, *key; - int len; - - *channels = NULL; - *keys = NULL; - - /* after the first space, the keys begin */ - parta = autojoin; - partb = strchr (autojoin, ' '); - if (partb) - partb++; - - while (1) + if (!serv->network || !((ircnet *)serv->network)->favchanlist) { - chan = parta; - key = partb; - - if (1) - { - while (parta[0] != 0 && parta[0] != ',' && parta[0] != ' ') - { - parta++; - } - } - - if (partb) - { - while (partb[0] != 0 && partb[0] != ',' && partb[0] != ' ') - { - partb++; - } - } - - len = parta - chan; - if (len < 1) - break; - *channels = g_slist_append (*channels, g_strndup (chan, len)); - - len = partb - key; - *keys = g_slist_append (*keys, len ? g_strndup (key, len) : NULL); - - if (parta[0] == ' ' || parta[0] == 0) - break; - parta++; - - if (partb) - { - if (partb[0] == 0 || partb[0] == ' ') - partb = NULL; /* no more keys, but maybe more channels? */ - else - partb++; - } + return FALSE; } -#if 0 - GSList *lista, *listb; - int i; - - printf("-----\n"); - i = 0; - lista = *channels; - listb = *keys; - while (lista) + if (g_slist_find_custom (((ircnet *)serv->network)->favchanlist, channel, (GCompareFunc) joinlist_find_chan)) { - printf("%d. |%s| |%s|\n", i, lista->data, listb->data); - i++; - lista = lista->next; - listb = listb->next; + return TRUE; + } + else + { + return FALSE; } - printf("-----\n\n"); -#endif } - - diff --git a/src/common/servlist.h b/src/common/servlist.h index b652f463..45b6dad6 100644 --- a/src/common/servlist.h +++ b/src/common/servlist.h @@ -25,6 +25,17 @@ typedef struct ircserver char *hostname; } ircserver; +typedef struct commandentry +{ + char *command; +} commandentry; + +typedef struct favchannel +{ + char *name; + char *key; +} favchannel; + typedef struct ircnet { char *name; @@ -33,14 +44,12 @@ typedef struct ircnet char *user; char *real; char *pass; - char *saslpass; - char *autojoin; - char *command; - char *nickserv; - int nstype; + int logintype; char *comment; char *encoding; GSList *servlist; + GSList *commandlist; + GSList *favchanlist; int selected; guint32 flags; } ircnet; @@ -49,13 +58,31 @@ extern GSList *network_list; #define FLAG_CYCLE 1 #define FLAG_USE_GLOBAL 2 -#define FLAG_USE_SSL 4 +#define FLAG_USE_SSL 4 #define FLAG_AUTO_CONNECT 8 #define FLAG_USE_PROXY 16 #define FLAG_ALLOW_INVALID 32 #define FLAG_FAVORITE 64 #define FLAG_COUNT 7 +/* Login methods. Use server password by default - if we had a NickServ password, it'd be set to 2 already by servlist_load() */ +#define LOGIN_DEFAULT_REAL LOGIN_PASS /* this is to set the default login method for unknown servers */ +#define LOGIN_DEFAULT 0 /* this is for the login type dropdown, doesn't serve any other purpose */ +#define LOGIN_MSG_NICKSERV 1 +#define LOGIN_NICKSERV 2 +#if 0 +#define LOGIN_NS 3 +#define LOGIN_MSG_NS 4 +#define LOGIN_AUTH 5 +#endif +#define LOGIN_SASL 6 +#define LOGIN_PASS 7 +#define LOGIN_CHALLENGEAUTH 8 +#define LOGIN_CUSTOM 9 + +#define CHALLENGEAUTH_ALGO "HMAC-SHA-256" +#define CHALLENGEAUTH_NICK "Q@CServe.quakenet.org" + /* DEFAULT_CHARSET is already defined in wingdi.h */ #define IRC_DEFAULT_CHARSET "UTF-8 (Unicode)" @@ -74,13 +101,28 @@ void servlist_net_remove (ircnet *net); ircnet *servlist_net_find (char *name, int *pos, int (*cmpfunc) (const char *, const char *)); ircnet *servlist_net_find_from_server (char *server_name); -void servlist_server_remove (ircnet *net, ircserver *serv); -ircserver *servlist_server_add (ircnet *net, char *name); ircserver *servlist_server_find (ircnet *net, char *name, int *pos); +commandentry *servlist_command_find (ircnet *net, char *cmd, int *pos); +favchannel *servlist_favchan_find (ircnet *net, char *channel, int *pos); + +ircserver *servlist_server_add (ircnet *net, char *name); +commandentry *servlist_command_add (ircnet *net, char *command); +void servlist_favchan_add (ircnet *net, char *channel); + +void servlist_command_free (commandentry *entry); +void servlist_favchan_free (favchannel *channel); + +void servlist_server_remove (ircnet *net, ircserver *serv); +void servlist_command_remove (ircnet *net, commandentry *entry); +void servlist_favchan_remove (ircnet *net, favchannel *channel); + +favchannel *servlist_favchan_copy (favchannel *fav); +GSList *servlist_favchan_listadd (GSList *chanlist, char *channel, char *key); -void joinlist_split (char *autojoin, GSList **channels, GSList **keys); gboolean joinlist_is_in_list (server *serv, char *channel); -void joinlist_free (GSList *channels, GSList *keys); -gchar *joinlist_merge (GSList *channels, GSList *keys); +/* FIXME +void joinlist_split (char *autojoin, GSList **channels, GSList **keys); +void joinlist_free (GSList *channels, GSList *keys); +*/ #endif diff --git a/src/common/textevents.in b/src/common/textevents.in index 3b0b676a..c86af2bc 100644 --- a/src/common/textevents.in +++ b/src/common/textevents.in @@ -529,13 +529,19 @@ pevt_generic_none_help Nick Clash XP_TE_NICKCLASH pevt_nickclash_help -%C23*%O$t%C28$1%C already in use. Retrying with %C18$2%O... +%C23*%O$t%C28$1%C is already in use. Retrying with %C18$2%O... +2 + +Nick Erroneous +XP_TE_NICKERROR +pevt_nickclash_help +%C23*%O$t%C28$1%C is erroneous. Retrying with %C18$2%O... 2 Nick Failed XP_TE_NICKFAIL pevt_generic_none_help -%C20*%O$tNickname already in use. Use /NICK to try another. +%C20*%O$tNickname is erroneous or already in use. Use /NICK to try another. 0 No DCC diff --git a/src/common/util.c b/src/common/util.c index 29a0f3ed..52464621 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1886,7 +1886,7 @@ int main (int argc, char *argv[]) list = get_subdirs ("foo"); display_list (list); #if GLIB_CHECK_VERSION(2,28,0) - g_slist_free_full (list, (GFunc) g_free); + g_slist_free_full (list, (GDestroyNotify) g_free); #else g_slist_foreach (list, (GFunc) g_free, NULL); g_slist_free (list); @@ -1931,19 +1931,15 @@ get_subdirs (const char *path) char * encode_sasl_pass (char *user, char *pass) { - int passlen; + int authlen; char *buffer; char *encoded; - /* passphrase generation, nicely copy-pasted from the CAP-SASL plugin */ - passlen = strlen (user) * 2 + 2 + strlen (pass); - buffer = (char*) malloc (passlen + 1); - strcpy (buffer, user); - strcpy (buffer + strlen (user) + 1, user); - strcpy (buffer + strlen (user) * 2 + 2, pass); - encoded = g_base64_encode ((unsigned char*) buffer, passlen); - - free (buffer); + /* we can't call strlen() directly on buffer thanks to the atrocious \0 characters it requires */ + authlen = strlen (user) * 2 + 2 + strlen (pass); + buffer = g_strdup_printf ("%s%c%s%c%s", user, '\0', user, '\0', pass); + encoded = g_base64_encode ((unsigned char*) buffer, authlen); + g_free (buffer); return encoded; } @@ -1978,3 +1974,79 @@ find_font (const char *fontname) return 0; } #endif + +static char * +str_sha256hash (char *string) +{ + int i; + unsigned char hash[SHA256_DIGEST_LENGTH]; + char buf[SHA256_DIGEST_LENGTH * 2 + 1]; /* 64 digit hash + '\0' */ + SHA256_CTX sha256; + + SHA256_Init (&sha256); + SHA256_Update (&sha256, string, strlen (string)); + SHA256_Final (hash, &sha256); + + for (i = 0; i < SHA256_DIGEST_LENGTH; i++) + { + sprintf (buf + (i * 2), "%02x", hash[i]); + } + + buf[SHA256_DIGEST_LENGTH * 2] = 0; + + return g_strdup (buf); +} + +/** + * \brief Generate CHALLENGEAUTH response for QuakeNet login. + * + * \param username QuakeNet user name + * \param password password for the user + * \param challenge the CHALLENGE response we got from Q + * + * After a successful connection to QuakeNet a CHALLENGE is requested from Q. + * Generate the CHALLENGEAUTH response from this CHALLENGE and our user + * credentials as per the + * <a href="http://www.quakenet.org/development/challengeauth">CHALLENGEAUTH</a> + * docs. As for using OpenSSL HMAC, see + * <a href="http://www.askyb.com/cpp/openssl-hmac-hasing-example-in-cpp/">example 1</a>, + * <a href="http://stackoverflow.com/questions/242665/understanding-engine-initialization-in-openssl">example 2</a>. + */ +char * +challengeauth_response (char *username, char *password, char *challenge) +{ + int i; + char *user; + char *pass; + char *passhash; + char *key; + char *keyhash; + unsigned char *digest; + GString *buf = g_string_new_len (NULL, SHA256_DIGEST_LENGTH * 2); + + user = g_strdup (username); + *user = rfc_tolower (*username); /* convert username to lowercase as per the RFC */ + + pass = g_strndup (password, 10); /* truncate to 10 characters */ + passhash = str_sha256hash (pass); + g_free (pass); + + key = g_strdup_printf ("%s:%s", user, passhash); + g_free (user); + g_free (passhash); + + keyhash = str_sha256hash (key); + g_free (key); + + digest = HMAC (EVP_sha256 (), keyhash, strlen (keyhash), (unsigned char *) challenge, strlen (challenge), NULL, NULL); + g_free (keyhash); + + for (i = 0; i < SHA256_DIGEST_LENGTH; i++) + { + g_string_append_printf (buf, "%02x", (unsigned int) digest[i]); + } + + digest = (unsigned char *) g_string_free (buf, FALSE); + + return (char *) digest; +} diff --git a/src/common/util.h b/src/common/util.h index 9e2d9f52..0ebd89d4 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -80,5 +80,6 @@ int portable_mode (); int unity_mode (); GSList *get_subdirs (const char *path); char *encode_sasl_pass (char *user, char *pass); +char *challengeauth_response (char *username, char *password, char *challenge); #endif |