diff --git a/.travis.yml b/.travis.yml
index 35226645..c8bbb6d8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,4 @@
+sudo: false
language: perl
perl:
- "5.20-shrplib"
@@ -7,28 +8,19 @@ env:
- CC=clang
- CC=gcc
+addons:
+ apt:
+ packages:
+ - libperl-dev
+ - elinks
+
before_install:
- - sudo apt-get update -qq
- perl -V
- - sudo apt-get build-dep -qq irssi
- - sudo apt-get install -qq lynx
install: true
script:
- - ./autogen.sh --with-proxy --with-bot --with-perl=module
+ - ./autogen.sh --with-proxy --with-bot --with-perl=module --prefix=$HOME/irssi-build
- cat config.log
- make
- - sudo make install
-
-notifications:
- irc:
- channels:
- - "irc.freenode.net#irssi"
- template:
- - "%{repository} (%{commit}: %{author}): %{message}"
- - "Build details : %{build_url}"
- on_success: always
- on_failure: always
- use_notice: true
- skip_join: true
+ - make install
diff --git a/INSTALL b/INSTALL
index 719369de..a83a6c76 100644
--- a/INSTALL
+++ b/INSTALL
@@ -94,6 +94,10 @@ things that can go wrong:
- If configure complains that it doesn't find some perl stuff, you're
probably missing libperl.so or libperl.a. In debian, you'll need to do
apt-get install libperl-dev
+ - For unprivileged home directory installations, using the local::lib CPAN
+ module is recommended. Read its docs, bootstrap it if needed, ensure that
+ the environment variables are set before running the configure script, and
+ append "--with-perl-lib=site" to the configure parameters to use it.
You can verify that the perl module is loaded and working with "/LOAD"
command. It should print something like:
diff --git a/NEWS b/NEWS
index d92f22e8..511f495a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,6 @@
v0.8.18-head 2014-XX-YY The Irssi team
diff --git a/irssi-version.sh b/irssi-version.sh
index 1e9eae34..1fc6a558 100755
--- a/irssi-version.sh
+++ b/irssi-version.sh
@@ -22,6 +22,7 @@ if echo "${VERSION}" | grep -q -- -head; then
# Because the git tag won't yet include the next release we modify the git
# describe output using the version defined from configure.ac.
version="${new_version}-$(echo "${git_version}" | sed 's/^.*-[0-9]\+-//')"
+ echo "#undef PACKAGE_VERSION"
echo "#define PACKAGE_VERSION \"${version}\""
fi
fi
diff --git a/src/common.h b/src/common.h
index 1b99753b..86a079fe 100644
--- a/src/common.h
+++ b/src/common.h
@@ -43,23 +43,6 @@
# include
#endif
-#if !GLIB_CHECK_VERSION(2,10,0)
-#define g_slice_alloc(size) g_malloc(size)
-#define g_slice_alloc0(size) g_malloc0(size)
-#define g_slice_free1(size, mem) g_free(mem)
-#define g_slice_new(type) g_new(type, 1)
-#define g_slice_new0(type) g_new0(type, 1)
-#define g_slice_free(type, mem) g_free(mem)
-#endif
-
-#if !GLIB_CHECK_VERSION(2,16,0)
-#define g_strcmp0(a, b) (!a ? -(a != b) : (!b ? (a != b) : strcmp(a, b)))
-#endif
-
-#ifdef USE_GC
-# define g_free(x) G_STMT_START { (x) = NULL; } G_STMT_END
-#endif
-
#if defined (UOFF_T_INT)
typedef unsigned int uoff_t;
#elif defined (UOFF_T_LONG)
diff --git a/src/core/core.c b/src/core/core.c
index b9debbb5..bbeec6f4 100644
--- a/src/core/core.c
+++ b/src/core/core.c
@@ -156,11 +156,17 @@ static void sig_init_finished(void)
static char *fix_path(const char *str)
{
char *new_str = convert_home(str);
+
if (!g_path_is_absolute(new_str)) {
char *tmp_str = new_str;
- new_str = g_strdup_printf("%s/%s", g_get_current_dir(), tmp_str);
+ char *current_dir = g_get_current_dir();
+
+ new_str = g_build_path(G_DIR_SEPARATOR_S, current_dir, tmp_str, NULL);
+
+ g_free(current_dir);
g_free(tmp_str);
}
+
return new_str;
}
diff --git a/src/core/log-away.c b/src/core/log-away.c
index 681edcbf..e2a0120b 100644
--- a/src/core/log-away.c
+++ b/src/core/log-away.c
@@ -24,6 +24,7 @@
#include "log.h"
#include "servers.h"
#include "settings.h"
+#include "write-buffer.h"
static LOG_REC *awaylog;
static int away_filepos;
@@ -62,6 +63,9 @@ static void awaylog_open(void)
return;
}
+ /* Flush the dirty buffers to disk before acquiring the file position */
+ write_buffer_flush();
+
awaylog = log;
away_filepos = lseek(log->handle, 0, SEEK_CUR);
away_msgs = 0;
@@ -83,6 +87,9 @@ static void awaylog_close(void)
if (awaylog == log) awaylog = NULL;
+ /* Flush the dirty buffers to disk before showing the away log */
+ write_buffer_flush();
+
signal_emit("awaylog show", 3, log, GINT_TO_POINTER(away_msgs),
GINT_TO_POINTER(away_filepos));
log_close(log);
diff --git a/src/core/misc.c b/src/core/misc.c
index 395e47ad..88c27255 100644
--- a/src/core/misc.c
+++ b/src/core/misc.c
@@ -215,7 +215,7 @@ void gslist_free_full (GSList *list, GDestroyNotify free_func)
{
GSList *tmp;
- if (!list)
+ if (list == NULL)
return;
for (tmp = list; tmp != NULL; tmp = tmp->next)
diff --git a/src/core/nicklist.c b/src/core/nicklist.c
index a96b8a9e..770b0afc 100644
--- a/src/core/nicklist.c
+++ b/src/core/nicklist.c
@@ -571,6 +571,14 @@ int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick)
}
}
+int nick_match_msg_everywhere(CHANNEL_REC *channel, const char *msg, const char *nick)
+{
+ g_return_val_if_fail(nick != NULL, FALSE);
+ g_return_val_if_fail(msg != NULL, FALSE);
+
+ return stristr_full(msg, nick) != NULL;
+}
+
void nicklist_init(void)
{
signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
diff --git a/src/core/nicklist.h b/src/core/nicklist.h
index 55dfd5ef..5e0f4f75 100644
--- a/src/core/nicklist.h
+++ b/src/core/nicklist.h
@@ -55,6 +55,7 @@ int nicklist_compare(NICK_REC *p1, NICK_REC *p2, const char *nick_prefix);
/* Check is `msg' is meant for `nick'. */
int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick);
+int nick_match_msg_everywhere(CHANNEL_REC *channel, const char *msg, const char *nick);
void nicklist_init(void);
void nicklist_deinit(void);
diff --git a/src/core/rawlog.c b/src/core/rawlog.c
index 2fa6b850..875c0ef2 100644
--- a/src/core/rawlog.c
+++ b/src/core/rawlog.c
@@ -32,6 +32,7 @@
static int rawlog_lines;
static int signal_rawlog;
static int log_file_create_mode;
+static int log_dir_create_mode;
RAWLOG_REC *rawlog_create(void)
{
@@ -145,9 +146,13 @@ void rawlog_close(RAWLOG_REC *rawlog)
void rawlog_save(RAWLOG_REC *rawlog, const char *fname)
{
- char *path;
+ char *path, *dir;
int f;
+ dir = g_path_get_dirname(fname);
+ mkpath(dir, log_dir_create_mode);
+ g_free(dir);
+
path = convert_home(fname);
f = open(path, O_WRONLY | O_APPEND | O_CREAT, log_file_create_mode);
g_free(path);
@@ -165,6 +170,11 @@ static void read_settings(void)
{
rawlog_set_size(settings_get_int("rawlog_lines"));
log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
+ log_dir_create_mode = log_file_create_mode;
+ if (log_file_create_mode & 0400) log_dir_create_mode |= 0100;
+ if (log_file_create_mode & 0040) log_dir_create_mode |= 0010;
+ if (log_file_create_mode & 0004) log_dir_create_mode |= 0001;
+
}
static void cmd_rawlog(const char *data, SERVER_REC *server, void *item)
diff --git a/src/core/server-setup-rec.h b/src/core/server-setup-rec.h
index ecdde3f3..2c9614c7 100644
--- a/src/core/server-setup-rec.h
+++ b/src/core/server-setup-rec.h
@@ -8,6 +8,9 @@ char *address;
int port;
char *password;
+int sasl_mechanism;
+char *sasl_password;
+
char *ssl_cert;
char *ssl_pkey;
char *ssl_pass;
diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c
index ae97ecd2..f419035b 100644
--- a/src/core/servers-reconnect.c
+++ b/src/core/servers-reconnect.c
@@ -420,8 +420,8 @@ static void cmd_reconnect(const char *data, SERVER_REC *server)
cmd_param_error(CMDERR_NOT_CONNECTED);
rec = reconnects->data;
} else {
- if (g_ascii_strncasecmp(data, "RECON-", 6) == 0)
- data += 6;
+ if (g_ascii_strncasecmp(tag, "RECON-", 6) == 0)
+ tag += 6;
tagnum = atoi(tag);
rec = tagnum <= 0 ? NULL : reconnect_find_tag(tagnum);
diff --git a/src/core/servers.h b/src/core/servers.h
index d6afbdf5..f39c650b 100644
--- a/src/core/servers.h
+++ b/src/core/servers.h
@@ -18,7 +18,7 @@
(SERVER_CONNECT(conn) ? TRUE : FALSE)
#define server_ischannel(server, channel) \
- (server)->ischannel(server, channel)
+ ((server)->ischannel(server, channel))
/* all strings should be either NULL or dynamically allocated */
/* address and nick are mandatory, rest are optional */
diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c
index a475f056..a15850e2 100644
--- a/src/fe-common/core/fe-common-core.c
+++ b/src/fe-common/core/fe-common-core.c
@@ -326,8 +326,11 @@ static void autoconnect_servers(void)
if (autocon_server != NULL) {
/* connect to specified server */
- str = g_strdup_printf(autocon_password == NULL ? "%s %d" : "%s %d %s",
- autocon_server, autocon_port, autocon_password);
+ if (autocon_password == NULL)
+ str = g_strdup_printf("%s %d", autocon_server, autocon_port);
+ else
+ str = g_strdup_printf("%s %d %s", autocon_server, autocon_port, autocon_password);
+
signal_emit("command connect", 1, str);
g_free(str);
return;
@@ -447,18 +450,7 @@ void fe_common_core_finish_init(void)
signal_add_first("setup changed", (SIGNAL_FUNC) sig_setup_changed);
/* _after_ windows are created.. */
-#if GLIB_CHECK_VERSION(2,6,0)
g_log_set_default_handler((GLogFunc) glog_func, NULL);
-#else
- g_log_set_handler(G_LOG_DOMAIN,
- (GLogLevelFlags) (G_LOG_LEVEL_CRITICAL |
- G_LOG_LEVEL_WARNING),
- (GLogFunc) glog_func, NULL);
- g_log_set_handler("GLib",
- (GLogLevelFlags) (G_LOG_LEVEL_CRITICAL |
- G_LOG_LEVEL_WARNING),
- (GLogFunc) glog_func, NULL); /* send glib errors to the same place */
-#endif
if (setup_changed)
signal_emit("setup changed", 0);
diff --git a/src/fe-common/core/fe-messages.c b/src/fe-common/core/fe-messages.c
index 3bd2b666..d09c1b0b 100644
--- a/src/fe-common/core/fe-messages.c
+++ b/src/fe-common/core/fe-messages.c
@@ -183,7 +183,9 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
nickrec = nicklist_find(chanrec, nick);
for_me = !settings_get_bool("hilight_nick_matches") ? FALSE :
- nick_match_msg(chanrec, msg, server->nick);
+ !settings_get_bool("hilight_nick_matches_everywhere") ?
+ nick_match_msg(chanrec, msg, server->nick) :
+ nick_match_msg_everywhere(chanrec, msg, server->nick);
hilight = for_me ? NULL :
hilight_match_nick(server, target, nick, address, MSGLEVEL_PUBLIC, msg);
color = (hilight == NULL) ? NULL : hilight_get_color(hilight);
@@ -694,6 +696,7 @@ void fe_messages_init(void)
(GCompareFunc) g_direct_equal);
settings_add_bool("lookandfeel", "hilight_nick_matches", TRUE);
+ settings_add_bool("lookandfeel", "hilight_nick_matches_everywhere", FALSE);
settings_add_bool("lookandfeel", "emphasis", TRUE);
settings_add_bool("lookandfeel", "emphasis_replace", FALSE);
settings_add_bool("lookandfeel", "emphasis_multiword", FALSE);
diff --git a/src/fe-common/core/keyboard.c b/src/fe-common/core/keyboard.c
index f138d4e2..fed7f9cb 100644
--- a/src/fe-common/core/keyboard.c
+++ b/src/fe-common/core/keyboard.c
@@ -31,6 +31,8 @@
#include "fe-windows.h"
#include "printtext.h"
+#define MAX_EXPAND_RECURSION 100
+
GSList *keyinfos;
static GHashTable *keys, *default_keys;
@@ -171,7 +173,7 @@ KEYINFO_REC *key_info_find(const char *id)
return NULL;
}
-static int expand_key(const char *key, GSList **out);
+static int expand_key(const char *key, GSList **out, int *limit);
#define expand_out_char(out, c) \
{ \
@@ -188,13 +190,17 @@ static int expand_key(const char *key, GSList **out);
g_slist_free(out); out = NULL; \
}
-static int expand_combo(const char *start, const char *end, GSList **out)
+static int expand_combo(const char *start, const char *end, GSList **out, int *limit)
{
KEY_REC *rec;
KEYINFO_REC *info;
GSList *tmp, *tmp2, *list, *copy, *newout;
char *str, *p;
+ if ((*limit)-- < 0) {
+ return FALSE;
+ }
+
if (start == end) {
/* single key */
expand_out_char(*out, *start);
@@ -229,7 +235,7 @@ static int expand_combo(const char *start, const char *end, GSList **out)
/* only one way to generate the combo, good */
rec = list->data;
g_slist_free(list);
- return expand_key(rec->key, out);
+ return expand_key(rec->key, out, limit);
}
/* multiple ways to generate the combo -
@@ -244,7 +250,11 @@ static int expand_combo(const char *start, const char *end, GSList **out)
copy = g_slist_append(copy, g_string_new(str->str));
}
- if (!expand_key(rec->key, ©)) {
+ if (!expand_key(rec->key, ©, limit)) {
+ if (*limit < 0) {
+ return FALSE;
+ }
+
/* illegal key combo, remove from list */
expand_out_free(copy);
} else {
@@ -254,7 +264,11 @@ static int expand_combo(const char *start, const char *end, GSList **out)
rec = list->data;
g_slist_free(list);
- if (!expand_key(rec->key, out)) {
+ if (!expand_key(rec->key, out, limit)) {
+ if (*limit < 0) {
+ return FALSE;
+ }
+
/* illegal key combo, remove from list */
expand_out_free(*out);
}
@@ -264,12 +278,16 @@ static int expand_combo(const char *start, const char *end, GSList **out)
}
/* Expand key code - returns TRUE if successful. */
-static int expand_key(const char *key, GSList **out)
+static int expand_key(const char *key, GSList **out, int *limit)
{
GSList *tmp;
const char *start;
int last_hyphen;
+ if ((*limit)-- < 0) {
+ return FALSE;
+ }
+
/* meta-^W^Gf -> ^[-^W-^G-f */
start = NULL; last_hyphen = TRUE;
for (; *key != '\0'; key++) {
@@ -279,7 +297,7 @@ static int expand_key(const char *key, GSList **out)
continue;
}
- if (!expand_combo(start, key-1, out))
+ if (!expand_combo(start, key-1, out, limit))
return FALSE;
expand_out_char(*out, '-');
start = NULL;
@@ -332,7 +350,7 @@ static int expand_key(const char *key, GSList **out)
}
if (start != NULL)
- return expand_combo(start, key-1, out);
+ return expand_combo(start, key-1, out, limit);
for (tmp = *out; tmp != NULL; tmp = tmp->next) {
GString *str = tmp->data;
@@ -346,12 +364,13 @@ static int expand_key(const char *key, GSList **out)
static void key_states_scan_key(const char *key, KEY_REC *rec)
{
GSList *tmp, *out;
+ int limit = MAX_EXPAND_RECURSION;
if (g_strcmp0(rec->info->id, "key") == 0)
return;
out = g_slist_append(NULL, g_string_new(NULL));
- if (expand_key(key, &out)) {
+ if (expand_key(key, &out, &limit)) {
for (tmp = out; tmp != NULL; tmp = tmp->next) {
GString *str = tmp->data;
diff --git a/src/fe-common/irc/Makefile.am b/src/fe-common/irc/Makefile.am
index 463f145c..a5dd4c77 100644
--- a/src/fe-common/irc/Makefile.am
+++ b/src/fe-common/irc/Makefile.am
@@ -26,6 +26,7 @@ real_sources = \
fe-netsplit.c \
fe-common-irc.c \
fe-whois.c \
+ fe-sasl.c \
irc-completion.c \
module-formats.c
diff --git a/src/fe-common/irc/dcc/fe-dcc-chat.c b/src/fe-common/irc/dcc/fe-dcc-chat.c
index e2706ba3..5bcb3475 100644
--- a/src/fe-common/irc/dcc/fe-dcc-chat.c
+++ b/src/fe-common/irc/dcc/fe-dcc-chat.c
@@ -44,7 +44,7 @@ static void dcc_request(CHAT_DCC_REC *dcc)
if (!IS_DCC_CHAT(dcc)) return;
printformat(dcc->server, NULL, MSGLEVEL_DCC,
- ischannel(*dcc->target) ? IRCTXT_DCC_CHAT_CHANNEL :
+ server_ischannel(SERVER(dcc->server), dcc->target) ? IRCTXT_DCC_CHAT_CHANNEL :
IRCTXT_DCC_CHAT, dcc->id, dcc->addrstr,
dcc->port, dcc->target);
}
diff --git a/src/fe-common/irc/dcc/fe-dcc-get.c b/src/fe-common/irc/dcc/fe-dcc-get.c
index 451463f9..675cab65 100644
--- a/src/fe-common/irc/dcc/fe-dcc-get.c
+++ b/src/fe-common/irc/dcc/fe-dcc-get.c
@@ -21,6 +21,7 @@
#include "module.h"
#include "signals.h"
#include "levels.h"
+#include "servers.h"
#include "irc.h"
#include "dcc-file.h"
@@ -35,12 +36,12 @@ static void dcc_request(GET_DCC_REC *dcc)
{
char *sizestr;
- if (!IS_DCC_GET(dcc)) return;
+ if (!IS_DCC_GET(dcc)) return;
sizestr = dcc_get_size_str(dcc->size);
printformat(dcc->server, NULL, MSGLEVEL_DCC,
- ischannel(*dcc->target) ? IRCTXT_DCC_SEND_CHANNEL :
+ server_ischannel(SERVER(dcc->server), dcc->target) ? IRCTXT_DCC_SEND_CHANNEL :
IRCTXT_DCC_SEND, dcc->nick, dcc->addrstr,
dcc->port, dcc->arg, sizestr, dcc->target);
diff --git a/src/fe-common/irc/fe-common-irc.c b/src/fe-common/irc/fe-common-irc.c
index 72606263..4a3ef1d3 100644
--- a/src/fe-common/irc/fe-common-irc.c
+++ b/src/fe-common/irc/fe-common-irc.c
@@ -28,13 +28,11 @@
#include "themes.h"
#include "fe-irc-server.h"
+#include "fe-irc-channels.h"
void fe_irc_modules_init(void);
void fe_irc_modules_deinit(void);
-void fe_irc_channels_init(void);
-void fe_irc_channels_deinit(void);
-
void fe_irc_queries_init(void);
void fe_irc_queries_deinit(void);
@@ -71,6 +69,9 @@ void fe_netjoin_deinit(void);
void fe_whois_init(void);
void fe_whois_deinit(void);
+void fe_sasl_init(void);
+void fe_sasl_deinit(void);
+
void irc_completion_init(void);
void irc_completion_deinit(void);
@@ -93,6 +94,7 @@ void fe_common_irc_init(void)
fe_netsplit_init();
fe_netjoin_init();
fe_whois_init();
+ fe_sasl_init();
irc_completion_init();
settings_check();
@@ -118,6 +120,7 @@ void fe_common_irc_deinit(void)
fe_netsplit_deinit();
fe_netjoin_deinit();
fe_whois_deinit();
+ fe_sasl_deinit();
irc_completion_deinit();
theme_unregister();
diff --git a/src/fe-common/irc/fe-ctcp.c b/src/fe-common/irc/fe-ctcp.c
index 2321bb7a..cbb591ba 100644
--- a/src/fe-common/irc/fe-ctcp.c
+++ b/src/fe-common/irc/fe-ctcp.c
@@ -49,7 +49,7 @@ static void ctcp_default_msg(IRC_SERVER_REC *server, const char *data,
data = p+1;
}
- printformat(server, ischannel(*target) ? target : nick, MSGLEVEL_CTCPS,
+ printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS,
IRCTXT_CTCP_REQUESTED_UNKNOWN,
nick, addr, cmd, data, target);
g_free(cmd);
@@ -113,8 +113,8 @@ static void ctcp_default_reply(IRC_SERVER_REC *server, const char *data,
ctcpdata = ptr+1;
}
- printformat(server, ischannel(*target) ? target : nick, MSGLEVEL_CTCPS,
- ischannel(*target) ? IRCTXT_CTCP_REPLY_CHANNEL :
+ printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS,
+ server_ischannel(SERVER(server), target) ? IRCTXT_CTCP_REPLY_CHANNEL :
IRCTXT_CTCP_REPLY, ctcp, nick, ctcpdata, target);
g_free(ctcp);
}
@@ -137,7 +137,7 @@ static void ctcp_ping_reply(IRC_SERVER_REC *server, const char *data,
g_get_current_time(&tv);
usecs = get_timeval_diff(&tv, &tv2);
- printformat(server, ischannel(*target) ? target : nick, MSGLEVEL_CTCPS,
+ printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS,
IRCTXT_CTCP_PING_REPLY, nick, usecs/1000, usecs%1000);
}
diff --git a/src/fe-common/irc/fe-events-numeric.c b/src/fe-common/irc/fe-events-numeric.c
index ec7669ab..3eb124fc 100644
--- a/src/fe-common/irc/fe-events-numeric.c
+++ b/src/fe-common/irc/fe-events-numeric.c
@@ -400,7 +400,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, &target);
- if (!ischannel(*target)) {
+ if (!server_ischannel(SERVER(server), target)) {
/* nick unavailable */
printformat(server, NULL, MSGLEVEL_CRAP,
IRCTXT_NICK_UNAVAILABLE, target);
@@ -583,7 +583,7 @@ static void print_event_received(IRC_SERVER_REC *server, const char *data,
return;
ptr++;
- if (ischannel(*data)) /* directed at channel */
+ if (server_ischannel(SERVER(server), data)) /* directed at channel */
target = g_strndup(data, (int)(ptr - data - 1));
else if (!target_param || *ptr == ':' || (ptr2 = strchr(ptr, ' ')) == NULL)
target = NULL;
diff --git a/src/fe-common/irc/fe-events.c b/src/fe-common/irc/fe-events.c
index 5cde9e4b..850174c5 100644
--- a/src/fe-common/irc/fe-events.c
+++ b/src/fe-common/irc/fe-events.c
@@ -41,6 +41,7 @@
#include "fe-queries.h"
#include "fe-windows.h"
#include "fe-irc-server.h"
+#include "fe-irc-channels.h"
static void event_privmsg(IRC_SERVER_REC *server, const char *data,
const char *nick, const char *addr)
@@ -52,15 +53,19 @@ static void event_privmsg(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
if (nick == NULL) nick = server->real_address;
if (addr == NULL) addr = "";
- if (*target == '@' && ischannel(target[1])) {
+
+ if (fe_channel_is_opchannel(server, target)) {
/* Hybrid 6 feature, send msg to all ops in channel */
- recoded = recode_in(SERVER(server), msg, target+1);
+ const char *cleantarget = fe_channel_skip_prefix(server, target);
+ recoded = recode_in(SERVER(server), msg, cleantarget);
+
+ /* pass the original target to the signal, with the @+ here
+ * the other one is only needed for recode_in*/
signal_emit("message irc op_public", 5,
- server, recoded, nick, addr,
- get_visible_target(server, target+1));
+ server, recoded, nick, addr, target);
} else {
- recoded = recode_in(SERVER(server), msg, ischannel(*target) ? target : nick);
- signal_emit(ischannel(*target) ?
+ recoded = recode_in(SERVER(server), msg, server_ischannel(SERVER(server), target) ? target : nick);
+ signal_emit(server_ischannel(SERVER(server), target) ?
"message public" : "message private", 5,
server, recoded, nick, addr,
get_visible_target(server, target));
diff --git a/src/fe-common/irc/fe-irc-channels.c b/src/fe-common/irc/fe-irc-channels.c
index e884aef4..a2737fc3 100644
--- a/src/fe-common/irc/fe-irc-channels.c
+++ b/src/fe-common/irc/fe-irc-channels.c
@@ -31,6 +31,51 @@
#include "fe-windows.h"
#include "window-items.h"
+int fe_channel_is_opchannel(IRC_SERVER_REC *server, const char *target)
+{
+ const char *statusmsg;
+
+ /* Quick check */
+ if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0)
+ return FALSE;
+
+ statusmsg = g_hash_table_lookup(server->isupport, "statusmsg");
+ if (statusmsg == NULL)
+ statusmsg = "@+";
+
+ return strchr(statusmsg, *target) != NULL;
+}
+
+const char *fe_channel_skip_prefix(IRC_SERVER_REC *server, const char *target)
+{
+ const char *statusmsg;
+
+ /* Quick check */
+ if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0)
+ return target;
+
+ /* Exit early if target doesn't name a channel */
+ if (server_ischannel(SERVER(server), target) == FALSE)
+ return target;
+
+ statusmsg = g_hash_table_lookup(server->isupport, "statusmsg");
+
+ /* Hack: for bahamut 1.4 which sends neither STATUSMSG nor
+ * WALLCHOPS in 005, accept @#chan and @+#chan (but not +#chan) */
+ if (statusmsg == NULL && *target != '@')
+ return target;
+
+ if (statusmsg == NULL)
+ statusmsg = "@+";
+
+ /* Strip the leading statusmsg prefixes */
+ while (strchr(statusmsg, *target) != NULL) {
+ target++;
+ }
+
+ return target;
+}
+
static void sig_channel_rejoin(SERVER_REC *server, REJOIN_REC *rec)
{
g_return_if_fail(rec != NULL);
@@ -46,7 +91,7 @@ static void sig_event_forward(SERVER_REC *server, const char *data,
char *params, *from, *to;
params = event_get_params(data, 3, NULL, &from, &to);
- if (from != NULL && to != NULL && ischannel(*from) && ischannel(*to)) {
+ if (from != NULL && to != NULL && server_ischannel(server, from) && server_ischannel(server, to)) {
channel = irc_channel_find(server, from);
if (channel != NULL && irc_channel_find(server, to) == NULL) {
window_bind_add(window_item_window(channel),
diff --git a/src/fe-common/irc/fe-irc-channels.h b/src/fe-common/irc/fe-irc-channels.h
new file mode 100644
index 00000000..d05c91a5
--- /dev/null
+++ b/src/fe-common/irc/fe-irc-channels.h
@@ -0,0 +1,10 @@
+#ifndef __FE_IRC_CHANNELS_H
+#define __FE_IRC_CHANNELS_H
+
+int fe_channel_is_opchannel(IRC_SERVER_REC *server, const char *target);
+const char *fe_channel_skip_prefix(IRC_SERVER_REC *server, const char *target);
+
+void fe_irc_channels_init(void);
+void fe_irc_channels_deinit(void);
+
+#endif
diff --git a/src/fe-common/irc/fe-irc-messages.c b/src/fe-common/irc/fe-irc-messages.c
index 94d1ad4a..40ca306d 100644
--- a/src/fe-common/irc/fe-irc-messages.c
+++ b/src/fe-common/irc/fe-irc-messages.c
@@ -36,32 +36,8 @@
#include "fe-queries.h"
#include "window-items.h"
-
-static const char *skip_target(IRC_SERVER_REC *server, const char *target)
-{
- int i = 0;
- const char *val, *chars;
-
- /* Quick check */
- if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0)
- return target;
-
- /* Hack: for bahamut 1.4 which sends neither STATUSMSG nor
- * WALLCHOPS in 005, accept @#chan and @+#chan (but not +#chan) */
- val = g_hash_table_lookup(server->isupport, "STATUSMSG");
- if (val == NULL && *target != '@')
- return target;
- chars = val ? val : "@+";
- for(i = 0; target[i] != '\0'; i++) {
- if (strchr(chars, target[i]) == NULL)
- break;
- };
-
- if(ischannel(target[i]))
- target += i;
-
- return target;
-}
+#include "fe-irc-channels.h"
+#include "fe-irc-server.h"
static void sig_message_own_public(SERVER_REC *server, const char *msg,
const char *target, const char *origtarget)
@@ -72,7 +48,7 @@ static void sig_message_own_public(SERVER_REC *server, const char *msg,
if (!IS_IRC_SERVER(server))
return;
oldtarget = target;
- target = skip_target(IRC_SERVER(server), target);
+ target = fe_channel_skip_prefix(IRC_SERVER(server), target);
if (target != oldtarget) {
/* Hybrid 6 / Bahamut feature, send msg to all
ops / ops+voices in channel */
@@ -95,18 +71,28 @@ static void sig_message_irc_op_public(SERVER_REC *server, const char *msg,
const char *nick, const char *address,
const char *target)
{
- char *nickmode, *optarget;
+ char *nickmode, *optarget, *prefix;
+ const char *cleantarget;
- nickmode = channel_get_nickmode(channel_find(server, target),
+ /* only skip here so the difference can be stored in prefix */
+ cleantarget = fe_channel_skip_prefix(IRC_SERVER(server), target);
+ prefix = g_strndup(target, cleantarget - target);
+
+ /* and clean the rest here */
+ cleantarget = get_visible_target(IRC_SERVER(server), cleantarget);
+
+ nickmode = channel_get_nickmode(channel_find(server, cleantarget),
nick);
- optarget = g_strconcat("@", target, NULL);
- printformat_module("fe-common/core", server, target,
+ optarget = g_strconcat(prefix, cleantarget, NULL);
+
+ printformat_module("fe-common/core", server, cleantarget,
MSGLEVEL_PUBLIC,
TXT_PUBMSG_CHANNEL,
nick, optarget, msg, nickmode);
g_free(nickmode);
- g_free(optarget);
+ g_free(optarget);
+ g_free(prefix);
}
static void sig_message_own_wall(SERVER_REC *server, const char *msg,
@@ -117,7 +103,8 @@ static void sig_message_own_wall(SERVER_REC *server, const char *msg,
nickmode = channel_get_nickmode(channel_find(server, target),
server->nick);
- optarget = g_strconcat("@", target, NULL);
+ /* this is always @, skip_prefix is not needed here */
+ optarget = g_strconcat("@", target, NULL);
printformat_module("fe-common/core", server, target,
MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT |
MSGLEVEL_NO_ACT,
@@ -135,8 +122,8 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
char *freemsg = NULL;
oldtarget = target;
- target = skip_target(IRC_SERVER(server), target);
- if (ischannel(*target))
+ target = fe_channel_skip_prefix(IRC_SERVER(server), target);
+ if (server_ischannel(SERVER(server), target))
item = irc_channel_find(server, target);
else
item = irc_query_find(server, target);
@@ -146,7 +133,7 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
printformat(server, target,
MSGLEVEL_ACTIONS | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT |
- (ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS),
+ (server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS),
item != NULL && oldtarget == target ? IRCTXT_OWN_ACTION : IRCTXT_OWN_ACTION_TARGET,
server->nick, msg, oldtarget);
g_free_not_null(freemsg);
@@ -163,10 +150,10 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
int own = FALSE;
oldtarget = target;
- target = skip_target(IRC_SERVER(server), target);
+ target = fe_channel_skip_prefix(IRC_SERVER(server), target);
level = MSGLEVEL_ACTIONS |
- (ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
+ (server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
if (ignore_check(SERVER(server), nick, address, target, msg, level))
return;
@@ -175,7 +162,7 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
level | MSGLEVEL_NO_ACT))
level |= MSGLEVEL_NO_ACT;
- if (ischannel(*target)) {
+ if (server_ischannel(SERVER(server), target)) {
item = irc_channel_find(server, target);
} else {
own = (!g_strcmp0(nick, server->nick));
@@ -185,7 +172,7 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
if (settings_get_bool("emphasis"))
msg = freemsg = expand_emphasis(item, msg);
- if (ischannel(*target)) {
+ if (server_ischannel(SERVER(server), target)) {
/* channel action */
if (window_item_is_active(item) && target == oldtarget) {
/* message to active channel in window */
@@ -219,7 +206,7 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
static void sig_message_own_notice(IRC_SERVER_REC *server, const char *msg,
const char *target)
{
- printformat(server, skip_target(server, target), MSGLEVEL_NOTICES |
+ printformat(server, fe_channel_skip_prefix(server, target), MSGLEVEL_NOTICES |
MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
IRCTXT_OWN_NOTICE, target, msg);
}
@@ -232,7 +219,7 @@ static void sig_message_irc_notice(SERVER_REC *server, const char *msg,
int level = MSGLEVEL_NOTICES;
oldtarget = target;
- target = skip_target(IRC_SERVER(server), target);
+ target = fe_channel_skip_prefix(IRC_SERVER(server), target);
if (address == NULL || *address == '\0') {
/* notice from server */
@@ -245,16 +232,16 @@ static void sig_message_irc_notice(SERVER_REC *server, const char *msg,
}
if (ignore_check(server, nick, address,
- ischannel(*target) ? target : NULL,
+ server_ischannel(SERVER(server), target) ? target : NULL,
msg, level))
return;
if (ignore_check(server, nick, address,
- ischannel(*target) ? target : NULL,
+ server_ischannel(SERVER(server), target) ? target : NULL,
msg, level | MSGLEVEL_NO_ACT))
level |= MSGLEVEL_NO_ACT;
- if (ischannel(*target)) {
+ if (server_ischannel(SERVER(server), target)) {
/* notice in some channel */
printformat(server, target, level,
IRCTXT_NOTICE_PUBLIC, nick, oldtarget, msg);
@@ -270,7 +257,7 @@ static void sig_message_irc_notice(SERVER_REC *server, const char *msg,
static void sig_message_own_ctcp(IRC_SERVER_REC *server, const char *cmd,
const char *data, const char *target)
{
- printformat(server, skip_target(server, target), MSGLEVEL_CTCPS |
+ printformat(server, fe_channel_skip_prefix(server, target), MSGLEVEL_CTCPS |
MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
IRCTXT_OWN_CTCP, target, cmd, data);
}
@@ -282,8 +269,8 @@ static void sig_message_irc_ctcp(IRC_SERVER_REC *server, const char *cmd,
const char *oldtarget;
oldtarget = target;
- target = skip_target(server, target);
- printformat(server, ischannel(*target) ? target : nick, MSGLEVEL_CTCPS,
+ target = fe_channel_skip_prefix(server, target);
+ printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS,
IRCTXT_CTCP_REQUESTED, nick, addr, cmd, data, oldtarget);
}
diff --git a/src/fe-common/irc/fe-irc-queries.c b/src/fe-common/irc/fe-irc-queries.c
index 0861c9e4..b2faefbc 100644
--- a/src/fe-common/irc/fe-irc-queries.c
+++ b/src/fe-common/irc/fe-irc-queries.c
@@ -63,7 +63,7 @@ static void event_privmsg(SERVER_REC *server, const char *data,
g_return_if_fail(data != NULL);
- if (nick == NULL || address == NULL || ischannel(*data) ||
+ if (nick == NULL || address == NULL || server_ischannel(server, data) ||
!settings_get_bool("query_track_nick_changes"))
return;
diff --git a/src/fe-common/irc/fe-ircnet.c b/src/fe-common/irc/fe-ircnet.c
index 6618edd7..bb0af313 100644
--- a/src/fe-common/irc/fe-ircnet.c
+++ b/src/fe-common/irc/fe-ircnet.c
@@ -56,7 +56,12 @@ static void cmd_network_list(void)
g_string_append_printf(str, "autosendcmd: %s, ", rec->autosendcmd);
if (rec->usermode != NULL)
g_string_append_printf(str, "usermode: %s, ", rec->usermode);
-
+ if (rec->sasl_mechanism != NULL)
+ g_string_append_printf(str, "sasl_mechanism: %s, ", rec->sasl_mechanism);
+ if (rec->sasl_username != NULL)
+ g_string_append_printf(str, "sasl_username: %s, ", rec->sasl_username);
+ if (rec->sasl_password != NULL)
+ g_string_append_printf(str, "sasl_password: (pass), ");
if (rec->cmd_queue_speed > 0)
g_string_append_printf(str, "cmdspeed: %d, ", rec->cmd_queue_speed);
if (rec->max_cmds_at_once > 0)
@@ -82,10 +87,12 @@ static void cmd_network_list(void)
}
/* SYNTAX: NETWORK ADD [-nick ] [-user ] [-realname ]
- [-host ] [-usermode ] [-autosendcmd ]
- [-querychans ] [-whois ] [-msgs ]
- [-kicks ] [-modes ]
- [-cmdspeed ] [-cmdmax ] */
+ [-host ] [-usermode ] [-autosendcmd ]
+ [-querychans ] [-whois ] [-msgs ]
+ [-kicks ] [-modes ] [-cmdspeed ]
+ [-cmdmax ] [-sasl_mechanism ]
+ [-sasl_username ] [-sasl_password ]
+ */
static void cmd_network_add(const char *data)
{
GHashTable *optlist;
@@ -112,6 +119,9 @@ static void cmd_network_add(const char *data)
}
if (g_hash_table_lookup(optlist, "usermode")) g_free_and_null(rec->usermode);
if (g_hash_table_lookup(optlist, "autosendcmd")) g_free_and_null(rec->autosendcmd);
+ if (g_hash_table_lookup(optlist, "sasl_mechanism")) g_free_and_null(rec->sasl_mechanism);
+ if (g_hash_table_lookup(optlist, "sasl_username")) g_free_and_null(rec->sasl_username);
+ if (g_hash_table_lookup(optlist, "sasl_password")) g_free_and_null(rec->sasl_password);
}
value = g_hash_table_lookup(optlist, "kicks");
@@ -148,6 +158,14 @@ static void cmd_network_add(const char *data)
value = g_hash_table_lookup(optlist, "autosendcmd");
if (value != NULL && *value != '\0') rec->autosendcmd = g_strdup(value);
+ /* the validity of the parameters is checked in sig_server_setup_fill_chatnet */
+ value = g_hash_table_lookup(optlist, "sasl_mechanism");
+ if (value != NULL && *value != '\0') rec->sasl_mechanism = g_strdup(value);
+ value = g_hash_table_lookup(optlist, "sasl_username");
+ if (value != NULL && *value != '\0') rec->sasl_username = g_strdup(value);
+ value = g_hash_table_lookup(optlist, "sasl_password");
+ if (value != NULL && *value != '\0') rec->sasl_password = g_strdup(value);
+
ircnet_create(rec);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NETWORK_ADDED, name);
@@ -186,7 +204,8 @@ void fe_ircnet_init(void)
command_bind("network add", NULL, (SIGNAL_FUNC) cmd_network_add);
command_bind("network remove", NULL, (SIGNAL_FUNC) cmd_network_remove);
- command_set_options("network add", "-kicks -msgs -modes -whois -cmdspeed -cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode");
+ command_set_options("network add", "-kicks -msgs -modes -whois -cmdspeed "
+ "-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
}
void fe_ircnet_deinit(void)
diff --git a/src/fe-common/irc/fe-modes.c b/src/fe-common/irc/fe-modes.c
index e5317c0f..53e56c97 100644
--- a/src/fe-common/irc/fe-modes.c
+++ b/src/fe-common/irc/fe-modes.c
@@ -168,7 +168,7 @@ static void sig_message_mode(IRC_SERVER_REC *server, const char *channel,
mode, MSGLEVEL_MODES))
return;
- if (!ischannel(*channel)) {
+ if (!server_ischannel(SERVER(server), channel)) {
/* user mode change */
printformat(server, NULL, MSGLEVEL_MODES,
IRCTXT_USERMODE_CHANGE, mode, channel);
diff --git a/src/fe-common/irc/fe-netjoin.c b/src/fe-common/irc/fe-netjoin.c
index 35c463e4..f5cb081e 100644
--- a/src/fe-common/irc/fe-netjoin.c
+++ b/src/fe-common/irc/fe-netjoin.c
@@ -400,7 +400,7 @@ static void msg_mode(IRC_SERVER_REC *server, const char *channel,
int show;
g_return_if_fail(data != NULL);
- if (!ischannel(*channel) || addr != NULL)
+ if (!server_ischannel(SERVER(server), channel) || addr != NULL)
return;
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
diff --git a/src/fe-common/irc/fe-sasl.c b/src/fe-common/irc/fe-sasl.c
new file mode 100644
index 00000000..331b38b0
--- /dev/null
+++ b/src/fe-common/irc/fe-sasl.c
@@ -0,0 +1,48 @@
+/*
+ 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 "module-formats.h"
+#include "signals.h"
+#include "levels.h"
+
+#include "printtext.h"
+
+static void sig_sasl_success(IRC_SERVER_REC *server)
+{
+ printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_SASL_SUCCESS);
+}
+
+static void sig_sasl_failure(IRC_SERVER_REC *server, const char *reason)
+{
+ printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_SASL_ERROR, reason);
+}
+
+void fe_sasl_init(void)
+{
+ signal_add("server sasl success", (SIGNAL_FUNC) sig_sasl_success);
+ signal_add("server sasl failure", (SIGNAL_FUNC) sig_sasl_failure);
+}
+
+void fe_sasl_deinit(void)
+{
+ signal_remove("server sasl success", (SIGNAL_FUNC) sig_sasl_success);
+ signal_remove("server sasl failure", (SIGNAL_FUNC) sig_sasl_failure);
+}
diff --git a/src/fe-common/irc/module-formats.c b/src/fe-common/irc/module-formats.c
index 6eaf18e8..f7b074ec 100644
--- a/src/fe-common/irc/module-formats.c
+++ b/src/fe-common/irc/module-formats.c
@@ -44,6 +44,8 @@ FORMAT_REC fecommon_irc_formats[] = {
{ "setupserver_header", "%#Server Port Network Settings", 0 },
{ "setupserver_line", "%#%|$[!20]0 $[5]1 $[10]2 $3", 4, { 0, 1, 0, 0 } },
{ "setupserver_footer", "", 0 },
+ { "sasl_success", "SASL authentication succeeded", 0 },
+ { "sasl_error", "Cannot authenticate via SASL ($0)", 1, { 0 } },
/* ---- */
{ NULL, "Channels", 0 },
diff --git a/src/fe-common/irc/module-formats.h b/src/fe-common/irc/module-formats.h
index 34dd3efc..c45f4562 100644
--- a/src/fe-common/irc/module-formats.h
+++ b/src/fe-common/irc/module-formats.h
@@ -22,6 +22,8 @@ enum {
IRCTXT_SETUPSERVER_HEADER,
IRCTXT_SETUPSERVER_LINE,
IRCTXT_SETUPSERVER_FOOTER,
+ IRCTXT_SASL_SUCCESS,
+ IRCTXT_SASL_ERROR,
IRCTXT_FILL_2,
diff --git a/src/fe-text/gui-printtext.c b/src/fe-text/gui-printtext.c
index 547d39c9..d8272df5 100644
--- a/src/fe-text/gui-printtext.c
+++ b/src/fe-text/gui-printtext.c
@@ -220,16 +220,15 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
get_colors(flags, &fg, &bg, &attr);
if (window == NULL) {
- g_return_if_fail(next_xpos != -1);
+ g_return_if_fail(next_xpos != -1);
term_set_color2(root_window, attr, fg, bg);
term_move(root_window, next_xpos, next_ypos);
if (flags & GUI_PRINT_FLAG_CLRTOEOL)
term_clrtoeol(root_window);
- term_addstr(root_window, str);
- next_xpos += strlen(str); /* FIXME utf8 or big5 */
- return;
+ next_xpos += term_addstr(root_window, str);
+ return;
}
lineinfo.level = dest == NULL ? 0 : dest->level;
diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c
index edc4c55d..ec61e317 100644
--- a/src/fe-text/gui-readline.c
+++ b/src/fe-text/gui-readline.c
@@ -237,7 +237,7 @@ static void paste_buffer_join_lines(GArray *buf)
last_lf = FALSE;
if (++line_len >= 400 && last_lf_pos != NULL) {
memmove(last_lf_pos+1, last_lf_pos,
- dest - last_lf_pos);
+ (dest - last_lf_pos) * sizeof(unichar));
*last_lf_pos = '\n'; last_lf_pos = NULL;
line_len = 0;
dest++;
diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c
index 6d5b9b13..b1fa5e22 100644
--- a/src/fe-text/irssi.c
+++ b/src/fe-text/irssi.c
@@ -286,22 +286,6 @@ static void winsock_init(void)
}
#endif
-#ifdef USE_GC
-#ifdef HAVE_GC_H
-# include
-#else
-# include
-#endif
-
-GMemVTable gc_mem_table = {
- GC_malloc,
- GC_realloc,
- GC_free,
-
- NULL, NULL, NULL
-};
-#endif
-
int main(int argc, char **argv)
{
static int version = 0;
@@ -312,10 +296,6 @@ int main(int argc, char **argv)
};
int loglev;
-#ifdef USE_GC
- g_mem_set_vtable(&gc_mem_table);
-#endif
-
core_register_options();
fe_common_core_register_options();
args_register(options);
@@ -369,9 +349,6 @@ int main(int argc, char **argv)
/* Does the same as g_main_run(main_loop), except we
can call our dirty-checker after each iteration */
while (!quitting) {
-#ifdef USE_GC
- GC_collect_a_little();
-#endif
if (!dummy) term_refresh_freeze();
g_main_iteration(TRUE);
if (!dummy) term_refresh_thaw();
diff --git a/src/fe-text/statusbar-items.c b/src/fe-text/statusbar-items.c
index 044c2fa0..e3e0c2a6 100644
--- a/src/fe-text/statusbar-items.c
+++ b/src/fe-text/statusbar-items.c
@@ -347,10 +347,14 @@ static void item_lag(SBAR_ITEM_REC *item, int get_size_only)
last_lag_unknown = lag_unknown;
if (lag_unknown) {
- g_snprintf(str, sizeof(str), "%d (?""?)", lag/100);
+ // "??)" in C becomes ']'
+ // See: https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#C
+ g_snprintf(str, sizeof(str), "%d (?""?)", lag / 100);
} else {
- g_snprintf(str, sizeof(str),
- lag%100 == 0 ? "%d" : "%d.%02d", lag/100, lag%100);
+ if (lag % 100 == 0)
+ g_snprintf(str, sizeof(str), "%d", lag / 100);
+ else
+ g_snprintf(str, sizeof(str), "%d.%02d", lag / 100, lag % 100);
}
statusbar_item_default_handler(item, get_size_only,
diff --git a/src/fe-text/term-terminfo.c b/src/fe-text/term-terminfo.c
index ded79c28..27be904e 100644
--- a/src/fe-text/term-terminfo.c
+++ b/src/fe-text/term-terminfo.c
@@ -522,15 +522,36 @@ void term_add_unichar(TERM_WINDOW *window, unichar chr)
}
}
-void term_addstr(TERM_WINDOW *window, const char *str)
+int term_addstr(TERM_WINDOW *window, const char *str)
{
- int len;
+ int len, raw_len;
+ unichar tmp;
+ const char *ptr;
if (vcmove) term_move_real();
- len = strlen(str); /* FIXME utf8 or big5 */
+
+ len = 0;
+ raw_len = strlen(str);
+
+ /* The string length depends on the terminal encoding */
+
+ ptr = str;
+
+ if (term_type == TERM_TYPE_UTF8) {
+ while (*ptr != '\0') {
+ tmp = g_utf8_get_char(ptr);
+ len += unichar_isprint(tmp) ? mk_wcwidth(tmp) : 1;
+ ptr = g_utf8_next_char(ptr);
+ }
+ } else
+ len = raw_len;
+
term_printed_text(len);
- fwrite(str, 1, len, window->term->out);
+ /* Use strlen() here since we need the number of raw bytes */
+ fwrite(str, 1, raw_len, window->term->out);
+
+ return len;
}
void term_clrtoeol(TERM_WINDOW *window)
diff --git a/src/fe-text/term.h b/src/fe-text/term.h
index cdcc787a..f0a76c42 100644
--- a/src/fe-text/term.h
+++ b/src/fe-text/term.h
@@ -83,7 +83,7 @@ void term_set_color(TERM_WINDOW *window, int col);
void term_move(TERM_WINDOW *window, int x, int y);
void term_addch(TERM_WINDOW *window, char chr);
void term_add_unichar(TERM_WINDOW *window, unichar chr);
-void term_addstr(TERM_WINDOW *window, const char *str);
+int term_addstr(TERM_WINDOW *window, const char *str);
void term_clrtoeol(TERM_WINDOW *window);
void term_move_cursor(int x, int y);
diff --git a/src/fe-text/terminfo-core.c b/src/fe-text/terminfo-core.c
index 0516cc5f..60927f1a 100644
--- a/src/fe-text/terminfo-core.c
+++ b/src/fe-text/terminfo-core.c
@@ -50,62 +50,66 @@ TERM_REC *current_term;
/* Define only what we might need */
static TERMINFO_REC tcaps[] = {
- /* Terminal size */
- { "cols", "co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, width) },
- { "lines", "li", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, height) },
+ /* Terminal size */
+ { "cols", "co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, width) },
+ { "lines", "li", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, height) },
- /* Cursor movement */
- { "smcup", "ti", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smcup) },
- { "rmcup", "te", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmcup) },
- { "cup", "cm", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cup) },
- { "hpa", "ch", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_hpa) },
- { "vpa", "vh", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_vpa) },
- { "cub1", "le", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cub1) },
- { "cuf1", "nd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cuf1) },
- { "civis", "vi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_civis) },
- { "cnorm", "ve", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cnorm) },
+ /* Cursor movement */
+ { "smcup", "ti", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smcup) },
+ { "rmcup", "te", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmcup) },
+ { "cup", "cm", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cup) },
+ { "hpa", "ch", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_hpa) },
+ { "vpa", "vh", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_vpa) },
+ { "cub1", "le", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cub1) },
+ { "cuf1", "nd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cuf1) },
+ { "civis", "vi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_civis) },
+ { "cnorm", "ve", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cnorm) },
- /* Scrolling */
- { "csr", "cs", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_csr) },
- { "wind", "wi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_wind) },
- { "ri", "sr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ri) },
- { "rin", "SR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rin) },
- { "ind", "sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ind) },
- { "indn", "SF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_indn) },
- { "il", "AL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il) },
- { "il1", "al", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il1) },
- { "dl", "DL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl) },
- { "dl1", "dl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl1) },
+ /* Scrolling */
+ { "csr", "cs", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_csr) },
+ { "wind", "wi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_wind) },
+ { "ri", "sr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ri) },
+ { "rin", "SR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rin) },
+ { "ind", "sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ind) },
+ { "indn", "SF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_indn) },
+ { "il", "AL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il) },
+ { "il1", "al", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il1) },
+ { "dl", "DL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl) },
+ { "dl1", "dl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl1) },
/* Clearing screen */
- { "clear", "cl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_clear) },
- { "ed", "cd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ed) },
+ { "clear", "cl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_clear) },
+ { "ed", "cd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ed) },
- /* Clearing to end of line */
- { "el", "ce", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_el) },
+ /* Clearing to end of line */
+ { "el", "ce", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_el) },
- /* Repeating character */
- { "rep", "rp", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rep) },
+ /* Repeating character */
+ { "rep", "rp", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rep) },
/* Colors */
- { "colors", "Co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, TI_colors) },
- { "sgr0", "me", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sgr0) },
- { "smul", "us", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smul) },
- { "rmul", "ue", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmul) },
- { "smso", "so", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smso) },
- { "rmso", "se", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmso) },
- { "sitm", "ZH", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sitm) },
- { "ritm", "ZR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ritm) },
- { "bold", "md", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bold) },
- { "blink", "mb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_blink) },
- { "rev", "mr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rev) },
- { "setaf", "AF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setaf) },
- { "setab", "AB", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setab) },
- { "setf", "Sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setf) },
- { "setb", "Sb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setb) },
+ { "colors", "Co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, TI_colors) },
+ { "sgr0", "me", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sgr0) },
+ { "smul", "us", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smul) },
+ { "rmul", "ue", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmul) },
+ { "smso", "so", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smso) },
+ { "rmso", "se", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmso) },
+ { "sitm", "ZH", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sitm) },
+ { "ritm", "ZR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ritm) },
+ { "bold", "md", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bold) },
+ { "blink", "mb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_blink) },
+ { "rev", "mr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rev) },
+ { "setaf", "AF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setaf) },
+ { "setab", "AB", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setab) },
+ { "setf", "Sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setf) },
+ { "setb", "Sb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setb) },
- /* Beep */
- { "bel", "bl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bel) },
+ /* Beep */
+ { "bel", "bl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bel) },
+
+ /* Keyboard-transmit mode */
+ { "smkx", "ks", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smkx) },
+ { "rmkx", "ke", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmkx) },
};
/* Move cursor (cursor_address / cup) */
@@ -498,6 +502,9 @@ static void terminfo_input_init(TERM_REC *term)
memcpy(&term->tio, &term->old_tio, sizeof(term->tio));
term->tio.c_lflag &= ~(ICANON | ECHO); /* CBREAK, no ECHO */
+ /* Disable the ICRNL flag to disambiguate ^J and Enter, also disable the
+ * software flow control to leave ^Q and ^S ready to be bound */
+ term->tio.c_iflag &= ~(ICRNL | IXON | IXOFF);
term->tio.c_cc[VMIN] = 1; /* read() is satisfied after 1 char */
term->tio.c_cc[VTIME] = 0; /* No timer */
@@ -523,7 +530,11 @@ static void terminfo_input_deinit(TERM_REC *term)
void terminfo_cont(TERM_REC *term)
{
if (term->TI_smcup)
- tput(tparm(term->TI_smcup));
+ tput(tparm(term->TI_smcup));
+
+ if (term->TI_smkx)
+ tput(tparm(term->TI_smkx));
+
terminfo_input_init(term);
}
@@ -538,6 +549,9 @@ void terminfo_stop(TERM_REC *term)
if (term->TI_rmcup)
tput(tparm(term->TI_rmcup));
+ if (term->TI_rmkx)
+ tput(tparm(term->TI_rmkx));
+
/* reset input settings */
terminfo_input_deinit(term);
fflush(term->out);
diff --git a/src/fe-text/terminfo-core.h b/src/fe-text/terminfo-core.h
index 6ab4637d..21398791 100644
--- a/src/fe-text/terminfo-core.h
+++ b/src/fe-text/terminfo-core.h
@@ -88,6 +88,10 @@ struct _TERM_REC {
/* Beep */
char *TI_bel;
+
+ /* Keyboard-transmit mode */
+ const char *TI_smkx;
+ const char *TI_rmkx;
};
extern TERM_REC *current_term;
diff --git a/src/irc/core/Makefile.am b/src/irc/core/Makefile.am
index 7d885d20..20caaeb1 100644
--- a/src/irc/core/Makefile.am
+++ b/src/irc/core/Makefile.am
@@ -27,6 +27,7 @@ libirc_core_a_SOURCES = \
irc-servers-setup.c \
irc-session.c \
irc-cap.c \
+ sasl.c \
lag.c \
massjoin.c \
modes.c \
@@ -50,6 +51,7 @@ pkginc_irc_core_HEADERS = \
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 b60e681b..68dd45c0 100644
--- a/src/irc/core/bans.c
+++ b/src/irc/core/bans.c
@@ -186,7 +186,7 @@ static void command_set_ban(const char *data, IRC_SERVER_REC *server,
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 (!server_ischannel(SERVER(server), channel)) cmd_param_error(CMDERR_NOT_JOINED);
if (*nicks == '\0') {
if (g_strcmp0(data, "*") != 0)
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
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/irc-cap.c b/src/irc/core/irc-cap.c
index c5bf4e67..cd3ae677 100644
--- a/src/irc/core/irc-cap.c
+++ b/src/irc/core/irc-cap.c
@@ -1,3 +1,22 @@
+/* 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"
diff --git a/src/irc/core/irc-channels.c b/src/irc/core/irc-channels.c
index e38cb98b..682be4c2 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);
diff --git a/src/irc/core/irc-chatnets.c b/src/irc/core/irc-chatnets.c
index d757bf8d..0796e0cb 100644
--- a/src/irc/core/irc-chatnets.c
+++ b/src/irc/core/irc-chatnets.c
@@ -35,10 +35,13 @@ 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;
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 +51,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 +63,7 @@ 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->max_cmds_at_once > 0)
iconfig_node_set_int(node, "cmdmax", rec->max_cmds_at_once);
@@ -73,12 +80,23 @@ 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->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..2bb10fa9 100644
--- a/src/irc/core/irc-chatnets.h
+++ b/src/irc/core/irc-chatnets.h
@@ -17,12 +17,15 @@
struct _IRC_CHATNET_REC {
#include "chatnet-rec.h"
- char *usermode;
+ char *usermode;
+
+ 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 e7fb5067..47644753 100644
--- a/src/irc/core/irc-commands.c
+++ b/src/irc/core/irc-commands.c
@@ -170,8 +170,11 @@ static void cmd_part(const char *data, IRC_SERVER_REC *server,
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);
@@ -191,7 +194,7 @@ static void cmd_kick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
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);
@@ -219,8 +222,12 @@ static void cmd_topic(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *ite
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);
@@ -279,14 +286,14 @@ static void cmd_who(const char *data, IRC_SERVER_REC *server,
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))
return;
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;
}
@@ -295,8 +302,11 @@ static void cmd_who(const char *data, IRC_SERVER_REC *server,
*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);
}
@@ -502,8 +512,11 @@ static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
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);
}
@@ -913,9 +926,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)
diff --git a/src/irc/core/irc-core.c b/src/irc/core/irc-core.c
index e3ceeeef..a9221e02 100644
--- a/src/irc/core/irc-core.c
+++ b/src/irc/core/irc-core.c
@@ -27,6 +27,7 @@
#include "irc-channels.h"
#include "irc-queries.h"
#include "irc-cap.h"
+#include "sasl.h"
#include "irc-servers-setup.h"
#include "channels-setup.h"
@@ -119,6 +120,7 @@ void irc_core_init(void)
netsplit_init();
irc_expandos_init();
cap_init();
+ sasl_init();
settings_check();
module_register("core", "irc");
@@ -128,6 +130,7 @@ void irc_core_deinit(void)
{
signal_emit("chat protocol deinit", 1, chat_protocol_find("IRC"));
+ sasl_deinit();
cap_deinit();
irc_expandos_deinit();
netsplit_deinit();
@@ -140,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 5438509e..667016aa 100644
--- a/src/irc/core/irc-nicklist.c
+++ b/src/irc/core/irc-nicklist.c
@@ -394,7 +394,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-queries.c b/src/irc/core/irc-queries.c
index 72c17df1..12861744 100644
--- a/src/irc/core/irc-queries.c
+++ b/src/irc/core/irc-queries.c
@@ -60,8 +60,8 @@ 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)
diff --git a/src/irc/core/irc-servers-setup.c b/src/irc/core/irc-servers-setup.c
index 5659991b..f425b587 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,
@@ -79,6 +86,29 @@ 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;
+
+ 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;
+ conn->sasl_username = NULL;
+ conn->sasl_password = NULL;
+ }
+ else
+ g_warning("Unsupported SASL mechanism \"%s\" selected", ircnet->sasl_mechanism);
+ }
}
static void init_userinfo(void)
diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c
index e9920d91..81f0c269 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -33,6 +33,9 @@
#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"
@@ -72,13 +75,20 @@ static int isnickflag_func(SERVER_REC *server, char flag)
static int ischannel_func(SERVER_REC *server, const char *data)
{
- if (*data == '@') {
- /* @#channel, @+#channel */
+ IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
+ char *chantypes, *statusmsg;
+
+ 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 = "@+";
+
+ while (strchr(statusmsg, *data) != NULL)
data++;
- if (*data == '+' && ischannel(data[1]))
- return 1;
- }
- return ischannel(*data);
+
+ return strchr(chantypes, *data) != NULL;
}
static char **split_line(const SERVER_REC *server, const char *line,
@@ -215,6 +225,11 @@ 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') {
@@ -602,32 +617,59 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
GString *chans, *keys;
char *ret;
int use_keys;
+ char *rejoin_channels_mode;
g_return_val_if_fail(server != NULL, FALSE);
+ rejoin_channels_mode = g_strdup(settings_get_str("rejoin_channels_on_reconnect"));
+
+ if (rejoin_channels_mode == NULL ||
+ (g_ascii_strcasecmp(rejoin_channels_mode, "on") != 0 &&
+ g_ascii_strcasecmp(rejoin_channels_mode, "off") != 0 &&
+ g_ascii_strcasecmp(rejoin_channels_mode, "auto") != 0)) {
+ g_warning("Invalid value for 'rejoin_channels_on_reconnect', valid values are 'on', 'off', 'auto', using 'on' as default value.");
+ g_free(rejoin_channels_mode);
+ rejoin_channels_mode = g_strdup("on");
+ }
+
chans = g_string_new(NULL);
keys = g_string_new(NULL);
use_keys = FALSE;
+ /* do we want to rejoin channels in the first place? */
+ if(g_ascii_strcasecmp(rejoin_channels_mode, "off") == 0) {
+ g_string_free(chans, TRUE);
+ g_string_free(keys, TRUE);
+ g_free(rejoin_channels_mode);
+ return g_strdup("");
+ }
+
/* 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 && g_ascii_strcasecmp(rejoin_channels_mode, "auto") == 0) ||
+ g_ascii_strcasecmp(rejoin_channels_mode, "on") == 0) {
+ 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);
- 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 ((setup != NULL && setup->autojoin && g_ascii_strcasecmp(rejoin_channels_mode, "auto") == 0) ||
+ g_ascii_strcasecmp(rejoin_channels_mode, "on") == 0) {
+ 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 (chans->len > 0) {
@@ -639,6 +681,7 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
ret = chans->str;
g_string_free(chans, FALSE);
g_string_free(keys, TRUE);
+ g_free(rejoin_channels_mode);
return ret;
}
@@ -984,6 +1027,7 @@ void irc_server_init_isupport(IRC_SERVER_REC *server)
void irc_servers_init(void)
{
+ settings_add_str("servers", "rejoin_channels_on_reconnect", "on");
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 f809fab5..41c4b9c2 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;
diff --git a/src/irc/core/irc-session.c b/src/irc/core/irc-session.c
index ea65d8a5..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,6 +67,10 @@ 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(config, node, "isupport", NODE_TYPE_BLOCK);
isupport_data.config = config;
@@ -90,6 +96,15 @@ 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);
diff --git a/src/irc/core/irc.c b/src/irc/core/irc.c
index 509418b7..4dce3fcf 100644
--- a/src/irc/core/irc.c
+++ b/src/irc/core/irc.c
@@ -212,8 +212,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)
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 a812691c..32a0c169 100644
--- a/src/irc/core/modes.c
+++ b/src/irc/core/modes.c
@@ -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);
@@ -856,7 +856,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..a04eaf45
--- /dev/null
+++ b/src/irc/core/sasl.c
@@ -0,0 +1,178 @@
+/*
+ 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"
+
+#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 = -1;
+
+ 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 != -1) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = -1;
+ }
+
+ params = event_get_params(data, 2, NULL, &error);
+
+ signal_emit("server sasl fail", 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 != -1) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = -1;
+ }
+
+ 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 != -1) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = -1;
+ }
+
+ signal_emit("server sasl success", 1, server);
+
+ /* The authentication succeeded, time to finish the CAP negotiation */
+ cap_finish_negotiation(server);
+}
+
+static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ IRC_SERVER_CONNECT_REC *conn;
+ GString *req;
+ char *enc_req;
+
+ conn = server->connrec;
+
+ /* Stop the timer */
+ if (server->sasl_timeout != -1) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = -1;
+ }
+
+ 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.
+ * The whole request is then encoded in base64. */
+
+ req = g_string_new(NULL);
+
+ g_string_append(req, conn->sasl_username);
+ g_string_append_c(req, '\0');
+ g_string_append(req, conn->sasl_username);
+ g_string_append_c(req, '\0');
+ g_string_append(req, conn->sasl_password);
+
+ enc_req = g_base64_encode((const guchar *)req->str, req->len);
+
+ irc_send_cmdv(server, "AUTHENTICATE %s", enc_req);
+
+ g_free(enc_req);
+ g_string_free(req, TRUE);
+ break;
+
+ case SASL_MECHANISM_EXTERNAL:
+ /* Empty response */
+ irc_send_cmdv(server, "+");
+ break;
+ }
+
+ /* We expect a response within a reasonable time */
+ server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
+}
+
+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);
+}
+
+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);
+}
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 f0a285df..aeb9c010 100644
--- a/src/irc/core/servers-redirect.c
+++ b/src/irc/core/servers-redirect.c
@@ -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);
diff --git a/src/irc/dcc/dcc-autoget.c b/src/irc/dcc/dcc-autoget.c
index 38170c56..de23a5d1 100644
--- a/src/irc/dcc/dcc-autoget.c
+++ b/src/irc/dcc/dcc-autoget.c
@@ -51,7 +51,7 @@ 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
diff --git a/src/irc/dcc/dcc-get.c b/src/irc/dcc/dcc-get.c
index 1208b5c5..a8b1c967 100644
--- a/src/irc/dcc/dcc-get.c
+++ b/src/irc/dcc/dcc-get.c
@@ -226,6 +226,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 +251,6 @@ void sig_dccget_connected(GET_DCC_REC *dcc)
/* close/remove the temp file */
ret_errno = errno;
- close(temphandle);
unlink(tempfname);
g_free(tempfname);
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/proxy/dump.c b/src/irc/proxy/dump.c
index 455a2fe3..e39c21a6 100644
--- a/src/irc/proxy/dump.c
+++ b/src/irc/proxy/dump.c
@@ -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, '\r\n');
+ g_string_append(str, "\r\n");
proxy_outdata(client, "%s", str->str);
create_names_start(str, channel, client);
first = TRUE;
@@ -186,7 +186,7 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
}
g_slist_free(nicks);
- g_string_append_c(str, '\r\n');
+ g_string_append(str, "\r\n");
proxy_outdata(client, "%s", str->str);
g_string_free(str, TRUE);
diff --git a/src/irc/proxy/listen.c b/src/irc/proxy/listen.c
index 2b7c7bda..dcc94e6b 100644
--- a/src/irc/proxy/listen.c
+++ b/src/irc/proxy/listen.c
@@ -37,6 +37,8 @@ GSList *proxy_clients;
static GString *next_line;
static int ignore_next;
+static int enabled = FALSE;
+
static void remove_client(CLIENT_REC *rec)
{
g_return_if_fail(rec != NULL);
@@ -45,8 +47,8 @@ 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:%d disconnected", rec->host, rec->port);
g_free(rec->proxy_address);
net_sendbuffer_destroy(rec->handle, TRUE);
@@ -126,6 +128,10 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
/* 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:%d connected",
+ client->host, client->port);
client->connected = TRUE;
proxy_dump_data(client);
}
@@ -262,7 +268,7 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
ignore_next = TRUE;
if (*msg != '\001' || msg[strlen(msg)-1] != '\001') {
- signal_emit(ischannel(*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) {
@@ -347,6 +353,7 @@ static void sig_listen(LISTEN_REC *listen)
rec->listen = listen;
rec->handle = sendbuf;
rec->host = g_strdup(host);
+ rec->port = port;
if (g_strcmp0(listen->ircnet, "*") == 0) {
rec->proxy_address = g_strdup("irc.proxy");
rec->server = servers == NULL ? NULL : IRC_SERVER(servers->data);
@@ -361,9 +368,10 @@ static void sig_listen(LISTEN_REC *listen)
proxy_clients = g_slist_prepend(proxy_clients, rec);
rec->listen->clients = g_slist_prepend(rec->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:%d on port %d (%s)",
+ rec->host, rec->port, listen->port, listen->ircnet);
}
static void sig_incoming(IRC_SERVER_REC *server, const char *line)
@@ -634,7 +642,8 @@ static void remove_listen(LISTEN_REC *rec)
static void read_settings(void)
{
LISTEN_REC *rec;
- GSList *remove_listens;
+ GSList *remove_listens = NULL;
+ GSList *add_listens = NULL;
char **ports, **tmp, *ircnet, *port;
int portnum;
@@ -653,17 +662,30 @@ static void read_settings(void)
continue;
rec = find_listen(ircnet, portnum);
- if (rec == NULL)
- add_listen(ircnet, portnum);
- else
+ if (rec == NULL) {
+ rec = g_new0(LISTEN_REC, 1);
+ rec->ircnet = ircnet; /* borrow */
+ rec->port = portnum;
+ 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);
+ g_free(rec);
+ add_listens = g_slist_remove(add_listens, add_listens->data);
+ }
+
+ g_strfreev(ports);
}
static void sig_dump(CLIENT_REC *client, const char *data)
@@ -676,6 +698,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 +724,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..ce79e2b7 100644
--- a/src/irc/proxy/proxy.c
+++ b/src/irc/proxy/proxy.c
@@ -23,11 +23,60 @@
#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)
+{
+ if (!settings_get_bool("irssiproxy")) {
+ printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy is currently disabled");
+ return;
+ }
+
+ GSList *tmp;
+
+ 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:%d connect%s to %d (%s)",
+ rec->host, rec->port,
+ rec->connected ? "ed" : "ing",
+ rec->listen->port, 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 +92,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");
}
diff --git a/src/irc/proxy/proxy.h b/src/irc/proxy/proxy.h
index 4ddc9da9..158b0675 100644
--- a/src/irc/proxy/proxy.h
+++ b/src/irc/proxy/proxy.h
@@ -19,6 +19,7 @@ typedef struct {
typedef struct {
char *nick, *host;
+ int port;
NET_SENDBUF_REC *handle;
int recv_tag;
char *proxy_address;
diff --git a/src/perl/irc/Irc.xs b/src/perl/irc/Irc.xs
index 18665cb7..a245206c 100644
--- a/src/perl/irc/Irc.xs
+++ b/src/perl/irc/Irc.xs
@@ -149,6 +149,7 @@ static void perl_client_fill_hash(HV *hv, CLIENT_REC *client)
{
(void) hv_store(hv, "nick", 4, new_pv(client->nick), 0);
(void) hv_store(hv, "host", 4, new_pv(client->host), 0);
+ (void) hv_store(hv, "port", 4, newSViv(client->port), 0);
(void) hv_store(hv, "proxy_address", 13, new_pv(client->proxy_address), 0);
(void) hv_store(hv, "server", 6, iobject_bless(client->server), 0);
(void) hv_store(hv, "pass_sent", 9, newSViv(client->pass_sent), 0);
diff --git a/syntax.pl b/syntax.pl
index bf10451f..33bd12b4 100755
--- a/syntax.pl
+++ b/syntax.pl
@@ -10,7 +10,7 @@
# Remember to include the asterisk ('*').
$SRC_PATH='src';
-@files = `find src -name '*.c'`;
+@files = sort `find src -name '*.c'`;
foreach $file (@files) {
open (FILE, "$file");