1
0
mirror of https://github.com/irssi/irssi.git synced 2024-10-13 05:03:45 -04:00

Merge pull request #1170 from ailin-nemui/starttls

use starttls / enable tls_verify
This commit is contained in:
ailin-nemui 2021-04-18 12:01:14 +02:00 committed by GitHub
commit 4432b0bf0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 649 additions and 240 deletions

View File

@ -1,10 +1,10 @@
servers = ( servers = (
{ address = "irc.dal.net"; chatnet = "DALnet"; port = "6667"; }, { address = "irc.dal.net"; chatnet = "DALnet"; port = "6667"; },
{ address = "ssl.efnet.org"; chatnet = "EFNet"; port = "9999"; use_tls = "yes"; }, { address = "ssl.efnet.org"; chatnet = "EFNet"; port = "9999"; use_tls = "yes"; tls_verify = "no"; },
{ address = "irc.esper.net"; chatnet = "EsperNet"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; }, { address = "irc.esper.net"; chatnet = "EsperNet"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "chat.freenode.net"; chatnet = "Freenode"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; }, { address = "chat.freenode.net"; chatnet = "Freenode"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "irc.gamesurge.net"; chatnet = "GameSurge"; port = "6667"; }, { address = "irc.gamesurge.net"; chatnet = "GameSurge"; port = "6667"; },
{ address = "eu.irc6.net"; chatnet = "IRCnet"; port = "6667"; use_tls = "yes"; }, { address = "ssl.ircnet.ovh"; chatnet = "IRCnet"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "open.ircnet.net"; chatnet = "IRCnet"; port = "6667"; }, { address = "open.ircnet.net"; chatnet = "IRCnet"; port = "6667"; },
{ address = "irc.ircsource.net"; chatnet = "IRCSource"; port = "6667"; }, { address = "irc.ircsource.net"; chatnet = "IRCSource"; port = "6667"; },
{ address = "irc.netfuze.net"; chatnet = "NetFuze"; port = "6667"; }, { address = "irc.netfuze.net"; chatnet = "NetFuze"; port = "6667"; },

View File

@ -6,7 +6,7 @@
#define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */ #define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */
#define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */ #define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */
#define IRSSI_ABI_VERSION 35 #define IRSSI_ABI_VERSION 36
#define DEFAULT_SERVER_ADD_PORT 6667 #define DEFAULT_SERVER_ADD_PORT 6667
#define DEFAULT_SERVER_ADD_TLS_PORT 6697 #define DEFAULT_SERVER_ADD_TLS_PORT 6697

View File

@ -40,7 +40,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
CHAT_PROTOCOL_REC *proto; CHAT_PROTOCOL_REC *proto;
SERVER_CONNECT_REC *conn; SERVER_CONNECT_REC *conn;
GHashTable *optlist; GHashTable *optlist;
char *addr, *portstr, *password, *nick, *chatnet, *host, *tmp; char *addr, *portstr, *password, *nick, *chatnet, *host;
void *free_arg; void *free_arg;
g_return_val_if_fail(data != NULL, NULL); g_return_val_if_fail(data != NULL, NULL);
@ -71,8 +71,8 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
if (chatnet == NULL) if (chatnet == NULL)
chatnet = g_hash_table_lookup(optlist, "network"); chatnet = g_hash_table_lookup(optlist, "network");
conn = server_create_conn(proto != NULL ? proto->id : -1, addr, conn = server_create_conn_opt(proto != NULL ? proto->id : -1, addr, atoi(portstr), chatnet,
atoi(portstr), chatnet, password, nick); password, nick, optlist);
if (conn == NULL) { if (conn == NULL) {
signal_emit("error command", 1, signal_emit("error command", 1,
GINT_TO_POINTER(CMDERR_NO_SERVER_DEFINED)); GINT_TO_POINTER(CMDERR_NO_SERVER_DEFINED));
@ -94,46 +94,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
if (strchr(addr, '/') != NULL) if (strchr(addr, '/') != NULL)
conn->unix_socket = TRUE; conn->unix_socket = TRUE;
if (g_hash_table_lookup(optlist, "6") != NULL) /* TLS options are handled in server_create_conn_opt ... -> server_setup_fill_optlist */
conn->family = AF_INET6;
else if (g_hash_table_lookup(optlist, "4") != NULL)
conn->family = AF_INET;
if (g_hash_table_lookup(optlist, "tls") != NULL || g_hash_table_lookup(optlist, "ssl") != NULL)
conn->use_tls = TRUE;
if ((tmp = g_hash_table_lookup(optlist, "tls_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
conn->tls_cert = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
conn->tls_pkey = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pass")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL)
conn->tls_pass = g_strdup(tmp);
if (g_hash_table_lookup(optlist, "tls_verify") != NULL || g_hash_table_lookup(optlist, "ssl_verify") != NULL)
conn->tls_verify = TRUE;
if ((tmp = g_hash_table_lookup(optlist, "tls_cafile")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
conn->tls_cafile = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_capath")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
conn->tls_capath = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL)
conn->tls_ciphers = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_cert")) != NULL)
conn->tls_pinned_cert = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_pubkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_pubkey")) != NULL)
conn->tls_pinned_pubkey = g_strdup(tmp);
if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0')
|| (conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0'))
conn->tls_verify = TRUE;
if ((conn->tls_cert != NULL && conn->tls_cert[0] != '\0') || conn->tls_verify)
conn->use_tls = TRUE;
if (g_hash_table_lookup(optlist, "!") != NULL)
conn->no_autojoin_channels = TRUE;
if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
conn->no_autosendcmd = TRUE;
if (g_hash_table_lookup(optlist, "noproxy") != NULL)
g_free_and_null(conn->proxy);
*rawlog_file = g_strdup(g_hash_table_lookup(optlist, "rawlog")); *rawlog_file = g_strdup(g_hash_table_lookup(optlist, "rawlog"));
@ -149,13 +110,13 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
return conn; return conn;
} }
/* SYNTAX: CONNECT [-4 | -6] [-tls] [-tls_cert <cert>] [-tls_pkey <pkey>] [-tls_pass <password>] /* SYNTAX: CONNECT [-4 | -6] [-tls_cert <cert>] [-tls_pkey <pkey>] [-tls_pass <password>]
[-tls_verify] [-tls_cafile <cafile>] [-tls_capath <capath>] [-tls_verify] [-tls_cafile <cafile>] [-tls_capath <capath>]
[-tls_ciphers <list>] [-tls_pinned_cert <fingerprint>] [-tls_pinned_pubkey <fingerprint>] [-tls_ciphers <list>] [-tls_pinned_cert <fingerprint>]
[-!] [-noautosendcmd] [-tls_pinned_pubkey <fingerprint>] [-!] [-noautosendcmd] [-tls | -notls]
[-noproxy] [-network <network>] [-host <hostname>] [-starttls | -disallow_starttls] [-noproxy] [-network <network>]
[-rawlog <file>] [-host <hostname>] [-rawlog <file>]
<address>|<chatnet> [<port> [<password> [<nick>]]] */ <address>|<chatnet> [<port> [<password> [<nick>]]] */
/* NOTE: -network replaces the old -ircnet flag. */ /* NOTE: -network replaces the old -ircnet flag. */
static void cmd_connect(const char *data) static void cmd_connect(const char *data)
{ {
@ -520,9 +481,9 @@ void chat_commands_init(void)
command_set_options( command_set_options(
"connect", "connect",
"4 6 !! -network ~ssl ~+ssl_cert ~+ssl_pkey ~+ssl_pass ~ssl_verify ~+ssl_cafile " "4 6 !! -network ~ssl ~+ssl_cert ~+ssl_pkey ~+ssl_pass ~ssl_verify ~+ssl_cafile "
"~+ssl_capath ~+ssl_ciphers ~+ssl_pinned_cert ~+ssl_pinned_pubkey tls +tls_cert " "~+ssl_capath ~+ssl_ciphers ~+ssl_pinned_cert ~+ssl_pinned_pubkey tls notls +tls_cert "
"+tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert " "+tls_pkey +tls_pass tls_verify notls_verify +tls_cafile +tls_capath +tls_ciphers "
"+tls_pinned_pubkey +host noproxy -rawlog noautosendcmd"); "+tls_pinned_cert +tls_pinned_pubkey +host noproxy -rawlog noautosendcmd");
command_set_options("msg", "channel nick"); command_set_options("msg", "channel nick");
} }

View File

@ -239,8 +239,11 @@ static void sig_reconnect(SERVER_REC *server)
if (reconnect_time == -1 || !server_should_reconnect(server)) if (reconnect_time == -1 || !server_should_reconnect(server))
return; return;
conn = server_connect_copy_skeleton(server->connrec, FALSE); sserver = server_setup_find(server->connrec->address, server->connrec->port,
g_return_if_fail(conn != NULL); server->connrec->chatnet);
conn = server_connect_copy_skeleton(server->connrec, sserver == NULL);
g_return_if_fail(conn != NULL);
/* save the server status */ /* save the server status */
if (server->connected) { if (server->connected) {
@ -249,10 +252,6 @@ static void sig_reconnect(SERVER_REC *server)
reconnect_save_status(conn, server); reconnect_save_status(conn, server);
} }
sserver = server_setup_find(server->connrec->address,
server->connrec->port,
server->connrec->chatnet);
if (sserver != NULL) { if (sserver != NULL) {
/* save the last connection time/status */ /* save the last connection time/status */
sserver->last_connect = server->connect_time == 0 ? sserver->last_connect = server->connect_time == 0 ?

View File

@ -140,8 +140,8 @@ void server_setup_fill_reconn(SERVER_CONNECT_REC *conn,
signal_emit("server setup fill reconn", 2, conn, sserver); signal_emit("server setup fill reconn", 2, conn, sserver);
} }
static void server_setup_fill(SERVER_CONNECT_REC *conn, static void server_setup_fill(SERVER_CONNECT_REC *conn, const char *address, int port,
const char *address, int port) GHashTable *optlist)
{ {
g_return_if_fail(conn != NULL); g_return_if_fail(conn != NULL);
g_return_if_fail(address != NULL); g_return_if_fail(address != NULL);
@ -177,7 +177,69 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn,
memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR)); memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR));
} }
signal_emit("server setup fill connect", 1, conn); signal_emit("server setup fill connect", 2, conn, optlist);
}
static void server_setup_fill_optlist(SERVER_CONNECT_REC *conn, GHashTable *optlist)
{
char *tmp;
if (g_hash_table_lookup(optlist, "6") != NULL)
conn->family = AF_INET6;
else if (g_hash_table_lookup(optlist, "4") != NULL)
conn->family = AF_INET;
/* ad-hoc TLS settings from command optlist */
if ((tmp = g_hash_table_lookup(optlist, "tls_cert")) != NULL ||
(tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
conn->tls_cert = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pkey")) != NULL ||
(tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
conn->tls_pkey = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pass")) != NULL ||
(tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL)
conn->tls_pass = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_cafile")) != NULL ||
(tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
conn->tls_cafile = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_capath")) != NULL ||
(tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
conn->tls_capath = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL ||
(tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL)
conn->tls_ciphers = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_cert")) != NULL ||
(tmp = g_hash_table_lookup(optlist, "ssl_pinned_cert")) != NULL)
conn->tls_pinned_cert = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_pubkey")) != NULL ||
(tmp = g_hash_table_lookup(optlist, "ssl_pinned_pubkey")) != NULL)
conn->tls_pinned_pubkey = g_strdup(tmp);
if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0') ||
(conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0'))
conn->tls_verify = TRUE;
if (g_hash_table_lookup(optlist, "notls_verify") != NULL)
conn->tls_verify = FALSE;
if (g_hash_table_lookup(optlist, "tls_verify") != NULL ||
g_hash_table_lookup(optlist, "ssl_verify") != NULL)
conn->tls_verify = TRUE;
if ((conn->tls_cert != NULL && conn->tls_cert[0] != '\0') || conn->tls_verify)
conn->use_tls = TRUE;
if (g_hash_table_lookup(optlist, "notls") != NULL)
conn->use_tls = FALSE;
if (g_hash_table_lookup(optlist, "tls") != NULL ||
g_hash_table_lookup(optlist, "ssl") != NULL)
conn->use_tls = TRUE;
if (g_hash_table_lookup(optlist, "!") != NULL)
conn->no_autojoin_channels = TRUE;
if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
conn->no_autosendcmd = TRUE;
if (g_hash_table_lookup(optlist, "noproxy") != NULL)
g_free_and_null(conn->proxy);
signal_emit("server setup fill optlist", 2, conn, optlist);
} }
static void server_setup_fill_server(SERVER_CONNECT_REC *conn, static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
@ -219,10 +281,9 @@ static void server_setup_fill_chatnet(SERVER_CONNECT_REC *conn,
signal_emit("server setup fill chatnet", 2, conn, chatnet); signal_emit("server setup fill chatnet", 2, conn, chatnet);
} }
static SERVER_CONNECT_REC * static SERVER_CONNECT_REC *create_addr_conn(int chat_type, const char *address, int port,
create_addr_conn(int chat_type, const char *address, int port, const char *chatnet, const char *password,
const char *chatnet, const char *password, const char *nick, GHashTable *optlist)
const char *nick)
{ {
CHAT_PROTOCOL_REC *proto; CHAT_PROTOCOL_REC *proto;
SERVER_CONNECT_REC *conn; SERVER_CONNECT_REC *conn;
@ -250,7 +311,7 @@ create_addr_conn(int chat_type, const char *address, int port,
conn->chatnet = g_strdup(chatnet); conn->chatnet = g_strdup(chatnet);
/* fill in the defaults */ /* fill in the defaults */
server_setup_fill(conn, address, port); server_setup_fill(conn, address, port, optlist);
/* fill the rest from chat network settings */ /* fill the rest from chat network settings */
chatnetrec = chatnet != NULL ? chatnet_find(chatnet) : chatnetrec = chatnet != NULL ? chatnet_find(chatnet) :
@ -263,6 +324,10 @@ create_addr_conn(int chat_type, const char *address, int port,
if (sserver != NULL) if (sserver != NULL)
server_setup_fill_server(conn, sserver); server_setup_fill_server(conn, sserver);
/* fill the optlist overrides */
if (g_hash_table_size(optlist))
server_setup_fill_optlist(conn, optlist);
/* nick / password given in command line overrides all settings */ /* nick / password given in command line overrides all settings */
if (password && *password) { if (password && *password) {
g_free_not_null(conn->password); g_free_not_null(conn->password);
@ -279,9 +344,8 @@ create_addr_conn(int chat_type, const char *address, int port,
/* Connect to server where last connect succeeded (or we haven't tried to /* Connect to server where last connect succeeded (or we haven't tried to
connect yet). If there's no such server, connect to server where we connect yet). If there's no such server, connect to server where we
haven't connected for the longest time */ haven't connected for the longest time */
static SERVER_CONNECT_REC * static SERVER_CONNECT_REC *create_chatnet_conn(const char *dest, int port, const char *password,
create_chatnet_conn(const char *dest, int port, const char *nick, GHashTable *optlist)
const char *password, const char *nick)
{ {
SERVER_SETUP_REC *bestrec; SERVER_SETUP_REC *bestrec;
GSList *tmp; GSList *tmp;
@ -308,16 +372,15 @@ create_chatnet_conn(const char *dest, int port,
} }
return bestrec == NULL ? NULL : return bestrec == NULL ? NULL :
create_addr_conn(bestrec->chat_type, bestrec->address, 0, create_addr_conn(bestrec->chat_type, bestrec->address, 0, dest,
dest, NULL, nick); NULL, nick, optlist);
} }
/* Create server connection record. `dest' is required, rest can be NULL. /* Create server connection record. `dest' is required, rest can be NULL.
`dest' is either a server address or chat network */ `dest' is either a server address or chat network */
SERVER_CONNECT_REC * SERVER_CONNECT_REC *server_create_conn_opt(int chat_type, const char *dest, int port,
server_create_conn(int chat_type, const char *dest, int port, const char *chatnet, const char *password,
const char *chatnet, const char *password, const char *nick, GHashTable *optlist)
const char *nick)
{ {
SERVER_CONNECT_REC *rec; SERVER_CONNECT_REC *rec;
CHATNET_REC *chatrec; CHATNET_REC *chatrec;
@ -326,7 +389,7 @@ server_create_conn(int chat_type, const char *dest, int port,
chatrec = chatnet_find(dest); chatrec = chatnet_find(dest);
if (chatrec != NULL) { if (chatrec != NULL) {
rec = create_chatnet_conn(chatrec->name, port, password, nick); rec = create_chatnet_conn(chatrec->name, port, password, nick, optlist);
/* If rec is NULL the chatnet has no url to connect to */ /* If rec is NULL the chatnet has no url to connect to */
return rec; return rec;
} }
@ -335,8 +398,20 @@ server_create_conn(int chat_type, const char *dest, int port,
if (chatrec != NULL) if (chatrec != NULL)
chatnet = chatrec->name; chatnet = chatrec->name;
return create_addr_conn(chat_type, dest, port, return create_addr_conn(chat_type, dest, port, chatnet, password, nick, optlist);
chatnet, password, nick); }
SERVER_CONNECT_REC *server_create_conn(int chat_type, const char *dest, int port,
const char *chatnet, const char *password, const char *nick)
{
SERVER_CONNECT_REC *ret;
GHashTable *opt;
opt = g_hash_table_new(NULL, NULL);
ret = server_create_conn_opt(chat_type, dest, port, chatnet, password, nick, opt);
g_hash_table_destroy(opt);
return ret;
} }
/* Find matching server from setup. Try to find record with a same port, /* Find matching server from setup. Try to find record with a same port,
@ -409,7 +484,8 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
rec->password = g_strdup(config_node_get_str(node, "password", NULL)); rec->password = g_strdup(config_node_get_str(node, "password", NULL));
rec->use_tls = config_node_get_bool(node, "use_tls", FALSE) || config_node_get_bool(node, "use_ssl", FALSE); rec->use_tls = config_node_get_bool(node, "use_tls", FALSE) || config_node_get_bool(node, "use_ssl", FALSE);
rec->tls_verify = config_node_get_bool(node, "tls_verify", FALSE) || config_node_get_bool(node, "ssl_verify", FALSE); rec->tls_verify = config_node_get_bool(node, "tls_verify", TRUE) ||
config_node_get_bool(node, "ssl_verify", FALSE);
value = config_node_get_str(node, "tls_cert", NULL); value = config_node_get_str(node, "tls_cert", NULL);
if (value == NULL) if (value == NULL)
@ -451,11 +527,6 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
value = config_node_get_str(node, "ssl_pinned_pubkey", NULL); value = config_node_get_str(node, "ssl_pinned_pubkey", NULL);
rec->tls_pinned_pubkey = g_strdup(value); rec->tls_pinned_pubkey = g_strdup(value);
if (rec->tls_cafile || rec->tls_capath)
rec->tls_verify = TRUE;
if (rec->tls_cert != NULL || rec->tls_verify)
rec->use_tls = TRUE;
rec->port = port; rec->port = port;
rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE); rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE); rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE);

View File

@ -31,6 +31,10 @@ server_create_conn(int chat_type, const char *dest, int port,
const char *chatnet, const char *password, const char *chatnet, const char *password,
const char *nick); const char *nick);
SERVER_CONNECT_REC *server_create_conn_opt(int chat_type, const char *dest, int port,
const char *chatnet, const char *password,
const char *nick, GHashTable *optlist);
/* Find matching server from setup. Try to find record with a same port, /* Find matching server from setup. Try to find record with a same port,
but fallback to any server with the same address. */ but fallback to any server with the same address. */
SERVER_SETUP_REC *server_setup_find(const char *address, int port, SERVER_SETUP_REC *server_setup_find(const char *address, int port,

View File

@ -254,23 +254,35 @@ static void session_restore_server(CONFIG_NODE *node)
proto = chat_protocol_find(chat_type); proto = chat_protocol_find(chat_type);
if (proto == NULL || proto->not_initialized) { if (proto == NULL || proto->not_initialized) {
if (handle < 0) close(handle); if (handle >= 0)
close(handle);
return; return;
} }
conn = server_create_conn(proto->id, address, port, conn = server_create_conn(proto->id, address, port,
chatnet, password, nick); chatnet, password, nick);
if (conn != NULL) { if (conn == NULL)
conn->reconnection = TRUE; return;
conn->connect_handle = i_io_channel_new(handle);
server = proto->server_init_connect(conn); conn->use_tls = config_node_get_bool(node, "use_tls", FALSE);
server->version = g_strdup(config_node_get_str(node, "version", NULL)); conn->tls_cert = g_strdup(config_node_get_str(node, "tls_cert", NULL));
server->session_reconnect = TRUE; conn->tls_pkey = g_strdup(config_node_get_str(node, "tls_pkey", NULL));
signal_emit("session restore server", 2, server, node); conn->tls_verify = config_node_get_bool(node, "tls_verify", TRUE);
conn->tls_cafile = g_strdup(config_node_get_str(node, "tls_cafile", NULL));
conn->tls_capath = g_strdup(config_node_get_str(node, "tls_capath", NULL));
conn->tls_ciphers = g_strdup(config_node_get_str(node, "tls_ciphers", NULL));
conn->tls_pinned_cert = g_strdup(config_node_get_str(node, "tls_pinned_cert", NULL));
conn->tls_pinned_pubkey = g_strdup(config_node_get_str(node, "tls_pinned_pubkey", NULL));
proto->server_connect(server); conn->reconnection = TRUE;
} conn->connect_handle = i_io_channel_new(handle);
server = proto->server_init_connect(conn);
server->version = g_strdup(config_node_get_str(node, "version", NULL));
server->session_reconnect = TRUE;
signal_emit("session restore server", 2, server, node);
proto->server_connect(server);
} }
static void sig_session_save(CONFIG_REC *config) static void sig_session_save(CONFIG_REC *config)

View File

@ -101,6 +101,7 @@ static SERVER_SETUP_REC *create_server_setup(GHashTable *optlist)
server = rec->create_server_setup(); server = rec->create_server_setup();
server->chat_type = rec->id; server->chat_type = rec->id;
server->tls_verify = TRUE;
return server; return server;
} }
@ -110,6 +111,7 @@ static void cmd_server_add_modify(const char *data, gboolean add)
SERVER_SETUP_REC *rec; SERVER_SETUP_REC *rec;
char *addr, *portstr, *password, *value, *chatnet; char *addr, *portstr, *password, *value, *chatnet;
void *free_arg; void *free_arg;
gboolean newrec;
int port; int port;
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS, if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
@ -135,6 +137,7 @@ static void cmd_server_add_modify(const char *data, gboolean add)
rec = server_setup_find(addr, port, chatnet); rec = server_setup_find(addr, port, chatnet);
if (rec == NULL) { if (rec == NULL) {
newrec = TRUE;
if (add == FALSE) { if (add == FALSE) {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
TXT_SETUPSERVER_NOT_FOUND, addr, port); TXT_SETUPSERVER_NOT_FOUND, addr, port);
@ -150,6 +153,7 @@ static void cmd_server_add_modify(const char *data, gboolean add)
rec->address = g_strdup(addr); rec->address = g_strdup(addr);
rec->port = port; rec->port = port;
} else { } else {
newrec = FALSE;
if (*portstr != '\0' || g_hash_table_lookup(optlist, "port")) if (*portstr != '\0' || g_hash_table_lookup(optlist, "port"))
rec->port = port; rec->port = port;
@ -165,20 +169,17 @@ static void cmd_server_add_modify(const char *data, gboolean add)
else if (g_hash_table_lookup(optlist, "4")) else if (g_hash_table_lookup(optlist, "4"))
rec->family = AF_INET; rec->family = AF_INET;
if (g_hash_table_lookup(optlist, "tls") || g_hash_table_lookup(optlist, "ssl")) {
rec->use_tls = TRUE;
}
else if (g_hash_table_lookup(optlist, "notls") || g_hash_table_lookup(optlist, "nossl")) {
rec->use_tls = FALSE;
/* tls_verify implies use_tls, disable it explicitly */
rec->tls_verify = FALSE;
}
value = g_hash_table_lookup(optlist, "tls_cert"); value = g_hash_table_lookup(optlist, "tls_cert");
if (value == NULL) if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_cert"); value = g_hash_table_lookup(optlist, "ssl_cert");
if (value != NULL && *value != '\0') if (value != NULL && *value != '\0') {
rec->tls_cert = g_strdup(value); rec->tls_cert = g_strdup(value);
if (newrec) {
/* convenience and backward compatibility, turn on tls if tls_cert is given
*/
rec->use_tls = TRUE;
}
}
value = g_hash_table_lookup(optlist, "tls_pkey"); value = g_hash_table_lookup(optlist, "tls_pkey");
if (value == NULL) if (value == NULL)
@ -192,11 +193,6 @@ static void cmd_server_add_modify(const char *data, gboolean add)
if (value != NULL && *value != '\0') if (value != NULL && *value != '\0')
rec->tls_pass = g_strdup(value); rec->tls_pass = g_strdup(value);
if (g_hash_table_lookup(optlist, "tls_verify") || g_hash_table_lookup(optlist, "ssl_verify"))
rec->tls_verify = TRUE;
else if (g_hash_table_lookup(optlist, "notls_verify") || g_hash_table_lookup(optlist, "nossl_verify"))
rec->tls_verify = FALSE;
value = g_hash_table_lookup(optlist, "tls_cafile"); value = g_hash_table_lookup(optlist, "tls_cafile");
if (value == NULL) if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_cafile"); value = g_hash_table_lookup(optlist, "ssl_cafile");
@ -231,8 +227,23 @@ static void cmd_server_add_modify(const char *data, gboolean add)
|| (rec->tls_capath != NULL && rec->tls_capath[0] != '\0')) || (rec->tls_capath != NULL && rec->tls_capath[0] != '\0'))
rec->tls_verify = TRUE; rec->tls_verify = TRUE;
if ((rec->tls_cert != NULL && rec->tls_cert[0] != '\0') || rec->tls_verify == TRUE) if (g_hash_table_lookup(optlist, "tls_verify") ||
g_hash_table_lookup(optlist, "ssl_verify")) {
rec->tls_verify = TRUE;
if (newrec) {
/* convenience and backward compatibility, turn on tls if tls_verify is
* given */
rec->use_tls = TRUE;
}
} else if (g_hash_table_lookup(optlist, "notls_verify") ||
g_hash_table_lookup(optlist, "nossl_verify")) {
rec->tls_verify = FALSE;
}
if (g_hash_table_lookup(optlist, "tls") || g_hash_table_lookup(optlist, "ssl"))
rec->use_tls = TRUE; rec->use_tls = TRUE;
else if (g_hash_table_lookup(optlist, "notls") || g_hash_table_lookup(optlist, "nossl"))
rec->use_tls = FALSE;
if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE; if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE;
if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE; if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE;
@ -246,7 +257,7 @@ static void cmd_server_add_modify(const char *data, gboolean add)
rec->own_ip4 = rec->own_ip6 = NULL; rec->own_ip4 = rec->own_ip6 = NULL;
} }
signal_emit("server add fill", 2, rec, optlist); signal_emit("server add fill", 3, rec, optlist, GINT_TO_POINTER(add));
server_setup_add(rec); server_setup_add(rec);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,

View File

@ -51,12 +51,13 @@ const char *get_visible_target(IRC_SERVER_REC *server, const char *target)
return target; return target;
} }
/* SYNTAX: SERVER ADD|MODIFY [-4 | -6] [-tls] [-tls_cert <cert>] [-tls_pkey <pkey>] [-tls_pass <password>] /* SYNTAX: SERVER ADD|MODIFY [-4 | -6] [-tls_cert <cert>] [-tls_pkey <pkey>]
[-tls_verify] [-tls_cafile <cafile>] [-tls_capath <capath>] [-tls_pass <password>] [-tls_verify] [-tls_cafile <cafile>]
[-tls_ciphers <list>] [-tls_capath <capath>] [-tls_ciphers <list>] [-tls | -notls]
[-starttls | -nostarttls | -disallow_starttls | -nodisallow_starttls]
[-auto | -noauto] [-network <network>] [-host <hostname>] [-auto | -noauto] [-network <network>] [-host <hostname>]
[-cmdspeed <ms>] [-cmdmax <count>] [-port <port>] [-cmdspeed <ms>] [-cmdmax <count>] [-port <port>] <address> [<port>
<address> [<port> [<password>]] */ [<password>]] */
/* NOTE: -network replaces the old -ircnet flag. */ /* NOTE: -network replaces the old -ircnet flag. */
static void sig_server_add_fill(IRC_SERVER_SETUP_REC *rec, static void sig_server_add_fill(IRC_SERVER_SETUP_REC *rec,
GHashTable *optlist) GHashTable *optlist)
@ -85,6 +86,28 @@ static void sig_server_add_fill(IRC_SERVER_SETUP_REC *rec,
if (value != NULL && *value != '\0') rec->max_cmds_at_once = atoi(value); if (value != NULL && *value != '\0') rec->max_cmds_at_once = atoi(value);
value = g_hash_table_lookup(optlist, "querychans"); value = g_hash_table_lookup(optlist, "querychans");
if (value != NULL && *value != '\0') rec->max_query_chans = atoi(value); if (value != NULL && *value != '\0') rec->max_query_chans = atoi(value);
if (g_hash_table_lookup(optlist, "nodisallow_starttls") ||
g_hash_table_lookup(optlist, "nostarttls"))
rec->starttls = STARTTLS_NOTSET;
if (g_hash_table_lookup(optlist, "disallow_starttls"))
rec->starttls = STARTTLS_DISALLOW;
if (g_hash_table_lookup(optlist, "starttls")) {
rec->starttls = STARTTLS_ENABLED;
rec->use_tls = 0;
}
if (g_hash_table_lookup(optlist, "nocap"))
rec->no_cap = 1;
if (g_hash_table_lookup(optlist, "cap"))
rec->no_cap = 0;
}
static void sig_server_waiting_info(IRC_SERVER_REC *server, const char *version)
{
if (!IS_IRC_SERVER(server))
return;
printformat(server, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_SERVER_WAITING_CAP_LS, server,
version);
} }
/* SYNTAX: SERVER LIST */ /* SYNTAX: SERVER LIST */
@ -108,29 +131,35 @@ static void cmd_server_list(const char *data)
g_string_append(str, "autoconnect, "); g_string_append(str, "autoconnect, ");
if (rec->no_proxy) if (rec->no_proxy)
g_string_append(str, "noproxy, "); g_string_append(str, "noproxy, ");
if (rec->use_tls) { if (rec->no_cap)
g_string_append(str, "nocap, ");
if (rec->starttls == STARTTLS_DISALLOW)
g_string_append(str, "disallow_starttls, ");
if (rec->starttls == STARTTLS_ENABLED)
g_string_append(str, "starttls, ");
if (rec->use_tls)
g_string_append(str, "tls, "); g_string_append(str, "tls, ");
if (rec->tls_cert) { if (rec->tls_cert) {
g_string_append_printf(str, "tls_cert: %s, ", rec->tls_cert); g_string_append_printf(str, "tls_cert: %s, ", rec->tls_cert);
if (rec->tls_pkey) if (rec->tls_pkey)
g_string_append_printf(str, "tls_pkey: %s, ", rec->tls_pkey); g_string_append_printf(str, "tls_pkey: %s, ", rec->tls_pkey);
if (rec->tls_pass) if (rec->tls_pass)
g_string_append_printf(str, "(pass), "); g_string_append_printf(str, "(pass), ");
}
if (rec->tls_verify)
g_string_append(str, "tls_verify, ");
if (rec->tls_cafile)
g_string_append_printf(str, "tls_cafile: %s, ", rec->tls_cafile);
if (rec->tls_capath)
g_string_append_printf(str, "tls_capath: %s, ", rec->tls_capath);
if (rec->tls_ciphers)
g_string_append_printf(str, "tls_ciphers: %s, ", rec->tls_ciphers);
if (rec->tls_pinned_cert)
g_string_append_printf(str, "tls_pinned_cert: %s, ", rec->tls_pinned_cert);
if (rec->tls_pinned_pubkey)
g_string_append_printf(str, "tls_pinned_pubkey: %s, ", rec->tls_pinned_pubkey);
} }
if (!rec->tls_verify)
g_string_append(str, "notls_verify, ");
if (rec->tls_cafile)
g_string_append_printf(str, "tls_cafile: %s, ", rec->tls_cafile);
if (rec->tls_capath)
g_string_append_printf(str, "tls_capath: %s, ", rec->tls_capath);
if (rec->tls_ciphers)
g_string_append_printf(str, "tls_ciphers: %s, ", rec->tls_ciphers);
if (rec->tls_pinned_cert)
g_string_append_printf(str, "tls_pinned_cert: %s, ", rec->tls_pinned_cert);
if (rec->tls_pinned_pubkey)
g_string_append_printf(str, "tls_pinned_pubkey: %s, ",
rec->tls_pinned_pubkey);
if (rec->max_cmds_at_once > 0) if (rec->max_cmds_at_once > 0)
g_string_append_printf(str, "cmdmax: %d, ", rec->max_cmds_at_once); g_string_append_printf(str, "cmdmax: %d, ", rec->max_cmds_at_once);
if (rec->cmd_queue_speed > 0) if (rec->cmd_queue_speed > 0)
@ -153,13 +182,20 @@ static void cmd_server_list(const char *data)
void fe_irc_server_init(void) void fe_irc_server_init(void)
{ {
signal_add("server add fill", (SIGNAL_FUNC) sig_server_add_fill); signal_add("server add fill", (SIGNAL_FUNC) sig_server_add_fill);
signal_add("server waiting cap ls", (SIGNAL_FUNC) sig_server_waiting_info);
command_bind("server list", NULL, (SIGNAL_FUNC) cmd_server_list); command_bind("server list", NULL, (SIGNAL_FUNC) cmd_server_list);
command_set_options("server add", "-ircnet -network -cmdspeed -cmdmax -querychans"); command_set_options("server add",
"-ircnet -network -cmdspeed -cmdmax -querychans starttls "
"nostarttls disallow_starttls nodisallow_starttls cap nocap");
command_set_options("server modify",
"-ircnet -network -cmdspeed -cmdmax -querychans starttls nostarttls "
"disallow_starttls nodisallow_starttls cap nocap");
} }
void fe_irc_server_deinit(void) void fe_irc_server_deinit(void)
{ {
signal_remove("server add fill", (SIGNAL_FUNC) sig_server_add_fill); signal_remove("server add fill", (SIGNAL_FUNC) sig_server_add_fill);
signal_remove("server waiting cap ls", (SIGNAL_FUNC) sig_server_waiting_info);
command_unbind("server list", (SIGNAL_FUNC) cmd_server_list); command_unbind("server list", (SIGNAL_FUNC) cmd_server_list);
} }

View File

@ -45,6 +45,7 @@ FORMAT_REC fecommon_irc_formats[] = {
{ "setupserver_header", "%#Server Port Network Settings", 0 }, { "setupserver_header", "%#Server Port Network Settings", 0 },
{ "setupserver_line", "%#%|$[!20]0 $[5]1 $[10]2 $3", 4, { 0, 1, 0, 0 } }, { "setupserver_line", "%#%|$[!20]0 $[5]1 $[10]2 $3", 4, { 0, 1, 0, 0 } },
{ "setupserver_footer", "", 0 }, { "setupserver_footer", "", 0 },
{ "server_waiting_cap_ls", "Waiting for CAP LS response...", 2, { 0, 0 } },
{ "sasl_success", "SASL authentication succeeded", 0 }, { "sasl_success", "SASL authentication succeeded", 0 },
{ "sasl_error", "Cannot authenticate via SASL ($0)", 1, { 0 } }, { "sasl_error", "Cannot authenticate via SASL ($0)", 1, { 0 } },
{ "cap_req", "Capabilities requested: $0", 1, { 0 } }, { "cap_req", "Capabilities requested: $0", 1, { 0 } },

View File

@ -23,6 +23,7 @@ enum {
IRCTXT_SETUPSERVER_HEADER, IRCTXT_SETUPSERVER_HEADER,
IRCTXT_SETUPSERVER_LINE, IRCTXT_SETUPSERVER_LINE,
IRCTXT_SETUPSERVER_FOOTER, IRCTXT_SETUPSERVER_FOOTER,
IRCTXT_SERVER_WAITING_CAP_LS,
IRCTXT_SASL_SUCCESS, IRCTXT_SASL_SUCCESS,
IRCTXT_SASL_ERROR, IRCTXT_SASL_ERROR,
IRCTXT_CAP_REQ, IRCTXT_CAP_REQ,

View File

@ -177,12 +177,23 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
for (; *lines != NULL; lines++) { for (; *lines != NULL; lines++) {
gchar *prefixedLine; gchar *prefixedLine;
int disconnected;
if (prefixedChoice) { if (prefixedChoice) {
prefixedLine = g_strdup_printf(":user %s\n", *lines); prefixedLine = g_strdup_printf(":user %s\n", *lines);
} else { } else {
prefixedLine = g_strdup_printf("%s\n", *lines); prefixedLine = g_strdup_printf("%s\n", *lines);
} }
server_ref(server);
signal_emit("server incoming", 2, server, prefixedLine); signal_emit("server incoming", 2, server, prefixedLine);
disconnected = server->disconnected;
if (disconnected) {
server_connect_unref(server->connrec);
}
server_unref(server);
if (disconnected) {
/* reconnect */
test_server();
}
g_free(prefixedLine); g_free(prefixedLine);
} }

View File

@ -90,6 +90,8 @@ static void sig_disconnected(IRC_SERVER_REC *server)
return; return;
rec = server->chanqueries; rec = server->chanqueries;
if (rec == NULL)
return;
g_return_if_fail(rec != NULL); g_return_if_fail(rec != NULL);
g_hash_table_destroy(rec->accountqueries); g_hash_table_destroy(rec->accountqueries);

View File

@ -104,12 +104,54 @@ static gboolean parse_cap_name(char *name, char **key, char **val)
return TRUE; return TRUE;
} }
static void cap_process_request_queue(IRC_SERVER_REC *server)
{
/* No CAP has been requested */
if (server->cap_queue == NULL) {
irc_cap_finish_negotiation(server);
} else {
GSList *tmp;
GString *cmd;
int avail_caps = 0;
cmd = g_string_new("CAP REQ :");
/* To process the queue in order, we need to reverse the stack once */
server->cap_queue = g_slist_reverse(server->cap_queue);
/* Check whether the cap is supported by the server */
for (tmp = server->cap_queue; tmp != NULL; tmp = tmp->next) {
if (g_hash_table_lookup_extended(server->cap_supported, tmp->data, NULL,
NULL)) {
if (avail_caps > 0)
g_string_append_c(cmd, ' ');
g_string_append(cmd, tmp->data);
avail_caps++;
}
}
/* Clear the queue here */
i_slist_free_full(server->cap_queue, (GDestroyNotify) g_free);
server->cap_queue = NULL;
/* If the server doesn't support any cap we requested close the negotiation here */
if (avail_caps > 0) {
signal_emit("server cap req", 2, server,
cmd->str + sizeof("CAP REQ :") - 1);
irc_send_cmd_now(server, cmd->str);
} else {
irc_cap_finish_negotiation(server);
}
g_string_free(cmd, TRUE);
}
}
static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *address) static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *address)
{ {
GSList *tmp;
GString *cmd;
char *params, *evt, *list, *star, **caps; char *params, *evt, *list, *star, **caps;
int i, caps_length, disable, avail_caps, multiline; int i, caps_length, disable, multiline;
params = event_get_params(args, 4, NULL, &evt, &star, &list); params = event_get_params(args, 4, NULL, &evt, &star, &list);
if (params == NULL) if (params == NULL)
@ -174,42 +216,20 @@ static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *add
/* A multiline response is always terminated by a normal one, /* A multiline response is always terminated by a normal one,
* wait until we receive that one to require any CAP */ * wait until we receive that one to require any CAP */
if (multiline == FALSE) { if (multiline == FALSE) {
/* No CAP has been requested */ gboolean want_starttls =
if (server->cap_queue == NULL) { i_slist_find_string(server->cap_queue, CAP_STARTTLS) != NULL;
irc_cap_finish_negotiation(server); server->cap_queue =
} i_slist_delete_string(server->cap_queue, CAP_STARTTLS, g_free);
else { if (server->connrec->starttls) {
cmd = g_string_new("CAP REQ :"); /* the connection has requested starttls,
no more data must be sent now */
avail_caps = 0; } else if (want_starttls &&
g_hash_table_lookup_extended(server->cap_supported, CAP_STARTTLS,
/* To process the queue in order, we need to reverse the stack once */ NULL, NULL)) {
server->cap_queue = g_slist_reverse(server->cap_queue); irc_server_send_starttls(server);
/* no more data must be sent now */
/* Check whether the cap is supported by the server */ } else {
for (tmp = server->cap_queue; tmp != NULL; tmp = tmp->next) { cap_process_request_queue(server);
if (g_hash_table_lookup_extended(server->cap_supported, tmp->data, NULL, NULL)) {
if (avail_caps > 0)
g_string_append_c(cmd, ' ');
g_string_append(cmd, tmp->data);
avail_caps++;
}
}
/* Clear the queue here */
i_slist_free_full(server->cap_queue, (GDestroyNotify) g_free);
server->cap_queue = NULL;
/* If the server doesn't support any cap we requested close the negotiation here */
if (avail_caps > 0) {
signal_emit("server cap req", 2, server, cmd->str + sizeof("CAP REQ :") - 1);
irc_send_cmd_now(server, cmd->str);
} else {
irc_cap_finish_negotiation(server);
}
g_string_free(cmd, TRUE);
} }
} }
} }
@ -301,12 +321,14 @@ static void event_invalid_cap (IRC_SERVER_REC *server, const char *data, const c
void irc_cap_init (void) void irc_cap_init (void)
{ {
signal_add_last("server cap continue", (SIGNAL_FUNC) cap_process_request_queue);
signal_add_first("event cap", (SIGNAL_FUNC) event_cap); signal_add_first("event cap", (SIGNAL_FUNC) event_cap);
signal_add_first("event 410", (SIGNAL_FUNC) event_invalid_cap); signal_add_first("event 410", (SIGNAL_FUNC) event_invalid_cap);
} }
void irc_cap_deinit (void) void irc_cap_deinit (void)
{ {
signal_remove("server cap continue", (SIGNAL_FUNC) cap_process_request_queue);
signal_remove("event cap", (SIGNAL_FUNC) event_cap); signal_remove("event cap", (SIGNAL_FUNC) event_cap);
signal_remove("event 410", (SIGNAL_FUNC) event_invalid_cap); signal_remove("event 410", (SIGNAL_FUNC) event_invalid_cap);
} }

View File

@ -1053,7 +1053,7 @@ void irc_commands_init(void)
signal_add("whois end", (SIGNAL_FUNC) event_end_of_whois); signal_add("whois end", (SIGNAL_FUNC) event_end_of_whois);
signal_add("whowas event", (SIGNAL_FUNC) event_whowas); signal_add("whowas event", (SIGNAL_FUNC) event_whowas);
command_set_options("connect", "+ircnet"); command_set_options("connect", "+ircnet starttls disallow_starttls nocap");
command_set_options("topic", "delete"); command_set_options("topic", "delete");
command_set_options("list", "yes"); command_set_options("list", "yes");
command_set_options("away", "one all"); command_set_options("away", "one all");

View File

@ -51,6 +51,9 @@ static void sig_server_connect_copy(SERVER_CONNECT_REC **dest,
rec->sasl_mechanism = src->sasl_mechanism; rec->sasl_mechanism = src->sasl_mechanism;
rec->sasl_username = g_strdup(src->sasl_username); rec->sasl_username = g_strdup(src->sasl_username);
rec->sasl_password = g_strdup(src->sasl_password); rec->sasl_password = g_strdup(src->sasl_password);
rec->disallow_starttls = src->disallow_starttls;
rec->starttls = src->starttls;
rec->no_cap = src->no_cap;
*dest = (SERVER_CONNECT_REC *) rec; *dest = (SERVER_CONNECT_REC *) rec;
} }
@ -62,7 +65,8 @@ static void sig_server_reconnect_save_status(IRC_SERVER_CONNECT_REC *conn,
return; return;
g_free_not_null(conn->channels); g_free_not_null(conn->channels);
conn->channels = irc_server_get_channels(server); conn->channels =
irc_server_get_channels(server, settings_get_choice("rejoin_channels_on_reconnect"));
g_free_not_null(conn->usermode); g_free_not_null(conn->usermode);
conn->usermode = g_strdup(server->wanted_usermode); conn->usermode = g_strdup(server->wanted_usermode);

View File

@ -44,9 +44,15 @@ static void sig_server_setup_fill_reconn(IRC_SERVER_CONNECT_REC *conn,
conn->max_cmds_at_once = sserver->max_cmds_at_once; conn->max_cmds_at_once = sserver->max_cmds_at_once;
if (sserver->max_query_chans > 0) if (sserver->max_query_chans > 0)
conn->max_query_chans = sserver->max_query_chans; conn->max_query_chans = sserver->max_query_chans;
if (sserver->starttls == STARTTLS_DISALLOW)
conn->disallow_starttls = 1;
else if (sserver->starttls == STARTTLS_ENABLED)
conn->starttls = 1;
if (sserver->no_cap)
conn->no_cap = 1;
} }
static void sig_server_setup_fill_connect(IRC_SERVER_CONNECT_REC *conn) static void sig_server_setup_fill_connect(IRC_SERVER_CONNECT_REC *conn, GHashTable *optlist)
{ {
const char *value; const char *value;
@ -62,6 +68,23 @@ static void sig_server_setup_fill_connect(IRC_SERVER_CONNECT_REC *conn)
g_strdup(value) : NULL; g_strdup(value) : NULL;
} }
static void sig_server_setup_fill_optlist(IRC_SERVER_CONNECT_REC *conn, GHashTable *optlist)
{
if (!IS_IRC_SERVER_CONNECT(conn))
return;
if (g_hash_table_lookup(optlist, "starttls") != NULL) {
conn->starttls = 1;
conn->use_tls = 0;
} else if (g_hash_table_lookup(optlist, "disallow_starttls") != NULL) {
conn->disallow_starttls = 1;
}
if (g_hash_table_lookup(optlist, "nocap"))
conn->no_cap = 1;
if (g_hash_table_lookup(optlist, "cap"))
conn->no_cap = 0;
}
static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn, static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn,
IRC_CHATNET_REC *ircnet) IRC_CHATNET_REC *ircnet)
{ {
@ -174,6 +197,11 @@ static void sig_server_setup_read(IRC_SERVER_SETUP_REC *rec, CONFIG_NODE *node)
rec->max_cmds_at_once = config_node_get_int(node, "cmds_max_at_once", 0); rec->max_cmds_at_once = config_node_get_int(node, "cmds_max_at_once", 0);
rec->cmd_queue_speed = config_node_get_int(node, "cmd_queue_speed", 0); rec->cmd_queue_speed = config_node_get_int(node, "cmd_queue_speed", 0);
rec->max_query_chans = config_node_get_int(node, "max_query_chans", 0); rec->max_query_chans = config_node_get_int(node, "max_query_chans", 0);
rec->starttls = config_node_get_bool(node, "starttls", STARTTLS_NOTSET);
if (rec->starttls == STARTTLS_ENABLED) {
rec->use_tls = 0;
}
rec->no_cap = config_node_get_bool(node, "no_cap", FALSE);
} }
static void sig_server_setup_saved(IRC_SERVER_SETUP_REC *rec, static void sig_server_setup_saved(IRC_SERVER_SETUP_REC *rec,
@ -188,6 +216,12 @@ static void sig_server_setup_saved(IRC_SERVER_SETUP_REC *rec,
iconfig_node_set_int(node, "cmd_queue_speed", rec->cmd_queue_speed); iconfig_node_set_int(node, "cmd_queue_speed", rec->cmd_queue_speed);
if (rec->max_query_chans > 0) if (rec->max_query_chans > 0)
iconfig_node_set_int(node, "max_query_chans", rec->max_query_chans); iconfig_node_set_int(node, "max_query_chans", rec->max_query_chans);
if (rec->starttls != STARTTLS_NOTSET)
iconfig_node_set_bool(node, "starttls", rec->starttls);
else
iconfig_node_set_str(node, "starttls", NULL);
if (rec->no_cap)
iconfig_node_set_bool(node, "no_cap", TRUE);
} }
void irc_servers_setup_init(void) void irc_servers_setup_init(void)
@ -199,6 +233,7 @@ void irc_servers_setup_init(void)
signal_add("server setup fill reconn", (SIGNAL_FUNC) sig_server_setup_fill_reconn); signal_add("server setup fill reconn", (SIGNAL_FUNC) sig_server_setup_fill_reconn);
signal_add("server setup fill connect", (SIGNAL_FUNC) sig_server_setup_fill_connect); signal_add("server setup fill connect", (SIGNAL_FUNC) sig_server_setup_fill_connect);
signal_add("server setup fill chatnet", (SIGNAL_FUNC) sig_server_setup_fill_chatnet); signal_add("server setup fill chatnet", (SIGNAL_FUNC) sig_server_setup_fill_chatnet);
signal_add("server setup fill optlist", (SIGNAL_FUNC) sig_server_setup_fill_optlist);
signal_add("server setup read", (SIGNAL_FUNC) sig_server_setup_read); signal_add("server setup read", (SIGNAL_FUNC) sig_server_setup_read);
signal_add("server setup saved", (SIGNAL_FUNC) sig_server_setup_saved); signal_add("server setup saved", (SIGNAL_FUNC) sig_server_setup_saved);
} }
@ -208,6 +243,7 @@ void irc_servers_setup_deinit(void)
signal_remove("server setup fill reconn", (SIGNAL_FUNC) sig_server_setup_fill_reconn); signal_remove("server setup fill reconn", (SIGNAL_FUNC) sig_server_setup_fill_reconn);
signal_remove("server setup fill connect", (SIGNAL_FUNC) sig_server_setup_fill_connect); signal_remove("server setup fill connect", (SIGNAL_FUNC) sig_server_setup_fill_connect);
signal_remove("server setup fill chatnet", (SIGNAL_FUNC) sig_server_setup_fill_chatnet); signal_remove("server setup fill chatnet", (SIGNAL_FUNC) sig_server_setup_fill_chatnet);
signal_remove("server setup fill optlist", (SIGNAL_FUNC) sig_server_setup_fill_optlist);
signal_remove("server setup read", (SIGNAL_FUNC) sig_server_setup_read); signal_remove("server setup read", (SIGNAL_FUNC) sig_server_setup_read);
signal_remove("server setup saved", (SIGNAL_FUNC) sig_server_setup_saved); signal_remove("server setup saved", (SIGNAL_FUNC) sig_server_setup_saved);
} }

View File

@ -11,6 +11,12 @@
#define IS_IRC_SERVER_SETUP(server) \ #define IS_IRC_SERVER_SETUP(server) \
(IRC_SERVER_SETUP(server) ? TRUE : FALSE) (IRC_SERVER_SETUP(server) ? TRUE : FALSE)
enum {
STARTTLS_DISALLOW = -1, /* */
STARTTLS_NOTSET = 0,
STARTTLS_ENABLED = 1
};
typedef struct { typedef struct {
#include <irssi/src/core/server-setup-rec.h> #include <irssi/src/core/server-setup-rec.h>
@ -18,6 +24,8 @@ typedef struct {
int max_cmds_at_once; int max_cmds_at_once;
int cmd_queue_speed; int cmd_queue_speed;
int max_query_chans; int max_query_chans;
int starttls;
int no_cap : 1;
} IRC_SERVER_SETUP_REC; } IRC_SERVER_SETUP_REC;
void irc_servers_setup_init(void); void irc_servers_setup_init(void);

View File

@ -20,10 +20,11 @@
#include "module.h" #include "module.h"
#include <irssi/src/core/net-sendbuffer.h>
#include <irssi/src/core/signals.h>
#include <irssi/src/core/rawlog.h>
#include <irssi/src/core/misc.h> #include <irssi/src/core/misc.h>
#include <irssi/src/core/net-sendbuffer.h>
#include <irssi/src/core/network.h>
#include <irssi/src/core/rawlog.h>
#include <irssi/src/core/signals.h>
#include <irssi/src/core/channels.h> #include <irssi/src/core/channels.h>
#include <irssi/src/core/queries.h> #include <irssi/src/core/queries.h>
@ -207,7 +208,7 @@ static char **split_message(SERVER_REC *server, const char *target,
strlen(target)); strlen(target));
} }
static void server_init(IRC_SERVER_REC *server) static void server_init_2(IRC_SERVER_REC *server)
{ {
IRC_SERVER_CONNECT_REC *conn; IRC_SERVER_CONNECT_REC *conn;
char *address, *ptr, *username, *cmd; char *address, *ptr, *username, *cmd;
@ -216,8 +217,56 @@ static void server_init(IRC_SERVER_REC *server)
conn = server->connrec; conn = server->connrec;
if (conn->proxy != NULL && conn->proxy_password != NULL && if (conn->password != NULL && *conn->password != '\0') {
*conn->proxy_password != '\0') { /* send password */
cmd = g_strdup_printf("PASS %s", conn->password);
irc_send_cmd_now(server, cmd);
g_free(cmd);
}
/* send nick */
cmd = g_strdup_printf("NICK %s", conn->nick);
irc_send_cmd_now(server, cmd);
g_free(cmd);
/* send user/realname */
address = server->connrec->address;
ptr = strrchr(address, ':');
if (ptr != NULL) {
/* IPv6 address .. doesn't work here, use the string after
the last : char */
address = ptr + 1;
if (*address == '\0')
address = "x";
}
username = g_strdup(conn->username);
ptr = strchr(username, ' ');
if (ptr != NULL)
*ptr = '\0';
cmd = g_strdup_printf("USER %s %s %s :%s", username, username, address, conn->realname);
irc_send_cmd_now(server, cmd);
g_free(cmd);
g_free(username);
if (conn->proxy != NULL && conn->proxy_string_after != NULL) {
cmd = g_strdup_printf(conn->proxy_string_after, conn->address, conn->port);
irc_send_cmd_now(server, cmd);
g_free(cmd);
}
}
static void server_init_1(IRC_SERVER_REC *server)
{
IRC_SERVER_CONNECT_REC *conn;
char *cmd;
g_return_if_fail(server != NULL);
conn = server->connrec;
if (conn->proxy != NULL && conn->proxy_password != NULL && *conn->proxy_password != '\0') {
cmd = g_strdup_printf("PASS %s", conn->proxy_password); cmd = g_strdup_printf("PASS %s", conn->proxy_password);
irc_send_cmd_now(server, cmd); irc_send_cmd_now(server, cmd);
g_free(cmd); g_free(cmd);
@ -243,45 +292,8 @@ static void server_init(IRC_SERVER_REC *server)
irc_cap_toggle(server, CAP_ACCOUNT_NOTIFY, TRUE); irc_cap_toggle(server, CAP_ACCOUNT_NOTIFY, TRUE);
irc_cap_toggle(server, CAP_SELF_MESSAGE, TRUE); irc_cap_toggle(server, CAP_SELF_MESSAGE, TRUE);
irc_cap_toggle(server, CAP_SERVER_TIME, TRUE); irc_cap_toggle(server, CAP_SERVER_TIME, TRUE);
if (!conn->use_tls && (conn->starttls || !conn->disallow_starttls)) {
irc_send_cmd_now(server, "CAP LS " CAP_LS_VERSION); irc_cap_toggle(server, CAP_STARTTLS, TRUE);
if (conn->password != NULL && *conn->password != '\0') {
/* send password */
cmd = g_strdup_printf("PASS %s", conn->password);
irc_send_cmd_now(server, cmd);
g_free(cmd);
}
/* send nick */
cmd = g_strdup_printf("NICK %s", conn->nick);
irc_send_cmd_now(server, cmd);
g_free(cmd);
/* send user/realname */
address = server->connrec->address;
ptr = strrchr(address, ':');
if (ptr != NULL) {
/* IPv6 address .. doesn't work here, use the string after
the last : char */
address = ptr+1;
if (*address == '\0')
address = "x";
}
username = g_strdup(conn->username);
ptr = strchr(username, ' ');
if (ptr != NULL) *ptr = '\0';
cmd = g_strdup_printf("USER %s %s %s :%s", username, username, address, conn->realname);
irc_send_cmd_now(server, cmd);
g_free(cmd);
g_free(username);
if (conn->proxy != NULL && conn->proxy_string_after != NULL) {
cmd = g_strdup_printf(conn->proxy_string_after, conn->address, conn->port);
irc_send_cmd_now(server, cmd);
g_free(cmd);
} }
server->isupport = g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal); server->isupport = g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal);
@ -296,6 +308,119 @@ static void server_init(IRC_SERVER_REC *server)
/* this will reset to 1 sec after we get the 001 event */ /* this will reset to 1 sec after we get the 001 event */
server->wait_cmd = g_get_real_time(); server->wait_cmd = g_get_real_time();
server->wait_cmd += 120 * G_USEC_PER_SEC; server->wait_cmd += 120 * G_USEC_PER_SEC;
if (!conn->no_cap) {
signal_emit("server waiting cap ls", 2, server, CAP_LS_VERSION);
irc_send_cmd_now(server, "CAP LS " CAP_LS_VERSION);
/* to detect non-CAP servers, send this bogus join */
irc_send_cmd_now(server, "JOIN ");
}
if (conn->starttls)
irc_server_send_starttls(server);
else if (conn->no_cap)
server_init_2(server);
}
static void init_ssl_loop(IRC_SERVER_REC *server, GIOChannel *handle)
{
int error;
server->connrec->starttls = 1;
if (server->starttls_tag) {
g_source_remove(server->starttls_tag);
server->starttls_tag = 0;
}
error = irssi_ssl_handshake(handle);
if (error == -1) {
server->connection_lost = TRUE;
server_disconnect((SERVER_REC *) server);
return;
}
if (error & 1) { /* wait */
server->starttls_tag =
i_input_add(handle, error == 1 ? I_INPUT_READ : I_INPUT_WRITE,
(GInputFunction) init_ssl_loop, server);
return;
}
/* continue */
rawlog_redirect(server->rawlog, "Now talking encrypted");
signal_emit("server connection switched", 1, server);
if (!server->cap_supported) {
server_init_2(server);
} else {
signal_emit("server cap continue", 1, server);
}
if (settings_get_bool("starttls_sts")) {
IRC_SERVER_SETUP_REC *ssetup = IRC_SERVER_SETUP(server_setup_find(
server->connrec->address, server->connrec->port, server->connrec->chatnet));
if (ssetup != NULL) {
ssetup->starttls = STARTTLS_ENABLED;
server_setup_add((SERVER_SETUP_REC *) ssetup);
}
}
}
#include <irssi/src/core/line-split.h>
void irc_server_send_starttls(IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
g_warning("[%s] Now attempting STARTTLS", server->tag);
irc_send_cmd_now(server, "STARTTLS");
}
static void event_starttls(IRC_SERVER_REC *server, const char *data)
{
GIOChannel *ssl_handle;
g_return_if_fail(server != NULL);
if (!IS_IRC_SERVER(server))
return;
if (server->handle->readbuffer != NULL &&
!line_split_is_empty(server->handle->readbuffer)) {
char *str;
line_split("", -1, &str, &server->handle->readbuffer);
}
ssl_handle = net_start_ssl((SERVER_REC *) server);
if (ssl_handle != NULL) {
g_source_remove(server->readtag);
server->readtag = -1;
server->handle->handle = ssl_handle;
init_ssl_loop(server, server->handle->handle);
} else {
g_warning("net_start_ssl failed");
}
}
static void event_registerfirst(IRC_SERVER_REC *server, const char *data)
{
g_return_if_fail(server != NULL);
if (!IS_IRC_SERVER(server))
return;
if (server->connected)
return;
if (!server->cap_supported && !server->connrec->starttls)
server_init_2(server);
}
static void event_capend(IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
if (!IS_IRC_SERVER(server))
return;
if (server->connected)
return;
server_init_2(server);
} }
SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn) SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
@ -348,6 +473,27 @@ void irc_server_connect(SERVER_REC *server)
{ {
g_return_if_fail(server != NULL); g_return_if_fail(server != NULL);
if (server->connrec->connect_handle != NULL) {
/* an existing handle from upgrade */
IRC_SERVER_CONNECT_REC *conn;
int tls_disconnect;
conn = ((IRC_SERVER_REC *) server)->connrec;
tls_disconnect = conn->use_tls || conn->starttls;
if (tls_disconnect) {
/* we cannot use it, it is encrypted. force a reconnect */
g_io_channel_unref(conn->connect_handle);
conn->connect_handle = NULL;
server->session_reconnect = FALSE;
server_connect_ref((SERVER_CONNECT_REC *) conn);
server_disconnect(server);
server_connect((SERVER_CONNECT_REC *) conn);
server_connect_unref((SERVER_CONNECT_REC *) conn);
return;
}
}
if (!server_start_connect(server)) { if (!server_start_connect(server)) {
server_connect_unref(server->connrec); server_connect_unref(server->connrec);
g_free(server); g_free(server);
@ -420,7 +566,7 @@ static void sig_connected(IRC_SERVER_REC *server)
server->splits = g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal); server->splits = g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal);
if (!server->session_reconnect) if (!server->session_reconnect)
server_init(server); server_init_1(server);
} }
static void isupport_destroy_hash(void *key, void *value) static void isupport_destroy_hash(void *key, void *value)
@ -429,6 +575,17 @@ static void isupport_destroy_hash(void *key, void *value)
g_free(value); g_free(value);
} }
static void sig_disconnected(IRC_SERVER_REC *server)
{
if (!IS_IRC_SERVER(server))
return;
if (server->starttls_tag) {
g_source_remove(server->starttls_tag);
server->starttls_tag = 0;
}
}
static void sig_destroyed(IRC_SERVER_REC *server) static void sig_destroyed(IRC_SERVER_REC *server)
{ {
GSList *tmp; GSList *tmp;
@ -650,20 +807,17 @@ void irc_servers_start_cmd_timeout(void)
/* Return a string of all channels (and keys, if any have them) in server, /* Return a string of all channels (and keys, if any have them) in server,
like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g" */ like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g" */
char *irc_server_get_channels(IRC_SERVER_REC *server) char *irc_server_get_channels(IRC_SERVER_REC *server, int rejoin_channels_mode)
{ {
GSList *tmp; GSList *tmp;
GString *chans, *keys; GString *chans, *keys;
char *ret; char *ret;
int use_keys; int use_keys;
int rejoin_channels_mode;
g_return_val_if_fail(server != NULL, FALSE); g_return_val_if_fail(server != NULL, FALSE);
rejoin_channels_mode = settings_get_choice("rejoin_channels_on_reconnect");
/* do we want to rejoin channels in the first place? */ /* do we want to rejoin channels in the first place? */
if(rejoin_channels_mode == 0) if (rejoin_channels_mode == REJOIN_CHANNELS_MODE_OFF)
return g_strdup(""); return g_strdup("");
chans = g_string_new(NULL); chans = g_string_new(NULL);
@ -674,7 +828,9 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *channel = tmp->data; CHANNEL_REC *channel = tmp->data;
CHANNEL_SETUP_REC *setup = channel_setup_find(channel->name, channel->server->connrec->chatnet); CHANNEL_SETUP_REC *setup = channel_setup_find(channel->name, channel->server->connrec->chatnet);
if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) { if ((setup != NULL && setup->autojoin &&
rejoin_channels_mode == REJOIN_CHANNELS_MODE_AUTO) ||
rejoin_channels_mode == REJOIN_CHANNELS_MODE_ON) {
g_string_append_printf(chans, "%s,", channel->name); g_string_append_printf(chans, "%s,", channel->name);
g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" : channel->key); g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" : channel->key);
if (channel->key != NULL) if (channel->key != NULL)
@ -687,7 +843,9 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
REJOIN_REC *rec = tmp->data; REJOIN_REC *rec = tmp->data;
CHANNEL_SETUP_REC *setup = channel_setup_find(rec->channel, server->tag); CHANNEL_SETUP_REC *setup = channel_setup_find(rec->channel, server->tag);
if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) { if ((setup != NULL && setup->autojoin &&
rejoin_channels_mode == REJOIN_CHANNELS_MODE_AUTO) ||
rejoin_channels_mode == REJOIN_CHANNELS_MODE_ON) {
g_string_append_printf(chans, "%s,", rec->channel); g_string_append_printf(chans, "%s,", rec->channel);
g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" : g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
rec->key); rec->key);
@ -1048,6 +1206,7 @@ void irc_server_init_isupport(IRC_SERVER_REC *server)
void irc_servers_init(void) void irc_servers_init(void)
{ {
settings_add_bool("servers", "starttls_sts", TRUE);
settings_add_choice("servers", "rejoin_channels_on_reconnect", 1, "off;on;auto"); settings_add_choice("servers", "rejoin_channels_on_reconnect", 1, "off;on;auto");
settings_add_str("misc", "usermode", DEFAULT_USER_MODE); settings_add_str("misc", "usermode", DEFAULT_USER_MODE);
settings_add_str("misc", "split_line_start", ""); settings_add_str("misc", "split_line_start", "");
@ -1059,9 +1218,13 @@ void irc_servers_init(void)
cmd_tag = -1; cmd_tag = -1;
signal_add_first("server connected", (SIGNAL_FUNC) sig_connected); signal_add_first("server connected", (SIGNAL_FUNC) sig_connected);
signal_add_first("server disconnected", (SIGNAL_FUNC) sig_disconnected);
signal_add_last("server destroyed", (SIGNAL_FUNC) sig_destroyed); signal_add_last("server destroyed", (SIGNAL_FUNC) sig_destroyed);
signal_add_last("server quit", (SIGNAL_FUNC) sig_server_quit); signal_add_last("server quit", (SIGNAL_FUNC) sig_server_quit);
signal_add("server cap ack " CAP_MAXLINE, (SIGNAL_FUNC) cap_maxline); signal_add("server cap ack " CAP_MAXLINE, (SIGNAL_FUNC) cap_maxline);
signal_add("event 670", (SIGNAL_FUNC) event_starttls);
signal_add("event 451", (SIGNAL_FUNC) event_registerfirst);
signal_add("server cap end", (SIGNAL_FUNC) event_capend);
signal_add("event 001", (SIGNAL_FUNC) event_connected); signal_add("event 001", (SIGNAL_FUNC) event_connected);
signal_add("event 004", (SIGNAL_FUNC) event_server_info); signal_add("event 004", (SIGNAL_FUNC) event_server_info);
signal_add("event 005", (SIGNAL_FUNC) event_isupport); signal_add("event 005", (SIGNAL_FUNC) event_isupport);
@ -1087,9 +1250,13 @@ void irc_servers_deinit(void)
g_source_remove(cmd_tag); g_source_remove(cmd_tag);
signal_remove("server connected", (SIGNAL_FUNC) sig_connected); signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
signal_remove("server destroyed", (SIGNAL_FUNC) sig_destroyed); signal_remove("server destroyed", (SIGNAL_FUNC) sig_destroyed);
signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit); signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
signal_remove("server cap ack " CAP_MAXLINE, (SIGNAL_FUNC) cap_maxline); signal_remove("server cap ack " CAP_MAXLINE, (SIGNAL_FUNC) cap_maxline);
signal_remove("event 670", (SIGNAL_FUNC) event_starttls);
signal_remove("event 451", (SIGNAL_FUNC) event_registerfirst);
signal_remove("server cap end", (SIGNAL_FUNC) event_capend);
signal_remove("event 001", (SIGNAL_FUNC) event_connected); signal_remove("event 001", (SIGNAL_FUNC) event_connected);
signal_remove("event 004", (SIGNAL_FUNC) event_server_info); signal_remove("event 004", (SIGNAL_FUNC) event_server_info);
signal_remove("event 005", (SIGNAL_FUNC) event_isupport); signal_remove("event 005", (SIGNAL_FUNC) event_isupport);

View File

@ -27,6 +27,7 @@
#define CAP_ACCOUNT_NOTIFY "account-notify" #define CAP_ACCOUNT_NOTIFY "account-notify"
#define CAP_SELF_MESSAGE "znc.in/self-message" #define CAP_SELF_MESSAGE "znc.in/self-message"
#define CAP_SERVER_TIME "server-time" #define CAP_SERVER_TIME "server-time"
#define CAP_STARTTLS "tls"
/* returns IRC_SERVER_REC if it's IRC server, NULL if it isn't */ /* returns IRC_SERVER_REC if it's IRC server, NULL if it isn't */
#define IRC_SERVER(server) \ #define IRC_SERVER(server) \
@ -42,6 +43,7 @@
#define IS_IRC_SERVER_CONNECT(conn) \ #define IS_IRC_SERVER_CONNECT(conn) \
(IRC_SERVER_CONNECT(conn) ? TRUE : FALSE) (IRC_SERVER_CONNECT(conn) ? TRUE : FALSE)
/* clang-format off */
/* all strings should be either NULL or dynamically allocated */ /* all strings should be either NULL or dynamically allocated */
/* address and nick are mandatory, rest are optional */ /* address and nick are mandatory, rest are optional */
struct _IRC_SERVER_CONNECT_REC { struct _IRC_SERVER_CONNECT_REC {
@ -59,7 +61,11 @@ struct _IRC_SERVER_CONNECT_REC {
int max_query_chans; int max_query_chans;
int max_kicks, max_msgs, max_modes, max_whois; int max_kicks, max_msgs, max_modes, max_whois;
int disallow_starttls:1;
int starttls:1;
int no_cap:1;
}; };
/* clang-format on */
#define STRUCT_SERVER_CONNECT_REC IRC_SERVER_CONNECT_REC #define STRUCT_SERVER_CONNECT_REC IRC_SERVER_CONNECT_REC
struct _IRC_SERVER_REC { struct _IRC_SERVER_REC {
@ -136,6 +142,7 @@ struct _IRC_SERVER_REC {
GSList *rejoin_channels; /* try to join to these channels after a while - GSList *rejoin_channels; /* try to join to these channels after a while -
channels go here if they're "temporarily unavailable" channels go here if they're "temporarily unavailable"
because of netsplits */ because of netsplits */
guint starttls_tag; /* Holds the source id of the running timeout */
struct _SERVER_QUERY_REC *chanqueries; struct _SERVER_QUERY_REC *chanqueries;
GHashTable *isupport; GHashTable *isupport;
@ -151,10 +158,17 @@ void irc_server_connect(SERVER_REC *server);
/* Purge server output, either all or for specified target */ /* Purge server output, either all or for specified target */
void irc_server_purge_output(IRC_SERVER_REC *server, const char *target); void irc_server_purge_output(IRC_SERVER_REC *server, const char *target);
enum {
REJOIN_CHANNELS_MODE_OFF = 0, /* */
REJOIN_CHANNELS_MODE_ON,
REJOIN_CHANNELS_MODE_AUTO
};
/* Return a string of all channels (and keys, if any have them) in server, /* Return a string of all channels (and keys, if any have them) in server,
like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g" */ like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g" */
char *irc_server_get_channels(IRC_SERVER_REC *server); char *irc_server_get_channels(IRC_SERVER_REC *server, int rejoin_channels_mode);
void irc_server_send_starttls(IRC_SERVER_REC *server);
/* INTERNAL: */ /* INTERNAL: */
void irc_server_send_action(IRC_SERVER_REC *server, const char *target, void irc_server_send_action(IRC_SERVER_REC *server, const char *target,
const char *data); const char *data);

View File

@ -23,8 +23,10 @@
#include <irssi/src/core/net-sendbuffer.h> #include <irssi/src/core/net-sendbuffer.h>
#include <irssi/src/lib-config/iconfig.h> #include <irssi/src/lib-config/iconfig.h>
#include <irssi/src/core/misc.h> #include <irssi/src/core/misc.h>
#include <irssi/src/core/network.h>
#include <irssi/src/irc/core/irc-servers.h> #include <irssi/src/irc/core/irc-servers.h>
#include <irssi/src/irc/core/irc-servers-setup.h>
#include <irssi/src/irc/core/irc-channels.h> #include <irssi/src/irc/core/irc-channels.h>
#include <irssi/src/irc/core/irc-nicklist.h> #include <irssi/src/irc/core/irc-nicklist.h>
@ -43,6 +45,7 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config,
GSList *tmp; GSList *tmp;
CONFIG_NODE *isupport; CONFIG_NODE *isupport;
struct _isupport_data isupport_data; struct _isupport_data isupport_data;
int tls_disconnect;
if (!IS_IRC_SERVER(server)) if (!IS_IRC_SERVER(server))
return; return;
@ -58,7 +61,15 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config,
break; break;
} }
} }
net_sendbuffer_flush(server->handle); /* we cannot upgrade TLS (yet?) */
tls_disconnect = server->connrec->use_tls || server->connrec->starttls;
if (tls_disconnect) {
config_node_set_str(config, node, "rejoin_channels",
irc_server_get_channels(server, REJOIN_CHANNELS_MODE_ON));
irc_send_cmd_now(server, "QUIT :[TLS] Client upgrade");
}
net_sendbuffer_flush(server->handle);
config_node_set_str(config, node, "real_address", server->real_address); config_node_set_str(config, node, "real_address", server->real_address);
config_node_set_str(config, node, "userhost", server->userhost); config_node_set_str(config, node, "userhost", server->userhost);
@ -71,18 +82,27 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config,
config_node_set_str(config, node, "sasl_username", server->connrec->sasl_username); config_node_set_str(config, node, "sasl_username", server->connrec->sasl_username);
config_node_set_str(config, node, "sasl_password", server->connrec->sasl_password); config_node_set_str(config, node, "sasl_password", server->connrec->sasl_password);
config_node_set_int(config, node, "starttls",
server->connrec->disallow_starttls ? STARTTLS_DISALLOW :
server->connrec->starttls ? STARTTLS_ENABLED :
STARTTLS_NOTSET);
config_node_set_bool(config, node, "no_cap", server->connrec->no_cap);
config_node_set_bool(config, node, "isupport_sent", server->isupport_sent); config_node_set_bool(config, node, "isupport_sent", server->isupport_sent);
isupport = config_node_section(config, node, "isupport", NODE_TYPE_BLOCK); isupport = config_node_section(config, node, "isupport", NODE_TYPE_BLOCK);
isupport_data.config = config; isupport_data.config = config;
isupport_data.node = isupport; isupport_data.node = isupport;
g_hash_table_foreach(server->isupport, (GHFunc) session_isupport_foreach, &isupport_data); g_hash_table_foreach(server->isupport, (GHFunc) session_isupport_foreach, &isupport_data);
/* we have to defer the disconnect to irc_server_connect */
} }
static void sig_session_restore_server(IRC_SERVER_REC *server, static void sig_session_restore_server(IRC_SERVER_REC *server,
CONFIG_NODE *node) CONFIG_NODE *node)
{ {
GSList *tmp; GSList *tmp;
int starttls_mode;
if (!IS_IRC_SERVER(server)) if (!IS_IRC_SERVER(server))
return; return;
@ -96,6 +116,7 @@ static void sig_session_restore_server(IRC_SERVER_REC *server,
server->emode_known = config_node_get_bool(node, "emode_known", FALSE); server->emode_known = config_node_get_bool(node, "emode_known", FALSE);
server->isupport_sent = config_node_get_bool(node, "isupport_sent", FALSE); server->isupport_sent = config_node_get_bool(node, "isupport_sent", FALSE);
server->connrec->no_cap = config_node_get_bool(node, "no_cap", FALSE);
server->connrec->sasl_mechanism = config_node_get_int(node, "sasl_mechanism", SASL_MECHANISM_NONE); server->connrec->sasl_mechanism = config_node_get_int(node, "sasl_mechanism", SASL_MECHANISM_NONE);
/* The fields below might have been filled when loading the chatnet /* The fields below might have been filled when loading the chatnet
* description from the config and we favor the content that's been saved * description from the config and we favor the content that's been saved
@ -105,6 +126,16 @@ static void sig_session_restore_server(IRC_SERVER_REC *server,
g_free(server->connrec->sasl_password); g_free(server->connrec->sasl_password);
server->connrec->sasl_password = g_strdup(config_node_get_str(node, "sasl_password", NULL)); server->connrec->sasl_password = g_strdup(config_node_get_str(node, "sasl_password", NULL));
server->connrec->channels = g_strdup(config_node_get_str(node, "rejoin_channels", NULL));
starttls_mode = config_node_get_int(node, "starttls", STARTTLS_NOTSET);
if (starttls_mode == STARTTLS_DISALLOW)
server->connrec->disallow_starttls = 1;
if (starttls_mode == STARTTLS_ENABLED) {
server->connrec->starttls = 1;
server->connrec->use_tls = 0;
}
if (server->isupport == NULL) { if (server->isupport == NULL) {
server->isupport = server->isupport =
g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal); g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal);
@ -123,6 +154,7 @@ static void sig_session_restore_server(IRC_SERVER_REC *server,
} }
irc_server_init_isupport(server); irc_server_init_isupport(server);
/* we will reconnect in irc_server_connect if the connection was TLS */
} }
static void sig_session_restore_nick(IRC_CHANNEL_REC *channel, static void sig_session_restore_nick(IRC_CHANNEL_REC *channel,

View File

@ -579,6 +579,7 @@ void irc_irc_init(void)
signal_add("server event", (SIGNAL_FUNC) irc_server_event); signal_add("server event", (SIGNAL_FUNC) irc_server_event);
signal_add("server event tags", (SIGNAL_FUNC) irc_server_event_tags); signal_add("server event tags", (SIGNAL_FUNC) irc_server_event_tags);
signal_add("server connected", (SIGNAL_FUNC) irc_init_server); signal_add("server connected", (SIGNAL_FUNC) irc_init_server);
signal_add("server connection switched", (SIGNAL_FUNC) irc_init_server);
signal_add("server incoming", (SIGNAL_FUNC) irc_parse_incoming_line); signal_add("server incoming", (SIGNAL_FUNC) irc_parse_incoming_line);
current_server_event = NULL; current_server_event = NULL;
@ -593,5 +594,6 @@ void irc_irc_deinit(void)
signal_remove("server event", (SIGNAL_FUNC) irc_server_event); signal_remove("server event", (SIGNAL_FUNC) irc_server_event);
signal_remove("server event tags", (SIGNAL_FUNC) irc_server_event_tags); signal_remove("server event tags", (SIGNAL_FUNC) irc_server_event_tags);
signal_remove("server connected", (SIGNAL_FUNC) irc_init_server); signal_remove("server connected", (SIGNAL_FUNC) irc_init_server);
signal_remove("server connection switched", (SIGNAL_FUNC) irc_init_server);
signal_remove("server incoming", (SIGNAL_FUNC) irc_parse_incoming_line); signal_remove("server incoming", (SIGNAL_FUNC) irc_parse_incoming_line);
} }

View File

@ -381,6 +381,9 @@ static void sig_disconnected(IRC_SERVER_REC *server)
if (!IS_IRC_SERVER(server)) if (!IS_IRC_SERVER(server))
return; return;
if (server->splits == NULL)
return;
g_hash_table_foreach(server->splits, g_hash_table_foreach(server->splits,
(GHFunc) netsplit_destroy_hash, server); (GHFunc) netsplit_destroy_hash, server);
g_hash_table_destroy(server->splits); g_hash_table_destroy(server->splits);

View File

@ -209,6 +209,9 @@ static void notifylist_deinit_server(IRC_SERVER_REC *server)
return; return;
mserver = MODULE_DATA(server); mserver = MODULE_DATA(server);
if (!mserver)
return;
while (mserver->notify_users != NULL) { while (mserver->notify_users != NULL) {
rec = mserver->notify_users->data; rec = mserver->notify_users->data;

View File

@ -1,5 +1,6 @@
#define PERL_NO_GET_CONTEXT #define PERL_NO_GET_CONTEXT
#include "module.h" #include "module.h"
#include <irssi/src/core/misc.h>
static GSList *register_hash2list(HV *hv) static GSList *register_hash2list(HV *hv)
{ {
@ -47,12 +48,20 @@ MODULE = Irssi::Irc::Server PACKAGE = Irssi::Irc::Server PREFIX = irc_server_
PROTOTYPES: ENABLE PROTOTYPES: ENABLE
void void
irc_server_get_channels(server) irc_server_get_channels(server, rejoin_channels_mode = "")
Irssi::Irc::Server server Irssi::Irc::Server server
char *rejoin_channels_mode
PREINIT: PREINIT:
char *ret; char *ret;
int mode;
SETTINGS_REC *setting;
PPCODE: PPCODE:
ret = irc_server_get_channels(server); setting = settings_get_record("rejoin_channels_on_reconnect");
mode = strarray_find(setting->choices, rejoin_channels_mode);
if (mode < 0)
mode = setting->default_value.v_int;
ret = irc_server_get_channels(server, mode);
XPUSHs(sv_2mortal(new_pv(ret))); XPUSHs(sv_2mortal(new_pv(ret)));
g_free(ret); g_free(ret);