summaryrefslogtreecommitdiff
path: root/src/irc
diff options
context:
space:
mode:
Diffstat (limited to 'src/irc')
-rw-r--r--src/irc/core/Makefile.am4
-rw-r--r--src/irc/core/bans.c20
-rw-r--r--src/irc/core/channel-events.c12
-rw-r--r--src/irc/core/channel-rejoin.c2
-rw-r--r--src/irc/core/channels-query.c7
-rw-r--r--src/irc/core/irc-cap.c192
-rw-r--r--src/irc/core/irc-cap.h9
-rw-r--r--src/irc/core/irc-channels-setup.c6
-rw-r--r--src/irc/core/irc-channels.c33
-rw-r--r--src/irc/core/irc-chatnets.c33
-rw-r--r--src/irc/core/irc-chatnets.h10
-rw-r--r--src/irc/core/irc-commands.c268
-rw-r--r--src/irc/core/irc-core.c8
-rw-r--r--src/irc/core/irc-expandos.c38
-rw-r--r--src/irc/core/irc-nicklist.c34
-rw-r--r--src/irc/core/irc-nicklist.h3
-rw-r--r--src/irc/core/irc-queries.c28
-rw-r--r--src/irc/core/irc-servers-reconnect.c3
-rw-r--r--src/irc/core/irc-servers-setup.c52
-rw-r--r--src/irc/core/irc-servers.c111
-rw-r--r--src/irc/core/irc-servers.h13
-rw-r--r--src/irc/core/irc-session.c19
-rw-r--r--src/irc/core/irc.c42
-rw-r--r--src/irc/core/irc.h6
-rw-r--r--src/irc/core/modes.c17
-rw-r--r--src/irc/core/sasl.c335
-rw-r--r--src/irc/core/sasl.h34
-rw-r--r--src/irc/core/servers-redirect.c14
-rw-r--r--src/irc/dcc/dcc-autoget.c13
-rw-r--r--src/irc/dcc/dcc-chat.c27
-rw-r--r--src/irc/dcc/dcc-get.c55
-rw-r--r--src/irc/dcc/dcc-resume.c4
-rw-r--r--src/irc/dcc/dcc-send.c14
-rw-r--r--src/irc/dcc/dcc-server.c2
-rw-r--r--src/irc/dcc/dcc.c8
-rw-r--r--src/irc/flood/autoignore.c2
-rw-r--r--src/irc/flood/flood.c4
-rw-r--r--src/irc/notifylist/notify-commands.c3
-rw-r--r--src/irc/notifylist/notify-ison.c4
-rw-r--r--src/irc/notifylist/notify-setup.c6
-rw-r--r--src/irc/notifylist/notifylist.c2
-rw-r--r--src/irc/proxy/dump.c40
-rw-r--r--src/irc/proxy/listen.c410
-rw-r--r--src/irc/proxy/proxy.c64
-rw-r--r--src/irc/proxy/proxy.h5
45 files changed, 1562 insertions, 454 deletions
diff --git a/src/irc/core/Makefile.am b/src/irc/core/Makefile.am
index 3db5cf0e..20caaeb1 100644
--- a/src/irc/core/Makefile.am
+++ b/src/irc/core/Makefile.am
@@ -26,6 +26,8 @@ libirc_core_a_SOURCES = \
irc-servers-reconnect.c \
irc-servers-setup.c \
irc-session.c \
+ irc-cap.c \
+ sasl.c \
lag.c \
massjoin.c \
modes.c \
@@ -48,6 +50,8 @@ pkginc_irc_core_HEADERS = \
irc-queries.h \
irc-servers.h \
irc-servers-setup.h \
+ irc-cap.h \
+ sasl.h \
modes.h \
mode-lists.h \
module.h \
diff --git a/src/irc/core/bans.c b/src/irc/core/bans.c
index d8d5d448..198fdc4a 100644
--- a/src/irc/core/bans.c
+++ b/src/irc/core/bans.c
@@ -88,7 +88,7 @@ char *ban_get_masks(IRC_CHANNEL_REC *channel, const char *nicks, int ban_type)
str = g_string_new(NULL);
banlist = g_strsplit(nicks, " ", -1);
for (ban = banlist; *ban != NULL; ban++) {
- if (strchr(*ban, '!') != NULL) {
+ if (**ban == '$' || strchr(*ban, '!') != NULL) {
/* explicit ban */
g_string_append_printf(str, "%s ", *ban);
continue;
@@ -184,11 +184,11 @@ static void command_set_ban(const char *data, IRC_SERVER_REC *server,
if (server == NULL || !server->connected || !IS_IRC_SERVER(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
- item, &channel, &nicks)) return;
- if (!ischannel(*channel)) cmd_param_error(CMDERR_NOT_JOINED);
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS, item, &channel, &nicks)) return;
+ if (!server_ischannel(SERVER(server), channel)) cmd_param_error(CMDERR_NOT_JOINED);
if (*nicks == '\0') {
- if (strcmp(data, "*") != 0)
+ if (g_strcmp0(data, "*") != 0)
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
/* /BAN * or /UNBAN * - ban/unban everyone */
nicks = (char *) data;
@@ -262,8 +262,8 @@ static void cmd_ban(const char *data, IRC_SERVER_REC *server, void *item)
CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 1 |
- PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"ban", &optlist, &ban))
return;
@@ -297,8 +297,8 @@ static void cmd_unban(const char *data, IRC_SERVER_REC *server, void *item)
CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 1 |
- PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"unban", &optlist, &ban))
return;
@@ -318,7 +318,7 @@ static void cmd_unban(const char *data, IRC_SERVER_REC *server, void *item)
static void read_settings(void)
{
if (default_ban_type_str != NULL &&
- strcmp(default_ban_type_str, settings_get_str("ban_type")) == 0)
+ g_strcmp0(default_ban_type_str, settings_get_str("ban_type")) == 0)
return;
g_free_not_null(default_ban_type_str);
diff --git a/src/irc/core/channel-events.c b/src/irc/core/channel-events.c
index 6fdfeef3..46bbd5fa 100644
--- a/src/irc/core/channel-events.c
+++ b/src/irc/core/channel-events.c
@@ -37,7 +37,7 @@ static void check_join_failure(IRC_SERVER_REC *server, const char *channel)
channel++; /* server didn't understand !channels */
chanrec = channel_find(SERVER(server), channel);
- if (chanrec == NULL && channel[0] == '!') {
+ if (chanrec == NULL && channel[0] == '!' && strlen(channel) > 6) {
/* it probably replied with the full !channel name,
find the channel with the short name.. */
chan2 = g_strdup_printf("!%s", channel+6);
@@ -138,7 +138,13 @@ static void channel_change_topic(IRC_SERVER_REC *server, const char *channel,
g_free_not_null(chanrec->topic_by);
chanrec->topic_by = g_strdup(setby);
- chanrec->topic_time = settime;
+ if (chanrec->topic_by == NULL) {
+ /* ensure invariant topic_time > 0 <=> topic_by != NULL.
+ this could be triggered by a topic command without sender */
+ chanrec->topic_time = 0;
+ } else {
+ chanrec->topic_time = settime;
+ }
signal_emit("channel topic changed", 1, chanrec);
}
@@ -270,7 +276,7 @@ static void event_join(IRC_SERVER_REC *server, const char *data, const char *nic
}
chanrec->joined = TRUE;
- if (strcmp(chanrec->name, channel) != 0) {
+ if (g_strcmp0(chanrec->name, channel) != 0) {
g_free(chanrec->name);
chanrec->name = g_strdup(channel);
}
diff --git a/src/irc/core/channel-rejoin.c b/src/irc/core/channel-rejoin.c
index 68a1dee1..154035ae 100644
--- a/src/irc/core/channel-rejoin.c
+++ b/src/irc/core/channel-rejoin.c
@@ -149,7 +149,7 @@ static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
- if (ischannel(*channel)) {
+ if (server_ischannel(SERVER(server), channel)) {
chanrec = irc_channel_find(server, channel);
if (chanrec != NULL && chanrec->joined) {
/* dalnet event - can't change nick while
diff --git a/src/irc/core/channels-query.c b/src/irc/core/channels-query.c
index 857ebaf0..d7dadf04 100644
--- a/src/irc/core/channels-query.c
+++ b/src/irc/core/channels-query.c
@@ -119,21 +119,22 @@ static void query_remove_all(IRC_CHANNEL_REC *channel)
int n;
rec = channel->server->chanqueries;
+ if (rec == NULL) return;
/* remove channel from query lists */
for (n = 0; n < CHANNEL_QUERIES; n++)
rec->queries[n] = g_slist_remove(rec->queries[n], channel);
rec->current_queries = g_slist_remove(rec->current_queries, channel);
- query_check(channel->server);
+ if (!channel->server->disconnected)
+ query_check(channel->server);
}
static void sig_channel_destroyed(IRC_CHANNEL_REC *channel)
{
g_return_if_fail(channel != NULL);
- if (IS_IRC_CHANNEL(channel) && !channel->server->disconnected &&
- !channel->synced)
+ if (IS_IRC_CHANNEL(channel))
query_remove_all(channel);
}
diff --git a/src/irc/core/irc-cap.c b/src/irc/core/irc-cap.c
new file mode 100644
index 00000000..5464e493
--- /dev/null
+++ b/src/irc/core/irc-cap.c
@@ -0,0 +1,192 @@
+/* irc-cap.c : irssi
+
+ Copyright (C) 2015 The Lemon Man
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+
+#include "irc-cap.h"
+#include "irc-servers.h"
+
+int cap_toggle (IRC_SERVER_REC *server, char *cap, int enable)
+{
+ if (cap == NULL || *cap == '\0')
+ return FALSE;
+
+ /* If the negotiation hasn't been completed yet just queue the requests */
+ if (!server->cap_complete) {
+ if (enable && !gslist_find_string(server->cap_queue, cap)) {
+ server->cap_queue = g_slist_prepend(server->cap_queue, g_strdup(cap));
+ return TRUE;
+ }
+ else if (!enable && gslist_find_string(server->cap_queue, cap)) {
+ server->cap_queue = gslist_remove_string(server->cap_queue, cap);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ if (enable && !gslist_find_string(server->cap_active, cap)) {
+ /* Make sure the required cap is supported by the server */
+ if (!gslist_find_string(server->cap_supported, cap))
+ return FALSE;
+
+ irc_send_cmdv(server, "CAP REQ %s", cap);
+ return TRUE;
+ }
+ else if (!enable && gslist_find_string(server->cap_active, cap)) {
+ irc_send_cmdv(server, "CAP REQ -%s", cap);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void cap_finish_negotiation (IRC_SERVER_REC *server)
+{
+ if (server->cap_complete)
+ return;
+
+ server->cap_complete = TRUE;
+ irc_send_cmd_now(server, "CAP END");
+
+ signal_emit("server cap end", 1, server);
+}
+
+static void cap_emit_signal (IRC_SERVER_REC *server, char *cmd, char *args)
+{
+ char *signal_name;
+
+ signal_name = g_strdup_printf("server cap %s %s", cmd, args? args: "");
+ signal_emit(signal_name, 1, server);
+ g_free(signal_name);
+}
+
+static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *address)
+{
+ GSList *tmp;
+ GString *cmd;
+ char *params, *evt, *list, **caps;
+ int i, caps_length, disable, avail_caps;
+
+ params = event_get_params(args, 3, NULL, &evt, &list);
+ if (params == NULL)
+ return;
+
+ /* Strip the trailing whitespaces before splitting the string, some servers send responses with
+ * superfluous whitespaces that g_strsplit the interprets as tokens */
+ caps = g_strsplit(g_strchomp(list), " ", -1);
+ caps_length = g_strv_length(caps);
+
+ if (!g_strcmp0(evt, "LS")) {
+ /* Create a list of the supported caps */
+ for (i = 0; i < caps_length; i++)
+ server->cap_supported = g_slist_prepend(server->cap_supported, g_strdup(caps[i]));
+
+ /* Request the required caps, if any */
+ if (server->cap_queue == NULL) {
+ cap_finish_negotiation(server);
+ }
+ else {
+ cmd = g_string_new("CAP REQ :");
+
+ avail_caps = 0;
+
+ /* Check whether the cap is supported by the server */
+ for (tmp = server->cap_queue; tmp != NULL; tmp = tmp->next) {
+ if (gslist_find_string(server->cap_supported, tmp->data)) {
+ if (avail_caps > 0)
+ g_string_append_c(cmd, ' ');
+ g_string_append(cmd, tmp->data);
+
+ avail_caps++;
+ }
+ }
+
+ /* Clear the queue here */
+ gslist_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)
+ irc_send_cmd_now(server, cmd->str);
+ else
+ cap_finish_negotiation(server);
+
+ g_string_free(cmd, TRUE);
+ }
+ }
+ else if (!g_strcmp0(evt, "ACK")) {
+ int got_sasl = FALSE;
+
+ /* Emit a signal for every ack'd cap */
+ for (i = 0; i < caps_length; i++) {
+ disable = (*caps[i] == '-');
+
+ if (disable)
+ server->cap_active = gslist_remove_string(server->cap_active, caps[i] + 1);
+ else
+ server->cap_active = g_slist_prepend(server->cap_active, g_strdup(caps[i]));
+
+ if (!g_strcmp0(caps[i], "sasl"))
+ got_sasl = TRUE;
+
+ cap_emit_signal(server, "ack", caps[i]);
+ }
+
+ /* Hopefully the server has ack'd all the caps requested and we're ready to terminate the
+ * negotiation, unless sasl was requested. In this case we must not terminate the negotiation
+ * until the sasl handshake is over. */
+ if (got_sasl == FALSE)
+ cap_finish_negotiation(server);
+ }
+ else if (!g_strcmp0(evt, "NAK")) {
+ g_warning("The server answered with a NAK to our CAP request, this should not happen");
+
+ /* A NAK'd request means that a required cap can't be enabled or disabled, don't update the
+ * list of active caps and notify the listeners. */
+ for (i = 0; i < caps_length; i++)
+ cap_emit_signal(server, "nak", caps[i]);
+ }
+
+ g_strfreev(caps);
+ g_free(params);
+}
+
+static void event_invalid_cap (IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ /* The server didn't understand one (or more) requested caps, terminate the negotiation.
+ * This could be handled in a graceful way but since it shouldn't really ever happen this seems a
+ * good way to deal with 410 errors. */
+ server->cap_complete = FALSE;
+ irc_send_cmd_now(server, "CAP END");
+}
+
+void cap_init (void)
+{
+ signal_add_first("event cap", (SIGNAL_FUNC) event_cap);
+ signal_add_first("event 410", (SIGNAL_FUNC) event_invalid_cap);
+}
+
+void cap_deinit (void)
+{
+ signal_remove("event cap", (SIGNAL_FUNC) event_cap);
+ signal_remove("event 410", (SIGNAL_FUNC) event_invalid_cap);
+}
diff --git a/src/irc/core/irc-cap.h b/src/irc/core/irc-cap.h
new file mode 100644
index 00000000..df957cd2
--- /dev/null
+++ b/src/irc/core/irc-cap.h
@@ -0,0 +1,9 @@
+#ifndef __IRC_CAP_H
+#define __IRC_CAP_H
+
+void cap_init(void);
+void cap_deinit(void);
+int cap_toggle (IRC_SERVER_REC *server, char *cap, int enable);
+void cap_finish_negotiation (IRC_SERVER_REC *server);
+
+#endif
diff --git a/src/irc/core/irc-channels-setup.c b/src/irc/core/irc-channels-setup.c
index 2320352d..bbbc095c 100644
--- a/src/irc/core/irc-channels-setup.c
+++ b/src/irc/core/irc-channels-setup.c
@@ -24,10 +24,12 @@
void irc_channels_setup_init(void)
{
- signal_add("channel wholist", (SIGNAL_FUNC) channel_send_autocommands);
+ signal_add("channel wholist", (SIGNAL_FUNC) channel_send_botcommands);
+ signal_add("channel joined", (SIGNAL_FUNC) channel_send_autocommands);
}
void irc_channels_setup_deinit(void)
{
- signal_remove("channel wholist", (SIGNAL_FUNC) channel_send_autocommands);
+ signal_remove("channel wholist", (SIGNAL_FUNC) channel_send_botcommands);
+ signal_remove("channel joined", (SIGNAL_FUNC) channel_send_autocommands);
}
diff --git a/src/irc/core/irc-channels.c b/src/irc/core/irc-channels.c
index e38cb98b..e775f530 100644
--- a/src/irc/core/irc-channels.c
+++ b/src/irc/core/irc-channels.c
@@ -99,7 +99,7 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
tmp = chanlist;
for (;; tmp++) {
if (*tmp != NULL) {
- channel = ischannel(**tmp) ? g_strdup(*tmp) :
+ channel = server_ischannel(SERVER(server), *tmp) ? g_strdup(*tmp) :
g_strdup_printf("#%s", *tmp);
chanrec = irc_channel_find(server, channel);
@@ -134,7 +134,7 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
if (use_keys)
cmdlen += outkeys->len;
if (*tmpstr != NULL)
- cmdlen += ischannel(**tmpstr) ? strlen(*tmpstr) :
+ cmdlen += server_ischannel(SERVER(server), *tmpstr) ? strlen(*tmpstr) :
strlen(*tmpstr)+1;
if (*tmpkey != NULL)
cmdlen += strlen(*tmpkey);
@@ -146,11 +146,13 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
continue;
}
if (outchans->len > 0) {
- g_string_truncate(outchans, outchans->len-1);
- g_string_truncate(outkeys, outkeys->len-1);
- irc_send_cmdv(IRC_SERVER(server),
- use_keys ? "JOIN %s %s" : "JOIN %s",
- outchans->str, outkeys->str);
+ g_string_truncate(outchans, outchans->len - 1);
+ g_string_truncate(outkeys, outkeys->len - 1);
+
+ if (use_keys)
+ irc_send_cmdv(IRC_SERVER(server), "JOIN %s %s", outchans->str, outkeys->str);
+ else
+ irc_send_cmdv(IRC_SERVER(server), "JOIN %s", outchans->str);
}
cmdlen = 0;
g_string_truncate(outchans,0);
@@ -172,6 +174,13 @@ static CHANNEL_REC *irc_channel_find_server(SERVER_REC *server,
const char *channel)
{
GSList *tmp;
+ char *fmt_channel;
+
+ /* if 'channel' has no leading # this lookup is going to fail, add a
+ * octothorpe in front of it to handle this case. */
+ fmt_channel = server_ischannel(SERVER(server), channel) ?
+ g_strdup(channel) :
+ g_strdup_printf("#%s", channel);
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *rec = tmp->data;
@@ -180,13 +189,19 @@ static CHANNEL_REC *irc_channel_find_server(SERVER_REC *server,
continue;
/* check both !ABCDEchannel and !channel */
- if (IRC_SERVER(server)->nick_comp_func(channel, rec->name) == 0)
+ if (IRC_SERVER(server)->nick_comp_func(fmt_channel, rec->name) == 0) {
+ g_free(fmt_channel);
return rec;
+ }
- if (IRC_SERVER(server)->nick_comp_func(channel, rec->visible_name) == 0)
+ if (IRC_SERVER(server)->nick_comp_func(fmt_channel, rec->visible_name) == 0) {
+ g_free(fmt_channel);
return rec;
+ }
}
+ g_free(fmt_channel);
+
return NULL;
}
diff --git a/src/irc/core/irc-chatnets.c b/src/irc/core/irc-chatnets.c
index d757bf8d..98ce89c3 100644
--- a/src/irc/core/irc-chatnets.c
+++ b/src/irc/core/irc-chatnets.c
@@ -35,10 +35,16 @@ void ircnet_create(IRC_CHATNET_REC *rec)
static void sig_chatnet_read(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
{
+ char *value;
+
if (!IS_IRC_CHATNET(rec))
return;
- rec->usermode = g_strdup(config_node_get_str(node, "usermode", NULL));
+ value = config_node_get_str(node, "usermode", NULL);
+ rec->usermode = (value != NULL && *value != '\0') ? g_strdup(value) : NULL;
+
+ value = config_node_get_str(node, "alternate_nick", NULL);
+ rec->alternate_nick = (value != NULL && *value != '\0') ? g_strdup(value) : NULL;
rec->max_cmds_at_once = config_node_get_int(node, "cmdmax", 0);
rec->cmd_queue_speed = config_node_get_int(node, "cmdspeed", 0);
@@ -48,6 +54,10 @@ static void sig_chatnet_read(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
rec->max_msgs = config_node_get_int(node, "max_msgs", 0);
rec->max_modes = config_node_get_int(node, "max_modes", 0);
rec->max_whois = config_node_get_int(node, "max_whois", 0);
+
+ rec->sasl_mechanism = g_strdup(config_node_get_str(node, "sasl_mechanism", NULL));
+ rec->sasl_username = g_strdup(config_node_get_str(node, "sasl_username", NULL));
+ rec->sasl_password = g_strdup(config_node_get_str(node, "sasl_password", NULL));
}
static void sig_chatnet_saved(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
@@ -56,7 +66,10 @@ static void sig_chatnet_saved(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
return;
if (rec->usermode != NULL)
- iconfig_node_set_str(node, "usermode", rec->usermode);
+ iconfig_node_set_str(node, "usermode", rec->usermode);
+
+ if (rec->alternate_nick != NULL)
+ iconfig_node_set_str(node, "alternate_nick", rec->alternate_nick);
if (rec->max_cmds_at_once > 0)
iconfig_node_set_int(node, "cmdmax", rec->max_cmds_at_once);
@@ -73,12 +86,24 @@ static void sig_chatnet_saved(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
iconfig_node_set_int(node, "max_modes", rec->max_modes);
if (rec->max_whois > 0)
iconfig_node_set_int(node, "max_whois", rec->max_whois);
+
+ if (rec->sasl_mechanism != NULL)
+ iconfig_node_set_str(node, "sasl_mechanism", rec->sasl_mechanism);
+ if (rec->sasl_username != NULL)
+ iconfig_node_set_str(node, "sasl_username", rec->sasl_username);
+ if (rec->sasl_password != NULL)
+ iconfig_node_set_str(node, "sasl_password", rec->sasl_password);
}
static void sig_chatnet_destroyed(IRC_CHATNET_REC *rec)
{
- if (IS_IRC_CHATNET(rec))
- g_free(rec->usermode);
+ if (IS_IRC_CHATNET(rec)) {
+ g_free(rec->usermode);
+ g_free(rec->alternate_nick);
+ g_free(rec->sasl_mechanism);
+ g_free(rec->sasl_username);
+ g_free(rec->sasl_password);
+ }
}
diff --git a/src/irc/core/irc-chatnets.h b/src/irc/core/irc-chatnets.h
index 22da90c5..3fd44472 100644
--- a/src/irc/core/irc-chatnets.h
+++ b/src/irc/core/irc-chatnets.h
@@ -17,12 +17,16 @@
struct _IRC_CHATNET_REC {
#include "chatnet-rec.h"
- char *usermode;
+ char *usermode;
+ char *alternate_nick;
+
+ char *sasl_mechanism;
+ char *sasl_username;
+ char *sasl_password;
int max_cmds_at_once;
int cmd_queue_speed;
- int max_query_chans; /* when syncing, max. number of channels to
- put in one MODE/WHO command */
+ int max_query_chans; /* when syncing, max. number of channels to put in one MODE/WHO command */
/* max. number of kicks/msgs/mode/whois per command */
int max_kicks, max_msgs, max_modes, max_whois;
diff --git a/src/irc/core/irc-commands.c b/src/irc/core/irc-commands.c
index 7c3d3f5f..32ee8845 100644
--- a/src/irc/core/irc-commands.c
+++ b/src/irc/core/irc-commands.c
@@ -61,18 +61,18 @@ static int knockout_tag;
/* SYNTAX: NOTICE <targets> <message> */
static void cmd_notice(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
const char *target, *msg;
char *recoded;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
- &target, &msg))
+ &target, &msg))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? NULL : window_item_get_target(item);
if (target == NULL || *target == '\0' || *msg == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -88,18 +88,18 @@ static void cmd_notice(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: CTCP <targets> <ctcp command> [<ctcp data>] */
static void cmd_ctcp(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
const char *target;
char *ctcpcmd, *ctcpdata;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
- &target, &ctcpcmd, &ctcpdata))
+ &target, &ctcpcmd, &ctcpdata))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? NULL : window_item_get_target(item);
if (target == NULL || *target == '\0' || *ctcpcmd == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -122,18 +122,18 @@ static void cmd_ctcp(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: NCTCP <targets> <ctcp command> [<ctcp data>] */
static void cmd_nctcp(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
const char *target;
char *ctcpcmd, *ctcpdata, *recoded;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
- &target, &ctcpcmd, &ctcpdata))
+ &target, &ctcpcmd, &ctcpdata))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? NULL : window_item_get_target(item);
if (target == NULL || *target == '\0' || *ctcpcmd == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -150,28 +150,31 @@ static void cmd_nctcp(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: PART [<channels>] [<message>] */
static void cmd_part(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
char *channame, *msg;
char *recoded = NULL;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
- PARAM_FLAG_OPTCHAN, item, &channame, &msg))
+ PARAM_FLAG_OPTCHAN, item, &channame, &msg))
return;
if (*channame == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (*msg == '\0') msg = (char *) settings_get_str("part_message");
- if (server->cmdcount > MAX_COMMANDS_ON_PART_UNTIL_PURGE)
+ if (server->cmdcount > MAX_COMMANDS_ON_PART_UNTIL_PURGE)
irc_server_purge_output(server, channame);
if (*msg != '\0')
recoded = recode_out(SERVER(server), msg, channame);
- irc_send_cmdv(server, ! recoded ? "PART %s" : "PART %s :%s",
- channame, recoded);
+
+ if (recoded == NULL)
+ irc_send_cmdv(server, "PART %s", channame);
+ else
+ irc_send_cmdv(server, "PART %s :%s", channame, recoded);
g_free(recoded);
cmd_params_free(free_arg);
@@ -183,15 +186,15 @@ static void cmd_kick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
char *channame, *nicks, *reason, *recoded;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST |
- PARAM_FLAG_OPTCHAN, item,
- &channame, &nicks, &reason))
+ PARAM_FLAG_OPTCHAN, item,
+ &channame, &nicks, &reason))
return;
if (*channame == '\0' || *nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
- if (!ischannel(*channame)) cmd_param_error(CMDERR_NOT_JOINED);
+ if (!server_ischannel(SERVER(server), channame)) cmd_param_error(CMDERR_NOT_JOINED);
recoded = recode_out(SERVER(server), reason, channame);
g_string_printf(tmpstr, "KICK %s %s :%s", channame, nicks, recoded);
@@ -210,17 +213,21 @@ static void cmd_topic(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *ite
char *recoded = NULL;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN |
- PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
- item, "topic", &optlist, &channame, &topic))
+ PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ item, "topic", &optlist, &channame, &topic))
return;
if (*topic != '\0' || g_hash_table_lookup(optlist, "delete") != NULL)
recoded = recode_out(SERVER(server), topic, channame);
- irc_send_cmdv(server, recoded == NULL ? "TOPIC %s" : "TOPIC %s :%s",
- channame, recoded);
+
+ if (recoded == NULL)
+ irc_send_cmdv(server, "TOPIC %s", channame);
+ else
+ irc_send_cmdv(server, "TOPIC %s :%s", channame, recoded);
+
g_free(recoded);
cmd_params_free(free_arg);
@@ -232,13 +239,13 @@ static void cmd_invite(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *it
char *nick, *channame;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2, &nick, &channame))
return;
if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
- if (*channame == '\0' || strcmp(channame, "*") == 0) {
+ if (*channame == '\0' || g_strcmp0(channame, "*") == 0) {
if (!IS_IRC_CHANNEL(item))
cmd_param_error(CMDERR_NOT_JOINED);
@@ -251,16 +258,17 @@ static void cmd_invite(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *it
/* SYNTAX: LIST [-yes] [<channel>] */
static void cmd_list(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
GHashTable *optlist;
char *str;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "list", &optlist, &str))
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ "list", &optlist, &str))
return;
if (*str == '\0' && g_hash_table_lookup(optlist, "yes") == NULL &&
@@ -274,55 +282,60 @@ static void cmd_list(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: WHO [<nicks> | <channels> | **] */
static void cmd_who(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
char *channel, *rest;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &channel, &rest))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS, &channel, &rest))
return;
- if (strcmp(channel, "*") == 0 || *channel == '\0') {
+ if (g_strcmp0(channel, "*") == 0 || *channel == '\0') {
if (!IS_IRC_CHANNEL(item))
- cmd_param_error(CMDERR_NOT_JOINED);
+ cmd_param_error(CMDERR_NOT_JOINED);
channel = IRC_CHANNEL(item)->name;
}
- if (strcmp(channel, "**") == 0) {
+ if (g_strcmp0(channel, "**") == 0) {
/* ** displays all nicks.. */
*channel = '\0';
}
- irc_send_cmdv(server, *rest == '\0' ? "WHO %s" : "WHO %s %s",
- channel, rest);
+ if (rest[0] == '\0')
+ irc_send_cmdv(server, "WHO %s", channel);
+ else
+ irc_send_cmdv(server, "WHO %s %s", channel, rest);
+
cmd_params_free(free_arg);
}
static void cmd_names(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
- GHashTable *optlist;
+ GHashTable *optlist;
char *channel;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "names", &optlist, &channel))
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ "names", &optlist, &channel))
return;
- if (strcmp(channel, "*") == 0 || *channel == '\0') {
+ if (g_strcmp0(channel, "*") == 0 || *channel == '\0') {
if (!IS_IRC_CHANNEL(item))
- cmd_param_error(CMDERR_NOT_JOINED);
+ cmd_param_error(CMDERR_NOT_JOINED);
channel = IRC_CHANNEL(item)->name;
}
- if (strcmp(channel, "**") == 0) {
+ if (g_strcmp0(channel, "**") == 0) {
/* ** displays all nicks.. */
- irc_send_cmd(server, "NAMES");
+ irc_send_cmd(server, "NAMES");
} else {
irc_send_cmdv(server, "NAMES %s", channel);
}
@@ -333,12 +346,12 @@ static void cmd_names(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: NICK <new nick> */
static void cmd_nick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
{
- char *nick;
+ char *nick;
void *free_arg;
g_return_if_fail(data != NULL);
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1, &nick))
return;
@@ -375,23 +388,23 @@ static char *get_redirect_nicklist(const char *nicks, int *free)
/* SYNTAX: WHOIS [-<server tag>] [<server>] [<nicks>] */
static void cmd_whois(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
GHashTable *optlist;
char *qserver, *query, *event_402, *str;
void *free_arg;
int free_nick;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_UNKNOWN_OPTIONS,
- "whois", &optlist, &qserver, &query))
+ PARAM_FLAG_UNKNOWN_OPTIONS,
+ "whois", &optlist, &qserver, &query))
return;
/* -<server tag> */
server = IRC_SERVER(cmd_options_get_server("whois", optlist,
- SERVER(server)));
+ SERVER(server)));
if (server == NULL) {
cmd_params_free(free_arg);
return;
@@ -409,7 +422,7 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server,
query = qserver = queryitem->name;
}
- if (strcmp(query, "*") == 0 &&
+ if (g_strcmp0(query, "*") == 0 &&
g_hash_table_lookup(optlist, "yes") == NULL)
cmd_param_error(CMDERR_NOT_GOOD_IDEA);
@@ -426,15 +439,15 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server,
str = g_strconcat(qserver, " ", query, NULL);
server_redirect_event(server, "whois", 1, str, TRUE,
- NULL,
- "event 318", "whois end",
- "event 402", event_402,
- "event 301", "whois away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
- "event 313", "whois oper",
- "event 401", (settings_get_bool("auto_whowas") ? "whois try whowas" : "whois event not found"),
- "event 311", "whois event",
- "", "whois default event", NULL);
- g_free(str);
+ NULL,
+ "event 318", "whois end",
+ "event 402", event_402,
+ "event 301", "whois away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
+ "event 313", "whois oper",
+ "event 401", (settings_get_bool("auto_whowas") ? "whois try whowas" : "whois event not found"),
+ "event 311", "whois event",
+ "", "whois default event", NULL);
+ g_free(str);
server->whois_found = FALSE;
irc_send_cmd_split(server, tmpstr->str, 2, server->max_whois_in_cmd);
@@ -444,7 +457,7 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server,
}
static void event_whois(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr)
+ const char *nick, const char *addr)
{
server->whois_found = TRUE;
signal_emit("event 311", 4, server, data, nick, addr);
@@ -460,23 +473,23 @@ static void sig_whois_try_whowas(IRC_SERVER_REC *server, const char *data)
server->whowas_found = FALSE;
server_redirect_event(server, "whowas", 1, nick, -1, NULL,
- "event 314", "whowas event",
- "event 369", "whowas event end",
- "event 406", "event empty", NULL);
+ "event 314", "whowas event",
+ "event 369", "whowas event end",
+ "event 406", "event empty", NULL);
irc_send_cmdv(server, "WHOWAS %s 1", nick);
g_free(params);
}
static void event_end_of_whois(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr)
+ const char *nick, const char *addr)
{
signal_emit("event 318", 4, server, data, nick, addr);
server->whois_found = FALSE;
}
static void event_whowas(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr)
+ const char *nick, const char *addr)
{
server->whowas_found = TRUE;
signal_emit("event 314", 4, server, data, nick, addr);
@@ -489,21 +502,25 @@ static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
void *free_arg;
int free_nick;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &nicks, &rest))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ &nicks, &rest))
return;
if (*nicks == '\0') nicks = server->nick;
nicks_redir = get_redirect_nicklist(nicks, &free_nick);
server_redirect_event(server, "whowas", 1, nicks_redir, -1, NULL,
- "event 301", "whowas away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
- "event 314", "whowas event", NULL);
+ "event 301", "whowas away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
+ "event 314", "whowas event", NULL);
if (free_nick) g_free(nicks_redir);
server->whowas_found = FALSE;
- irc_send_cmdv(server, *rest == '\0' ? "WHOWAS %s" :
- "WHOWAS %s %s", nicks, rest);
+
+ if (rest[0] == '\0')
+ irc_send_cmdv(server, "WHOWAS %s", nicks);
+ else
+ irc_send_cmdv(server, "WHOWAS %s %s", nicks, rest);
cmd_params_free(free_arg);
}
@@ -512,9 +529,9 @@ static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
static void cmd_ping(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
{
GTimeVal tv;
- char *str;
+ char *str;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (*data == '\0') {
if (!IS_QUERY(item))
@@ -537,7 +554,7 @@ static void cmd_away(const char *data, IRC_SERVER_REC *server)
void *free_arg;
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "away", &optlist, &reason)) return;
+ PARAM_FLAG_GETREST, "away", &optlist, &reason)) return;
if (g_hash_table_lookup(optlist, "one") != NULL)
irc_server_send_away(server, reason);
@@ -550,7 +567,7 @@ static void cmd_away(const char *data, IRC_SERVER_REC *server)
/* SYNTAX: SCONNECT <new server> [[<port>] <existing server>] */
static void cmd_sconnect(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "CONNECT %s", data);
@@ -583,11 +600,11 @@ static void cmd_wait(const char *data, IRC_SERVER_REC *server)
void *free_arg;
int n;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
- NULL, &optlist, &msecs))
+ PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+ NULL, &optlist, &msecs))
return;
if (*msecs == '\0')
@@ -595,7 +612,7 @@ static void cmd_wait(const char *data, IRC_SERVER_REC *server)
/* -<server tag> */
server = IRC_SERVER(cmd_options_get_server(NULL, optlist,
- SERVER(server)));
+ SERVER(server)));
n = atoi(msecs);
if (server != NULL && n > 0) {
@@ -618,10 +635,10 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
IRC_CHANNEL_REC *chanrec;
GSList *tmp, *nicks;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN |
- PARAM_FLAG_GETREST, item, &channame, &msg))
+ PARAM_FLAG_GETREST, item, &channame, &msg))
return;
if (*msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -637,11 +654,11 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
/* Fall back to manually noticing each op */
nicks = NULL;
g_hash_table_foreach(chanrec->nicks,
- (GHFunc) cmd_wall_hash, &nicks);
+ (GHFunc) cmd_wall_hash, &nicks);
args = g_strconcat(chanrec->name, " ", recoded, NULL);
msg = parse_special_string(settings_get_str("wall_format"),
- SERVER(server), item, args, NULL, 0);
+ SERVER(server), item, args, NULL, 0);
g_free(args);
for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
@@ -649,7 +666,7 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
if (rec != chanrec->ownnick) {
irc_send_cmdv(server, "NOTICE %s :%s",
- rec->nick, msg);
+ rec->nick, msg);
}
}
@@ -663,17 +680,17 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
/* SYNTAX: KICKBAN [<channel>] <nicks> <reason> */
static void cmd_kickban(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
- IRC_CHANNEL_REC *chanrec;
+ IRC_CHANNEL_REC *chanrec;
char *channel, *nicks, *reason, *kickcmd, *bancmd, *recoded;
- char **nicklist, *spacenicks;
+ char **nicklist, *spacenicks;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
- item, &channel, &nicks, &reason))
+ item, &channel, &nicks, &reason))
return;
if (*channel == '\0' || *nicks == '\0')
@@ -684,7 +701,7 @@ static void cmd_kickban(const char *data, IRC_SERVER_REC *server,
cmd_param_error(CMDERR_CHAN_NOT_FOUND);
nicklist = g_strsplit(nicks, ",", -1);
- spacenicks = g_strjoinv(" ", nicklist);
+ spacenicks = g_strjoinv(" ", nicklist);
g_strfreev(nicklist);
recoded = recode_out(SERVER(server), reason, channel);
@@ -692,9 +709,9 @@ static void cmd_kickban(const char *data, IRC_SERVER_REC *server,
g_free(recoded);
bancmd = g_strdup_printf("%s %s", chanrec->name, spacenicks);
- g_free(spacenicks);
+ g_free(spacenicks);
- if (settings_get_bool("kick_first_on_kickban")) {
+ if (settings_get_bool("kick_first_on_kickban")) {
signal_emit("command kick", 3, kickcmd, server, chanrec);
signal_emit("command ban", 3, bancmd, server, chanrec);
} else {
@@ -725,14 +742,14 @@ static void knockout_timeout_server(IRC_SERVER_REC *server)
if (!IS_IRC_SERVER(server))
return;
- now = time(NULL);
+ now = time(NULL);
for (tmp = server->knockoutlist; tmp != NULL; tmp = next) {
KNOCKOUT_REC *rec = tmp->data;
next = tmp->next;
if (rec->unban_time <= now) {
/* timeout, unban. */
- ban_remove(rec->channel, rec->ban);
+ signal_emit("command unban", 3, rec->ban, server, rec->channel);
knockout_destroy(server, rec);
}
}
@@ -746,16 +763,16 @@ static int knockout_timeout(void)
/* SYNTAX: KNOCKOUT [<time>] <nicks> <reason> */
static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
- IRC_CHANNEL_REC *channel)
+ IRC_CHANNEL_REC *channel)
{
KNOCKOUT_REC *rec;
char *nicks, *reason, *timeoutstr, *kickcmd, *bancmd, *recoded;
- char **nicklist, *spacenicks, *banmasks;
+ char **nicklist, *spacenicks, *banmasks;
void *free_arg;
int timeleft;
GSList *ptr;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!IS_IRC_CHANNEL(channel))
cmd_return_error(CMDERR_NOT_JOINED);
@@ -763,14 +780,14 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
if (i_isdigit(*data)) {
/* first argument is the timeout */
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
- &timeoutstr, &nicks, &reason))
+ &timeoutstr, &nicks, &reason))
return;
if (!parse_time_interval(timeoutstr, &timeleft))
cmd_param_error(CMDERR_INVALID_TIME);
} else {
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
- &nicks, &reason))
+ &nicks, &reason))
return;
timeleft = settings_get_time("knockout_time");
}
@@ -778,7 +795,7 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
if (*nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
nicklist = g_strsplit(nicks, ",", -1);
- spacenicks = g_strjoinv(" ", nicklist);
+ spacenicks = g_strjoinv(" ", nicklist);
g_strfreev(nicklist);
banmasks = ban_get_masks(channel, spacenicks, 0);
@@ -791,7 +808,7 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
bancmd = *banmasks == '\0'? NULL :
g_strdup_printf("%s %s", channel->name, banmasks);
- if (settings_get_bool("kick_first_on_kickban")) {
+ if (settings_get_bool("kick_first_on_kickban")) {
signal_emit("command kick", 3, kickcmd, server, channel);
if (bancmd != NULL)
signal_emit("command ban", 3, bancmd, server, channel);
@@ -810,7 +827,7 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
for (ptr = server->knockoutlist; ptr != NULL; ptr = ptr->next) {
rec = ptr->data;
if (channel == rec->channel &&
- !strcmp(rec->ban, banmasks))
+ !g_strcmp0(rec->ban, banmasks))
break;
}
if (ptr == NULL) {
@@ -828,10 +845,10 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: SERVER PURGE [<target>] */
static void cmd_server_purge(const char *data, IRC_SERVER_REC *server)
{
- char *target;
+ char *target;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1, &target))
return;
@@ -878,12 +895,12 @@ static void cmd_oper(const char *data, IRC_SERVER_REC *server)
char *nick, *password;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- /* asking for password is handled by fe-common */
+ /* asking for password is handled by fe-common */
if (!cmd_get_params(data, &free_arg, 2, &nick, &password))
return;
- if (*password == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+ if (*password == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "OPER %s %s", nick, password);
cmd_params_free(free_arg);
@@ -892,7 +909,7 @@ static void cmd_oper(const char *data, IRC_SERVER_REC *server)
/* SYNTAX: ACCEPT [[-]nick,...] */
static void cmd_accept(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (*data == '\0')
irc_send_cmd(server, "ACCEPT *");
@@ -903,7 +920,7 @@ static void cmd_accept(const char *data, IRC_SERVER_REC *server)
/* SYNTAX: UNSILENCE <nick!user@host> */
static void cmd_unsilence(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (*data == '\0')
cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -913,9 +930,12 @@ static void cmd_unsilence(const char *data, IRC_SERVER_REC *server)
static void command_self(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- irc_send_cmdv(server, *data == '\0' ? "%s" : "%s %s", current_command, data);
+ if (data[0] == '\0')
+ irc_send_cmdv(server, "%s", current_command);
+ else
+ irc_send_cmdv(server, "%s %s", current_command, data);
}
static void command_1self(const char *data, IRC_SERVER_REC *server)
@@ -933,7 +953,7 @@ static void command_2self(const char *data, IRC_SERVER_REC *server)
char *target, *text;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &text))
return;
@@ -978,8 +998,8 @@ void irc_commands_init(void)
command_bind_irc("admin", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: INFO [<server>] */
command_bind_irc("info", NULL, (SIGNAL_FUNC) command_self);
- /* SYNTAX: KNOCK <channel> */
- command_bind_irc("knock", NULL, (SIGNAL_FUNC) command_self);
+ /* SYNTAX: KNOCK <channel> */
+ command_bind_irc("knock", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: LINKS [[<server>] <mask>] */
command_bind_irc("links", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: LUSERS [<server mask> [<remote server>]] */
@@ -998,11 +1018,15 @@ void irc_commands_init(void)
command_bind_irc("trace", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: VERSION [<server>|<nick>] */
command_bind_irc("version", NULL, (SIGNAL_FUNC) command_self);
+ /* SYNTAX: SERVLIST [<mask> [<type>]] */
+ command_bind_irc("servlist", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: SILENCE [[+|-]<nick!user@host>]
SILENCE [<nick>] */
command_bind_irc("silence", NULL, (SIGNAL_FUNC) command_self);
command_bind_irc("unsilence", NULL, (SIGNAL_FUNC) cmd_unsilence);
command_bind_irc("sconnect", NULL, (SIGNAL_FUNC) cmd_sconnect);
+ /* SYNTAX: SQUERY <service> [<message>] */
+ command_bind_irc("squery", NULL, (SIGNAL_FUNC) command_2self);
/* SYNTAX: DIE */
command_bind_irc("die", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: HASH */
@@ -1061,7 +1085,7 @@ void irc_commands_deinit(void)
command_unbind("accept", (SIGNAL_FUNC) cmd_accept);
command_unbind("admin", (SIGNAL_FUNC) command_self);
command_unbind("info", (SIGNAL_FUNC) command_self);
- command_unbind("knock", (SIGNAL_FUNC) command_self);
+ command_unbind("knock", (SIGNAL_FUNC) command_self);
command_unbind("links", (SIGNAL_FUNC) command_self);
command_unbind("lusers", (SIGNAL_FUNC) command_self);
command_unbind("map", (SIGNAL_FUNC) command_self);
@@ -1071,9 +1095,11 @@ void irc_commands_deinit(void)
command_unbind("time", (SIGNAL_FUNC) command_self);
command_unbind("trace", (SIGNAL_FUNC) command_self);
command_unbind("version", (SIGNAL_FUNC) command_self);
+ command_unbind("servlist", (SIGNAL_FUNC) command_self);
command_unbind("silence", (SIGNAL_FUNC) command_self);
command_unbind("unsilence", (SIGNAL_FUNC) cmd_unsilence);
command_unbind("sconnect", (SIGNAL_FUNC) cmd_sconnect);
+ command_unbind("squery", (SIGNAL_FUNC) command_2self);
command_unbind("die", (SIGNAL_FUNC) command_self);
command_unbind("hash", (SIGNAL_FUNC) command_self);
command_unbind("oper", (SIGNAL_FUNC) cmd_oper);
diff --git a/src/irc/core/irc-core.c b/src/irc/core/irc-core.c
index bf7386ad..a9221e02 100644
--- a/src/irc/core/irc-core.c
+++ b/src/irc/core/irc-core.c
@@ -26,6 +26,8 @@
#include "irc-chatnets.h"
#include "irc-channels.h"
#include "irc-queries.h"
+#include "irc-cap.h"
+#include "sasl.h"
#include "irc-servers-setup.h"
#include "channels-setup.h"
@@ -117,6 +119,8 @@ void irc_core_init(void)
lag_init();
netsplit_init();
irc_expandos_init();
+ cap_init();
+ sasl_init();
settings_check();
module_register("core", "irc");
@@ -126,6 +130,8 @@ void irc_core_deinit(void)
{
signal_emit("chat protocol deinit", 1, chat_protocol_find("IRC"));
+ sasl_deinit();
+ cap_deinit();
irc_expandos_deinit();
netsplit_deinit();
lag_deinit();
@@ -137,7 +143,7 @@ void irc_core_deinit(void)
irc_irc_deinit();
irc_servers_deinit();
irc_chatnets_deinit();
- irc_session_deinit();
+ irc_session_deinit();
chat_protocol_unregister("IRC");
}
diff --git a/src/irc/core/irc-expandos.c b/src/irc/core/irc-expandos.c
index 5d2de503..62ef577a 100644
--- a/src/irc/core/irc-expandos.c
+++ b/src/irc/core/irc-expandos.c
@@ -27,6 +27,10 @@
#include "irc-channels.h"
#include "nicklist.h"
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
static char *last_join;
/* last person to join a channel you are on */
@@ -56,7 +60,7 @@ static char *expando_userhost(SERVER_REC *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver;
const char *username;
- char hostname[100];
+ char hostname[HOST_NAME_MAX + 1];
ircserver = IRC_SERVER(server);
@@ -72,10 +76,36 @@ static char *expando_userhost(SERVER_REC *server, void *item, int *free_ret)
username = ircserver->connrec->username;
if (gethostname(hostname, sizeof(hostname)) != 0 || *hostname == '\0')
- strcpy(hostname, "??");
+ strcpy(hostname, "(none)");
return g_strconcat(username, "@", hostname, NULL);;
}
+/* your hostname address (host) */
+static char *expando_hostname(SERVER_REC *server, void *item, int *free_ret)
+{
+ IRC_SERVER_REC *ircserver;
+ char hostname[HOST_NAME_MAX + 1];
+ char **list;
+ char *hostname_split;
+
+ ircserver = IRC_SERVER(server);
+
+ *free_ret = TRUE;
+
+ /* prefer the _real_ /userhost reply */
+ if (ircserver != NULL && ircserver->userhost != NULL) {
+ list = g_strsplit(ircserver->userhost, "@", -1);
+ hostname_split = g_strdup(list[1]);
+ g_strfreev(list);
+ return hostname_split;
+ }
+
+ /* haven't received userhost reply yet. guess something */
+ if (gethostname(hostname, sizeof(hostname)) != 0 || *hostname == '\0')
+ strcpy(hostname, "(none)");
+ return g_strdup(hostname);
+}
+
/* user mode in active server */
static char *expando_usermode(SERVER_REC *server, void *item, int *free_ret)
{
@@ -136,6 +166,9 @@ void irc_expandos_init(void)
expando_create("X", expando_userhost,
"window changed", EXPANDO_ARG_NONE,
"window server changed", EXPANDO_ARG_WINDOW, NULL);
+ expando_create("x", expando_hostname,
+ "window changed", EXPANDO_ARG_NONE,
+ "window server changed", EXPANDO_ARG_WINDOW, NULL);
expando_create("usermode", expando_usermode,
"window changed", EXPANDO_ARG_NONE,
"window server changed", EXPANDO_ARG_WINDOW,
@@ -164,6 +197,7 @@ void irc_expandos_deinit(void)
expando_destroy("H", expando_server_numeric);
expando_destroy("S", expando_servername);
expando_destroy("X", expando_userhost);
+ expando_destroy("x", expando_hostname);
expando_destroy("usermode", expando_usermode);
expando_destroy("cumode", expando_cumode);
diff --git a/src/irc/core/irc-nicklist.c b/src/irc/core/irc-nicklist.c
index 1f8c1fc9..3e16db80 100644
--- a/src/irc/core/irc-nicklist.c
+++ b/src/irc/core/irc-nicklist.c
@@ -48,37 +48,13 @@ NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick,
rec->send_massjoin = send_massjoin;
if (prefixes != NULL) {
- strocpy(rec->prefixes, prefixes, sizeof(rec->prefixes));
+ g_strlcpy(rec->prefixes, prefixes, sizeof(rec->prefixes));
}
nicklist_insert(CHANNEL(channel), rec);
return rec;
}
-#define isnickchar(a) \
- (i_isalnum(a) || (a) == '`' || (a) == '-' || (a) == '_' || \
- (a) == '[' || (a) == ']' || (a) == '{' || (a) == '}' || \
- (a) == '|' || (a) == '\\' || (a) == '^')
-
-/* Remove all "extra" characters from `nick'. Like _nick_ -> nick */
-char *irc_nick_strip(const char *nick)
-{
- char *stripped, *spos;
-
- g_return_val_if_fail(nick != NULL, NULL);
-
- spos = stripped = g_strdup(nick);
- while (isnickchar(*nick)) {
- if (i_isalnum(*nick))
- *spos++ = *nick;
- nick++;
- }
- if ((unsigned char) *nick >= 128)
- *spos++ = *nick; /* just add it so that nicks won't match.. */
- *spos = '\0';
- return stripped;
-}
-
int irc_nickcmp_rfc1459(const char *m, const char *n)
{
while (*m != '\0' && *n != '\0') {
@@ -338,7 +314,11 @@ static void event_whois_ircop(SERVER_REC *server, const char *data)
static void event_nick_invalid(IRC_SERVER_REC *server, const char *data)
{
if (!server->connected)
- server_disconnect((SERVER_REC *) server);
+ /* we used to call server_disconnect but that crashes
+ irssi because of undefined memory access. instead,
+ indicate that the connection should be dropped and
+ let the irc method to the clean-up. */
+ server->connection_lost = server->no_reconnect = TRUE;
}
static void event_nick_in_use(IRC_SERVER_REC *server, const char *data)
@@ -405,7 +385,7 @@ static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
- if (!ischannel(*channel)) {
+ if (!server_ischannel(SERVER(server), channel)) {
/* nick is unavailable. */
event_nick_in_use(server, data);
}
diff --git a/src/irc/core/irc-nicklist.h b/src/irc/core/irc-nicklist.h
index 7302556b..2ae17d2c 100644
--- a/src/irc/core/irc-nicklist.h
+++ b/src/irc/core/irc-nicklist.h
@@ -8,9 +8,6 @@ NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick,
int op, int halfop, int voice, int send_massjoin,
const char *prefixes);
-/* Remove all "extra" characters from `nick'. Like _nick_ -> nick */
-char *irc_nick_strip(const char *nick);
-
int irc_nickcmp_rfc1459(const char *, const char *);
int irc_nickcmp_ascii(const char *, const char *);
diff --git a/src/irc/core/irc-queries.c b/src/irc/core/irc-queries.c
index ac1a72a1..64995ead 100644
--- a/src/irc/core/irc-queries.c
+++ b/src/irc/core/irc-queries.c
@@ -45,6 +45,8 @@ QUERY_REC *irc_query_find(IRC_SERVER_REC *server, const char *nick)
{
GSList *tmp;
+ g_return_val_if_fail(nick != NULL, NULL);
+
for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
QUERY_REC *rec = tmp->data;
@@ -60,39 +62,25 @@ static void check_query_changes(IRC_SERVER_REC *server, const char *nick,
{
QUERY_REC *query;
- if (ischannel(*target))
- return;
+ if (server_ischannel(SERVER(server), target))
+ return;
query = irc_query_find(server, nick);
if (query == NULL)
return;
- if (strcmp(query->name, nick) != 0) {
+ if (g_strcmp0(query->name, nick) != 0) {
/* upper/lowercase chars in nick changed */
query_change_nick(query, nick);
}
if (address != NULL && (query->address == NULL ||
- strcmp(query->address, address) != 0)) {
+ g_strcmp0(query->address, address) != 0)) {
/* host changed */
query_change_address(query, address);
}
}
-static void event_privmsg(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *address)
-{
- char *params, *target, *msg;
-
- g_return_if_fail(data != NULL);
- if (nick == NULL)
- return;
-
- params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
- check_query_changes(server, nick, address, target);
- g_free(params);
-}
-
static void ctcp_action(IRC_SERVER_REC *server, const char *msg,
const char *nick, const char *address,
const char *target)
@@ -109,7 +97,7 @@ static void event_nick(SERVER_REC *server, const char *data,
query = query_find(server, orignick);
if (query != NULL) {
params = event_get_params(data, 1, &nick);
- if (strcmp(query->name, nick) != 0)
+ if (g_strcmp0(query->name, nick) != 0)
query_change_nick(query, nick);
g_free(params);
}
@@ -117,14 +105,12 @@ static void event_nick(SERVER_REC *server, const char *data,
void irc_queries_init(void)
{
- signal_add_last("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_add_last("ctcp action", (SIGNAL_FUNC) ctcp_action);
signal_add("event nick", (SIGNAL_FUNC) event_nick);
}
void irc_queries_deinit(void)
{
- signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_remove("ctcp action", (SIGNAL_FUNC) ctcp_action);
signal_remove("event nick", (SIGNAL_FUNC) event_nick);
}
diff --git a/src/irc/core/irc-servers-reconnect.c b/src/irc/core/irc-servers-reconnect.c
index b0aad26f..ca61492d 100644
--- a/src/irc/core/irc-servers-reconnect.c
+++ b/src/irc/core/irc-servers-reconnect.c
@@ -48,6 +48,9 @@ static void sig_server_connect_copy(SERVER_CONNECT_REC **dest,
rec->max_whois = src->max_whois;
rec->usermode = g_strdup(src->usermode);
rec->alternate_nick = g_strdup(src->alternate_nick);
+ rec->sasl_mechanism = src->sasl_mechanism;
+ rec->sasl_username = src->sasl_username;
+ rec->sasl_password = src->sasl_password;
*dest = (SERVER_CONNECT_REC *) rec;
}
diff --git a/src/irc/core/irc-servers-setup.c b/src/irc/core/irc-servers-setup.c
index 5659991b..e79557ab 100644
--- a/src/irc/core/irc-servers-setup.c
+++ b/src/irc/core/irc-servers-setup.c
@@ -28,6 +28,7 @@
#include "irc-chatnets.h"
#include "irc-servers-setup.h"
#include "irc-servers.h"
+#include "sasl.h"
/* Fill information to connection from server setup record */
static void sig_server_setup_fill_reconn(IRC_SERVER_CONNECT_REC *conn,
@@ -47,12 +48,18 @@ static void sig_server_setup_fill_reconn(IRC_SERVER_CONNECT_REC *conn,
static void sig_server_setup_fill_connect(IRC_SERVER_CONNECT_REC *conn)
{
+ const char *value;
+
if (!IS_IRC_SERVER_CONNECT(conn))
return;
- conn->alternate_nick = *settings_get_str("alternate_nick") != '\0' ?
- g_strdup(settings_get_str("alternate_nick")) : NULL;
- conn->usermode = g_strdup(settings_get_str("usermode"));
+ value = settings_get_str("alternate_nick");
+ conn->alternate_nick = (value != NULL && *value != '\0') ?
+ g_strdup(value) : NULL;
+
+ value = settings_get_str("usermode");
+ conn->usermode = (value != NULL && *value != '\0') ?
+ g_strdup(value) : NULL;
}
static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn,
@@ -62,7 +69,10 @@ static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn,
return;
g_return_if_fail(IS_IRCNET(ircnet));
- if (ircnet->nick != NULL) g_free_and_null(conn->alternate_nick);
+ if (ircnet->alternate_nick != NULL) {
+ g_free_and_null(conn->alternate_nick);
+ conn->alternate_nick = g_strdup(ircnet->alternate_nick);
+ }
if (ircnet->usermode != NULL) {
g_free_and_null(conn->usermode);
conn->usermode = g_strdup(ircnet->usermode);
@@ -79,18 +89,44 @@ static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn,
conn->cmd_queue_speed = ircnet->cmd_queue_speed;
if (ircnet->max_query_chans > 0)
conn->max_query_chans = ircnet->max_query_chans;
+
+ /* Validate the SASL parameters filled by sig_chatnet_read() or cmd_network_add */
+ conn->sasl_mechanism = SASL_MECHANISM_NONE;
+ conn->sasl_username = NULL;
+ conn->sasl_password = NULL;
+
+ if (ircnet->sasl_mechanism != NULL) {
+ if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "plain")) {
+ /* The PLAIN method needs both the username and the password */
+ if (ircnet->sasl_username != NULL && *ircnet->sasl_username &&
+ ircnet->sasl_password != NULL && *ircnet->sasl_password) {
+ conn->sasl_mechanism = SASL_MECHANISM_PLAIN;
+ conn->sasl_username = ircnet->sasl_username;
+ conn->sasl_password = ircnet->sasl_password;
+ } else
+ g_warning("The fields sasl_username and sasl_password are either missing or empty");
+ }
+ else if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "external")) {
+ conn->sasl_mechanism = SASL_MECHANISM_EXTERNAL;
+ }
+ else
+ g_warning("Unsupported SASL mechanism \"%s\" selected", ircnet->sasl_mechanism);
+ }
}
static void init_userinfo(void)
{
+ unsigned int changed;
const char *set, *nick, *user_name, *str;
+ changed = 0;
/* check if nick/username/realname wasn't read from setup.. */
set = settings_get_str("real_name");
if (set == NULL || *set == '\0') {
str = g_getenv("IRCNAME");
settings_set_str("real_name",
str != NULL ? str : g_get_real_name());
+ changed |= USER_SETTINGS_REAL_NAME;
}
/* username */
@@ -101,6 +137,7 @@ static void init_userinfo(void)
str != NULL ? str : g_get_user_name());
user_name = settings_get_str("user_name");
+ changed |= USER_SETTINGS_USER_NAME;
}
/* nick */
@@ -110,15 +147,20 @@ static void init_userinfo(void)
settings_set_str("nick", str != NULL ? str : user_name);
nick = settings_get_str("nick");
+ changed |= USER_SETTINGS_NICK;
}
/* host name */
set = settings_get_str("hostname");
if (set == NULL || *set == '\0') {
str = g_getenv("IRCHOST");
- if (str != NULL)
+ if (str != NULL) {
settings_set_str("hostname", str);
+ changed |= USER_SETTINGS_HOSTNAME;
+ }
}
+
+ signal_emit("irssi init userinfo changed", 1, GUINT_TO_POINTER(changed));
}
static void sig_server_setup_read(IRC_SERVER_SETUP_REC *rec, CONFIG_NODE *node)
diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c
index baf8a0d2..4eaab712 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -32,6 +32,10 @@
#include "irc-queries.h"
#include "irc-servers-setup.h"
#include "irc-servers.h"
+#include "irc-cap.h"
+#include "sasl.h"
+
+#include "channels-setup.h"
#include "channel-rejoin.h"
#include "servers-idle.h"
#include "servers-reconnect.h"
@@ -71,13 +75,28 @@ static int isnickflag_func(SERVER_REC *server, char flag)
static int ischannel_func(SERVER_REC *server, const char *data)
{
- if (*data == '@') {
- /* @#channel, @+#channel */
- data++;
- if (*data == '+' && ischannel(data[1]))
- return 1;
- }
- return ischannel(*data);
+ IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
+ char *chantypes, *statusmsg;
+
+ g_return_val_if_fail(data != NULL, FALSE);
+
+ /* empty string is no channel */
+ if (*data == '\0')
+ return FALSE;
+
+ chantypes = g_hash_table_lookup(irc_server->isupport, "chantypes");
+ if (chantypes == NULL)
+ chantypes = "#&!+"; /* normal, local, secure, modeless */
+
+ statusmsg = g_hash_table_lookup(irc_server->isupport, "statusmsg");
+ if (statusmsg == NULL)
+ statusmsg = "@";
+
+ data += strspn(data, statusmsg);
+
+ /* strchr(3) considers the trailing NUL as part of the string, make sure
+ * we didn't advance too much. */
+ return *data != '\0' && strchr(chantypes, *data) != NULL;
}
static char **split_line(const SERVER_REC *server, const char *line,
@@ -97,11 +116,14 @@ static char **split_line(const SERVER_REC *server, const char *line,
* the code much simpler. It's worth it.
*/
len -= strlen(recoded_start) + strlen(recoded_end);
+ g_warn_if_fail(len > 0);
if (len <= 0) {
/* There is no room for anything. */
g_free(recoded_start);
g_free(recoded_end);
- return NULL;
+ lines = g_new(char *, 1);
+ lines[0] = NULL;
+ return lines;
}
lines = recode_split(server, line, target, len, onspace);
@@ -195,7 +217,6 @@ static void server_init(IRC_SERVER_REC *server)
{
IRC_SERVER_CONNECT_REC *conn;
char *address, *ptr, *username, *cmd;
- GTimeVal now;
g_return_if_fail(server != NULL);
@@ -214,6 +235,13 @@ static void server_init(IRC_SERVER_REC *server)
g_free(cmd);
}
+ if (conn->sasl_mechanism != SASL_MECHANISM_NONE)
+ cap_toggle(server, "sasl", TRUE);
+
+ cap_toggle(server, "multi-prefix", TRUE);
+
+ irc_send_cmd_now(server, "CAP LS");
+
if (conn->password != NULL && *conn->password != '\0') {
/* send password */
cmd = g_strdup_printf("PASS %s", conn->password);
@@ -263,9 +291,8 @@ static void server_init(IRC_SERVER_REC *server)
/* prevent the queue from sending too early, we have a max cut off of 120 secs */
/* this will reset to 1 sec after we get the 001 event */
- g_get_current_time(&now);
- memcpy(&((IRC_SERVER_REC *)server)->wait_cmd, &now, sizeof(GTimeVal));
- ((IRC_SERVER_REC *)server)->wait_cmd.tv_sec += 120;
+ g_get_current_time(&server->wait_cmd);
+ g_time_val_add(&server->wait_cmd, 120 * G_USEC_PER_SEC);
}
SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
@@ -286,7 +313,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
if (server->connrec->port <= 0) {
server->connrec->port =
- server->connrec->use_ssl ? 6697 : 6667;
+ server->connrec->use_tls ? 6697 : 6667;
}
server->cmd_queue_speed = ircconn->cmd_queue_speed > 0 ?
@@ -304,7 +331,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
ircconn->max_whois : DEFAULT_MAX_WHOIS;
server->max_msgs_in_cmd = ircconn->max_msgs > 0 ?
ircconn->max_msgs : DEFAULT_MAX_MSGS;
- server->connrec->use_ssl = conn->use_ssl;
+ server->connrec->use_tls = conn->use_tls;
modes_server_init(server);
@@ -314,6 +341,8 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
void irc_server_connect(SERVER_REC *server)
{
+ g_return_if_fail(server != NULL);
+
if (!server_start_connect(server)) {
server_connect_unref(server->connrec);
g_free(server);
@@ -409,7 +438,18 @@ static void sig_disconnected(IRC_SERVER_REC *server)
server_redirect_destroy(tmp->next->data);
}
g_slist_free(server->cmdqueue);
- server->cmdqueue = NULL;
+ server->cmdqueue = NULL;
+
+ gslist_free_full(server->cap_active, (GDestroyNotify) g_free);
+ server->cap_active = NULL;
+
+ gslist_free_full(server->cap_supported, (GDestroyNotify) g_free);
+ server->cap_supported = NULL;
+
+ gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
+ server->cap_queue = NULL;
+
+ g_free_and_null(server->sasl_buffer);
/* these are dynamically allocated only if isupport was sent */
g_hash_table_foreach(server->isupport,
@@ -500,7 +540,7 @@ void irc_server_send_data(IRC_SERVER_REC *server, const char *data, int len)
server->wait_cmd.tv_sec = 0;
else {
memcpy(&server->wait_cmd, &server->last_cmd, sizeof(GTimeVal));
- server->wait_cmd.tv_sec += 2 + len/100;
+ g_time_val_add(&server->wait_cmd, (2 + len/100) * G_USEC_PER_SEC);
}
}
@@ -588,9 +628,16 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
GString *chans, *keys;
char *ret;
int use_keys;
+ int rejoin_channels_mode;
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? */
+ if(rejoin_channels_mode == 0)
+ return g_strdup("");
+
chans = g_string_new(NULL);
keys = g_string_new(NULL);
use_keys = FALSE;
@@ -598,22 +645,27 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
/* get currently joined channels */
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *channel = tmp->data;
-
- g_string_append_printf(chans, "%s,", channel->name);
- g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" :
- channel->key);
- if (channel->key != NULL)
- use_keys = TRUE;
+ 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) {
+ g_string_append_printf(chans, "%s,", channel->name);
+ g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" : channel->key);
+ if (channel->key != NULL)
+ use_keys = TRUE;
+ }
}
/* get also the channels that are in rejoin list */
for (tmp = server->rejoin_channels; tmp != NULL; tmp = tmp->next) {
REJOIN_REC *rec = tmp->data;
+ CHANNEL_SETUP_REC *setup = channel_setup_find(rec->channel, server->tag);
+
+ if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
+ g_string_append_printf(chans, "%s,", rec->channel);
+ g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
+ rec->key);
- g_string_append_printf(chans, "%s,", rec->channel);
- g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
- rec->key);
- if (rec->key != NULL) use_keys = TRUE;
+ if (rec->key != NULL) use_keys = TRUE;
+ }
}
if (chans->len > 0) {
@@ -632,13 +684,12 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
static void event_connected(IRC_SERVER_REC *server, const char *data, const char *from)
{
char *params, *nick;
- GTimeVal now;
g_return_if_fail(server != NULL);
params = event_get_params(data, 1, &nick);
- if (strcmp(server->nick, nick) != 0) {
+ if (g_strcmp0(server->nick, nick) != 0) {
/* nick changed unexpectedly .. connected via proxy, etc. */
g_free(server->nick);
server->nick = g_strdup(nick);
@@ -655,8 +706,7 @@ static void event_connected(IRC_SERVER_REC *server, const char *data, const char
server->real_connect_time = time(NULL);
/* let the queue send now that we are identified */
- g_get_current_time(&now);
- memcpy(&server->wait_cmd, &now, sizeof(GTimeVal));
+ g_get_current_time(&server->wait_cmd);
if (server->connrec->usermode != NULL) {
/* Send the user mode, before the autosendcmd.
@@ -970,6 +1020,7 @@ void irc_server_init_isupport(IRC_SERVER_REC *server)
void irc_servers_init(void)
{
+ settings_add_choice("servers", "rejoin_channels_on_reconnect", 1, "off;on;auto");
settings_add_str("misc", "usermode", DEFAULT_USER_MODE);
settings_add_str("misc", "split_line_start", "");
settings_add_str("misc", "split_line_end", "");
diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h
index 7e4eeabf..09f3f81d 100644
--- a/src/irc/core/irc-servers.h
+++ b/src/irc/core/irc-servers.h
@@ -27,6 +27,10 @@ struct _IRC_SERVER_CONNECT_REC {
char *usermode;
char *alternate_nick;
+ int sasl_mechanism;
+ char *sasl_username;
+ char *sasl_password;
+
int max_cmds_at_once;
int cmd_queue_speed;
int max_query_chans;
@@ -63,12 +67,21 @@ struct _IRC_SERVER_REC {
unsigned int nick_collision:1; /* We're just now being killed because of nick collision */
unsigned int motd_got:1; /* We've received MOTD */
unsigned int isupport_sent:1; /* Server has sent us an isupport reply */
+ unsigned int cap_complete:1; /* We've done the initial CAP negotiation */
+ unsigned int sasl_success:1; /* Did we authenticate successfully ? */
int max_kicks_in_cmd; /* max. number of people to kick with one /KICK command */
int max_modes_in_cmd; /* max. number of mode changes in one /MODE command */
int max_whois_in_cmd; /* max. number of nicks in one /WHOIS command */
int max_msgs_in_cmd; /* max. number of targets in one /MSG */
+ GSList *cap_supported; /* A list of caps supported by the server */
+ GSList *cap_active; /* A list of caps active for this session */
+ GSList *cap_queue; /* A list of caps to request on connection */
+
+ GString *sasl_buffer; /* Buffer used to reassemble a fragmented SASL payload */
+ guint sasl_timeout; /* Holds the source id of the running timeout */
+
/* Command sending queue */
int cmdcount; /* number of commands in `cmdqueue'. Can be more than
there actually is, to make flood control remember
diff --git a/src/irc/core/irc-session.c b/src/irc/core/irc-session.c
index 42d82734..18e8e5c7 100644
--- a/src/irc/core/irc-session.c
+++ b/src/irc/core/irc-session.c
@@ -28,6 +28,8 @@
#include "irc-channels.h"
#include "irc-nicklist.h"
+#include "sasl.h"
+
struct _isupport_data { CONFIG_REC *config; CONFIG_NODE *node; };
static void session_isupport_foreach(char *key, char *value, struct _isupport_data *data)
@@ -65,8 +67,12 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config,
config_node_set_str(config, node, "away_reason", server->away_reason);
config_node_set_bool(config, node, "emode_known", server->emode_known);
+ config_node_set_int(config, node, "sasl_mechanism", server->connrec->sasl_mechanism);
+ 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_bool(config, node, "isupport_sent", server->isupport_sent);
- isupport = config_node_section(node, "isupport", NODE_TYPE_BLOCK);
+ isupport = config_node_section(config, node, "isupport", NODE_TYPE_BLOCK);
isupport_data.config = config;
isupport_data.node = isupport;
@@ -90,12 +96,21 @@ static void sig_session_restore_server(IRC_SERVER_REC *server,
server->emode_known = config_node_get_bool(node, "emode_known", FALSE);
server->isupport_sent = config_node_get_bool(node, "isupport_sent", FALSE);
+ 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
+ * description from the config and we favor the content that's been saved
+ * in the session file over that. */
+ g_free(server->connrec->sasl_username);
+ server->connrec->sasl_username = g_strdup(config_node_get_str(node, "sasl_username", NULL));
+ g_free(server->connrec->sasl_password);
+ server->connrec->sasl_password = g_strdup(config_node_get_str(node, "sasl_password", NULL));
+
if (server->isupport == NULL) {
server->isupport = g_hash_table_new((GHashFunc) g_istr_hash,
(GCompareFunc) g_istr_equal);
}
- node = config_node_section(node, "isupport", -1);
+ node = config_node_section(NULL, node, "isupport", -1);
tmp = node == NULL ? NULL : config_node_first(node->value);
for (; tmp != NULL; tmp = config_node_next(tmp)) {
diff --git a/src/irc/core/irc.c b/src/irc/core/irc.c
index 509418b7..a740b0da 100644
--- a/src/irc/core/irc.c
+++ b/src/irc/core/irc.c
@@ -40,6 +40,8 @@ static int signal_server_incoming;
# define MAX_SOCKET_READS 5
#endif
+static void strip_params_colon(char *const);
+
/* The core of the irc_send_cmd* functions. If `raw' is TRUE, the `cmd'
won't be checked at all if it's 512 bytes or not, or if it contains
line feeds or not. Use with extreme caution! */
@@ -212,8 +214,12 @@ void irc_send_cmd_split(IRC_SERVER_REC *server, const char *cmd,
count = 0;
if (nickstr->len > 0)
g_string_truncate(nickstr, nickstr->len-1);
- irc_send_cmdv(server, post == NULL ? "%s %s" : "%s %s %s",
- pre, nickstr->str, post);
+
+ if (post == NULL)
+ irc_send_cmdv(server, "%s %s", pre, nickstr->str);
+ else
+ irc_send_cmdv(server, "%s %s %s", pre, nickstr->str, post);
+
g_string_truncate(nickstr, 0);
if (*tmp == NULL || tmp[1] == NULL)
@@ -265,8 +271,9 @@ char *event_get_params(const char *data, int count, ...)
while (count-- > 0) {
str = (char **) va_arg(args, char **);
if (count == 0 && rest) {
- /* put the rest to last parameter */
- tmp = *datad == ':' ? datad+1 : datad;
+ /* Put the rest into the last parameter. */
+ strip_params_colon(datad);
+ tmp = datad;
} else {
tmp = event_get_param(&datad);
}
@@ -277,6 +284,33 @@ char *event_get_params(const char *data, int count, ...)
return duprec;
}
+/* Given a string containing <params>, strip any colon prefixing <trailing>. */
+static void strip_params_colon(char *const params)
+{
+ char *s;
+
+ if (params == NULL) {
+ return;
+ }
+
+ s = params;
+ while (*s != '\0') {
+ if (*s == ':') {
+ memmove(s, s+1, strlen(s+1)+1);
+ return;
+ }
+
+ s = strchr(s, ' ');
+ if (s == NULL) {
+ return;
+ }
+
+ while (*s == ' ') {
+ s++;
+ }
+ }
+}
+
static void irc_server_event(IRC_SERVER_REC *server, const char *line,
const char *nick, const char *address)
{
diff --git a/src/irc/core/irc.h b/src/irc/core/irc.h
index de19e084..b5bd833a 100644
--- a/src/irc/core/irc.h
+++ b/src/irc/core/irc.h
@@ -22,12 +22,6 @@ typedef struct _REDIRECT_REC REDIRECT_REC;
#define isnickflag(server, a) \
(server->prefix[(int)(unsigned char) a] != '\0')
-#define ischannel(a) \
- ((a) == '#' || /* normal */ \
- (a) == '&' || /* local */ \
- (a) == '!' || /* secure */ \
- (a) == '+') /* modeless */
-
#define IS_IRC_ITEM(rec) (IS_IRC_CHANNEL(rec) || IS_IRC_QUERY(rec))
#define IRC_PROTOCOL (chat_protocol_lookup("IRC"))
diff --git a/src/irc/core/modes.c b/src/irc/core/modes.c
index ebaf4b8f..cc3d0faf 100644
--- a/src/irc/core/modes.c
+++ b/src/irc/core/modes.c
@@ -398,7 +398,7 @@ void parse_channel_modes(IRC_CHANNEL_REC *channel, const char *setby,
old_key = NULL;
}
- if (strcmp(newmode->str, channel->mode) != 0) {
+ if (g_strcmp0(newmode->str, channel->mode) != 0) {
g_free(channel->mode);
channel->mode = g_strdup(newmode->str);
@@ -488,7 +488,7 @@ static void event_mode(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
&channel, &mode);
- if (!ischannel(*channel)) {
+ if (!server_ischannel(SERVER(server), channel)) {
/* user mode change */
parse_user_mode(server, mode);
} else {
@@ -536,7 +536,7 @@ static void sig_req_usermode_change(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
&target, &mode);
- if (!ischannel(*target)) {
+ if (!server_ischannel(SERVER(server), target)) {
/* we requested a user mode change, save this */
mode = modes_join(NULL, server->wanted_usermode, mode, FALSE);
g_free_not_null(server->wanted_usermode);
@@ -743,6 +743,7 @@ static char *get_nicks(IRC_SERVER_REC *server, WI_ITEM_REC *item,
g_hash_table_lookup(optlist, "yes") == NULL) {
/* too many matches */
g_string_free(str, TRUE);
+ g_strfreev(matches);
cmd_params_free(free_arg);
signal_emit("error command", 1,
@@ -756,7 +757,7 @@ static char *get_nicks(IRC_SERVER_REC *server, WI_ITEM_REC *item,
if (str->len > 0) g_string_truncate(str, str->len-1);
ret = str->str;
g_string_free(str, FALSE);
-
+ g_strfreev(matches);
cmd_params_free(free_arg);
*ret_channel = channel;
@@ -835,14 +836,14 @@ static void cmd_mode(const char *data, IRC_SERVER_REC *server,
if (*data == '+' || *data == '-') {
target = "*";
- if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_GETREST, &mode))
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, &mode))
return;
} else {
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &mode))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, &target, &mode))
return;
}
- if (strcmp(target, "*") == 0) {
+ if (g_strcmp0(target, "*") == 0) {
if (!IS_IRC_CHANNEL(channel))
cmd_param_error(CMDERR_NOT_JOINED);
@@ -856,7 +857,7 @@ static void cmd_mode(const char *data, IRC_SERVER_REC *server,
target = chanrec->name;
irc_send_cmdv(server, "MODE %s", target);
- } else if (ischannel(*target))
+ } else if (server_ischannel(SERVER(server), target))
channel_set_mode(server, target, mode);
else {
if (g_ascii_strcasecmp(target, server->nick) == 0) {
diff --git a/src/irc/core/sasl.c b/src/irc/core/sasl.c
new file mode 100644
index 00000000..2b589579
--- /dev/null
+++ b/src/irc/core/sasl.c
@@ -0,0 +1,335 @@
+/*
+ fe-sasl.c : irssi
+
+ Copyright (C) 2015 The Lemon Man
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "module.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "irc-cap.h"
+#include "irc-servers.h"
+#include "sasl.h"
+
+/*
+ * Based on IRCv3 SASL Extension Specification:
+ * http://ircv3.net/specs/extensions/sasl-3.1.html
+ */
+#define AUTHENTICATE_CHUNK_SIZE 400 /* bytes */
+
+/*
+ * Maximum size to allow the buffer to grow to before the next fragment comes in. Note that
+ * due to the way fragmentation works, the maximum message size will actually be:
+ * floor(AUTHENTICATE_MAX_SIZE / AUTHENTICATE_CHUNK_SIZE) + AUTHENTICATE_CHUNK_SIZE - 1
+ */
+#define AUTHENTICATE_MAX_SIZE 8192 /* bytes */
+
+#define SASL_TIMEOUT (20 * 1000) /* ms */
+
+static gboolean sasl_timeout(IRC_SERVER_REC *server)
+{
+ /* The authentication timed out, we can't do much beside terminating it */
+ irc_send_cmd_now(server, "AUTHENTICATE *");
+ cap_finish_negotiation(server);
+
+ server->sasl_timeout = 0;
+ server->sasl_success = FALSE;
+
+ signal_emit("server sasl failure", 2, server, "The authentication timed out");
+
+ return FALSE;
+}
+
+static void sasl_start(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ IRC_SERVER_CONNECT_REC *conn;
+
+ conn = server->connrec;
+
+ switch (conn->sasl_mechanism) {
+ case SASL_MECHANISM_PLAIN:
+ irc_send_cmd_now(server, "AUTHENTICATE PLAIN");
+ break;
+
+ case SASL_MECHANISM_EXTERNAL:
+ irc_send_cmd_now(server, "AUTHENTICATE EXTERNAL");
+ break;
+ }
+ server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
+}
+
+static void sasl_fail(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ char *params, *error;
+
+ /* Stop any pending timeout, if any */
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ params = event_get_params(data, 2, NULL, &error);
+
+ server->sasl_success = FALSE;
+
+ signal_emit("server sasl failure", 2, server, error);
+
+ /* Terminate the negotiation */
+ cap_finish_negotiation(server);
+
+ g_free(params);
+}
+
+static void sasl_already(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ server->sasl_success = TRUE;
+
+ signal_emit("server sasl success", 1, server);
+
+ /* We're already authenticated, do nothing */
+ cap_finish_negotiation(server);
+}
+
+static void sasl_success(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ server->sasl_success = TRUE;
+
+ signal_emit("server sasl success", 1, server);
+
+ /* The authentication succeeded, time to finish the CAP negotiation */
+ cap_finish_negotiation(server);
+}
+
+/*
+ * Responsible for reassembling incoming SASL requests. SASL requests must be split
+ * into 400 byte requests to stay below the IRC command length limit of 512 bytes.
+ * The spec says that if there is 400 bytes, then there is expected to be a
+ * continuation in the next chunk. If a message is exactly a multiple of 400 bytes,
+ * there must be a blank message of "AUTHENTICATE +" to indicate the end.
+ *
+ * This function returns the fully reassembled and decoded AUTHENTICATION message if
+ * completed or NULL if there are more messages expected.
+ */
+static gboolean sasl_reassemble_incoming(IRC_SERVER_REC *server, const char *fragment, GString **decoded)
+{
+ GString *enc_req;
+ gsize fragment_len;
+
+ fragment_len = strlen(fragment);
+
+ /* Check if there is an existing fragment to prepend. */
+ if (server->sasl_buffer != NULL) {
+ if (g_strcmp0("+", fragment) == 0) {
+ enc_req = server->sasl_buffer;
+ } else {
+ enc_req = g_string_append_len(server->sasl_buffer, fragment, fragment_len);
+ }
+ server->sasl_buffer = NULL;
+ } else {
+ enc_req = g_string_new_len(fragment, fragment_len);
+ }
+
+ /*
+ * Fail authentication with this server. They have sent too much data.
+ */
+ if (enc_req->len > AUTHENTICATE_MAX_SIZE) {
+ return FALSE;
+ }
+
+ /*
+ * If the the request is exactly the chunk size, this is a fragment
+ * and more data is expected.
+ */
+ if (fragment_len == AUTHENTICATE_CHUNK_SIZE) {
+ server->sasl_buffer = enc_req;
+ return TRUE;
+ }
+
+ if (enc_req->len == 1 && *enc_req->str == '+') {
+ *decoded = g_string_new_len("", 0);
+ } else {
+ gsize dec_len;
+ gint state = 0;
+ guint save = 0;
+
+ /* Since we're not going to use the enc_req GString anymore we
+ * can perform the decoding in place. */
+ dec_len = g_base64_decode_step(enc_req->str, enc_req->len,
+ (guchar *)enc_req->str,
+ &state, &save);
+ /* A copy of the data is made when the GString is created. */
+ *decoded = g_string_new_len(enc_req->str, dec_len);
+ }
+
+ g_string_free(enc_req, TRUE);
+ return TRUE;
+}
+
+/*
+ * Splits the response into appropriately sized chunks for the AUTHENTICATION
+ * command to be sent to the IRC server. If |response| is NULL, then the empty
+ * response is sent to the server.
+ */
+void sasl_send_response(IRC_SERVER_REC *server, GString *response)
+{
+ char *enc;
+ size_t offset, enc_len, chunk_len;
+
+ if (response == NULL) {
+ irc_send_cmdv(server, "AUTHENTICATE +");
+ return;
+ }
+
+ enc = g_base64_encode((guchar *) response->str, response->len);
+ enc_len = strlen(enc);
+
+ for (offset = 0; offset < enc_len; offset += AUTHENTICATE_CHUNK_SIZE) {
+ chunk_len = enc_len - offset;
+ if (chunk_len > AUTHENTICATE_CHUNK_SIZE)
+ chunk_len = AUTHENTICATE_CHUNK_SIZE;
+
+ irc_send_cmdv(server, "AUTHENTICATE %.*s", (int) chunk_len, enc + offset);
+ }
+
+ if (offset == enc_len) {
+ irc_send_cmdv(server, "AUTHENTICATE +");
+ }
+ g_free(enc);
+}
+
+/*
+ * Called when the incoming SASL request is completely received.
+ */
+static void sasl_step_complete(IRC_SERVER_REC *server, GString *data)
+{
+ IRC_SERVER_CONNECT_REC *conn;
+ GString *resp;
+
+ conn = server->connrec;
+
+ switch (conn->sasl_mechanism) {
+ case SASL_MECHANISM_PLAIN:
+ /* At this point we assume that conn->sasl_{username, password} are non-NULL.
+ * The PLAIN mechanism expects a NULL-separated string composed by the authorization identity, the
+ * authentication identity and the password.
+ * The authorization identity field is explicitly set to the user provided username.
+ */
+
+ resp = g_string_new(NULL);
+
+ g_string_append(resp, conn->sasl_username);
+ g_string_append_c(resp, '\0');
+ g_string_append(resp, conn->sasl_username);
+ g_string_append_c(resp, '\0');
+ g_string_append(resp, conn->sasl_password);
+
+ sasl_send_response(server, resp);
+ g_string_free(resp, TRUE);
+
+ break;
+
+ case SASL_MECHANISM_EXTERNAL:
+ /* Empty response */
+ sasl_send_response(server, NULL);
+ break;
+ }
+}
+
+static void sasl_step_fail(IRC_SERVER_REC *server)
+{
+ irc_send_cmd_now(server, "AUTHENTICATE *");
+ cap_finish_negotiation(server);
+
+ server->sasl_timeout = 0;
+
+ signal_emit("server sasl failure", 2, server, "The server sent an invalid payload");
+}
+
+static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ GString *req = NULL;
+
+ /* Stop the timer */
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ if (!sasl_reassemble_incoming(server, data, &req)) {
+ sasl_step_fail(server);
+ return;
+ }
+
+ if (req != NULL) {
+ sasl_step_complete(server, req);
+ g_string_free(req, TRUE);
+ }
+
+ /* We expect a response within a reasonable time */
+ server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
+}
+
+static void sasl_disconnected(IRC_SERVER_REC *server)
+{
+ g_return_if_fail(server != NULL);
+
+ if (!IS_IRC_SERVER(server)) {
+ return;
+ }
+
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+}
+
+void sasl_init(void)
+{
+ signal_add_first("server cap ack sasl", (SIGNAL_FUNC) sasl_start);
+ signal_add_first("event authenticate", (SIGNAL_FUNC) sasl_step);
+ signal_add_first("event 903", (SIGNAL_FUNC) sasl_success);
+ signal_add_first("event 902", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 904", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 905", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 906", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 907", (SIGNAL_FUNC) sasl_already);
+ signal_add_first("server disconnected", (SIGNAL_FUNC) sasl_disconnected);
+}
+
+void sasl_deinit(void)
+{
+ signal_remove("server cap ack sasl", (SIGNAL_FUNC) sasl_start);
+ signal_remove("event authenticate", (SIGNAL_FUNC) sasl_step);
+ signal_remove("event 903", (SIGNAL_FUNC) sasl_success);
+ signal_remove("event 902", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 904", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 905", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 906", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 907", (SIGNAL_FUNC) sasl_already);
+ signal_remove("server disconnected", (SIGNAL_FUNC) sasl_disconnected);
+}
diff --git a/src/irc/core/sasl.h b/src/irc/core/sasl.h
new file mode 100644
index 00000000..0693989d
--- /dev/null
+++ b/src/irc/core/sasl.h
@@ -0,0 +1,34 @@
+/*
+ fe-sasl.c : irssi
+
+ Copyright (C) 2015 The Lemon Man
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __SASL_H
+#define __SASL_H
+
+enum {
+ SASL_MECHANISM_NONE = 0,
+ SASL_MECHANISM_PLAIN,
+ SASL_MECHANISM_EXTERNAL,
+ SASL_MECHANISM_MAX
+};
+
+void sasl_init(void);
+void sasl_deinit(void);
+
+#endif
diff --git a/src/irc/core/servers-redirect.c b/src/irc/core/servers-redirect.c
index 518248cb..aeb9c010 100644
--- a/src/irc/core/servers-redirect.c
+++ b/src/irc/core/servers-redirect.c
@@ -339,7 +339,7 @@ static GSList *redirect_cmd_list_find(GSList *list, const char *event)
while (list != NULL) {
const char *str = list->data;
- if (strcmp(str, event) == 0)
+ if (g_strcmp0(str, event) == 0)
break;
list = list->next->next;
}
@@ -365,7 +365,7 @@ static const char *redirect_match(REDIRECT_REC *redirect, const char *event,
use the default signal */
signal = NULL;
for (tmp = redirect->signals; tmp != NULL; tmp = tmp->next->next) {
- if (strcmp(tmp->data, event) == 0) {
+ if (g_strcmp0(tmp->data, event) == 0) {
signal = tmp->next->data;
break;
}
@@ -433,9 +433,11 @@ static void redirect_abort(IRC_SERVER_REC *server, REDIRECT_REC *rec)
if (rec->aborted || !rec->destroyed) {
/* emit the failure signal */
- str = g_strdup_printf(rec->failure_signal != NULL ?
- "FAILED %s: %s" : "FAILED %s",
- rec->cmd->name, rec->failure_signal);
+ if (rec->failure_signal != NULL)
+ str = g_strdup_printf("FAILED %s: %s", rec->cmd->name, rec->failure_signal);
+ else
+ str = g_strdup_printf("FAILED %s", rec->cmd->name);
+
rawlog_redirect(server->rawlog, str);
g_free(str);
@@ -527,7 +529,7 @@ server_redirect_get(IRC_SERVER_REC *server, const char *prefix,
next = ptr->next;
r = ptr->data;
if (prefix != NULL && r->prefix != NULL &&
- strcmp(prefix, r->prefix)) {
+ g_strcmp0(prefix, r->prefix)) {
/* not from this server */
continue;
}
diff --git a/src/irc/dcc/dcc-autoget.c b/src/irc/dcc/dcc-autoget.c
index 4768641b..dd12f438 100644
--- a/src/irc/dcc/dcc-autoget.c
+++ b/src/irc/dcc/dcc-autoget.c
@@ -23,6 +23,7 @@
#include "masks.h"
#include "settings.h"
#include "servers.h"
+#include "misc.h"
#include "dcc-get.h"
@@ -30,7 +31,7 @@ static void sig_dcc_request(GET_DCC_REC *dcc, const char *nickaddr)
{
struct stat statbuf;
const char *masks;
- char *str, *file;
+ char *str, *file, *esc_arg;
int max_size;
if (!IS_DCC_GET(dcc)) return;
@@ -51,13 +52,13 @@ static void sig_dcc_request(GET_DCC_REC *dcc, const char *nickaddr)
/* Unless specifically said in dcc_autoget_masks, don't do autogets
sent to channels. */
- if (*masks == '\0' && dcc->target != NULL && ischannel(*dcc->target))
+ if (*masks == '\0' && dcc->target != NULL && server_ischannel(SERVER(dcc->server), dcc->target))
return;
/* don't autoget files beginning with a dot, if download dir is
our home dir (stupid kludge for stupid people) */
if (*dcc->arg == '.' &&
- strcmp(settings_get_str("dcc_download_path"), "~") == 0)
+ g_strcmp0(settings_get_str("dcc_download_path"), "~") == 0)
return;
/* check file size limit, NOTE: it's still possible to send a
@@ -68,11 +69,13 @@ static void sig_dcc_request(GET_DCC_REC *dcc, const char *nickaddr)
/* ok. but do we want/need to resume? */
file = dcc_get_download_path(dcc->arg);
+ esc_arg = escape_string(dcc->arg);
str = g_strdup_printf(settings_get_bool("dcc_autoresume") &&
stat(file, &statbuf) == 0 ?
- "RESUME %s %s" : "GET %s %s",
- dcc->nick, dcc->arg);
+ "RESUME %s \"%s\"" : "GET %s \"%s\"",
+ dcc->nick, esc_arg);
signal_emit("command dcc", 2, str, dcc->server);
+ g_free(esc_arg);
g_free(file);
g_free(str);
}
diff --git a/src/irc/dcc/dcc-chat.c b/src/irc/dcc/dcc-chat.c
index 8ee4decd..88c577f7 100644
--- a/src/irc/dcc/dcc-chat.c
+++ b/src/irc/dcc/dcc-chat.c
@@ -66,6 +66,13 @@ CHAT_DCC_REC *dcc_chat_create(IRC_SERVER_REC *server,
dcc->id = dcc_chat_get_new_id(nick);
dcc_init_rec(DCC(dcc), server, chat, nick, arg);
+ if (dcc->module_data == NULL) {
+ /* failed to successfully init; TODO: change init_rec API */
+ g_free(dcc->id);
+ g_free(dcc);
+ return NULL;
+ }
+
return dcc;
}
@@ -191,7 +198,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
return;
/* handle only DCC messages */
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
dcc = item_get_dcc(item);
else if (*target == '=')
dcc = dcc_chat_find_id(target+1);
@@ -471,6 +478,7 @@ static void cmd_dcc_chat(const char *data, IRC_SERVER_REC *server)
/* We are accepting a passive DCC CHAT. */
dcc_chat_passive(dcc);
}
+ cmd_params_free(free_arg);
return;
}
@@ -485,6 +493,11 @@ static void cmd_dcc_chat(const char *data, IRC_SERVER_REC *server)
cmd_param_error(CMDERR_NOT_CONNECTED);
dcc = dcc_chat_create(server, NULL, nick, "chat");
+ if (dcc == NULL) {
+ cmd_params_free(free_arg);
+ g_warn_if_reached();
+ return;
+ }
if (g_hash_table_lookup(optlist, "passive") == NULL) {
/* Standard DCC CHAT... let's listen for incoming connections */
@@ -619,13 +632,16 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
/* CHAT <unused> <address> <port> */
/* CHAT <unused> <address> 0 <id> (DCC CHAT passive protocol) */
params = g_strsplit(data, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 3) {
g_strfreev(params);
return;
}
- passive = paramcount == 4 && strcmp(params[2], "0") == 0;
+ passive = paramcount == 4 && g_strcmp0(params[2], "0") == 0;
+
+ if (nick == NULL)
+ nick = "";
dcc = DCC_CHAT(dcc_find_request(DCC_CHAT_TYPE, nick, NULL));
if (dcc != NULL) {
@@ -658,6 +674,11 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
}
dcc = dcc_chat_create(server, chat, nick, params[0]);
+ if (dcc == NULL) {
+ g_strfreev(params);
+ g_warn_if_reached();
+ return;
+ }
dcc->target = g_strdup(target);
dcc->port = atoi(params[2]);
diff --git a/src/irc/dcc/dcc-get.c b/src/irc/dcc/dcc-get.c
index 69fdc746..cecbb076 100644
--- a/src/irc/dcc/dcc-get.c
+++ b/src/irc/dcc/dcc-get.c
@@ -30,6 +30,8 @@
#include "dcc-get.h"
#include "dcc-send.h"
+static char *dcc_get_recv_buffer;
+
GET_DCC_REC *dcc_get_create(IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
const char *nick, const char *arg)
{
@@ -41,6 +43,12 @@ GET_DCC_REC *dcc_get_create(IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
dcc->fhandle = -1;
dcc_init_rec(DCC(dcc), server, chat, nick, arg);
+ if (dcc->module_data == NULL) {
+ /* failed to successfully init; TODO: change API */
+ g_free(dcc);
+ return NULL;
+ }
+
return dcc;
}
@@ -139,14 +147,20 @@ static void sig_dccget_send(GET_DCC_REC *dcc)
dcc_get_send_received(dcc);
}
+#define DCC_GET_RECV_BUFFER_SIZE 32768
+
/* input function: DCC GET received data */
static void sig_dccget_receive(GET_DCC_REC *dcc)
{
- char buffer[512];
int ret;
+ if (dcc_get_recv_buffer == NULL) {
+ dcc_get_recv_buffer = g_malloc(DCC_GET_RECV_BUFFER_SIZE);
+ }
+
for (;;) {
- ret = net_receive(dcc->handle, buffer, sizeof(buffer));
+ ret = net_receive(dcc->handle, dcc_get_recv_buffer,
+ DCC_GET_RECV_BUFFER_SIZE);
if (ret == 0) break;
if (ret < 0) {
@@ -156,7 +170,7 @@ static void sig_dccget_receive(GET_DCC_REC *dcc)
return;
}
- if (write(dcc->fhandle, buffer, ret) != ret) {
+ if (write(dcc->fhandle, dcc_get_recv_buffer, ret) != ret) {
/* most probably out of disk space */
signal_emit("dcc error write", 2,
dcc, g_strerror(errno));
@@ -226,6 +240,8 @@ void sig_dccget_connected(GET_DCC_REC *dcc)
else
ret = fchmod(temphandle, dcc_file_create_mode);
+ close(temphandle);
+
if (ret != -1) {
ret = link(tempfname, dcc->file);
@@ -249,7 +265,6 @@ void sig_dccget_connected(GET_DCC_REC *dcc)
/* close/remove the temp file */
ret_errno = errno;
- close(temphandle);
unlink(tempfname);
g_free(tempfname);
@@ -373,6 +388,8 @@ int get_file_params_count(char **params, int paramcount)
if (*params[0] == '"') {
/* quoted file name? */
for (pos = 0; pos < paramcount-3; pos++) {
+ if (strlen(params[pos]) == 0)
+ continue;
if (params[pos][strlen(params[pos])-1] == '"' &&
get_params_match(params, pos+1))
return pos+1;
@@ -419,10 +436,15 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
int p_id = -1;
int passive = FALSE;
+ if (addr == NULL)
+ addr = "";
+ if (nick == NULL)
+ nick = "";
+
/* SEND <file name> <address> <port> <size> [...] */
/* SEND <file name> <address> 0 <size> <id> (DCC SEND passive protocol) */
params = g_strsplit(data, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 4) {
signal_emit("dcc error ctcp", 5, "SEND", data,
@@ -472,8 +494,8 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
net_ip2host(&temp_dcc->addr, temp_dcc->addrstr);
else {
/* with IPv6, show it to us as it was sent */
- strocpy(temp_dcc->addrstr, address,
- sizeof(temp_dcc->addrstr));
+ g_strlcpy(temp_dcc->addrstr, address,
+ sizeof(temp_dcc->addrstr));
}
/* This new signal is added to let us invoke
@@ -497,6 +519,12 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
dcc_destroy(DCC(dcc)); /* remove the old DCC */
dcc = dcc_get_create(server, chat, nick, fname);
+ if (dcc == NULL) {
+ g_free(address);
+ g_free(fname);
+ g_warn_if_reached();
+ return;
+ }
dcc->target = g_strdup(target);
if (passive && port == 0)
@@ -507,7 +535,7 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
net_ip2host(&dcc->addr, dcc->addrstr);
else {
/* with IPv6, show it to us as it was sent */
- strocpy(dcc->addrstr, address, sizeof(dcc->addrstr));
+ g_strlcpy(dcc->addrstr, address, sizeof(dcc->addrstr));
}
dcc->port = port;
dcc->size = size;
@@ -525,14 +553,14 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
{
GET_DCC_REC *dcc;
GSList *tmp, *next;
- char *nick, *fname;
+ char *nick, *arg, *fname;
void *free_arg;
int found;
g_return_if_fail(data != NULL);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
- &nick, &fname))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS, &nick, &arg))
return;
if (*nick == '\0') {
@@ -547,6 +575,8 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
return;
}
+ fname = cmd_get_quoted_param(&arg);
+
found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next) {
GET_DCC_REC *dcc = tmp->data;
@@ -554,7 +584,7 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
next = tmp->next;
if (IS_DCC_GET(dcc) && g_ascii_strcasecmp(dcc->nick, nick) == 0 &&
(dcc_is_waiting_user(dcc) || dcc->from_dccserver) &&
- (*fname == '\0' || strcmp(dcc->arg, fname) == 0)) {
+ (*fname == '\0' || g_strcmp0(dcc->arg, fname) == 0)) {
found = TRUE;
if (!dcc_is_passive(dcc))
accept_func(dcc);
@@ -593,4 +623,5 @@ void dcc_get_deinit(void)
signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
signal_remove("ctcp msg dcc send", (SIGNAL_FUNC) ctcp_msg_dcc_send);
command_unbind("dcc get", (SIGNAL_FUNC) cmd_dcc_get);
+ g_free_and_null(dcc_get_recv_buffer);
}
diff --git a/src/irc/dcc/dcc-resume.c b/src/irc/dcc/dcc-resume.c
index 11b28aef..ce0ac925 100644
--- a/src/irc/dcc/dcc-resume.c
+++ b/src/irc/dcc/dcc-resume.c
@@ -62,6 +62,8 @@ int get_file_params_count_resume(char **params, int paramcount)
if (*params[0] == '"') {
/* quoted file name? */
for (pos = 0; pos < paramcount-2; pos++) {
+ if (strlen(params[pos]) == 0)
+ continue;
if (params[pos][strlen(params[pos])-1] == '"' &&
get_params_match_resume(params, pos+1))
return pos+1;
@@ -88,7 +90,7 @@ static int dcc_ctcp_resume_parse(int type, const char *data, const char *nick,
/* RESUME|ACCEPT <file name> <port> <size> */
/* RESUME|ACCEPT <file name> 0 <size> <id> (passive protocol) */
params = g_strsplit(data, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 3)
return 0;
diff --git a/src/irc/dcc/dcc-send.c b/src/irc/dcc/dcc-send.c
index 2ce84f18..912129b7 100644
--- a/src/irc/dcc/dcc-send.c
+++ b/src/irc/dcc/dcc-send.c
@@ -174,8 +174,8 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
int queue, mode, passive;
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "dcc send",
- &optlist, &nick, &fileargs))
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ "dcc send", &optlist, &nick, &fileargs))
return;
chat = item_get_dcc(item);
@@ -237,6 +237,12 @@ static SEND_DCC_REC *dcc_send_create(IRC_SERVER_REC *server,
dcc->queue = -1;
dcc_init_rec(DCC(dcc), server, chat, nick, arg);
+ if (dcc->module_data == NULL) {
+ /* failed to successfully init; TODO: change API */
+ g_free(dcc);
+ return NULL;
+ }
+
return dcc;
}
@@ -417,6 +423,10 @@ static int dcc_send_one_file(int queue, const char *target, const char *fname,
dcc = dcc_send_create(server, chat, target, str);
g_free(str);
+ if (dcc == NULL) {
+ g_warn_if_reached();
+ return FALSE;
+ }
dcc->handle = handle;
dcc->port = port;
diff --git a/src/irc/dcc/dcc-server.c b/src/irc/dcc/dcc-server.c
index 30224ff9..7ae572cd 100644
--- a/src/irc/dcc/dcc-server.c
+++ b/src/irc/dcc/dcc-server.c
@@ -245,7 +245,7 @@ static void dcc_server_msg(SERVER_DCC_REC *dcc, const char *msg)
/* 120 clientnickname filesize filename */
params = g_strsplit(msg, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 3) {
g_strfreev(params);
diff --git a/src/irc/dcc/dcc.c b/src/irc/dcc/dcc.c
index 6f0d5c81..c51f7c7a 100644
--- a/src/irc/dcc/dcc.c
+++ b/src/irc/dcc/dcc.c
@@ -149,7 +149,7 @@ DCC_REC *dcc_find_request(int type, const char *nick, const char *arg)
if (dcc->type == type && !dcc_is_connected(dcc) &&
g_ascii_strcasecmp(dcc->nick, nick) == 0 &&
- (arg == NULL || strcmp(dcc->arg, arg) == 0))
+ (arg == NULL || g_strcmp0(dcc->arg, arg) == 0))
return dcc;
}
@@ -490,7 +490,7 @@ static void event_no_such_nick(IRC_SERVER_REC *server, char *data)
static void cmd_dcc_close(char *data, IRC_SERVER_REC *server)
{
GSList *tmp, *next;
- char *typestr, *nick, *arg;
+ char *typestr, *nick, *arg, *fname;
void *free_arg;
int found, type;
@@ -510,13 +510,15 @@ static void cmd_dcc_close(char *data, IRC_SERVER_REC *server)
return;
}
+ fname = cmd_get_quoted_param(&arg);
+
found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next) {
DCC_REC *dcc = tmp->data;
next = tmp->next;
if (dcc->type == type && g_ascii_strcasecmp(dcc->nick, nick) == 0 &&
- (*arg == '\0' || strcmp(dcc->arg, arg) == 0)) {
+ (*fname == '\0' || g_strcmp0(dcc->arg, fname) == 0)) {
dcc_reject(dcc, server);
found = TRUE;
}
diff --git a/src/irc/flood/autoignore.c b/src/irc/flood/autoignore.c
index 250a1fe8..86ff3ec5 100644
--- a/src/irc/flood/autoignore.c
+++ b/src/irc/flood/autoignore.c
@@ -66,7 +66,7 @@ static void sig_flood(IRC_SERVER_REC *server, const char *nick, const char *host
mask = g_strdup_printf("%s!%s", nick, host);
if (level & check_level) {
- rec = ignore_find(server->tag, mask, NULL);
+ rec = ignore_find_full(server->tag, mask, NULL, NULL, 0);
if (rec == NULL)
autoignore_add(server, mask, level);
else
diff --git a/src/irc/flood/flood.c b/src/irc/flood/flood.c
index 29502ca6..0944a6eb 100644
--- a/src/irc/flood/flood.c
+++ b/src/irc/flood/flood.c
@@ -250,7 +250,7 @@ static void flood_privmsg(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2, &target, &text);
- level = ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS;
+ level = server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS;
if (addr != NULL && !ignore_check(SERVER(server), nick, addr, target, text, level))
flood_newmsg(server, level, nick, addr, target);
@@ -287,7 +287,7 @@ static void flood_ctcp(IRC_SERVER_REC *server, const char *data,
return;
level = g_ascii_strncasecmp(data, "ACTION ", 7) != 0 ? MSGLEVEL_CTCPS :
- (ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
+ (server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
if (!ignore_check(SERVER(server), nick, addr, target, data, level))
flood_newmsg(server, level, nick, addr, target);
}
diff --git a/src/irc/notifylist/notify-commands.c b/src/irc/notifylist/notify-commands.c
index 67076106..0d4fd4f2 100644
--- a/src/irc/notifylist/notify-commands.c
+++ b/src/irc/notifylist/notify-commands.c
@@ -36,7 +36,8 @@ static void cmd_notify(gchar *data)
g_return_if_fail(data != NULL);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg,
+ 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"notify", &optlist, &mask, &ircnets))
return;
if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
diff --git a/src/irc/notifylist/notify-ison.c b/src/irc/notifylist/notify-ison.c
index f9ca7b37..8a8865cc 100644
--- a/src/irc/notifylist/notify-ison.c
+++ b/src/irc/notifylist/notify-ison.c
@@ -80,6 +80,10 @@ static void ison_send(IRC_SERVER_REC *server, GString *cmd)
{
MODULE_SERVER_REC *mserver;
+ if (!server->connected) {
+ return;
+ }
+
mserver = MODULE_DATA(server);
mserver->ison_count++;
diff --git a/src/irc/notifylist/notify-setup.c b/src/irc/notifylist/notify-setup.c
index d6f91361..9ea481ca 100644
--- a/src/irc/notifylist/notify-setup.c
+++ b/src/irc/notifylist/notify-setup.c
@@ -30,7 +30,7 @@ void notifylist_add_config(NOTIFYLIST_REC *rec)
CONFIG_NODE *node;
node = iconfig_node_traverse("notifies", TRUE);
- node = config_node_section(node, rec->mask, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, rec->mask, NODE_TYPE_BLOCK);
if (rec->away_check)
iconfig_node_set_bool(node, "away_check", TRUE);
@@ -39,7 +39,7 @@ void notifylist_add_config(NOTIFYLIST_REC *rec)
iconfig_node_set_str(node, "ircnets", NULL);
if (rec->ircnets != NULL && *rec->ircnets != NULL) {
- node = config_node_section(node, "ircnets", NODE_TYPE_LIST);
+ node = iconfig_node_section(node, "ircnets", NODE_TYPE_LIST);
iconfig_node_add_list(node, rec->ircnets);
}
}
@@ -73,7 +73,7 @@ void notifylist_read_config(void)
rec->mask = g_strdup(node->key);
rec->away_check = config_node_get_bool(node, "away_check", FALSE);
- node = config_node_section(node, "ircnets", -1);
+ node = iconfig_node_section(node, "ircnets", -1);
if (node != NULL) rec->ircnets = config_node_get_list(node);
}
}
diff --git a/src/irc/notifylist/notifylist.c b/src/irc/notifylist/notifylist.c
index b0b7ee1c..573f7a7f 100644
--- a/src/irc/notifylist/notifylist.c
+++ b/src/irc/notifylist/notifylist.c
@@ -91,7 +91,7 @@ int notifylist_ircnets_match(NOTIFYLIST_REC *rec, const char *ircnet)
if (rec->ircnets == NULL) return TRUE;
if (ircnet == NULL) return FALSE;
- if (strcmp(ircnet, "*") == 0) return TRUE;
+ if (g_strcmp0(ircnet, "*") == 0) return TRUE;
for (tmp = rec->ircnets; *tmp != NULL; tmp++) {
if (g_ascii_strcasecmp(*tmp, ircnet) == 0)
diff --git a/src/irc/proxy/dump.c b/src/irc/proxy/dump.c
index 80fd7c2e..e39c21a6 100644
--- a/src/irc/proxy/dump.c
+++ b/src/irc/proxy/dump.c
@@ -83,7 +83,7 @@ void proxy_outserver(CLIENT_REC *client, const char *data, ...)
va_start(args, data);
str = g_strdup_vprintf(data, args);
- proxy_outdata(client, ":%s!%s@proxy %s\n", client->nick,
+ proxy_outdata(client, ":%s!%s@proxy %s\r\n", client->nick,
settings_get_str("user_name"), str);
g_free(str);
@@ -106,7 +106,7 @@ void proxy_outserver_all(IRC_SERVER_REC *server, const char *data, ...)
CLIENT_REC *rec = tmp->data;
if (rec->connected && rec->server == server) {
- proxy_outdata(rec, ":%s!%s@proxy %s\n", rec->nick,
+ proxy_outdata(rec, ":%s!%s@proxy %s\r\n", rec->nick,
settings_get_str("user_name"), str);
}
}
@@ -132,7 +132,7 @@ void proxy_outserver_all_except(CLIENT_REC *client, const char *data, ...)
if (rec->connected && rec != client &&
rec->server == client->server) {
- proxy_outdata(rec, ":%s!%s@proxy %s\n", rec->nick,
+ proxy_outdata(rec, ":%s!%s@proxy %s\r\n", rec->nick,
settings_get_str("user_name"), str);
}
}
@@ -169,7 +169,7 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
NICK_REC *nick = tmp->data;
if (str->len >= 500) {
- g_string_append_c(str, '\n');
+ g_string_append(str, "\r\n");
proxy_outdata(client, "%s", str->str);
create_names_start(str, channel, client);
first = TRUE;
@@ -186,21 +186,21 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
}
g_slist_free(nicks);
- g_string_append_c(str, '\n');
+ g_string_append(str, "\r\n");
proxy_outdata(client, "%s", str->str);
g_string_free(str, TRUE);
- proxy_outdata(client, ":%s 366 %s %s :End of /NAMES list.\n",
+ proxy_outdata(client, ":%s 366 %s %s :End of /NAMES list.\r\n",
client->proxy_address, client->nick, channel->name);
if (channel->topic != NULL) {
/* this is needed because the topic may be encoded into other charsets internaly */
recoded = recode_out(SERVER(client->server), channel->topic, channel->name);
- proxy_outdata(client, ":%s 332 %s %s :%s\n",
+ proxy_outdata(client, ":%s 332 %s %s :%s\r\n",
client->proxy_address, client->nick,
channel->name, recoded);
g_free(recoded);
if (channel->topic_time > 0)
- proxy_outdata(client, ":%s 333 %s %s %s %d\n",
+ proxy_outdata(client, ":%s 333 %s %s %s %d\r\n",
client->proxy_address, client->nick,
channel->name, channel->topic_by, channel->topic_time);
}
@@ -209,10 +209,10 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
void proxy_client_reset_nick(CLIENT_REC *client)
{
if (client->server == NULL ||
- strcmp(client->nick, client->server->nick) == 0)
+ g_strcmp0(client->nick, client->server->nick) == 0)
return;
- proxy_outdata(client, ":%s!proxy NICK :%s\n",
+ proxy_outdata(client, ":%s!proxy NICK :%s\r\n",
client->nick, client->server->nick);
g_free(client->nick);
@@ -236,13 +236,13 @@ void proxy_dump_data(CLIENT_REC *client)
proxy_client_reset_nick(client);
/* welcome info */
- proxy_outdata(client, ":%s 001 %s :Welcome to the Internet Relay Network %s!%s@proxy\n", client->proxy_address, client->nick, client->nick, settings_get_str("user_name"));
- proxy_outdata(client, ":%s 002 %s :Your host is irssi-proxy, running version %s\n", client->proxy_address, client->nick, PACKAGE_VERSION);
- proxy_outdata(client, ":%s 003 %s :This server was created ...\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 001 %s :Welcome to the Internet Relay Network %s!%s@proxy\r\n", client->proxy_address, client->nick, client->nick, settings_get_str("user_name"));
+ proxy_outdata(client, ":%s 002 %s :Your host is irssi-proxy, running version %s\r\n", client->proxy_address, client->nick, PACKAGE_VERSION);
+ proxy_outdata(client, ":%s 003 %s :This server was created ...\r\n", client->proxy_address, client->nick);
if (client->server == NULL || !client->server->emode_known)
- proxy_outdata(client, ":%s 004 %s %s %s oirw abiklmnopqstv\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
+ proxy_outdata(client, ":%s 004 %s %s %s oirw abiklmnopqstv\r\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
else
- proxy_outdata(client, ":%s 004 %s %s %s oirw abeIiklmnopqstv\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
+ proxy_outdata(client, ":%s 004 %s %s %s oirw abeIiklmnopqstv\r\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
if (client->server != NULL && client->server->isupport_sent) {
isupport_out = g_string_new(NULL);
@@ -267,7 +267,7 @@ void proxy_dump_data(CLIENT_REC *client)
count = 0;
if (paramstr->len > 0)
g_string_truncate(paramstr, paramstr->len-1);
- g_string_append_printf(paramstr, " :are supported by this server\n");
+ g_string_append_printf(paramstr, " :are supported by this server\r\n");
proxy_outdata(client, "%s", paramstr->str);
g_string_truncate(paramstr, 0);
g_string_printf(paramstr, ":%s 005 %s ", client->proxy_address, client->nick);
@@ -281,9 +281,9 @@ void proxy_dump_data(CLIENT_REC *client)
g_strfreev(paramlist);
}
- proxy_outdata(client, ":%s 251 %s :There are 0 users and 0 invisible on 1 servers\n", client->proxy_address, client->nick);
- proxy_outdata(client, ":%s 255 %s :I have 0 clients, 0 services and 0 servers\n", client->proxy_address, client->nick);
- proxy_outdata(client, ":%s 422 %s :MOTD File is missing\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 251 %s :There are 0 users and 0 invisible on 1 servers\r\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 255 %s :I have 0 clients, 0 services and 0 servers\r\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 422 %s :MOTD File is missing\r\n", client->proxy_address, client->nick);
/* user mode / away status */
if (client->server != NULL) {
@@ -293,7 +293,7 @@ void proxy_dump_data(CLIENT_REC *client)
client->server->usermode);
}
if (client->server->usermode_away) {
- proxy_outdata(client, ":%s 306 %s :You have been marked as being away\n",
+ proxy_outdata(client, ":%s 306 %s :You have been marked as being away\r\n",
client->proxy_address, client->nick);
}
diff --git a/src/irc/proxy/listen.c b/src/irc/proxy/listen.c
index 33392285..cde4c0be 100644
--- a/src/irc/proxy/listen.c
+++ b/src/irc/proxy/listen.c
@@ -31,12 +31,76 @@
#include "fe-common/core/printtext.h" /* FIXME: evil. need to do fe-proxy */
+#include <sys/un.h>
+
GSList *proxy_listens;
GSList *proxy_clients;
static GString *next_line;
static int ignore_next;
+static int enabled = FALSE;
+
+static int is_all_digits(const char *s)
+{
+ return strspn(s, "0123456789") == strlen(s);
+}
+
+static GIOChannel *net_listen_unix(const char *path)
+{
+ struct sockaddr_un sa;
+ int saved_errno, handle;
+
+ g_return_val_if_fail(path != NULL, NULL);
+
+ handle = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (handle == -1) {
+ return NULL;
+ }
+
+ fcntl(handle, F_SETFL, O_NONBLOCK);
+
+ memset(&sa, '\0', sizeof sa);
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, path, sizeof sa.sun_path - 1);
+ if (bind(handle, (struct sockaddr *)&sa, sizeof sa) == -1) {
+ saved_errno = errno;
+ goto error_close;
+ }
+
+ if (listen(handle, 1) == -1) {
+ saved_errno = errno;
+ goto error_unlink;
+ }
+
+ return g_io_channel_new(handle);
+
+error_unlink:
+ unlink(sa.sun_path);
+error_close:
+ close(handle);
+ errno = saved_errno;
+ return NULL;
+}
+
+static GIOChannel *net_accept_unix(GIOChannel *handle)
+{
+ struct sockaddr_un sa;
+ int ret;
+ socklen_t addrlen;
+
+ g_return_val_if_fail(handle != NULL, NULL);
+
+ addrlen = sizeof sa;
+ ret = accept(g_io_channel_unix_get_fd(handle), (struct sockaddr *)&sa, &addrlen);
+
+ if (ret < 0)
+ return NULL;
+
+ fcntl(ret, F_SETFL, O_NONBLOCK);
+ return g_io_channel_new(ret);
+}
+
static void remove_client(CLIENT_REC *rec)
{
g_return_if_fail(rec != NULL);
@@ -45,19 +109,19 @@ static void remove_client(CLIENT_REC *rec)
rec->listen->clients = g_slist_remove(rec->listen->clients, rec);
signal_emit("proxy client disconnected", 1, rec);
- printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
- "Proxy: Client disconnected from %s", rec->host);
+ printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: Client %s disconnected", rec->addr);
g_free(rec->proxy_address);
net_sendbuffer_destroy(rec->handle, TRUE);
g_source_remove(rec->recv_tag);
g_free_not_null(rec->nick);
- g_free_not_null(rec->host);
+ g_free_not_null(rec->addr);
g_free(rec);
}
static void proxy_redirect_event(CLIENT_REC *client, const char *command,
- int count, const char *arg, int remote)
+ int count, const char *arg, int remote)
{
char *str;
@@ -65,7 +129,7 @@ static void proxy_redirect_event(CLIENT_REC *client, const char *command,
str = g_strdup_printf("proxy %p", client);
server_redirect_event(client->server, command, count,
- arg, remote, NULL, "", str, NULL);
+ arg, remote, NULL, "", str, NULL);
g_free(str);
}
@@ -82,7 +146,7 @@ static void grab_who(CLIENT_REC *client, const char *channel)
arg = g_string_new(channel);
for (tmp = list, count = 0; *tmp != NULL; tmp++, count++) {
- if (strcmp(*tmp, "0") == 0) {
+ if (g_strcmp0(*tmp, "0") == 0) {
/* /who 0 displays everyone */
**tmp = '*';
}
@@ -92,40 +156,74 @@ static void grab_who(CLIENT_REC *client, const char *channel)
}
proxy_redirect_event(client, "who",
- client->server->one_endofwho ? 1 : count,
- arg->str, -1);
+ client->server->one_endofwho ? 1 : count,
+ arg->str, -1);
g_strfreev(list);
g_string_free(arg, TRUE);
}
static void handle_client_connect_cmd(CLIENT_REC *client,
- const char *cmd, const char *args)
+ const char *cmd, const char *args)
{
const char *password;
password = settings_get_str("irssiproxy_password");
- if (password != NULL && strcmp(cmd, "PASS") == 0) {
- if (strcmp(password, args) == 0)
- client->pass_sent = TRUE;
- else {
+ if (g_strcmp0(cmd, "PASS") == 0) {
+ const char *args_pass;
+
+ if (!client->multiplex) {
+ args_pass = args;
+ } else {
+ IRC_SERVER_REC *server;
+ char *tag;
+ const char *tag_end;
+
+ if ((tag_end = strchr(args, ':')) != NULL) {
+ args_pass = tag_end + 1;
+ } else {
+ tag_end = args + strlen(args);
+ args_pass = "";
+ }
+
+ tag = g_strndup(args, tag_end - args);
+ server = IRC_SERVER(server_find_chatnet(tag));
+ g_free(tag);
+
+ if (!server) {
+ /* an invalid network was specified */
+ remove_client(client);
+ return;
+ }
+
+ client->server = server;
+ g_free(client->proxy_address);
+ client->proxy_address = g_strdup_printf("%.*s.proxy", (int)(tag_end - args), args);
+ }
+
+ if (g_strcmp0(password, args_pass) != 0) {
/* wrong password! */
remove_client(client);
- return;
+ return;
}
- } else if (strcmp(cmd, "NICK") == 0) {
+ client->pass_sent = TRUE;
+ } else if (g_strcmp0(cmd, "NICK") == 0) {
g_free_not_null(client->nick);
client->nick = g_strdup(args);
- } else if (strcmp(cmd, "USER") == 0) {
+ } else if (g_strcmp0(cmd, "USER") == 0) {
client->user_sent = TRUE;
}
if (client->nick != NULL && client->user_sent) {
- if (*password != '\0' && !client->pass_sent) {
+ if ((*password != '\0' || client->multiplex) && !client->pass_sent) {
/* client didn't send us PASS, kill it */
remove_client(client);
} else {
+ signal_emit("proxy client connected", 1, client);
+ printtext(client->server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: Client %s connected",
+ client->addr);
client->connected = TRUE;
proxy_dump_data(client);
}
@@ -133,7 +231,7 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
}
static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
- const char *data)
+ const char *data)
{
GSList *tmp;
if (!client->connected) {
@@ -141,12 +239,12 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
return;
}
- if (strcmp(cmd, "QUIT") == 0) {
+ if (g_strcmp0(cmd, "QUIT") == 0) {
remove_client(client);
return;
}
- if (strcmp(cmd, "PING") == 0) {
+ if (g_strcmp0(cmd, "PING") == 0) {
/* Reply to PING, if the target parameter is either
proxy_adress, our own nick or empty. */
char *params, *origin, *target;
@@ -155,38 +253,38 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
if (*target == '\0' ||
g_ascii_strcasecmp(target, client->proxy_address) == 0 ||
g_ascii_strcasecmp(target, client->nick) == 0) {
- proxy_outdata(client, ":%s PONG %s :%s\n",
- client->proxy_address,
- client->proxy_address, origin);
+ proxy_outdata(client, ":%s PONG %s :%s\r\n",
+ client->proxy_address,
+ client->proxy_address, origin);
g_free(params);
return;
}
g_free(params);
}
- if (strcmp(cmd, "PROXY") == 0) {
+ if (g_strcmp0(cmd, "PROXY") == 0) {
if (g_ascii_strcasecmp(args, "CTCP ON") == 0) {
- /* client wants all ctcps */
+ /* client wants all ctcps */
client->want_ctcp = 1;
- for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+ for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
- if ((g_ascii_strcasecmp(client->listen->ircnet,rec->listen->ircnet) == 0) &&
- /* kludgy way to check if the clients aren't the same */
- (client->recv_tag != rec->recv_tag)) {
- if (rec->want_ctcp == 1)
- proxy_outdata(rec, ":%s NOTICE %s :Another client is now receiving CTCPs sent to %s\n",
- rec->proxy_address, rec->nick, rec->listen->ircnet);
- rec->want_ctcp = 0;
- }
+ if (g_ascii_strcasecmp(client->listen->ircnet, rec->listen->ircnet) == 0 &&
+ /* kludgy way to check if the clients aren't the same */
+ client->recv_tag != rec->recv_tag) {
+ if (rec->want_ctcp == 1)
+ proxy_outdata(rec, ":%s NOTICE %s :Another client is now receiving CTCPs sent to %s\r\n",
+ rec->proxy_address, rec->nick, rec->listen->ircnet);
+ rec->want_ctcp = 0;
+ }
}
- proxy_outdata(client, ":%s NOTICE %s :You're now receiving CTCPs sent to %s\n",
- client->proxy_address, client->nick,client->listen->ircnet);
+ proxy_outdata(client, ":%s NOTICE %s :You're now receiving CTCPs sent to %s\r\n",
+ client->proxy_address, client->nick, client->listen->ircnet);
} else if (g_ascii_strcasecmp(args, "CTCP OFF") == 0) {
- /* client wants proxy to handle all ctcps */
+ /* client wants proxy to handle all ctcps */
client->want_ctcp = 0;
- proxy_outdata(client, ":%s NOTICE %s :Proxy is now handling itself CTCPs sent to %s\n",
- client->proxy_address, client->nick, client->listen->ircnet);
+ proxy_outdata(client, ":%s NOTICE %s :Proxy is now handling itself CTCPs sent to %s\r\n",
+ client->proxy_address, client->nick, client->listen->ircnet);
} else {
signal_emit("proxy client command", 3, client, args, data);
}
@@ -194,17 +292,17 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
}
if (client->server == NULL || !client->server->connected) {
- proxy_outdata(client, ":%s NOTICE %s :Not connected to server\n",
- client->proxy_address, client->nick);
- return;
+ proxy_outdata(client, ":%s NOTICE %s :Not connected to server\r\n",
+ client->proxy_address, client->nick);
+ return;
}
- /* check if the command could be redirected */
- if (strcmp(cmd, "WHO") == 0)
+ /* check if the command could be redirected */
+ if (g_strcmp0(cmd, "WHO") == 0)
grab_who(client, args);
- else if (strcmp(cmd, "WHOWAS") == 0)
+ else if (g_strcmp0(cmd, "WHOWAS") == 0)
proxy_redirect_event(client, "whowas", 1, args, -1);
- else if (strcmp(cmd, "WHOIS") == 0) {
+ else if (g_strcmp0(cmd, "WHOIS") == 0) {
char *p;
/* convert dots to spaces */
@@ -212,11 +310,11 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
if (*p == ',') *p = ' ';
proxy_redirect_event(client, "whois", 1, args, TRUE);
- } else if (strcmp(cmd, "ISON") == 0)
+ } else if (g_strcmp0(cmd, "ISON") == 0)
proxy_redirect_event(client, "ison", 1, args, -1);
- else if (strcmp(cmd, "USERHOST") == 0)
+ else if (g_strcmp0(cmd, "USERHOST") == 0)
proxy_redirect_event(client, "userhost", 1, args, -1);
- else if (strcmp(cmd, "MODE") == 0) {
+ else if (g_strcmp0(cmd, "MODE") == 0) {
/* convert dots to spaces */
char *slist, *str, mode, *p;
int argc;
@@ -252,40 +350,40 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
}
g_free(str);
g_free(slist);
- } else if (strcmp(cmd, "PRIVMSG") == 0) {
+ } else if (g_strcmp0(cmd, "PRIVMSG") == 0) {
/* send the message to other clients as well */
char *params, *target, *msg;
params = event_get_params(args, 2 | PARAM_FLAG_GETREST,
- &target, &msg);
+ &target, &msg);
proxy_outserver_all_except(client, "PRIVMSG %s", args);
ignore_next = TRUE;
if (*msg != '\001' || msg[strlen(msg)-1] != '\001') {
- signal_emit(ischannel(*target) ?
- "message own_public" : "message own_private", 4,
- client->server, msg, target, target);
+ signal_emit(server_ischannel(SERVER(client->server), target) ?
+ "message own_public" : "message own_private", 4,
+ client->server, msg, target, target);
} else if (strncmp(msg+1, "ACTION ", 7) == 0) {
/* action */
- msg[strlen(msg)-1] = '\0';
+ msg[strlen(msg)-1] = '\0';
signal_emit("message irc own_action", 3,
- client->server, msg+8, target);
+ client->server, msg+8, target);
} else {
- /* CTCP */
+ /* CTCP */
char *p;
msg[strlen(msg)-1] = '\0';
p = strchr(msg, ' ');
- if (p != NULL) *p++ = '\0'; else p = "";
+ if (p != NULL) *p++ = '\0'; else p = "";
signal_emit("message irc own_ctcp", 4,
- client->server, msg+1, p, target);
+ client->server, msg+1, p, target);
}
ignore_next = FALSE;
g_free(params);
- } else if (strcmp(cmd, "PING") == 0) {
+ } else if (g_strcmp0(cmd, "PING") == 0) {
proxy_redirect_event(client, "ping", 1, NULL, TRUE);
- } else if (strcmp(cmd, "AWAY") == 0) {
+ } else if (g_strcmp0(cmd, "AWAY") == 0) {
/* set the away reason */
if (args != NULL) {
g_free(client->server->away_reason);
@@ -331,23 +429,38 @@ static void sig_listen(LISTEN_REC *listen)
CLIENT_REC *rec;
IPADDR ip;
NET_SENDBUF_REC *sendbuf;
- GIOChannel *handle;
+ GIOChannel *handle;
char host[MAX_IP_LEN];
int port;
+ char *addr;
g_return_if_fail(listen != NULL);
/* accept connection */
- handle = net_accept(listen->handle, &ip, &port);
- if (handle == NULL)
- return;
- net_ip2host(&ip, host);
+ if (listen->port) {
+ handle = net_accept(listen->handle, &ip, &port);
+ if (handle == NULL)
+ return;
+ net_ip2host(&ip, host);
+ addr = g_strdup_printf("%s:%d", host, port);
+ } else {
+ /* no port => this is a unix socket */
+ handle = net_accept_unix(listen->handle);
+ if (handle == NULL)
+ return;
+ addr = g_strdup("(local)");
+ }
+
sendbuf = net_sendbuffer_create(handle, 0);
rec = g_new0(CLIENT_REC, 1);
rec->listen = listen;
rec->handle = sendbuf;
- rec->host = g_strdup(host);
- if (strcmp(listen->ircnet, "*") == 0) {
+ rec->addr = addr;
+ if (g_strcmp0(listen->ircnet, "?") == 0) {
+ rec->multiplex = TRUE;
+ rec->proxy_address = g_strdup("multiplex.proxy");
+ rec->server = NULL;
+ } else if (g_strcmp0(listen->ircnet, "*") == 0) {
rec->proxy_address = g_strdup("irc.proxy");
rec->server = servers == NULL ? NULL : IRC_SERVER(servers->data);
} else {
@@ -356,14 +469,15 @@ static void sig_listen(LISTEN_REC *listen)
IRC_SERVER(server_find_chatnet(listen->ircnet));
}
rec->recv_tag = g_input_add(handle, G_INPUT_READ,
- (GInputFunction) sig_listen_client, rec);
+ (GInputFunction) sig_listen_client, rec);
proxy_clients = g_slist_prepend(proxy_clients, rec);
- rec->listen->clients = g_slist_prepend(rec->listen->clients, rec);
+ listen->clients = g_slist_prepend(listen->clients, rec);
- signal_emit("proxy client connected", 1, rec);
- printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
- "Proxy: Client connected from %s", rec->host);
+ signal_emit("proxy client connecting", 1, rec);
+ printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: New client %s on port %s (%s)",
+ rec->addr, listen->port_or_path, listen->ircnet);
}
static void sig_incoming(IRC_SERVER_REC *server, const char *line)
@@ -371,7 +485,7 @@ static void sig_incoming(IRC_SERVER_REC *server, const char *line)
g_return_if_fail(line != NULL);
/* send server event to all clients */
- g_string_printf(next_line, "%s\n", line);
+ g_string_printf(next_line, "%s\r\n", line);
}
static void sig_server_event(IRC_SERVER_REC *server, const char *line,
@@ -415,7 +529,7 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
}
}
- if (strcmp(event, "event privmsg") == 0 &&
+ if (g_strcmp0(event, "event privmsg") == 0 &&
strstr(args, " :\001") != NULL &&
strstr(args, " :\001ACTION") == NULL) {
/* CTCP - either answer ourself or forward it to one client */
@@ -435,8 +549,8 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
return;
}
- if (strcmp(event, "event ping") == 0 ||
- strcmp(event, "event pong") == 0) {
+ if (g_strcmp0(event, "event ping") == 0 ||
+ g_strcmp0(event, "event pong") == 0) {
/* We want to answer ourself to PINGs and CTCPs.
Also hide PONGs from clients. */
g_free(event);
@@ -452,21 +566,21 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
static void event_connected(IRC_SERVER_REC *server)
{
GSList *tmp;
- const char *chatnet;
+ const char *chatnet;
if (!IS_IRC_SERVER(server))
return;
- chatnet = server->connrec->chatnet;
+ chatnet = server->connrec->chatnet;
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
if (rec->connected && rec->server == NULL &&
- (strcmp(rec->listen->ircnet, "*") == 0 ||
+ (g_strcmp0(rec->listen->ircnet, "*") == 0 ||
(chatnet != NULL &&
g_ascii_strcasecmp(chatnet, rec->listen->ircnet) == 0))) {
- proxy_outdata(rec, ":%s NOTICE %s :Connected to server\n",
- rec->proxy_address, rec->nick);
+ proxy_outdata(rec, ":%s NOTICE %s :Connected to server\r\n",
+ rec->proxy_address, rec->nick);
rec->server = server;
proxy_client_reset_nick(rec);
}
@@ -474,11 +588,11 @@ static void event_connected(IRC_SERVER_REC *server)
}
static void proxy_server_disconnected(CLIENT_REC *client,
- IRC_SERVER_REC *server)
+ IRC_SERVER_REC *server)
{
GSList *tmp;
- proxy_outdata(client, ":%s NOTICE %s :Connection lost to server %s\n",
+ proxy_outdata(client, ":%s NOTICE %s :Connection lost to server %s\r\n",
client->proxy_address, client->nick,
server->connrec->address);
@@ -559,14 +673,19 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
proxy_outserver_all(server, "PRIVMSG %s :\001ACTION %s\001", target, msg);
}
-static LISTEN_REC *find_listen(const char *ircnet, int port)
+static LISTEN_REC *find_listen(const char *ircnet, int port, const char *port_or_path)
{
GSList *tmp;
for (tmp = proxy_listens; tmp != NULL; tmp = tmp->next) {
LISTEN_REC *rec = tmp->data;
- if (rec->port == port &&
+ if ((port
+ ? /* a tcp port */
+ rec->port == port
+ : /* a unix socket path */
+ g_strcmp0(rec->port_or_path, port_or_path) == 0
+ ) &&
g_ascii_strcasecmp(rec->ircnet, ircnet) == 0)
return rec;
}
@@ -574,48 +693,53 @@ static LISTEN_REC *find_listen(const char *ircnet, int port)
return NULL;
}
-static void add_listen(const char *ircnet, int port)
+static void add_listen(const char *ircnet, int port, const char *port_or_path)
{
LISTEN_REC *rec;
IPADDR ip4, ip6, *my_ip;
+ GIOChannel *handle;
- if (port <= 0 || *ircnet == '\0')
+ if (*port_or_path == '\0' || port < 0 || *ircnet == '\0')
return;
- /* bind to specific host/ip? */
- my_ip = NULL;
- if (*settings_get_str("irssiproxy_bind") != '\0') {
- if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
- &ip4, &ip6) != 0) {
- printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
- "Proxy: can not resolve '%s' - aborting",
- settings_get_str("irssiproxy_bind"));
- return;
+ if (port == 0) {
+ /* listening on a unix socket */
+ handle = net_listen_unix(port_or_path);
+ } else {
+ /* bind to specific host/ip? */
+ my_ip = NULL;
+ if (*settings_get_str("irssiproxy_bind") != '\0') {
+ if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
+ &ip4, &ip6) != 0) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: can not resolve '%s' - aborting",
+ settings_get_str("irssiproxy_bind"));
+ return;
+ }
+
+ my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
+ settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
}
+ handle = net_listen(my_ip, &port);
+ }
- my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
- settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
+ if (handle == NULL) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: Listen in port %s failed: %s",
+ port_or_path, g_strerror(errno));
+ return;
}
rec = g_new0(LISTEN_REC, 1);
+ rec->handle = handle;
rec->ircnet = g_strdup(ircnet);
rec->port = port;
-
- rec->handle = net_listen(my_ip, &rec->port);
-
- if (rec->handle == NULL) {
- printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
- "Proxy: Listen in port %d failed: %s",
- rec->port, g_strerror(errno));
- g_free(rec->ircnet);
- g_free(rec);
- return;
- }
+ rec->port_or_path = g_strdup(port_or_path);
rec->tag = g_input_add(rec->handle, G_INPUT_READ,
- (GInputFunction) sig_listen, rec);
+ (GInputFunction) sig_listen, rec);
- proxy_listens = g_slist_append(proxy_listens, rec);
+ proxy_listens = g_slist_append(proxy_listens, rec);
}
static void remove_listen(LISTEN_REC *rec)
@@ -625,8 +749,13 @@ static void remove_listen(LISTEN_REC *rec)
while (rec->clients != NULL)
remove_client(rec->clients->data);
+ /* remove unix socket because bind wants to (re)create it */
+ if (rec->port == 0)
+ unlink(rec->port_or_path);
+
net_disconnect(rec->handle);
g_source_remove(rec->tag);
+ g_free(rec->port_or_path);
g_free(rec->ircnet);
g_free(rec);
}
@@ -634,8 +763,9 @@ static void remove_listen(LISTEN_REC *rec)
static void read_settings(void)
{
LISTEN_REC *rec;
- GSList *remove_listens;
- char **ports, **tmp, *ircnet, *port;
+ GSList *remove_listens = NULL;
+ GSList *add_listens = NULL;
+ char **ports, **tmp, *ircnet, *port_or_path;
int portnum;
remove_listens = g_slist_copy(proxy_listens);
@@ -643,27 +773,45 @@ static void read_settings(void)
ports = g_strsplit(settings_get_str("irssiproxy_ports"), " ", -1);
for (tmp = ports; *tmp != NULL; tmp++) {
ircnet = *tmp;
- port = strchr(ircnet, '=');
- if (port == NULL)
+ port_or_path = strchr(ircnet, '=');
+ if (port_or_path == NULL)
continue;
- *port++ = '\0';
- portnum = atoi(port);
- if (portnum <= 0)
- continue;
+ *port_or_path++ = '\0';
+ if (is_all_digits(port_or_path)) {
+ portnum = atoi(port_or_path);
+ if (portnum <= 0)
+ continue;
+ } else {
+ portnum = 0;
+ }
- rec = find_listen(ircnet, portnum);
- if (rec == NULL)
- add_listen(ircnet, portnum);
- else
+ rec = find_listen(ircnet, portnum, port_or_path);
+ if (rec == NULL) {
+ rec = g_new0(LISTEN_REC, 1);
+ rec->ircnet = ircnet; /* borrow */
+ rec->port = portnum;
+ rec->port_or_path = port_or_path; /* borrow */
+ add_listens = g_slist_prepend(add_listens, rec);
+ } else {
+ /* remove from the list of listens to remove == keep it */
remove_listens = g_slist_remove(remove_listens, rec);
+ }
}
- g_strfreev(ports);
while (remove_listens != NULL) {
- remove_listen(remove_listens->data);
+ remove_listen(remove_listens->data);
remove_listens = g_slist_remove(remove_listens, remove_listens->data);
}
+
+ while (add_listens != NULL) {
+ rec = add_listens->data;
+ add_listen(rec->ircnet, rec->port, rec->port_or_path);
+ add_listens = g_slist_remove(add_listens, rec);
+ g_free(rec);
+ }
+
+ g_strfreev(ports);
}
static void sig_dump(CLIENT_REC *client, const char *data)
@@ -676,6 +824,11 @@ static void sig_dump(CLIENT_REC *client, const char *data)
void proxy_listen_init(void)
{
+ if (enabled) {
+ return;
+ }
+ enabled = TRUE;
+
next_line = g_string_new(NULL);
proxy_clients = NULL;
@@ -697,6 +850,11 @@ void proxy_listen_init(void)
void proxy_listen_deinit(void)
{
+ if (!enabled) {
+ return;
+ }
+ enabled = FALSE;
+
while (proxy_listens != NULL)
remove_listen(proxy_listens->data);
g_string_free(next_line, TRUE);
diff --git a/src/irc/proxy/proxy.c b/src/irc/proxy/proxy.c
index c8f47bdf..2875d2c2 100644
--- a/src/irc/proxy/proxy.c
+++ b/src/irc/proxy/proxy.c
@@ -23,11 +23,61 @@
#include "settings.h"
#include "levels.h"
+#include "fe-common/core/printtext.h"
+
+/* SYNTAX: IRSSIPROXY STATUS */
+static void cmd_irssiproxy_status(const char *data, IRC_SERVER_REC *server)
+{
+ GSList *tmp;
+
+ if (!settings_get_bool("irssiproxy")) {
+ printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy is currently disabled");
+ return;
+ }
+
+
+ printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: Currently connected clients: %d",
+ g_slist_length(proxy_clients));
+
+ for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+ CLIENT_REC *rec = tmp->data;
+
+ printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
+ " %s connect%s to %s (%s)",
+ rec->addr,
+ rec->connected ? "ed" : "ing",
+ rec->listen->port_or_path, rec->listen->ircnet);
+ }
+}
+
+/* SYNTAX: IRSSIPROXY */
+static void cmd_irssiproxy(const char *data, IRC_SERVER_REC *server, void *item)
+{
+ if (*data == '\0') {
+ cmd_irssiproxy_status(data, server);
+ return;
+ }
+
+ command_runsub("irssiproxy", data, server, item);
+}
+
+static void irc_proxy_setup_changed(void)
+{
+ if (settings_get_bool("irssiproxy")) {
+ proxy_listen_init();
+ } else {
+ proxy_listen_deinit();
+ }
+}
+
void irc_proxy_init(void)
{
settings_add_str("irssiproxy", "irssiproxy_ports", "");
settings_add_str("irssiproxy", "irssiproxy_password", "");
settings_add_str("irssiproxy", "irssiproxy_bind", "");
+ settings_add_bool("irssiproxy", "irssiproxy", TRUE);
if (*settings_get_str("irssiproxy_password") == '\0') {
/* no password - bad idea! */
@@ -43,7 +93,14 @@ void irc_proxy_init(void)
"... to set them.");
}
- proxy_listen_init();
+ command_bind("irssiproxy", NULL, (SIGNAL_FUNC) cmd_irssiproxy);
+ command_bind("irssiproxy status", NULL, (SIGNAL_FUNC) cmd_irssiproxy_status);
+
+ signal_add_first("setup changed", (SIGNAL_FUNC) irc_proxy_setup_changed);
+
+ if (settings_get_bool("irssiproxy")) {
+ proxy_listen_init();
+ }
settings_check();
module_register("proxy", "irc");
}
@@ -52,3 +109,8 @@ void irc_proxy_deinit(void)
{
proxy_listen_deinit();
}
+
+void irc_proxy_abicheck(int *version)
+{
+ *version = IRSSI_ABI_VERSION;
+}
diff --git a/src/irc/proxy/proxy.h b/src/irc/proxy/proxy.h
index 4ddc9da9..620ea605 100644
--- a/src/irc/proxy/proxy.h
+++ b/src/irc/proxy/proxy.h
@@ -9,16 +9,18 @@
typedef struct {
int port;
+ char *port_or_path;
char *ircnet;
int tag;
GIOChannel *handle;
GSList *clients;
+
} LISTEN_REC;
typedef struct {
- char *nick, *host;
+ char *nick, *addr;
NET_SENDBUF_REC *handle;
int recv_tag;
char *proxy_address;
@@ -28,6 +30,7 @@ typedef struct {
unsigned int user_sent:1;
unsigned int connected:1;
unsigned int want_ctcp:1;
+ unsigned int multiplex:1;
} CLIENT_REC;
#endif