summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/common.h18
-rw-r--r--src/core/Makefile.am5
-rw-r--r--src/core/channels-setup.c65
-rw-r--r--src/core/channels-setup.h3
-rw-r--r--src/core/channels.c33
-rw-r--r--src/core/channels.h1
-rw-r--r--src/core/chat-commands.c69
-rw-r--r--src/core/chatnets.c4
-rw-r--r--src/core/commands.c13
-rw-r--r--src/core/commands.h7
-rw-r--r--src/core/core.c14
-rw-r--r--src/core/expandos.c31
-rw-r--r--src/core/ignore.c89
-rw-r--r--src/core/ignore.h16
-rw-r--r--src/core/levels.c4
-rw-r--r--src/core/log-away.c7
-rw-r--r--src/core/log.c38
-rw-r--r--src/core/misc.c230
-rw-r--r--src/core/misc.h23
-rw-r--r--src/core/modules-load.c44
-rw-r--r--src/core/modules.c4
-rw-r--r--src/core/modules.h1
-rw-r--r--src/core/net-disconnect.c2
-rw-r--r--src/core/net-nonblock.c10
-rw-r--r--src/core/net-nonblock.h2
-rw-r--r--src/core/net-sendbuffer.c6
-rw-r--r--src/core/net-sendbuffer.h2
-rw-r--r--src/core/network-openssl.c398
-rw-r--r--src/core/network.c146
-rw-r--r--src/core/network.h14
-rw-r--r--src/core/nicklist.c12
-rw-r--r--src/core/nicklist.h1
-rw-r--r--src/core/query-rec.h2
-rw-r--r--src/core/rawlog.c17
-rw-r--r--src/core/recode.c20
-rw-r--r--src/core/recode.h2
-rw-r--r--src/core/server-connect-rec.h17
-rw-r--r--src/core/server-setup-rec.h20
-rw-r--r--src/core/servers-reconnect.c27
-rw-r--r--src/core/servers-setup.c208
-rw-r--r--src/core/servers-setup.h3
-rw-r--r--src/core/servers.c53
-rw-r--r--src/core/servers.h2
-rw-r--r--src/core/session.c32
-rw-r--r--src/core/settings.c131
-rw-r--r--src/core/settings.h19
-rw-r--r--src/core/special-vars.c23
-rw-r--r--src/core/special-vars.h7
-rw-r--r--src/core/tls.c214
-rw-r--r--src/core/tls.h90
-rw-r--r--src/core/utf8.c135
-rw-r--r--src/core/utf8.h56
-rw-r--r--src/core/wcwidth.c (renamed from src/fe-common/core/wcwidth.c)4
-rw-r--r--src/fe-common/core/Makefile.am7
-rw-r--r--src/fe-common/core/chat-completion.c85
-rw-r--r--src/fe-common/core/command-history.c57
-rw-r--r--src/fe-common/core/command-history.h1
-rw-r--r--src/fe-common/core/completion.c90
-rw-r--r--src/fe-common/core/fe-channels.c129
-rw-r--r--src/fe-common/core/fe-common-core.c39
-rw-r--r--src/fe-common/core/fe-core-commands.c3
-rw-r--r--src/fe-common/core/fe-exec.c17
-rw-r--r--src/fe-common/core/fe-help.c6
-rw-r--r--src/fe-common/core/fe-ignore.c30
-rw-r--r--src/fe-common/core/fe-log.c43
-rw-r--r--src/fe-common/core/fe-messages.c53
-rw-r--r--src/fe-common/core/fe-modules.c4
-rw-r--r--src/fe-common/core/fe-queries.c7
-rw-r--r--src/fe-common/core/fe-recode.c2
-rw-r--r--src/fe-common/core/fe-server.c97
-rw-r--r--src/fe-common/core/fe-settings.c65
-rw-r--r--src/fe-common/core/fe-tls.c94
-rw-r--r--src/fe-common/core/fe-tls.h25
-rw-r--r--src/fe-common/core/fe-windows.c236
-rw-r--r--src/fe-common/core/fe-windows.h1
-rw-r--r--src/fe-common/core/formats.c56
-rw-r--r--src/fe-common/core/formats.h4
-rw-r--r--src/fe-common/core/hilight-text.c226
-rw-r--r--src/fe-common/core/hilight-text.h11
-rw-r--r--src/fe-common/core/keyboard.c61
-rw-r--r--src/fe-common/core/module-formats.c20
-rw-r--r--src/fe-common/core/module-formats.h17
-rw-r--r--src/fe-common/core/module.h2
-rw-r--r--src/fe-common/core/printtext.c18
-rw-r--r--src/fe-common/core/printtext.h1
-rw-r--r--src/fe-common/core/themes.c14
-rw-r--r--src/fe-common/core/utf8.c26
-rw-r--r--src/fe-common/core/utf8.h17
-rw-r--r--src/fe-common/core/window-commands.c27
-rw-r--r--src/fe-common/core/windows-layout.c15
-rw-r--r--src/fe-common/irc/Makefile.am2
-rw-r--r--src/fe-common/irc/dcc/fe-dcc-chat-messages.c2
-rw-r--r--src/fe-common/irc/dcc/fe-dcc-chat.c4
-rw-r--r--src/fe-common/irc/dcc/fe-dcc-get.c5
-rw-r--r--src/fe-common/irc/fe-common-irc.c9
-rw-r--r--src/fe-common/irc/fe-ctcp.c8
-rw-r--r--src/fe-common/irc/fe-events-numeric.c10
-rw-r--r--src/fe-common/irc/fe-events.c17
-rw-r--r--src/fe-common/irc/fe-irc-channels.c44
-rw-r--r--src/fe-common/irc/fe-irc-channels.h10
-rw-r--r--src/fe-common/irc/fe-irc-commands.c16
-rw-r--r--src/fe-common/irc/fe-irc-messages.c114
-rw-r--r--src/fe-common/irc/fe-irc-queries.c9
-rw-r--r--src/fe-common/irc/fe-irc-server.c48
-rw-r--r--src/fe-common/irc/fe-ircnet.c63
-rw-r--r--src/fe-common/irc/fe-modes.c4
-rw-r--r--src/fe-common/irc/fe-netjoin.c47
-rw-r--r--src/fe-common/irc/fe-netsplit.c32
-rw-r--r--src/fe-common/irc/fe-sasl.c75
-rw-r--r--src/fe-common/irc/fe-whois.c12
-rw-r--r--src/fe-common/irc/module-formats.c2
-rw-r--r--src/fe-common/irc/module-formats.h2
-rw-r--r--src/fe-fuzz/Makefile.am25
-rw-r--r--src/fe-fuzz/irssi.c57
-rw-r--r--src/fe-fuzz/tokens.txt143
-rw-r--r--src/fe-text/Makefile.am23
-rw-r--r--src/fe-text/gui-entry.c184
-rw-r--r--src/fe-text/gui-entry.h29
-rw-r--r--src/fe-text/gui-printtext.c20
-rw-r--r--src/fe-text/gui-printtext.h1
-rw-r--r--src/fe-text/gui-readline.c289
-rw-r--r--src/fe-text/gui-windows.c10
-rw-r--r--src/fe-text/irssi.c136
-rw-r--r--src/fe-text/lastlog.c48
-rw-r--r--src/fe-text/mainwindows-layout.c3
-rw-r--r--src/fe-text/module-formats.c1
-rw-r--r--src/fe-text/module-formats.h1
-rw-r--r--src/fe-text/statusbar-config.c26
-rw-r--r--src/fe-text/statusbar-items.c38
-rw-r--r--src/fe-text/statusbar.c8
-rw-r--r--src/fe-text/term-curses.c415
-rw-r--r--src/fe-text/term-dummy.c106
-rw-r--r--src/fe-text/term-terminfo.c44
-rw-r--r--src/fe-text/term.h7
-rw-r--r--src/fe-text/terminfo-core.c189
-rw-r--r--src/fe-text/terminfo-core.h8
-rw-r--r--src/fe-text/textbuffer-view.c14
-rw-r--r--src/fe-text/textbuffer-view.h5
-rw-r--r--src/fe-text/textbuffer.c40
-rw-r--r--src/fe-text/tparm.c740
-rw-r--r--src/irc/core/Makefile.am4
-rw-r--r--src/irc/core/bans.c22
-rw-r--r--src/irc/core/channel-events.c8
-rw-r--r--src/irc/core/channel-rejoin.c4
-rw-r--r--src/irc/core/channels-query.c2
-rw-r--r--src/irc/core/irc-cap.c192
-rw-r--r--src/irc/core/irc-cap.h9
-rw-r--r--src/irc/core/irc-channels-setup.c6
-rw-r--r--src/irc/core/irc-channels.c43
-rw-r--r--src/irc/core/irc-chatnets.c26
-rw-r--r--src/irc/core/irc-chatnets.h9
-rw-r--r--src/irc/core/irc-commands.c274
-rw-r--r--src/irc/core/irc-core.c8
-rw-r--r--src/irc/core/irc-expandos.c38
-rw-r--r--src/irc/core/irc-nicklist.c38
-rw-r--r--src/irc/core/irc-nicklist.h3
-rw-r--r--src/irc/core/irc-queries.c28
-rw-r--r--src/irc/core/irc-servers-reconnect.c5
-rw-r--r--src/irc/core/irc-servers-setup.c36
-rw-r--r--src/irc/core/irc-servers.c136
-rw-r--r--src/irc/core/irc-servers.h13
-rw-r--r--src/irc/core/irc-session.c21
-rw-r--r--src/irc/core/irc.c8
-rw-r--r--src/irc/core/irc.h6
-rw-r--r--src/irc/core/modes.c25
-rw-r--r--src/irc/core/sasl.c329
-rw-r--r--src/irc/core/sasl.h34
-rw-r--r--src/irc/core/servers-redirect.c18
-rw-r--r--src/irc/dcc/dcc-autoget.c4
-rw-r--r--src/irc/dcc/dcc-chat.c16
-rw-r--r--src/irc/dcc/dcc-get.c40
-rw-r--r--src/irc/dcc/dcc-resume.c4
-rw-r--r--src/irc/dcc/dcc-send.c12
-rw-r--r--src/irc/dcc/dcc-server.c2
-rw-r--r--src/irc/dcc/dcc.c16
-rw-r--r--src/irc/dcc/dcc.h2
-rw-r--r--src/irc/flood/autoignore.c4
-rw-r--r--src/irc/flood/flood.c4
-rw-r--r--src/irc/notifylist/notify-commands.c3
-rw-r--r--src/irc/notifylist/notify-setup.c6
-rw-r--r--src/irc/notifylist/notifylist.c2
-rw-r--r--src/irc/proxy/dump.c40
-rw-r--r--src/irc/proxy/listen.c414
-rw-r--r--src/irc/proxy/proxy.c64
-rw-r--r--src/irc/proxy/proxy.h5
-rw-r--r--src/lib-config/get.c62
-rw-r--r--src/lib-config/iconfig.h4
-rw-r--r--src/lib-config/parse.c4
-rw-r--r--src/lib-config/set.c10
-rw-r--r--src/perl/Makefile.am14
-rw-r--r--src/perl/Makefile_silent.pm76
-rw-r--r--src/perl/common/Core.xs2
-rw-r--r--src/perl/common/Expando.xs11
-rw-r--r--src/perl/common/Irssi.pm3
-rw-r--r--src/perl/common/Makefile.PL.in2
-rwxr-xr-xsrc/perl/get-signals.pl1
-rw-r--r--src/perl/irc/Irc.xs148
-rw-r--r--src/perl/irc/Makefile.PL.in2
-rw-r--r--src/perl/irc/Server.xs9
-rw-r--r--src/perl/irc/module.h1
-rw-r--r--src/perl/irssi-core.pl4
-rw-r--r--src/perl/module.h2
-rw-r--r--src/perl/perl-common.c240
-rw-r--r--src/perl/perl-core.c34
-rw-r--r--src/perl/perl-core.h2
-rw-r--r--src/perl/perl-fe.c20
-rw-r--r--src/perl/perl-signals.c45
-rw-r--r--src/perl/perl-signals.h2
-rw-r--r--src/perl/textui/Makefile.PL.in2
-rw-r--r--src/perl/textui/Statusbar.xs9
-rw-r--r--src/perl/textui/TextUI.xs66
-rw-r--r--src/perl/ui/Makefile.PL.in2
-rw-r--r--src/perl/ui/UI.xs81
214 files changed, 6153 insertions, 3839 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 76a4af4f..a7fb2ee2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,6 +6,10 @@ if BUILD_IRSSIBOT
BOTUI=fe-none
endif
+if BUILD_IRSSIFUZZER
+FUZZERUI=fe-fuzz
+endif
+
if HAVE_PERL
PERLDIR=perl
endif
@@ -14,4 +18,4 @@ pkginc_srcdir=$(pkgincludedir)/src
pkginc_src_HEADERS = \
common.h
-SUBDIRS = lib-config core irc fe-common $(PERLDIR) $(TEXTUI) $(BOTUI)
+SUBDIRS = lib-config core irc fe-common $(PERLDIR) $(TEXTUI) $(BOTUI) $(FUZZERUI)
diff --git a/src/common.h b/src/common.h
index bb246962..43596580 100644
--- a/src/common.h
+++ b/src/common.h
@@ -6,6 +6,8 @@
#define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */
#define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */
+#define IRSSI_ABI_VERSION 7
+
#define DEFAULT_SERVER_ADD_PORT 6667
#ifdef HAVE_CONFIG_H
@@ -34,28 +36,12 @@
# include <dirent.h>
#endif
#include <fcntl.h>
-#ifdef WIN32
-# include <win32-compat.h>
-#endif
#include <glib.h>
#ifdef HAVE_GMODULE
# include <gmodule.h>
#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
-
-#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/Makefile.am b/src/core/Makefile.am
index fc32e17e..10bd035a 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -44,6 +44,9 @@ libcore_a_SOURCES = \
settings.c \
signals.c \
special-vars.c \
+ utf8.c \
+ wcwidth.c \
+ tls.c \
write-buffer.c
structure_headers = \
@@ -93,6 +96,8 @@ pkginc_core_HEADERS = \
settings.h \
signals.h \
special-vars.h \
+ utf8.h \
window-item-def.h \
+ tls.h \
write-buffer.h \
$(structure_headers)
diff --git a/src/core/channels-setup.c b/src/core/channels-setup.c
index e1289c23..4966d77d 100644
--- a/src/core/channels-setup.c
+++ b/src/core/channels-setup.c
@@ -30,17 +30,36 @@
GSList *setupchannels;
+static int compare_channel_setup (CONFIG_NODE *node, CHANNEL_SETUP_REC *channel)
+{
+ char *name, *chatnet;
+
+ name = config_node_get_str(node, "name", NULL);
+ chatnet = config_node_get_str(node, "chatnet", NULL);
+
+ if (g_strcmp0(name, channel->name) != 0 ||
+ g_strcmp0(chatnet, channel->chatnet) != 0)
+ return 1;
+
+ return 0;
+}
+
static void channel_setup_save(CHANNEL_SETUP_REC *channel)
{
- CONFIG_NODE *parentnode, *node;
- int index;
+ CONFIG_NODE *parent_node, *node;
+ GSList *config_node;
- index = g_slist_index(setupchannels, channel);
+ parent_node = iconfig_node_traverse("(channels", TRUE);
- parentnode = iconfig_node_traverse("(channels", TRUE);
- node = config_node_nth(parentnode, index);
- if (node == NULL)
- node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
+ /* Try to find this channel in the configuration */
+ config_node = g_slist_find_custom(parent_node->value, channel,
+ (GCompareFunc)compare_channel_setup);
+ if (config_node != NULL)
+ /* Let's update this channel record */
+ node = config_node->data;
+ else
+ /* Create a brand-new channel record */
+ node = iconfig_node_section(parent_node, NULL, NODE_TYPE_BLOCK);
iconfig_node_clear(node);
iconfig_node_set_str(node, "name", channel->name);
@@ -65,10 +84,21 @@ void channel_setup_create(CHANNEL_SETUP_REC *channel)
static void channel_config_remove(CHANNEL_SETUP_REC *channel)
{
- CONFIG_NODE *node;
+ CONFIG_NODE *parent_node;
+ GSList *config_node;
- node = iconfig_node_traverse("channels", FALSE);
- if (node != NULL) iconfig_node_list_remove(node, g_slist_index(setupchannels, channel));
+ parent_node = iconfig_node_traverse("channels", FALSE);
+
+ if (parent_node == NULL)
+ return;
+
+ /* Try to find this channel in the configuration */
+ config_node = g_slist_find_custom(parent_node->value, channel,
+ (GCompareFunc)compare_channel_setup);
+
+ if (config_node != NULL)
+ /* Delete the channel from the configuration */
+ iconfig_node_remove(parent_node, config_node->data);
}
static void channel_setup_destroy(CHANNEL_SETUP_REC *channel)
@@ -86,6 +116,21 @@ static void channel_setup_destroy(CHANNEL_SETUP_REC *channel)
g_free(channel);
}
+void channel_setup_remove_chatnet(const char *chatnet)
+{
+ GSList *tmp, *next;
+
+ g_return_if_fail(chatnet != NULL);
+
+ for (tmp = setupchannels; tmp != NULL; tmp = next) {
+ CHANNEL_SETUP_REC *rec = tmp->data;
+
+ next = tmp->next;
+ if (g_ascii_strcasecmp(rec->chatnet, chatnet) == 0)
+ channel_setup_remove(rec);
+ }
+}
+
void channel_setup_remove(CHANNEL_SETUP_REC *channel)
{
channel_config_remove(channel);
diff --git a/src/core/channels-setup.h b/src/core/channels-setup.h
index 61b828b2..3bb7da7f 100644
--- a/src/core/channels-setup.h
+++ b/src/core/channels-setup.h
@@ -21,6 +21,9 @@ void channels_setup_deinit(void);
void channel_setup_create(CHANNEL_SETUP_REC *channel);
void channel_setup_remove(CHANNEL_SETUP_REC *channel);
+/* Remove channels attached to chatnet */
+void channel_setup_remove_chatnet(const char *chatnet);
+
CHANNEL_SETUP_REC *channel_setup_find(const char *channel,
const char *chatnet);
diff --git a/src/core/channels.c b/src/core/channels.c
index 8235a4c7..9c3b92ba 100644
--- a/src/core/channels.c
+++ b/src/core/channels.c
@@ -167,7 +167,7 @@ static GSList *servers_find_chatnet_except(SERVER_REC *server)
SERVER_REC *rec = tmp->data;
if (server != rec && rec->connrec->chatnet != NULL &&
- strcmp(server->connrec->chatnet,
+ g_strcmp0(server->connrec->chatnet,
rec->connrec->chatnet) == 0) {
/* chatnets match */
list = g_slist_append(list, rec);
@@ -233,23 +233,46 @@ static int match_nick_flags(SERVER_REC *server, NICK_REC *nick, char flag)
void channel_send_autocommands(CHANNEL_REC *channel)
{
CHANNEL_SETUP_REC *rec;
- NICK_REC *nick;
- char **bots, **bot;
g_return_if_fail(IS_CHANNEL(channel));
if (channel->session_rejoin)
- return;
+ return;
rec = channel_setup_find(channel->name, channel->server->connrec->chatnet);
if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
return;
+ /* if the autosendcmd alone (with no -bots parameter) has been
+ * specified then send it right after joining the channel, when
+ * the WHO list hasn't been yet retrieved.
+ * Depending on the value of the 'channel_max_who_sync' option
+ * the WHO list might not be retrieved after the join event. */
+
if (rec->botmasks == NULL || !*rec->botmasks) {
/* just send the command. */
eval_special_string(rec->autosendcmd, "", channel->server, channel);
- return;
}
+}
+
+void channel_send_botcommands(CHANNEL_REC *channel)
+{
+ CHANNEL_SETUP_REC *rec;
+ NICK_REC *nick;
+ char **bots, **bot;
+
+ g_return_if_fail(IS_CHANNEL(channel));
+
+ if (channel->session_rejoin)
+ return;
+
+ rec = channel_setup_find(channel->name, channel->server->connrec->chatnet);
+ if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
+ return;
+
+ /* this case has already been handled by channel_send_autocommands */
+ if (rec->botmasks == NULL || !*rec->botmasks)
+ return;
/* find first available bot.. */
bots = g_strsplit(rec->botmasks, " ", -1);
diff --git a/src/core/channels.h b/src/core/channels.h
index 0839d69b..bd136fe2 100644
--- a/src/core/channels.h
+++ b/src/core/channels.h
@@ -31,6 +31,7 @@ void channel_change_visible_name(CHANNEL_REC *channel, const char *name);
/* Send the auto send command to channel */
void channel_send_autocommands(CHANNEL_REC *channel);
+void channel_send_botcommands(CHANNEL_REC *channel);
void channels_init(void);
void channels_deinit(void);
diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c
index 235a9fc4..c737b810 100644
--- a/src/core/chat-commands.c
+++ b/src/core/chat-commands.c
@@ -58,7 +58,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
return NULL;
}
- if (strcmp(password, "-") == 0)
+ if (g_strcmp0(password, "-") == 0)
*password = '\0';
/* check if -<chatnet> option is used to specify chat protocol */
@@ -73,6 +73,13 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
conn = server_create_conn(proto != NULL ? proto->id : -1, addr,
atoi(portstr), chatnet, password, nick);
+ if (conn == NULL) {
+ signal_emit("error command", 1,
+ GINT_TO_POINTER(CMDERR_NO_SERVER_DEFINED));
+ cmd_params_free(free_arg);
+ return NULL;
+ }
+
if (proto == NULL)
proto = chat_protocol_find_id(conn->chat_type);
@@ -92,31 +99,37 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
else if (g_hash_table_lookup(optlist, "4") != NULL)
conn->family = AF_INET;
- if (g_hash_table_lookup(optlist, "ssl") != NULL)
- conn->use_ssl = TRUE;
- if ((tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
- conn->ssl_cert = g_strdup(tmp);
- if ((tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
- conn->ssl_pkey = g_strdup(tmp);
- if ((tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL)
- conn->ssl_pass = g_strdup(tmp);
- if (g_hash_table_lookup(optlist, "ssl_verify") != NULL)
- conn->ssl_verify = TRUE;
- if ((tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
- conn->ssl_cafile = g_strdup(tmp);
- if ((tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
- conn->ssl_capath = g_strdup(tmp);
- if ((conn->ssl_capath != NULL && conn->ssl_capath[0] != '\0')
- || (conn->ssl_cafile != NULL && conn->ssl_cafile[0] != '\0'))
- conn->ssl_verify = TRUE;
- if ((conn->ssl_cert != NULL && conn->ssl_cert[0] != '\0') || conn->ssl_verify)
- conn->use_ssl = TRUE;
+ if (g_hash_table_lookup(optlist, "tls") != NULL || g_hash_table_lookup(optlist, "ssl") != NULL)
+ conn->use_tls = TRUE;
+ if ((tmp = g_hash_table_lookup(optlist, "tls_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
+ conn->tls_cert = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_pkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
+ conn->tls_pkey = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_pass")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL)
+ conn->tls_pass = g_strdup(tmp);
+ if (g_hash_table_lookup(optlist, "tls_verify") != NULL || g_hash_table_lookup(optlist, "ssl_verify") != NULL)
+ conn->tls_verify = TRUE;
+ if ((tmp = g_hash_table_lookup(optlist, "tls_cafile")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
+ conn->tls_cafile = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_capath")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
+ conn->tls_capath = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL)
+ conn->tls_ciphers = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_cert")) != NULL)
+ conn->tls_pinned_cert = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_pubkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_pubkey")) != NULL)
+ conn->tls_pinned_pubkey = g_strdup(tmp);
+ if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0')
+ || (conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0'))
+ conn->tls_verify = TRUE;
+ if ((conn->tls_cert != NULL && conn->tls_cert[0] != '\0') || conn->tls_verify)
+ conn->use_tls = TRUE;
if (g_hash_table_lookup(optlist, "!") != NULL)
conn->no_autojoin_channels = TRUE;
- if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
- conn->no_autosendcmd = TRUE;
+ if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
+ conn->no_autosendcmd = TRUE;
if (g_hash_table_lookup(optlist, "noproxy") != NULL)
g_free_and_null(conn->proxy);
@@ -138,6 +151,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
/* SYNTAX: CONNECT [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>]
[-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
+ [-ssl_ciphers <list>]
[-!] [-noautosendcmd]
[-noproxy] [-network <network>] [-host <hostname>]
[-rawlog <file>]
@@ -244,6 +258,7 @@ static void sig_default_command_server(const char *data, SERVER_REC *server,
/* SYNTAX: SERVER [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>]
[-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
+ [-ssl_ciphers <list>]
[-!] [-noautosendcmd]
[-noproxy] [-network <network>] [-host <hostname>]
[-rawlog <file>]
@@ -283,7 +298,7 @@ static void cmd_disconnect(const char *data, SERVER_REC *server)
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &tag, &msg))
return;
- if (*tag != '\0' && strcmp(tag, "*") != 0) {
+ if (*tag != '\0' && g_strcmp0(tag, "*") != 0) {
server = server_find_tag(tag);
if (server == NULL)
server = server_find_lookup_tag(tag);
@@ -321,7 +336,7 @@ static void cmd_quit(const char *data)
signal_emit("gui exit", 0);
}
-/* SYNTAX: MSG [-<server tag>] [-channel | -nick] <targets> <message> */
+/* SYNTAX: MSG [-<server tag>] [-channel | -nick] *|<targets> <message> */
static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
{
GHashTable *optlist;
@@ -343,7 +358,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
origtarget = target;
free_ret = FALSE;
- if (strcmp(target, ",") == 0 || strcmp(target, ".") == 0) {
+ if (g_strcmp0(target, ",") == 0 || g_strcmp0(target, ".") == 0) {
target = parse_special(&target, server, item,
NULL, &free_ret, NULL, 0);
if (target != NULL && *target == '\0') {
@@ -355,7 +370,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
}
if (target != NULL) {
- if (strcmp(target, "*") == 0) {
+ if (g_strcmp0(target, "*") == 0) {
/* send to active channel/query */
if (item == NULL)
cmd_param_error(CMDERR_NOT_JOINED);
@@ -483,7 +498,7 @@ void chat_commands_init(void)
signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server);
signal_add("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
- command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +host noproxy -rawlog noautosendcmd");
+ command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_pinned_cert +ssl_pinned_pubkey tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey +host noproxy -rawlog noautosendcmd");
command_set_options("msg", "channel nick");
}
diff --git a/src/core/chatnets.c b/src/core/chatnets.c
index 3c794ab4..e0a7a8d9 100644
--- a/src/core/chatnets.c
+++ b/src/core/chatnets.c
@@ -36,7 +36,7 @@ static void chatnet_config_save(CHATNET_REC *chatnet)
CONFIG_NODE *node;
node = iconfig_node_traverse("chatnets", TRUE);
- node = config_node_section(node, chatnet->name, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, chatnet->name, NODE_TYPE_BLOCK);
iconfig_node_clear(node);
iconfig_node_set_str(node, "type", chat_protocol_find_id(chatnet->chat_type)->name);
@@ -132,7 +132,7 @@ static void chatnet_read(CONFIG_NODE *node)
CHATNET_REC *rec;
char *type;
- if (node == NULL || node->key == NULL)
+ if (node == NULL || node->key == NULL || !is_node_list(node))
return;
type = config_node_get_str(node, "type", NULL);
diff --git a/src/core/commands.c b/src/core/commands.c
index ed82f44e..607baf77 100644
--- a/src/core/commands.c
+++ b/src/core/commands.c
@@ -666,7 +666,7 @@ get_optional_channel(WI_ITEM_REC *active_item, char **data, int require_name)
const char *ret;
char *tmp, *origtmp, *channel;
- if (active_item == NULL) {
+ if (active_item == NULL || active_item->server == NULL) {
/* no active channel in window, channel required */
return cmd_get_param(data);
}
@@ -674,11 +674,13 @@ get_optional_channel(WI_ITEM_REC *active_item, char **data, int require_name)
origtmp = tmp = g_strdup(*data);
channel = cmd_get_param(&tmp);
- if (strcmp(channel, "*") == 0 && !require_name) {
+ if (g_strcmp0(channel, "*") == 0 && IS_CHANNEL(active_item) &&
+ !require_name) {
/* "*" means active channel */
cmd_get_param(data);
ret = window_item_get_target(active_item);
- } else if (!server_ischannel(active_item->server, channel)) {
+ } else if (IS_CHANNEL(active_item) &&
+ !server_ischannel(active_item->server, channel)) {
/* we don't have channel parameter - use active channel */
ret = window_item_get_target(active_item);
} else {
@@ -748,6 +750,11 @@ int cmd_get_params(const char *data, gpointer *free_me, int count, ...)
if (cnt == 0 && count & PARAM_FLAG_GETREST) {
/* get rest */
arg = datad;
+
+ /* strip the trailing whitespace */
+ if (count & PARAM_FLAG_STRIP_TRAILING_WS) {
+ arg = g_strchomp(arg);
+ }
} else {
arg = (count & PARAM_FLAG_NOQUOTES) ?
cmd_get_param(&datad) :
diff --git a/src/core/commands.h b/src/core/commands.h
index c68c5b24..93d6276b 100644
--- a/src/core/commands.h
+++ b/src/core/commands.h
@@ -41,7 +41,8 @@ enum {
CMDERR_INVALID_TIME, /* invalid time specification */
CMDERR_INVALID_CHARSET, /* invalid charset specification */
CMDERR_EVAL_MAX_RECURSE, /* eval hit recursion limit */
- CMDERR_PROGRAM_NOT_FOUND /* program not found */
+ CMDERR_PROGRAM_NOT_FOUND, /* program not found */
+ CMDERR_NO_SERVER_DEFINED, /* no server has been defined for a given chatnet */
};
/* Return the full command for `alias' */
@@ -152,13 +153,15 @@ int command_have_option(const char *cmd, const char *option);
#define PARAM_FLAG_OPTCHAN 0x00010000
/* optional channel in first argument, but don't treat "*" as current channel */
#define PARAM_FLAG_OPTCHAN_NAME (0x00020000|PARAM_FLAG_OPTCHAN)
+/* strip the trailing whitespace */
+#define PARAM_FLAG_STRIP_TRAILING_WS 0x00040000
char *cmd_get_param(char **data);
char *cmd_get_quoted_param(char **data);
/* get parameters from command - you should point free_me somewhere and
cmd_params_free() it after you don't use any of the parameters anymore.
- Returns TRUE if all ok, FALSE if error occured. */
+ Returns TRUE if all ok, FALSE if error occurred. */
int cmd_get_params(const char *data, gpointer *free_me, int count, ...);
void cmd_params_free(void *free_me);
diff --git a/src/core/core.c b/src/core/core.c
index b9debbb5..bf7cdd6b 100644
--- a/src/core/core.c
+++ b/src/core/core.c
@@ -83,7 +83,6 @@ static void sig_reload_config(int signo)
static void read_settings(void)
{
-#ifndef WIN32
static int signals[] = {
SIGINT, SIGQUIT, SIGTERM,
SIGALRM, SIGUSR1, SIGUSR2
@@ -124,7 +123,6 @@ static void read_settings(void)
settings_set_bool("override_coredump_limit", FALSE);
}
#endif
-#endif
}
static void sig_gui_dialog(const char *type, const char *text)
@@ -156,11 +154,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;
}
@@ -219,9 +223,7 @@ void core_init(void)
client_start_time = time(NULL);
modules_init();
-#ifndef WIN32
pidwait_init();
-#endif
net_disconnect_init();
signals_init();
@@ -297,9 +299,7 @@ void core_deinit(void)
signals_deinit();
net_disconnect_deinit();
-#ifndef WIN32
pidwait_deinit();
-#endif
modules_deinit();
g_free(irssi_dir);
diff --git a/src/core/expandos.c b/src/core/expandos.c
index bed6c5eb..67aea837 100644
--- a/src/core/expandos.c
+++ b/src/core/expandos.c
@@ -57,7 +57,7 @@ static char *last_sent_msg, *last_sent_msg_body;
static char *last_privmsg_from, *last_public_from;
static char *sysname, *sysrelease, *sysarch;
-static const char *timestamp_format;
+static char *timestamp_format;
static int timestamp_seconds;
static time_t last_timestamp;
@@ -414,6 +414,13 @@ static char *expando_releasetime(SERVER_REC *server, void *item, int *free_ret)
return g_strdup_printf("%04d", IRSSI_VERSION_TIME);
}
+/* client abi */
+static char *expando_abiversion(SERVER_REC *server, void *item, int *free_ret)
+{
+ *free_ret = TRUE;
+ return g_strdup_printf("%d", IRSSI_ABI_VERSION);
+}
+
/* current working directory */
static char *expando_workdir(SERVER_REC *server, void *item, int *free_ret)
{
@@ -567,7 +574,9 @@ static int sig_timer(void)
static void read_settings(void)
{
- timestamp_format = settings_get_str("timestamp_format");
+ g_free_not_null(timestamp_format);
+ timestamp_format = g_strdup(settings_get_str("timestamp_format"));
+
timestamp_seconds =
strstr(timestamp_format, "%r") != NULL ||
strstr(timestamp_format, "%s") != NULL ||
@@ -656,6 +665,8 @@ void expandos_init(void)
"", EXPANDO_NEVER, NULL);
expando_create("versiontime", expando_releasetime,
"", EXPANDO_NEVER, NULL);
+ expando_create("abiversion", expando_abiversion,
+ "", EXPANDO_NEVER, NULL);
expando_create("W", expando_workdir, NULL);
expando_create("Y", expando_realname,
"window changed", EXPANDO_ARG_NONE,
@@ -708,14 +719,18 @@ void expandos_deinit(void)
g_free_not_null(char_expandos[n]);
g_hash_table_foreach_remove(expandos, free_expando, NULL);
- g_hash_table_destroy(expandos);
+ g_hash_table_destroy(expandos);
- g_free_not_null(last_sent_msg); g_free_not_null(last_sent_msg_body);
- g_free_not_null(last_privmsg_from); g_free_not_null(last_public_from);
- g_free_not_null(sysname); g_free_not_null(sysrelease);
- g_free_not_null(sysarch);
+ g_free_not_null(last_sent_msg);
+ g_free_not_null(last_sent_msg_body);
+ g_free_not_null(last_privmsg_from);
+ g_free_not_null(last_public_from);
+ g_free_not_null(sysname);
+ g_free_not_null(sysrelease);
+ g_free_not_null(sysarch);
+ g_free_not_null(timestamp_format);
- g_source_remove(timer_tag);
+ g_source_remove(timer_tag);
signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
diff --git a/src/core/ignore.c b/src/core/ignore.c
index 3c45967c..d4a92e3c 100644
--- a/src/core/ignore.c
+++ b/src/core/ignore.c
@@ -67,11 +67,12 @@ static int ignore_match_pattern(IGNORE_REC *rec, const char *text)
return FALSE;
if (rec->regexp) {
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ return rec->preg != NULL &&
+ g_regex_match(rec->preg, text, 0, NULL);
+#else
return rec->regexp_compiled &&
regexec(&rec->preg, text, 0, NULL, 0) == 0;
-#else
- return FALSE;
#endif
}
@@ -97,8 +98,8 @@ static int ignore_match_pattern(IGNORE_REC *rec, const char *text)
match_wildcards((rec)->mask, nick)))
#define ignore_match_server(rec, server) \
- ((rec)->servertag == NULL || \
- g_ascii_strcasecmp((server)->tag, (rec)->servertag) == 0)
+ ((rec)->servertag == NULL || ((server) != NULL && \
+ g_ascii_strcasecmp((server)->tag, (rec)->servertag) == 0))
#define ignore_match_channel(rec, channel) \
((rec)->channels == NULL || ((channel) != NULL && \
@@ -135,7 +136,6 @@ int ignore_check(SERVER_REC *server, const char *nick, const char *host,
char *nickmask;
int len, best_mask, best_match, best_patt;
- g_return_val_if_fail(server != NULL, 0);
if (nick == NULL) nick = "";
chanrec = server == NULL || channel == NULL ? NULL :
@@ -187,24 +187,17 @@ int ignore_check(SERVER_REC *server, const char *nick, const char *host,
return ignore_check_replies(chanrec, text, level);
}
-IGNORE_REC *ignore_find(const char *servertag, const char *mask,
- char **channels)
-{
- return ignore_find_noact(servertag, mask, channels, 0);
-}
-
-
-IGNORE_REC *ignore_find_noact(const char *servertag, const char *mask,
- char **channels, int noact)
+IGNORE_REC *ignore_find_full(const char *servertag, const char *mask, const char *pattern,
+ char **channels, const int flags)
{
GSList *tmp;
char **chan;
int ignore_servertag;
- if (mask != NULL && (*mask == '\0' || strcmp(mask, "*") == 0))
+ if (mask != NULL && (*mask == '\0' || g_strcmp0(mask, "*") == 0))
mask = NULL;
- ignore_servertag = servertag != NULL && strcmp(servertag, "*") == 0;
+ ignore_servertag = servertag != NULL && g_strcmp0(servertag, "*") == 0;
for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
IGNORE_REC *rec = tmp->data;
@@ -217,28 +210,39 @@ IGNORE_REC *ignore_find_noact(const char *servertag, const char *mask,
continue;
}
- if (noact && (rec->level & MSGLEVEL_NO_ACT) == 0)
+ if ((flags & IGNORE_FIND_NOACT) && (rec->level & MSGLEVEL_NO_ACT) == 0)
continue;
- if (!noact && (rec->level & MSGLEVEL_NO_ACT) != 0)
+ if (!(flags & IGNORE_FIND_NOACT) && (rec->level & MSGLEVEL_NO_ACT) != 0)
continue;
if ((rec->mask == NULL && mask != NULL) ||
- (rec->mask != NULL && mask == NULL)) continue;
+ (rec->mask != NULL && mask == NULL))
+ continue;
if (rec->mask != NULL && g_ascii_strcasecmp(rec->mask, mask) != 0)
continue;
+ /* match the pattern too if requested */
+ if (flags & IGNORE_FIND_PATTERN) {
+ if ((rec->pattern == NULL && pattern != NULL) ||
+ (rec->pattern != NULL && pattern == NULL))
+ continue;
+
+ if (rec->pattern != NULL && g_ascii_strcasecmp(rec->pattern, pattern) != 0)
+ continue;
+ }
+
if ((channels == NULL && rec->channels == NULL))
return rec; /* no channels - ok */
- if (channels != NULL && strcmp(*channels, "*") == 0)
+ if (channels != NULL && g_strcmp0(*channels, "*") == 0)
return rec; /* ignore channels */
if (channels == NULL || rec->channels == NULL)
continue; /* other doesn't have channels */
- if (strarray_length(channels) != strarray_length(rec->channels))
+ if (g_strv_length(channels) != g_strv_length(rec->channels))
continue; /* different amount of channels */
/* check that channels match */
@@ -254,6 +258,16 @@ IGNORE_REC *ignore_find_noact(const char *servertag, const char *mask,
return NULL;
}
+IGNORE_REC *ignore_find(const char *servertag, const char *mask, char **channels)
+{
+ return ignore_find_full(servertag, mask, NULL, channels, 0);
+}
+
+IGNORE_REC *ignore_find_noact(const char *servertag, const char *mask, char **channels, int noact)
+{
+ return ignore_find_full(servertag, mask, NULL, channels, IGNORE_FIND_NOACT);
+}
+
static void ignore_set_config(IGNORE_REC *rec)
{
CONFIG_NODE *node;
@@ -263,7 +277,7 @@ static void ignore_set_config(IGNORE_REC *rec)
return;
node = iconfig_node_traverse("(ignores", TRUE);
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
if (rec->mask != NULL) iconfig_node_set_str(node, "mask", rec->mask);
if (rec->level) {
@@ -281,7 +295,7 @@ static void ignore_set_config(IGNORE_REC *rec)
iconfig_node_set_str(node, "servertag", rec->servertag);
if (rec->channels != NULL && *rec->channels != NULL) {
- node = config_node_section(node, "channels", NODE_TYPE_LIST);
+ node = iconfig_node_section(node, "channels", NODE_TYPE_LIST);
iconfig_node_add_list(node, rec->channels);
}
}
@@ -313,12 +327,27 @@ static void ignore_remove_config(IGNORE_REC *rec)
static void ignore_init_rec(IGNORE_REC *rec)
{
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg != NULL)
+ g_regex_unref(rec->preg);
+
+ if (rec->regexp && rec->pattern != NULL) {
+ GError *re_error = NULL;
+
+ rec->preg = g_regex_new(rec->pattern, G_REGEX_OPTIMIZE | G_REGEX_RAW | G_REGEX_CASELESS, 0, &re_error);
+
+ if (rec->preg == NULL) {
+ g_warning("Failed to compile regexp '%s': %s", rec->pattern, re_error->message);
+ g_error_free(re_error);
+ }
+ }
+#else
char *errbuf;
int errcode, errbuf_len;
if (rec->regexp_compiled) regfree(&rec->preg);
rec->regexp_compiled = FALSE;
+
if (rec->regexp && rec->pattern != NULL) {
errcode = regcomp(&rec->preg, rec->pattern,
REG_EXTENDED|REG_ICASE|REG_NOSUB);
@@ -352,7 +381,9 @@ static void ignore_destroy(IGNORE_REC *rec, int send_signal)
if (send_signal)
signal_emit("ignore destroyed", 1, rec);
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg != NULL) g_regex_unref(rec->preg);
+#else
if (rec->regexp_compiled) regfree(&rec->preg);
#endif
if (rec->channels != NULL) g_strfreev(rec->channels);
@@ -360,8 +391,6 @@ static void ignore_destroy(IGNORE_REC *rec, int send_signal)
g_free_not_null(rec->servertag);
g_free_not_null(rec->pattern);
g_free(rec);
-
- nickmatch_rebuild(nickmatch);
}
void ignore_update_rec(IGNORE_REC *rec)
@@ -380,8 +409,8 @@ void ignore_update_rec(IGNORE_REC *rec)
ignore_init_rec(rec);
signal_emit("ignore changed", 1, rec);
- nickmatch_rebuild(nickmatch);
}
+ nickmatch_rebuild(nickmatch);
}
static int unignore_timeout(void)
@@ -438,7 +467,7 @@ static void read_ignores(void)
rec->unignore_time = config_node_get_int(node, "unignore_time", 0);
rec->servertag = g_strdup(config_node_get_str(node, "servertag", 0));
- node = config_node_section(node, "channels", -1);
+ node = iconfig_node_section(node, "channels", -1);
if (node != NULL) rec->channels = config_node_get_list(node);
ignore_init_rec(rec);
diff --git a/src/core/ignore.h b/src/core/ignore.h
index 46025d4c..80ae1d12 100644
--- a/src/core/ignore.h
+++ b/src/core/ignore.h
@@ -1,7 +1,7 @@
#ifndef __IGNORE_H
#define __IGNORE_H
-#ifdef HAVE_REGEX_H
+#ifndef USE_GREGEX
# include <regex.h>
#endif
@@ -20,7 +20,9 @@ struct _IGNORE_REC {
unsigned int regexp:1;
unsigned int fullword:1;
unsigned int replies:1; /* ignore replies to nick in channel */
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ GRegex *preg;
+#else
unsigned int regexp_compiled:1; /* should always be TRUE, unless regexp is invalid */
regex_t preg;
#endif
@@ -31,6 +33,16 @@ extern GSList *ignores;
int ignore_check(SERVER_REC *server, const char *nick, const char *host,
const char *channel, const char *text, int level);
+enum {
+ IGNORE_FIND_PATTERN = 0x01, // Match the pattern
+ IGNORE_FIND_NOACT = 0x02, // Exclude the targets with NOACT level
+};
+
+IGNORE_REC *ignore_find_full (const char *servertag, const char *mask, const char *pattern,
+ char **channels, const int flags);
+
+// Convenience wrappers around ignore_find_full, for compatibility purpose
+
IGNORE_REC *ignore_find(const char *servertag, const char *mask, char **channels);
IGNORE_REC *ignore_find_noact(const char *servertag, const char *mask, char **channels, int noact);
diff --git a/src/core/levels.c b/src/core/levels.c
index 7997ba98..e623c4de 100644
--- a/src/core/levels.c
+++ b/src/core/levels.c
@@ -54,7 +54,7 @@ int level_get(const char *level)
{
int n, len, match;
- if (g_ascii_strcasecmp(level, "ALL") == 0 || strcmp(level, "*") == 0)
+ if (g_ascii_strcasecmp(level, "ALL") == 0 || g_strcmp0(level, "*") == 0)
return MSGLEVEL_ALL;
if (g_ascii_strcasecmp(level, "NEVER") == 0)
@@ -177,7 +177,7 @@ int combine_level(int dest, const char *src)
itemname = *item + (**item == '+' || **item == '-' ? 1 : 0);
itemlevel = level_get(itemname);
- if (strcmp(itemname, "NONE") == 0)
+ if (g_strcmp0(itemname, "NONE") == 0)
dest = 0;
else if (**item == '-')
dest &= ~(itemlevel);
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/log.c b/src/core/log.c
index 263b3526..6af1effc 100644
--- a/src/core/log.c
+++ b/src/core/log.c
@@ -41,7 +41,7 @@ static const char *log_item_types[] = {
NULL
};
-const char *log_timestamp;
+static char *log_timestamp;
static int log_file_create_mode;
static int log_dir_create_mode;
static int rotate_tag;
@@ -110,11 +110,11 @@ int log_start_logging(LOG_REC *log)
log->real_fname = log_filename(log);
if (log->real_fname != NULL &&
- strcmp(log->real_fname, log->fname) != 0) {
+ g_strcmp0(log->real_fname, log->fname) != 0) {
/* path may contain variables (%time, $vars),
make sure the directory is created */
dir = g_path_get_dirname(log->real_fname);
- mkpath(dir, log_dir_create_mode);
+ g_mkdir_with_parents(dir, log_dir_create_mode);
g_free(dir);
}
@@ -181,7 +181,7 @@ static void log_rotate_check(LOG_REC *log)
return;
new_fname = log_filename(log);
- if (strcmp(new_fname, log->real_fname) != 0) {
+ if (g_strcmp0(new_fname, log->real_fname) != 0) {
/* rotate log */
log_stop_logging(log);
signal_emit("log rotated", 1, log);
@@ -245,7 +245,7 @@ static int itemcmp(const char *patt, const char *item)
{
/* returns 0 on match, nonzero otherwise */
- if (!strcmp(patt, "*"))
+ if (!g_strcmp0(patt, "*"))
return 0;
return item ? g_ascii_strcasecmp(patt, item) : 1;
}
@@ -320,7 +320,7 @@ LOG_REC *log_find(const char *fname)
for (tmp = logs; tmp != NULL; tmp = tmp->next) {
LOG_REC *rec = tmp->data;
- if (strcmp(rec->fname, fname) == 0)
+ if (g_strcmp0(rec->fname, fname) == 0)
return rec;
}
@@ -332,11 +332,11 @@ static void log_items_update_config(LOG_REC *log, CONFIG_NODE *parent)
GSList *tmp;
CONFIG_NODE *node;
- parent = config_node_section(parent, "items", NODE_TYPE_LIST);
+ parent = iconfig_node_section(parent, "items", NODE_TYPE_LIST);
for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
LOG_ITEM_REC *rec = tmp->data;
- node = config_node_section(parent, NULL, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(parent, NULL, NODE_TYPE_BLOCK);
iconfig_node_set_str(node, "type", log_item_types[rec->type]);
iconfig_node_set_str(node, "name", rec->name);
iconfig_node_set_str(node, "server", rec->servertag);
@@ -352,7 +352,7 @@ static void log_update_config(LOG_REC *log)
return;
node = iconfig_node_traverse("logs", TRUE);
- node = config_node_section(node, log->fname, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, log->fname, NODE_TYPE_BLOCK);
if (log->autoopen)
iconfig_node_set_bool(node, "auto_open", TRUE);
@@ -544,7 +544,7 @@ static void log_read_config(void)
signal_emit("log config read", 2, log, node);
- node = config_node_section(node, "items", -1);
+ node = iconfig_node_section(node, "items", -1);
if (node != NULL)
log_items_read_config(node, log);
@@ -558,13 +558,15 @@ static void log_read_config(void)
static void read_settings(void)
{
- log_timestamp = settings_get_str("log_timestamp");
+ g_free_not_null(log_timestamp);
+ log_timestamp = g_strdup(settings_get_str("log_timestamp"));
+
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;
+ 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;
}
void log_init(void)
@@ -595,7 +597,9 @@ void log_deinit(void)
while (logs != NULL)
log_close(logs->data);
+ g_free_not_null(log_timestamp);
+
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
- signal_remove("setup reread", (SIGNAL_FUNC) log_read_config);
- signal_remove("irssi init finished", (SIGNAL_FUNC) log_read_config);
+ signal_remove("setup reread", (SIGNAL_FUNC) log_read_config);
+ signal_remove("irssi init finished", (SIGNAL_FUNC) log_read_config);
}
diff --git a/src/core/misc.c b/src/core/misc.c
index 5e6087cb..1cfa15b6 100644
--- a/src/core/misc.c
+++ b/src/core/misc.c
@@ -20,8 +20,9 @@
#include "module.h"
#include "misc.h"
+#include "commands.h"
-#ifdef HAVE_REGEX_H
+#ifndef USE_GREGEX
# include <regex.h>
#endif
@@ -150,27 +151,13 @@ int find_substr(const char *list, const char *item)
return FALSE;
}
-int strarray_length(char **array)
-{
- int len;
-
- g_return_val_if_fail(array != NULL, 0);
-
- len = 0;
- while (*array) {
- len++;
- array++;
- }
- return len;
-}
-
int strarray_find(char **array, const char *item)
{
char **tmp;
int index;
- g_return_val_if_fail(array != NULL, 0);
- g_return_val_if_fail(item != NULL, 0);
+ g_return_val_if_fail(array != NULL, -1);
+ g_return_val_if_fail(item != NULL, -1);
index = 0;
for (tmp = array; *tmp != NULL; tmp++, index++) {
@@ -184,7 +171,7 @@ int strarray_find(char **array, const char *item)
GSList *gslist_find_string(GSList *list, const char *key)
{
for (; list != NULL; list = list->next)
- if (strcmp(list->data, key) == 0) return list;
+ if (g_strcmp0(list->data, key) == 0) return list;
return NULL;
}
@@ -211,6 +198,30 @@ void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, const void *data
return NULL;
}
+void gslist_free_full (GSList *list, GDestroyNotify free_func)
+{
+ GSList *tmp;
+
+ if (list == NULL)
+ return;
+
+ for (tmp = list; tmp != NULL; tmp = tmp->next)
+ free_func(tmp->data);
+
+ g_slist_free(list);
+}
+
+GSList *gslist_remove_string (GSList *list, const char *str)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(list, str, (GCompareFunc) g_strcmp0);
+ if (l != NULL)
+ return g_slist_remove_link(list, l);
+
+ return list;
+}
+
/* `list' contains pointer to structure with a char* to string. */
char *gslistptr_to_string(GSList *list, int offset, const char *delimiter)
{
@@ -255,21 +266,30 @@ void hash_save_key(char *key, void *value, GSList **list)
*list = g_slist_append(*list, key);
}
-/* save all keys in hash table to linked list - you shouldn't remove any
- items while using this list, use g_slist_free() after you're done with it */
-GSList *hashtable_get_keys(GHashTable *hash)
+/* remove all the options from the optlist hash table that are valid for the
+ * command cmd */
+GList *optlist_remove_known(const char *cmd, GHashTable *optlist)
{
- GSList *list;
+ GList *list, *tmp, *next;
+
+ list = g_hash_table_get_keys(optlist);
+ if (cmd != NULL && list != NULL) {
+ for (tmp = list; tmp != NULL; tmp = next) {
+ char *option = tmp->data;
+ next = tmp->next;
+
+ if (command_have_option(cmd, option))
+ list = g_list_remove(list, option);
+ }
+ }
- list = NULL;
- g_hash_table_foreach(hash, (GHFunc) hash_save_key, &list);
return list;
}
GList *glist_find_string(GList *list, const char *key)
{
for (; list != NULL; list = list->next)
- if (strcmp(list->data, key) == 0) return list;
+ if (g_strcmp0(list->data, key) == 0) return list;
return NULL;
}
@@ -365,66 +385,6 @@ char *stristr_full(const char *data, const char *key)
return strstr_full_case(data, key, TRUE);
}
-int regexp_match(const char *str, const char *regexp)
-{
-#ifdef HAVE_REGEX_H
- regex_t preg;
- int ret;
-
- if (regcomp(&preg, regexp, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0)
- return 0;
-
- ret = regexec(&preg, str, 0, NULL, 0);
- regfree(&preg);
-
- return ret == 0;
-#else
- return FALSE;
-#endif
-}
-
-/* Create the directory and all it's parent directories */
-int mkpath(const char *path, int mode)
-{
- struct stat statbuf;
- const char *p;
- char *dir;
-
- g_return_val_if_fail(path != NULL, -1);
-
- p = g_path_skip_root((char *) path);
- if (p == NULL) {
- /* not a full path, maybe not what we wanted
- but continue anyway.. */
- p = path;
- }
- for (;;) {
- if (*p != G_DIR_SEPARATOR && *p != '\0') {
- p++;
- continue;
- }
-
- dir = g_strndup(path, (int) (p-path));
- if (stat(dir, &statbuf) != 0) {
-#ifndef WIN32
- if (mkdir(dir, mode) == -1)
-#else
- if (_mkdir(dir) == -1)
-#endif
- {
- g_free(dir);
- return -1;
- }
- }
- g_free(dir);
-
- if (*p++ == '\0')
- break;
- }
-
- return 0;
-}
-
/* convert ~/ to $HOME */
char *convert_home(const char *path)
{
@@ -451,22 +411,15 @@ int g_istr_cmp(gconstpointer v, gconstpointer v2)
return g_ascii_strcasecmp((const char *) v, (const char *) v2);
}
-/* a char* hash function from ASU */
-unsigned int g_istr_hash(gconstpointer v)
+guint g_istr_hash(gconstpointer v)
{
- const char *s = (const char *) v;
- unsigned int h = 0, g;
+ const signed char *p;
+ guint32 h = 5381;
- while (*s != '\0') {
- h = (h << 4) + i_toupper(*s);
- if ((g = h & 0xf0000000UL)) {
- h = h ^ (g >> 24);
- h = h ^ g;
- }
- s++;
- }
+ for (p = v; *p != '\0'; p++)
+ h = (h << 5) + h + g_ascii_toupper(*p);
- return h /* % M */;
+ return h;
}
/* Find `mask' from `data', you can use * and ? wildcards. */
@@ -572,15 +525,11 @@ int dec2octal(int decimal)
/* string -> uoff_t */
uoff_t str_to_uofft(const char *str)
{
- uoff_t ret;
-
- ret = 0;
- while (*str != '\0') {
- ret = ret*10 + (*str - '0');
- str++;
- }
-
- return ret;
+#ifdef UOFF_T_LONG_LONG
+ return (uoff_t)strtoull(str, NULL, 10);
+#else
+ return (uoff_t)strtoul(str, NULL, 10);
+#endif
}
/* convert all low-ascii (<32) to ^<A..> combinations */
@@ -791,20 +740,6 @@ char *escape_string(const char *str)
return ret;
}
-int strocpy(char *dest, const char *src, size_t dstsize)
-{
- if (dstsize == 0)
- return -1;
-
- while (*src != '\0' && dstsize > 1) {
- *dest++ = *src++;
- dstsize--;
- }
-
- *dest++ = '\0';
- return *src == '\0' ? 0 : -1;
-}
-
int nearest_power(int num)
{
int n = 1;
@@ -967,20 +902,51 @@ char *ascii_strdown(char *str)
return str;
}
-char **strsplit_len(const char *str, int len)
+char **strsplit_len(const char *str, int len, gboolean onspace)
+{
+ char **ret = g_new(char *, 1);
+ int n;
+ int offset;
+
+ for (n = 0; *str != '\0'; n++, str += offset) {
+ offset = MIN(len, strlen(str));
+ if (onspace && strlen(str) > len) {
+ /*
+ * Try to find a space to split on and leave
+ * the space on the previous line.
+ */
+ int i;
+ for (i = len - 1; i > 0; i--) {
+ if (str[i] == ' ') {
+ offset = i;
+ break;
+ }
+ }
+ }
+ ret[n] = g_strndup(str, offset);
+ ret = g_renew(char *, ret, n + 2);
+ }
+ ret[n] = NULL;
+
+ return ret;
+}
+
+char *binary_to_hex(unsigned char *buffer, size_t size)
{
- char **ret;
- size_t total_len = strlen(str);
- int n = total_len / len;
+ static const char hex[] = "0123456789ABCDEF";
+ char *result = NULL;
int i;
- if (total_len % len)
- n++;
+ if (buffer == NULL || size == 0)
+ return NULL;
+
+ result = g_malloc(3 * size);
- ret = g_new(char *, n + 1);
- for (i = 0; i < n; i++, str += len)
- ret[i] = g_strndup(str, len);
- ret[n] = NULL;
+ for (i = 0; i < size; i++) {
+ result[i * 3 + 0] = hex[(buffer[i] >> 4) & 0xf];
+ result[i * 3 + 1] = hex[(buffer[i] >> 0) & 0xf];
+ result[i * 3 + 2] = i == size - 1 ? '\0' : ':';
+ }
- return ret;
+ return result;
}
diff --git a/src/core/misc.h b/src/core/misc.h
index 8fb5078f..00637da0 100644
--- a/src/core/misc.h
+++ b/src/core/misc.h
@@ -21,6 +21,9 @@ GSList *gslist_find_string(GSList *list, const char *key);
GSList *gslist_find_icase_string(GSList *list, const char *key);
GList *glist_find_string(GList *list, const char *key);
GList *glist_find_icase_string(GList *list, const char *key);
+GSList *gslist_remove_string (GSList *list, const char *str);
+
+void gslist_free_full (GSList *list, GDestroyNotify free_func);
void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, const void *data);
@@ -29,15 +32,8 @@ char *gslistptr_to_string(GSList *list, int offset, const char *delimiter);
/* `list' contains char* */
char *gslist_to_string(GSList *list, const char *delimiter);
-/* save all keys in hash table to linked list - you shouldn't remove any
- items while using this list, use g_slist_free() after you're done with it */
-GSList *hashtable_get_keys(GHashTable *hash);
-
-/* easy way to check if regexp matches */
-int regexp_match(const char *str, const char *regexp);
+GList *optlist_remove_known(const char *cmd, GHashTable *optlist);
-/* Create the directory and all it's parent directories */
-int mkpath(const char *path, int mode);
/* convert ~/ to $HOME */
char *convert_home(const char *path);
@@ -82,9 +78,6 @@ int parse_size(const char *size, int *bytes);
Stop when `end_char' is found from string. */
int is_numeric(const char *str, char end_char);
-/* Like strlcpy(), but return -1 if buffer was overflown, 0 if not. */
-int strocpy(char *dest, const char *src, size_t dstsize);
-
/* strstr() with case-ignoring */
char *stristr(const char *data, const char *key);
@@ -104,8 +97,6 @@ char *show_lowascii(const char *str);
/* replace all `from' chars in string to `to' chars. returns `str' */
char *replace_chars(char *str, char from, char to);
-/* return how many items `array' has */
-int strarray_length(char **array);
/* return index of `item' in `array' or -1 if not found */
int strarray_find(char **array, const char *item);
@@ -116,6 +107,10 @@ uoff_t str_to_uofft(const char *str);
int find_substr(const char *list, const char *item);
/* split `str' into `len' sized substrings */
-char **strsplit_len(const char *str, int len);
+char **strsplit_len(const char *str, int len, gboolean onspace);
+
+/* Convert a given buffer to a printable, colon-delimited, hex string and
+ * return a pointer to the newly allocated buffer */
+char *binary_to_hex(unsigned char *buffer, size_t size);
#endif
diff --git a/src/core/modules-load.c b/src/core/modules-load.c
index 49f811de..9baac1d7 100644
--- a/src/core/modules-load.c
+++ b/src/core/modules-load.c
@@ -78,7 +78,7 @@ static char *module_get_root(const char *name, char **prefixes)
/* skip the _core part */
len = strlen(name);
- if (len > 5 && strcmp(name+len-5, "_core") == 0)
+ if (len > 5 && g_strcmp0(name+len-5, "_core") == 0)
return g_strndup(name, len-5);
return g_strdup(name);
@@ -94,11 +94,11 @@ static char *module_get_sub(const char *name, const char *root)
g_return_val_if_fail(namelen >= rootlen, g_strdup(name));
if (strncmp(name, root, rootlen) == 0 &&
- strcmp(name+rootlen, "_core") == 0)
+ g_strcmp0(name+rootlen, "_core") == 0)
return g_strdup("core");
if (namelen > rootlen && name[namelen-rootlen-1] == '_' &&
- strcmp(name+namelen-rootlen, root) == 0)
+ g_strcmp0(name+namelen-rootlen, root) == 0)
return g_strndup(name, namelen-rootlen-1);
return g_strdup(name);
@@ -140,10 +140,10 @@ static GModule *module_open(const char *name, int *found)
static char *module_get_func(const char *rootmodule, const char *submodule,
const char *function)
{
- if (strcmp(submodule, "core") == 0)
+ if (g_strcmp0(submodule, "core") == 0)
return g_strconcat(rootmodule, "_core_", function, NULL);
- if (strcmp(rootmodule, submodule) == 0)
+ if (g_strcmp0(rootmodule, submodule) == 0)
return g_strconcat(rootmodule, "_", function, NULL);
return g_strconcat(submodule, "_", rootmodule, "_", function, NULL);
@@ -160,11 +160,14 @@ static int module_load_name(const char *path, const char *rootmodule,
{
void (*module_init) (void);
void (*module_deinit) (void);
+ void (*module_version) (int *);
GModule *gmodule;
MODULE_REC *module;
MODULE_FILE_REC *rec;
+ gpointer value_version = NULL;
gpointer value1, value2 = NULL;
- char *initfunc, *deinitfunc;
+ char *versionfunc, *initfunc, *deinitfunc;
+ int module_abi_version = 0;
int found;
gmodule = module_open(path, &found);
@@ -176,6 +179,27 @@ static int module_load_name(const char *path, const char *rootmodule,
return found ? 0 : -1;
}
+ /* get the module's irssi abi version and bail out on mismatch */
+ versionfunc = module_get_func(rootmodule, submodule, "abicheck");
+ if (!g_module_symbol(gmodule, versionfunc, &value_version)) {
+ g_free(versionfunc);
+ module_error(MODULE_ERROR_VERSION_MISMATCH, "0",
+ rootmodule, submodule);
+ g_module_close(gmodule);
+ return 0;
+ }
+ g_free(versionfunc);
+ module_version = value_version;
+ module_version(&module_abi_version);
+ if (module_abi_version != IRSSI_ABI_VERSION) {
+ char *module_abi_versionstr = g_strdup_printf("%d", module_abi_version);
+ module_error(MODULE_ERROR_VERSION_MISMATCH, module_abi_versionstr,
+ rootmodule, submodule);
+ g_free(module_abi_versionstr);
+ g_module_close(gmodule);
+ return 0;
+ }
+
/* get the module's init() and deinit() functions */
initfunc = module_get_func(rootmodule, submodule, "init");
deinitfunc = module_get_func(rootmodule, submodule, "deinit");
@@ -200,7 +224,7 @@ static int module_load_name(const char *path, const char *rootmodule,
module = module_find(rootmodule);
rec = module == NULL ? NULL :
- strcmp(rootmodule, submodule) == 0 ?
+ g_strcmp0(rootmodule, submodule) == 0 ?
module_file_find(module, "core") :
module_file_find(module, submodule);
if (rec == NULL) {
@@ -277,7 +301,7 @@ static int module_load_full(const char *path, const char *rootmodule,
return FALSE;
module = module_find(rootmodule);
- if (module != NULL && (strcmp(submodule, rootmodule) == 0 ||
+ if (module != NULL && (g_strcmp0(submodule, rootmodule) == 0 ||
module_file_find(module, submodule) != NULL)) {
/* module is already loaded */
module_error(MODULE_ERROR_ALREADY_LOADED, NULL,
@@ -286,7 +310,7 @@ static int module_load_full(const char *path, const char *rootmodule,
}
/* check if the given module exists.. */
- try_prefixes = strcmp(rootmodule, submodule) == 0;
+ try_prefixes = g_strcmp0(rootmodule, submodule) == 0;
status = module_load_name(path, rootmodule, submodule, try_prefixes);
if (status == -1 && try_prefixes) {
/* nope, try loading the module_core,
@@ -340,7 +364,7 @@ int module_load_sub(const char *path, const char *submodule, char **prefixes)
g_free(name);
full_path = g_string_new(exppath);
- if (strcmp(submodule, "core") == 0)
+ if (g_strcmp0(submodule, "core") == 0)
g_string_insert(full_path, end, "_core");
else {
g_string_insert_c(full_path, start, '_');
diff --git a/src/core/modules.c b/src/core/modules.c
index b002819b..a2542c84 100644
--- a/src/core/modules.c
+++ b/src/core/modules.c
@@ -44,7 +44,7 @@ void *module_check_cast_module(void *object, int type_pos,
str = module_find_id_str(module,
G_STRUCT_MEMBER(int, object, type_pos));
- return str == NULL || strcmp(str, id) != 0 ? NULL : object;
+ return str == NULL || g_strcmp0(str, id) != 0 ? NULL : object;
}
/* return unique number across all modules for `id' */
@@ -251,7 +251,7 @@ MODULE_FILE_REC *module_file_find(MODULE_REC *module, const char *name)
for (tmp = module->files; tmp != NULL; tmp = tmp->next) {
MODULE_FILE_REC *rec = tmp->data;
- if (strcmp(rec->name, name) == 0)
+ if (g_strcmp0(rec->name, name) == 0)
return rec;
}
diff --git a/src/core/modules.h b/src/core/modules.h
index 75a77c77..b2fa2fa4 100644
--- a/src/core/modules.h
+++ b/src/core/modules.h
@@ -27,6 +27,7 @@
enum {
MODULE_ERROR_ALREADY_LOADED,
MODULE_ERROR_LOAD,
+ MODULE_ERROR_VERSION_MISMATCH,
MODULE_ERROR_INVALID
};
diff --git a/src/core/net-disconnect.c b/src/core/net-disconnect.c
index 321f79ac..6476e776 100644
--- a/src/core/net-disconnect.c
+++ b/src/core/net-disconnect.c
@@ -116,7 +116,6 @@ void net_disconnect_init(void)
void net_disconnect_deinit(void)
{
-#ifndef WIN32
NET_DISCONNECT_REC *rec;
time_t now, max;
int first, fd;
@@ -155,5 +154,4 @@ void net_disconnect_deinit(void)
first = 0;
}
}
-#endif
}
diff --git a/src/core/net-nonblock.c b/src/core/net-nonblock.c
index e637e673..d6e767b6 100644
--- a/src/core/net-nonblock.c
+++ b/src/core/net-nonblock.c
@@ -77,14 +77,11 @@ int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe,
{
RESOLVED_IP_REC rec;
const char *errorstr;
-#ifndef WIN32
int pid;
-#endif
int len;
g_return_val_if_fail(addr != NULL, FALSE);
-#ifndef WIN32
pid = fork();
if (pid > 0) {
/* parent */
@@ -97,7 +94,6 @@ int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe,
g_warning("net_connect_thread(): fork() failed! "
"Using blocking resolving");
}
-#endif
/* child */
srand(time(NULL));
@@ -138,10 +134,8 @@ int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe,
}
}
-#ifndef WIN32
if (pid == 0)
_exit(99);
-#endif
/* we used blocking lookup */
return 0;
@@ -157,9 +151,7 @@ int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec)
rec->host4 = NULL;
rec->host6 = NULL;
-#ifndef WIN32
fcntl(g_io_channel_unix_get_fd(pipe), F_SETFL, O_NONBLOCK);
-#endif
/* get ip+error */
if (g_io_channel_read_block(pipe, rec, sizeof(*rec)) == -1) {
@@ -201,9 +193,7 @@ void net_disconnect_nonblock(int pid)
{
g_return_if_fail(pid > 0);
-#ifndef WIN32
kill(pid, SIGKILL);
-#endif
}
static void simple_init(SIMPLE_THREAD_REC *rec, GIOChannel *handle)
diff --git a/src/core/net-nonblock.h b/src/core/net-nonblock.h
index 32cfac70..af5968c8 100644
--- a/src/core/net-nonblock.h
+++ b/src/core/net-nonblock.h
@@ -29,7 +29,7 @@ int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe,
int reverse_lookup);
/* Get host's name, call func when finished */
int net_gethostbyaddr_nonblock(IPADDR *ip, NET_HOST_CALLBACK func, void *data);
-/* get the resolved IP address. returns -1 if some error occured with read() */
+/* get the resolved IP address. returns -1 if some error occurred with read() */
int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec);
/* Connect to server, call func when finished */
diff --git a/src/core/net-sendbuffer.c b/src/core/net-sendbuffer.c
index 9d4b0e37..97fb551f 100644
--- a/src/core/net-sendbuffer.c
+++ b/src/core/net-sendbuffer.c
@@ -109,7 +109,7 @@ static int buffer_add(NET_SENDBUF_REC *rec, const void *data, int size)
/* Send data, if all of it couldn't be sent immediately, it will be resent
automatically after a while. Returns -1 if some unrecoverable error
- occured. */
+ occurred. */
int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size)
{
int ret;
@@ -160,13 +160,9 @@ void net_sendbuffer_flush(NET_SENDBUF_REC *rec)
/* set the socket blocking while doing this */
handle = g_io_channel_unix_get_fd(rec->handle);
-#ifndef WIN32
fcntl(handle, F_SETFL, 0);
-#endif
while (!buffer_send(rec)) ;
-#ifndef WIN32
fcntl(handle, F_SETFL, O_NONBLOCK);
-#endif
}
/* Returns the socket handle */
diff --git a/src/core/net-sendbuffer.h b/src/core/net-sendbuffer.h
index 785f59ae..bdeb7156 100644
--- a/src/core/net-sendbuffer.h
+++ b/src/core/net-sendbuffer.h
@@ -24,7 +24,7 @@ void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close);
/* Send data, if all of it couldn't be sent immediately, it will be resent
automatically after a while. Returns -1 if some unrecoverable error
- occured. */
+ occurred. */
int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size);
int net_sendbuffer_receive_line(NET_SENDBUF_REC *rec, char **str, int read_socket);
diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c
index 768fd540..1eb85341 100644
--- a/src/core/network-openssl.c
+++ b/src/core/network-openssl.c
@@ -22,8 +22,8 @@
#include "network.h"
#include "misc.h"
#include "servers.h"
-
-#ifdef HAVE_OPENSSL
+#include "signals.h"
+#include "tls.h"
#include <openssl/crypto.h>
#include <openssl/x509.h>
@@ -32,11 +32,6 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
-#ifdef HAVE_DANE
-#include <validator/validator.h>
-#include <validator/val_dane.h>
-#endif
-
/* ssl i/o channel object */
typedef struct
{
@@ -203,78 +198,13 @@ static gboolean irssi_ssl_verify_hostname(X509 *cert, const char *hostname)
return matched;
}
-static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, int port, X509 *cert, SERVER_REC *server)
+static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, int port, X509 *cert, SERVER_REC *server, TLS_REC *tls_rec)
{
long result;
-#ifdef HAVE_DANE
- int dane_ret;
- struct val_daneparams daneparams;
- struct val_danestatus *danestatus = NULL;
-
- // Check if a TLSA record is available.
- daneparams.port = port;
- daneparams.proto = DANE_PARAM_PROTO_TCP;
-
- dane_ret = val_getdaneinfo(NULL, hostname, &daneparams, &danestatus);
-
- if (dane_ret == VAL_DANE_NOERROR) {
- signal_emit("tlsa available", 1, server);
- }
-
- if (danestatus != NULL) {
- int do_certificate_check = 1;
-
- if (val_dane_check(NULL, ssl, danestatus, &do_certificate_check) != VAL_DANE_NOERROR) {
- g_warning("DANE: TLSA record for hostname %s port %d could not be verified", hostname, port);
- signal_emit("tlsa verification failed", 1, server);
- val_free_dane(danestatus);
- return FALSE;
- }
-
- signal_emit("tlsa verification success", 1, server);
- val_free_dane(danestatus);
-
- if (do_certificate_check == 0) {
- return TRUE;
- }
- }
-#endif
result = SSL_get_verify_result(ssl);
if (result != X509_V_OK) {
- unsigned char md[EVP_MAX_MD_SIZE];
- unsigned int n;
- char *str;
-
- g_warning("Could not verify SSL servers certificate: %s",
- X509_verify_cert_error_string(result));
- if ((str = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)) == NULL)
- g_warning(" Could not get subject-name from peer certificate");
- else {
- g_warning(" Subject : %s", str);
- free(str);
- }
- if ((str = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0)) == NULL)
- g_warning(" Could not get issuer-name from peer certificate");
- else {
- g_warning(" Issuer : %s", str);
- free(str);
- }
- if (! X509_digest(cert, EVP_md5(), md, &n))
- g_warning(" Could not get fingerprint from peer certificate");
- else {
- char hex[] = "0123456789ABCDEF";
- char fp[EVP_MAX_MD_SIZE*3];
- if (n < sizeof(fp)) {
- unsigned int i;
- for (i = 0; i < n; i++) {
- fp[i*3+0] = hex[(md[i] >> 4) & 0xF];
- fp[i*3+1] = hex[(md[i] >> 0) & 0xF];
- fp[i*3+2] = i == n - 1 ? '\0' : ':';
- }
- g_warning(" MD5 Fingerprint : %s", fp);
- }
- }
+ g_warning("Could not verify TLS servers certificate: %s", X509_verify_cert_error_string(result));
return FALSE;
} else if (! irssi_ssl_verify_hostname(cert, hostname)){
return FALSE;
@@ -289,6 +219,7 @@ static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, gsize len, gsize
const char *errstr;
gchar *errmsg;
+ ERR_clear_error();
ret1 = SSL_read(chan->ssl, buf, len);
if(ret1 <= 0)
{
@@ -334,6 +265,7 @@ static GIOStatus irssi_ssl_write(GIOChannel *handle, const gchar *buf, gsize len
const char *errstr;
gchar *errmsg;
+ ERR_clear_error();
ret1 = SSL_write(chan->ssl, (const char *)buf, len);
if(ret1 <= 0)
{
@@ -455,12 +387,13 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
SSL *ssl;
SSL_CTX *ctx = NULL;
- const char *mycert = server->connrec->ssl_cert;
- const char *mypkey = server->connrec->ssl_pkey;
- const char *mypass = server->connrec->ssl_pass;
- const char *cafile = server->connrec->ssl_cafile;
- const char *capath = server->connrec->ssl_capath;
- gboolean verify = server->connrec->ssl_verify;
+ const char *mycert = server->connrec->tls_cert;
+ const char *mypkey = server->connrec->tls_pkey;
+ const char *mypass = server->connrec->tls_pass;
+ const char *cafile = server->connrec->tls_cafile;
+ const char *capath = server->connrec->tls_capath;
+ const char *ciphers = server->connrec->tls_ciphers;
+ gboolean verify = server->connrec->tls_verify;
g_return_val_if_fail(handle != NULL, NULL);
@@ -470,20 +403,27 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
if(!(fd = g_io_channel_unix_get_fd(handle)))
return NULL;
+ ERR_clear_error();
ctx = SSL_CTX_new(SSLv23_client_method());
if (ctx == NULL) {
g_error("Could not allocate memory for SSL context");
return NULL;
}
- SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
SSL_CTX_set_default_passwd_cb(ctx, get_pem_password_callback);
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *)mypass);
+ if (ciphers != NULL && ciphers[0] != '\0') {
+ if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1)
+ g_warning("No valid SSL cipher suite could be selected");
+ }
+
if (mycert && *mycert) {
char *scert = NULL, *spkey = NULL;
scert = convert_home(mycert);
if (mypkey && *mypkey)
spkey = convert_home(mypkey);
+ ERR_clear_error();
if (! SSL_CTX_use_certificate_file(ctx, scert, SSL_FILETYPE_PEM))
g_warning("Loading of client certificate '%s' failed: %s", mycert, ERR_reason_error_string(ERR_get_error()));
else if (! SSL_CTX_use_PrivateKey_file(ctx, spkey ? spkey : scert, SSL_FILETYPE_PEM))
@@ -502,7 +442,7 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
if (capath && *capath)
scapath = convert_home(capath);
if (! SSL_CTX_load_verify_locations(ctx, scafile, scapath)) {
- g_warning("Could not load CA list for verifying SSL server certificate");
+ g_warning("Could not load CA list for verifying TLS server certificate");
g_free(scafile);
g_free(scapath);
SSL_CTX_free(ctx);
@@ -531,6 +471,10 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
return NULL;
}
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ SSL_set_tlsext_host_name(ssl, server->connrec->address);
+#endif
+
SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE |
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
@@ -552,6 +496,198 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
return gchan;
}
+static void set_cipher_info(TLS_REC *tls, SSL *ssl)
+{
+ g_return_if_fail(tls != NULL);
+ g_return_if_fail(ssl != NULL);
+
+ tls_rec_set_protocol_version(tls, SSL_get_version(ssl));
+
+ tls_rec_set_cipher(tls, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)));
+ tls_rec_set_cipher_size(tls, SSL_get_cipher_bits(ssl, NULL));
+}
+
+static void set_pubkey_info(TLS_REC *tls, X509 *cert, unsigned char *cert_fingerprint, size_t cert_fingerprint_size, unsigned char *public_key_fingerprint, size_t public_key_fingerprint_size)
+{
+ g_return_if_fail(tls != NULL);
+ g_return_if_fail(cert != NULL);
+
+ EVP_PKEY *pubkey = NULL;
+ char *cert_fingerprint_hex = NULL;
+ char *public_key_fingerprint_hex = NULL;
+
+ BIO *bio = NULL;
+ char buffer[128];
+ size_t length;
+
+ pubkey = X509_get_pubkey(cert);
+
+ cert_fingerprint_hex = binary_to_hex(cert_fingerprint, cert_fingerprint_size);
+ tls_rec_set_certificate_fingerprint(tls, cert_fingerprint_hex);
+ tls_rec_set_certificate_fingerprint_algorithm(tls, "SHA256");
+
+ // Show algorithm.
+ switch (EVP_PKEY_id(pubkey)) {
+ case EVP_PKEY_RSA:
+ tls_rec_set_public_key_algorithm(tls, "RSA");
+ break;
+
+ case EVP_PKEY_DSA:
+ tls_rec_set_public_key_algorithm(tls, "DSA");
+ break;
+
+ case EVP_PKEY_EC:
+ tls_rec_set_public_key_algorithm(tls, "EC");
+ break;
+
+ default:
+ tls_rec_set_public_key_algorithm(tls, "Unknown");
+ break;
+ }
+
+ public_key_fingerprint_hex = binary_to_hex(public_key_fingerprint, public_key_fingerprint_size);
+ tls_rec_set_public_key_fingerprint(tls, public_key_fingerprint_hex);
+ tls_rec_set_public_key_size(tls, EVP_PKEY_bits(pubkey));
+ tls_rec_set_public_key_fingerprint_algorithm(tls, "SHA256");
+
+ // Read the NotBefore timestamp.
+ bio = BIO_new(BIO_s_mem());
+ ASN1_TIME_print(bio, X509_get_notBefore(cert));
+ length = BIO_read(bio, buffer, sizeof(buffer));
+ buffer[length] = '\0';
+ BIO_free(bio);
+ tls_rec_set_not_before(tls, buffer);
+
+ // Read the NotAfter timestamp.
+ bio = BIO_new(BIO_s_mem());
+ ASN1_TIME_print(bio, X509_get_notAfter(cert));
+ length = BIO_read(bio, buffer, sizeof(buffer));
+ buffer[length] = '\0';
+ BIO_free(bio);
+ tls_rec_set_not_after(tls, buffer);
+
+ g_free(cert_fingerprint_hex);
+ g_free(public_key_fingerprint_hex);
+ EVP_PKEY_free(pubkey);
+}
+
+static void set_peer_cert_chain_info(TLS_REC *tls, SSL *ssl)
+{
+ g_return_if_fail(tls != NULL);
+ g_return_if_fail(ssl != NULL);
+
+ int nid;
+ char *key = NULL;
+ char *value = NULL;
+ STACK_OF(X509) *chain = NULL;
+ int i;
+ int j;
+ TLS_CERT_REC *cert_rec = NULL;
+ X509_NAME *name = NULL;
+ X509_NAME_ENTRY *entry = NULL;
+ TLS_CERT_ENTRY_REC *tls_cert_entry_rec = NULL;
+ ASN1_STRING *data = NULL;
+
+ chain = SSL_get_peer_cert_chain(ssl);
+
+ if (chain == NULL)
+ return;
+
+ for (i = 0; i < sk_X509_num(chain); i++) {
+ cert_rec = tls_cert_create_rec();
+
+ // Subject.
+ name = X509_get_subject_name(sk_X509_value(chain, i));
+
+ for (j = 0; j < X509_NAME_entry_count(name); j++) {
+ entry = X509_NAME_get_entry(name, j);
+
+ nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry));
+ key = (char *)OBJ_nid2sn(nid);
+
+ if (key == NULL)
+ key = (char *)OBJ_nid2ln(nid);
+
+ data = X509_NAME_ENTRY_get_data(entry);
+ value = (char *)ASN1_STRING_data(data);
+
+ tls_cert_entry_rec = tls_cert_entry_create_rec(key, value);
+ tls_cert_rec_append_subject_entry(cert_rec, tls_cert_entry_rec);
+ }
+
+ // Issuer.
+ name = X509_get_issuer_name(sk_X509_value(chain, i));
+
+ for (j = 0; j < X509_NAME_entry_count(name); j++) {
+ entry = X509_NAME_get_entry(name, j);
+
+ nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry));
+ key = (char *)OBJ_nid2sn(nid);
+
+ if (key == NULL)
+ key = (char *)OBJ_nid2ln(nid);
+
+ data = X509_NAME_ENTRY_get_data(entry);
+ value = (char *)ASN1_STRING_data(data);
+
+ tls_cert_entry_rec = tls_cert_entry_create_rec(key, value);
+ tls_cert_rec_append_issuer_entry(cert_rec, tls_cert_entry_rec);
+ }
+
+ tls_rec_append_cert(tls, cert_rec);
+ }
+}
+
+static void set_server_temporary_key_info(TLS_REC *tls, SSL *ssl)
+{
+ g_return_if_fail(tls != NULL);
+ g_return_if_fail(ssl != NULL);
+
+#ifdef SSL_get_server_tmp_key
+ // Show ephemeral key information.
+ EVP_PKEY *ephemeral_key = NULL;
+
+ // OPENSSL_NO_EC is for solaris 11.3 (2016), github ticket #598
+#ifndef OPENSSL_NO_EC
+ EC_KEY *ec_key = NULL;
+#endif
+ char *ephemeral_key_algorithm = NULL;
+ char *cname = NULL;
+ int nid;
+
+ if (SSL_get_server_tmp_key(ssl, &ephemeral_key)) {
+ switch (EVP_PKEY_id(ephemeral_key)) {
+ case EVP_PKEY_DH:
+ tls_rec_set_ephemeral_key_algorithm(tls, "DH");
+ tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
+ break;
+
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC:
+ ec_key = EVP_PKEY_get1_EC_KEY(ephemeral_key);
+ nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+ EC_KEY_free(ec_key);
+ cname = (char *)OBJ_nid2sn(nid);
+ ephemeral_key_algorithm = g_strdup_printf("ECDH: %s", cname);
+
+ tls_rec_set_ephemeral_key_algorithm(tls, ephemeral_key_algorithm);
+ tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
+
+ g_free_and_null(ephemeral_key_algorithm);
+ break;
+#endif
+
+ default:
+ tls_rec_set_ephemeral_key_algorithm(tls, "Unknown");
+ tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
+ break;
+ }
+
+ EVP_PKEY_free(ephemeral_key);
+ }
+#endif // SSL_get_server_tmp_key.
+}
+
GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server)
{
GIOChannel *handle, *ssl_handle;
@@ -569,9 +705,21 @@ int irssi_ssl_handshake(GIOChannel *handle)
{
GIOSSLChannel *chan = (GIOSSLChannel *)handle;
int ret, err;
- X509 *cert;
- const char *errstr;
-
+ const char *errstr = NULL;
+ X509 *cert = NULL;
+ X509_PUBKEY *pubkey = NULL;
+ int pubkey_size = 0;
+ unsigned char *pubkey_der = NULL;
+ unsigned char *pubkey_der_tmp = NULL;
+ unsigned char pubkey_fingerprint[EVP_MAX_MD_SIZE];
+ unsigned int pubkey_fingerprint_size;
+ unsigned char cert_fingerprint[EVP_MAX_MD_SIZE];
+ unsigned int cert_fingerprint_size;
+ const char *pinned_cert_fingerprint = chan->server->connrec->tls_pinned_cert;
+ const char *pinned_pubkey_fingerprint = chan->server->connrec->tls_pinned_pubkey;
+ TLS_REC *tls = NULL;
+
+ ERR_clear_error();
ret = SSL_connect(chan->ssl);
if (ret <= 0) {
err = SSL_get_error(chan->ssl, ret);
@@ -597,22 +745,74 @@ int irssi_ssl_handshake(GIOChannel *handle)
}
cert = SSL_get_peer_certificate(chan->ssl);
+ pubkey = X509_get_X509_PUBKEY(cert);
+
if (cert == NULL) {
- g_warning("SSL server supplied no certificate");
- return -1;
+ g_warning("TLS server supplied no certificate");
+ ret = 0;
+ goto done;
}
- ret = !chan->verify || irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server);
- X509_free(cert);
- return ret ? 0 : -1;
-}
-#else /* HAVE_OPENSSL */
+ if (pubkey == NULL) {
+ g_warning("TLS server supplied no certificate public key");
+ ret = 0;
+ goto done;
+ }
-GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server)
-{
- g_warning("Connection failed: SSL support not enabled in this build.");
- errno = ENOSYS;
- return NULL;
-}
+ if (! X509_digest(cert, EVP_sha256(), cert_fingerprint, &cert_fingerprint_size)) {
+ g_warning("Unable to generate certificate fingerprint");
+ ret = 0;
+ goto done;
+ }
+
+ pubkey_size = i2d_X509_PUBKEY(pubkey, NULL);
+ pubkey_der = pubkey_der_tmp = g_new(unsigned char, pubkey_size);
+ i2d_X509_PUBKEY(pubkey, &pubkey_der_tmp);
-#endif /* ! HAVE_OPENSSL */
+ EVP_Digest(pubkey_der, pubkey_size, pubkey_fingerprint, &pubkey_fingerprint_size, EVP_sha256(), 0);
+
+ tls = tls_create_rec();
+ set_cipher_info(tls, chan->ssl);
+ set_pubkey_info(tls, cert, cert_fingerprint, cert_fingerprint_size, pubkey_fingerprint, pubkey_fingerprint_size);
+ set_peer_cert_chain_info(tls, chan->ssl);
+ set_server_temporary_key_info(tls, chan->ssl);
+
+ // Emit the TLS rec.
+ signal_emit("tls handshake finished", 2, chan->server, tls);
+
+ ret = 1;
+
+ if (pinned_cert_fingerprint != NULL && pinned_cert_fingerprint[0] != '\0') {
+ ret = g_ascii_strcasecmp(pinned_cert_fingerprint, tls->certificate_fingerprint) == 0;
+
+ if (! ret) {
+ g_warning(" Pinned certificate mismatch");
+ goto done;
+ }
+ }
+
+ if (pinned_pubkey_fingerprint != NULL && pinned_pubkey_fingerprint[0] != '\0') {
+ ret = g_ascii_strcasecmp(pinned_pubkey_fingerprint, tls->public_key_fingerprint) == 0;
+
+ if (! ret) {
+ g_warning(" Pinned public key mismatch");
+ goto done;
+ }
+ }
+
+ if (chan->verify) {
+ ret = irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server, tls);
+
+ if (! ret) {
+ // irssi_ssl_verify emits a warning itself.
+ goto done;
+ }
+ }
+
+done:
+ tls_rec_free(tls);
+ X509_free(cert);
+ g_free(pubkey_der);
+
+ return ret ? 0 : -1;
+}
diff --git a/src/core/network.c b/src/core/network.c
index 3659ab36..3e1b7c70 100644
--- a/src/core/network.c
+++ b/src/core/network.c
@@ -30,26 +30,16 @@
union sockaddr_union {
struct sockaddr sa;
struct sockaddr_in sin;
-#ifdef HAVE_IPV6
struct sockaddr_in6 sin6;
-#endif
};
-#ifdef HAVE_IPV6
-# define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
+#define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
sizeof(so.sin6) : sizeof(so.sin))
-#else
-# define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
-#endif
GIOChannel *g_io_channel_new(int handle)
{
GIOChannel *chan;
-#ifdef WIN32
- chan = g_io_channel_win32_new_socket(handle);
-#else
chan = g_io_channel_unix_new(handle);
-#endif
g_io_channel_set_encoding(chan, NULL, NULL);
g_io_channel_set_buffered(chan, FALSE);
return chan;
@@ -60,7 +50,7 @@ GIOChannel *g_io_channel_new(int handle)
IPADDR ip4_any = {
AF_INET,
-#if defined(HAVE_IPV6) && defined(IN6ADDR_ANY_INIT)
+#if defined(IN6ADDR_ANY_INIT)
IN6ADDR_ANY_INIT
#else
{ INADDR_ANY }
@@ -72,10 +62,8 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
if (ip1->family != ip2->family)
return 0;
-#ifdef HAVE_IPV6
if (ip1->family == AF_INET6)
return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0;
-#endif
return memcmp(&ip1->ip, &ip2->ip, 4) == 0;
}
@@ -84,22 +72,16 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
static void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
{
if (ip == NULL) {
-#ifdef HAVE_IPV6
so->sin6.sin6_family = AF_INET6;
so->sin6.sin6_addr = in6addr_any;
-#else
- so->sin.sin_family = AF_INET;
- so->sin.sin_addr.s_addr = INADDR_ANY;
-#endif
return;
}
so->sin.sin_family = ip->family;
-#ifdef HAVE_IPV6
+
if (ip->family == AF_INET6)
memcpy(&so->sin6.sin6_addr, &ip->ip, sizeof(ip->ip));
else
-#endif
memcpy(&so->sin.sin_addr, &ip->ip, 4);
}
@@ -107,31 +89,25 @@ void sin_get_ip(const union sockaddr_union *so, IPADDR *ip)
{
ip->family = so->sin.sin_family;
-#ifdef HAVE_IPV6
if (ip->family == AF_INET6)
memcpy(&ip->ip, &so->sin6.sin6_addr, sizeof(ip->ip));
else
-#endif
memcpy(&ip->ip, &so->sin.sin_addr, 4);
}
static void sin_set_port(union sockaddr_union *so, int port)
{
-#ifdef HAVE_IPV6
if (so->sin.sin_family == AF_INET6)
so->sin6.sin6_port = htons((unsigned short)port);
else
-#endif
so->sin.sin_port = htons((unsigned short)port);
}
static int sin_get_port(union sockaddr_union *so)
{
-#ifdef HAVE_IPV6
- if (so->sin.sin_family == AF_INET6)
- return ntohs(so->sin6.sin6_port);
-#endif
- return ntohs(so->sin.sin_port);
+ return ntohs((so->sin.sin_family == AF_INET6) ?
+ so->sin6.sin6_port :
+ so->sin.sin_port);
}
/* Connect to socket */
@@ -188,9 +164,7 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
return NULL;
/* set socket options */
-#ifndef WIN32
fcntl(handle, F_SETFL, O_NONBLOCK);
-#endif
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
@@ -211,11 +185,7 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
sin_set_port(&so, port);
ret = connect(handle, &so.sa, SIZEOF_SOCKADDR(so));
-#ifndef WIN32
if (ret < 0 && errno != EINPROGRESS)
-#else
- if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
-#endif
{
int old_errno = errno;
close(handle);
@@ -238,9 +208,7 @@ GIOChannel *net_connect_unix(const char *path)
return NULL;
/* set socket options */
-#ifndef WIN32
fcntl(handle, F_SETFL, O_NONBLOCK);
-#endif
/* connect */
memset(&sa, 0, sizeof(sa));
@@ -284,7 +252,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port)
/* create the socket */
handle = socket(so.sin.sin_family, SOCK_STREAM, 0);
-#ifdef HAVE_IPV6
+
if (handle == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) {
/* IPv6 is not supported by OS */
so.sin.sin_family = AF_INET;
@@ -292,14 +260,12 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port)
handle = socket(AF_INET, SOCK_STREAM, 0);
}
-#endif
+
if (handle == -1)
return NULL;
/* set socket options */
-#ifndef WIN32
fcntl(handle, F_SETFL, O_NONBLOCK);
-#endif
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
@@ -342,9 +308,7 @@ GIOChannel *net_accept(GIOChannel *handle, IPADDR *addr, int *port)
if (addr != NULL) sin_get_ip(&so, addr);
if (port != NULL) *port = sin_get_port(&so);
-#ifndef WIN32
fcntl(ret, F_SETFL, O_NONBLOCK);
-#endif
return g_io_channel_new(ret);
}
@@ -415,23 +379,18 @@ int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port)
Returns 0 = ok, others = error code for net_gethosterror() */
int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
{
-#ifdef HAVE_IPV6
union sockaddr_union *so;
struct addrinfo hints, *ai, *ailist;
int ret, count_v4, count_v6, use_v4, use_v6;
-#else
- struct hostent *hp;
- int count;
-#endif
g_return_val_if_fail(addr != NULL, -1);
memset(ip4, 0, sizeof(IPADDR));
memset(ip6, 0, sizeof(IPADDR));
-#ifdef HAVE_IPV6
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG;
/* save error to host_error for later use */
ret = getaddrinfo(addr, NULL, &hints, &ailist);
@@ -470,85 +429,40 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
}
freeaddrinfo(ailist);
return 0;
-#else
- hp = gethostbyname(addr);
- if (hp == NULL)
- return h_errno;
-
- /* count IPs */
- count = 0;
- while (hp->h_addr_list[count] != NULL)
- count++;
-
- if (count == 0)
- return HOST_NOT_FOUND; /* shouldn't happen? */
-
- /* if there are multiple addresses, return random one */
- ip4->family = AF_INET;
- memcpy(&ip4->ip, hp->h_addr_list[rand() % count], 4);
-
- return 0;
-#endif
}
/* Get name for host, *name should be g_free()'d unless it's NULL.
Return values are the same as with net_gethostbyname() */
int net_gethostbyaddr(IPADDR *ip, char **name)
{
-#ifdef HAVE_IPV6
union sockaddr_union so;
int host_error;
char hostname[NI_MAXHOST];
-#else
- struct hostent *hp;
-#endif
g_return_val_if_fail(ip != NULL, -1);
g_return_val_if_fail(name != NULL, -1);
*name = NULL;
-#ifdef HAVE_IPV6
+
memset(&so, 0, sizeof(so));
sin_set_ip(&so, ip);
/* save error to host_error for later use */
- host_error = getnameinfo((struct sockaddr *) &so, sizeof(so),
- hostname, sizeof(hostname), NULL, 0, 0);
+ host_error = getnameinfo((struct sockaddr *)&so, sizeof(so),
+ hostname, sizeof(hostname),
+ NULL, 0,
+ NI_NAMEREQD);
if (host_error != 0)
return host_error;
*name = g_strdup(hostname);
-#else
- if (ip->family != AF_INET) return -1;
- hp = gethostbyaddr((const char *) &ip->ip, 4, AF_INET);
- if (hp == NULL) return -1;
-
- *name = g_strdup(hp->h_name);
-#endif
return 0;
}
int net_ip2host(IPADDR *ip, char *host)
{
-#ifdef HAVE_IPV6
- if (!inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN))
- return -1;
-#else
- unsigned long ip4;
-
- if (ip->family != AF_INET) {
- strcpy(host, "0.0.0.0");
- } else {
- ip4 = ntohl(ip->ip.s_addr);
- g_snprintf(host, MAX_IP_LEN, "%lu.%lu.%lu.%lu",
- (ip4 & 0xff000000UL) >> 24,
- (ip4 & 0x00ff0000) >> 16,
- (ip4 & 0x0000ff00) >> 8,
- (ip4 & 0x000000ff));
- }
-#endif
- return 0;
+ return inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN) ? 0 : -1;
}
int net_host2ip(const char *host, IPADDR *ip)
@@ -558,12 +472,8 @@ int net_host2ip(const char *host, IPADDR *ip)
if (strchr(host, ':') != NULL) {
/* IPv6 */
ip->family = AF_INET6;
-#ifdef HAVE_IPV6
if (inet_pton(AF_INET6, host, &ip->ip) == 0)
return -1;
-#else
- ip->ip.s_addr = 0;
-#endif
} else {
/* IPv4 */
ip->family = AF_INET;
@@ -598,40 +508,24 @@ int net_geterror(GIOChannel *handle)
/* get error of net_gethostname() */
const char *net_gethosterror(int error)
{
-#ifdef HAVE_IPV6
g_return_val_if_fail(error != 0, NULL);
- return gai_strerror(error);
-#else
- switch (error) {
- case HOST_NOT_FOUND:
- return "Host not found";
- case NO_ADDRESS:
- return "No IP address found for name";
- case NO_RECOVERY:
- return "A non-recovable name server error occurred";
- case TRY_AGAIN:
- return "A temporary error on an authoritative name server";
+ if (error == EAI_SYSTEM) {
+ return strerror(errno);
+ } else {
+ return gai_strerror(error);
}
-
- /* unknown error */
- return NULL;
-#endif
}
/* return TRUE if host lookup failed because it didn't exist (ie. not
some error with name server) */
int net_hosterror_notfound(int error)
{
-#ifdef HAVE_IPV6
-#ifdef EAI_NODATA /* NODATA is depricated */
+#ifdef EAI_NODATA /* NODATA is deprecated */
return error != 1 && (error == EAI_NONAME || error == EAI_NODATA);
#else
return error != 1 && (error == EAI_NONAME);
#endif
-#else
- return error == HOST_NOT_FOUND || error == NO_ADDRESS;
-#endif
}
/* Get name of TCP service */
diff --git a/src/core/network.h b/src/core/network.h
index fa7e9675..03f3b813 100644
--- a/src/core/network.h
+++ b/src/core/network.h
@@ -6,12 +6,10 @@
#endif
#include <sys/types.h>
-#ifndef WIN32
# include <sys/socket.h>
# include <netinet/in.h>
# include <netdb.h>
# include <arpa/inet.h>
-#endif
#ifndef AF_INET6
# ifdef PF_INET6
@@ -23,19 +21,11 @@
struct _IPADDR {
unsigned short family;
-#ifdef HAVE_IPV6
struct in6_addr ip;
-#else
- struct in_addr ip;
-#endif
};
/* maxmimum string length of IP address */
-#ifdef HAVE_IPV6
-# define MAX_IP_LEN INET6_ADDRSTRLEN
-#else
-# define MAX_IP_LEN 20
-#endif
+#define MAX_IP_LEN INET6_ADDRSTRLEN
#define IPADDR_IS_V6(ip) ((ip)->family != AF_INET)
@@ -47,7 +37,7 @@ GIOChannel *g_io_channel_new(int handle);
int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
/* Connect to socket */
-GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip);
+GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) G_GNUC_DEPRECATED;
/* Connect to socket with ip address and SSL*/
GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server);
int irssi_ssl_handshake(GIOChannel *handle);
diff --git a/src/core/nicklist.c b/src/core/nicklist.c
index a5f25f34..770b0afc 100644
--- a/src/core/nicklist.c
+++ b/src/core/nicklist.c
@@ -342,7 +342,7 @@ GSList *nicklist_get_same_unique(SERVER_REC *server, void *id)
return rec.list;
}
-/* nick record comparision for sort functions */
+/* nick record comparison for sort functions */
int nicklist_compare(NICK_REC *p1, NICK_REC *p2, const char *nick_prefix)
{
int i;
@@ -478,7 +478,7 @@ static NICK_REC *nick_nfind(CHANNEL_REC *channel, const char *nick, int len)
if (rec != NULL) {
/* if there's multiple, get the one with identical case */
while (rec->next != NULL) {
- if (strcmp(rec->nick, tmpnick) == 0)
+ if (g_strcmp0(rec->nick, tmpnick) == 0)
break;
rec = rec->next;
}
@@ -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/query-rec.h b/src/core/query-rec.h
index fc08d2ef..59519ad4 100644
--- a/src/core/query-rec.h
+++ b/src/core/query-rec.h
@@ -7,5 +7,5 @@ char *server_tag;
time_t last_unread_msg;
unsigned int unwanted:1; /* TRUE if the other side closed or
- some error occured (DCC chats!) */
+ some error occurred (DCC chats!) */
unsigned int destroying:1;
diff --git a/src/core/rawlog.c b/src/core/rawlog.c
index 2fa6b850..5927e730 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,13 +146,22 @@ 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);
+ g_mkdir_with_parents(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);
+ if (f < 0) {
+ g_warning("rawlog open() failed: %s", strerror(errno));
+ return;
+ }
+
rawlog_dump(rawlog, f);
close(f);
}
@@ -165,6 +175,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/recode.c b/src/core/recode.c
index 029d7ff1..d001a46a 100644
--- a/src/core/recode.c
+++ b/src/core/recode.c
@@ -183,7 +183,7 @@ char *recode_out(const SERVER_REC *server, const char *str, const char *target)
}
char **recode_split(const SERVER_REC *server, const char *str,
- const char *target, int len)
+ const char *target, int len, gboolean onspace)
{
GIConv cd = (GIConv)-1;
const char *from = translit_charset;
@@ -219,7 +219,7 @@ char **recode_split(const SERVER_REC *server, const char *str,
cd = g_iconv_open(to, from);
if (cd == (GIConv)-1) {
/* Fall back to splitting by byte. */
- ret = strsplit_len(str, len);
+ ret = strsplit_len(str, len, onspace);
goto out;
}
@@ -235,11 +235,25 @@ char **recode_split(const SERVER_REC *server, const char *str,
*/
ret[n] = NULL;
g_strfreev(ret);
- ret = strsplit_len(str, len);
+ ret = strsplit_len(str, len, onspace);
goto out;
}
/* Outbuf overflowed, split the input string. */
+ if (onspace) {
+ /*
+ * Try to find a space to split on and leave
+ * the space on the previous line.
+ */
+ int i;
+ for (i = 0; i < inbuf - previnbuf; i++) {
+ if (previnbuf[inbuf-previnbuf-1-i] == ' ') {
+ inbuf -= i;
+ inbytesleft += i;
+ break;
+ }
+ }
+ }
ret[n++] = g_strndup(previnbuf, inbuf - previnbuf);
ret = g_renew(char *, ret, n + 1);
previnbuf = inbuf;
diff --git a/src/core/recode.h b/src/core/recode.h
index b70ec630..719e8f54 100644
--- a/src/core/recode.h
+++ b/src/core/recode.h
@@ -4,7 +4,7 @@
char *recode_in (const SERVER_REC *server, const char *str, const char *target);
char *recode_out (const SERVER_REC *server, const char *str, const char *target);
char **recode_split(const SERVER_REC *server, const char *str,
- const char *target, int len);
+ const char *target, int len, gboolean onspace);
gboolean is_valid_charset(const char *charset);
gboolean is_utf8(void);
void recode_update_charset(void);
diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h
index 17537508..fa348769 100644
--- a/src/core/server-connect-rec.h
+++ b/src/core/server-connect-rec.h
@@ -23,11 +23,14 @@ char *nick;
char *username;
char *realname;
-char *ssl_cert;
-char *ssl_pkey;
-char *ssl_pass;
-char *ssl_cafile;
-char *ssl_capath;
+char *tls_cert;
+char *tls_pkey;
+char *tls_pass;
+char *tls_cafile;
+char *tls_capath;
+char *tls_ciphers;
+char *tls_pinned_cert;
+char *tls_pinned_pubkey;
GIOChannel *connect_handle; /* connect using this handle */
@@ -37,8 +40,8 @@ unsigned int reconnecting:1; /* we're trying to reconnect any connection */
unsigned int no_autojoin_channels:1; /* don't autojoin any channels */
unsigned int no_autosendcmd:1; /* don't execute autosendcmd */
unsigned int unix_socket:1; /* Connect using named unix socket */
-unsigned int use_ssl:1; /* this connection uses SSL */
-unsigned int ssl_verify:1;
+unsigned int use_tls:1; /* this connection uses TLS */
+unsigned int tls_verify:1;
unsigned int no_connect:1; /* don't connect() at all, it's done by plugin */
char *channels;
char *away_reason;
diff --git a/src/core/server-setup-rec.h b/src/core/server-setup-rec.h
index ae797559..e6b0431c 100644
--- a/src/core/server-setup-rec.h
+++ b/src/core/server-setup-rec.h
@@ -8,11 +8,17 @@ char *address;
int port;
char *password;
-char *ssl_cert;
-char *ssl_pkey;
-char *ssl_pass;
-char *ssl_cafile;
-char *ssl_capath;
+int sasl_mechanism;
+char *sasl_password;
+
+char *tls_cert;
+char *tls_pkey;
+char *tls_pass;
+char *tls_cafile;
+char *tls_capath;
+char *tls_ciphers;
+char *tls_pinned_cert;
+char *tls_pinned_pubkey;
char *own_host; /* address to use when connecting this server */
IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */
@@ -24,7 +30,7 @@ unsigned int no_proxy:1;
unsigned int last_failed:1; /* if last connection attempt failed */
unsigned int banned:1; /* if we're banned from this server */
unsigned int dns_error:1; /* DNS said the host doesn't exist */
-unsigned int use_ssl:1; /* this connection uses SSL */
-unsigned int ssl_verify:1;
+unsigned int use_tls:1; /* this connection uses TLS */
+unsigned int tls_verify:1;
GHashTable *module_data;
diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c
index 0a08b461..1727704c 100644
--- a/src/core/servers-reconnect.c
+++ b/src/core/servers-reconnect.c
@@ -190,13 +190,17 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
dest->away_reason = g_strdup(src->away_reason);
dest->no_autojoin_channels = src->no_autojoin_channels;
dest->no_autosendcmd = src->no_autosendcmd;
-
- dest->use_ssl = src->use_ssl;
- dest->ssl_cert = g_strdup(src->ssl_cert);
- dest->ssl_pkey = g_strdup(src->ssl_pkey);
- dest->ssl_verify = src->ssl_verify;
- dest->ssl_cafile = g_strdup(src->ssl_cafile);
- dest->ssl_capath = g_strdup(src->ssl_capath);
+ dest->unix_socket = src->unix_socket;
+
+ dest->use_tls = src->use_tls;
+ dest->tls_cert = g_strdup(src->tls_cert);
+ dest->tls_pkey = g_strdup(src->tls_pkey);
+ dest->tls_verify = src->tls_verify;
+ dest->tls_cafile = g_strdup(src->tls_cafile);
+ dest->tls_capath = g_strdup(src->tls_capath);
+ dest->tls_ciphers = g_strdup(src->tls_ciphers);
+ dest->tls_pinned_cert = g_strdup(src->tls_pinned_cert);
+ dest->tls_pinned_pubkey = g_strdup(src->tls_pinned_pubkey);
return dest;
}
@@ -253,6 +257,9 @@ static void sig_reconnect(SERVER_REC *server)
conn->port = server->connrec->port;
conn->password = g_strdup(server->connrec->password);
+ if (strchr(conn->address, '/') != NULL)
+ conn->unix_socket = TRUE;
+
server_reconnect_add(conn, (server->connect_time == 0 ? time(NULL) :
server->connect_time) + reconnect_time);
server_connect_unref(conn);
@@ -381,7 +388,7 @@ static void cmd_reconnect(const char *data, SERVER_REC *server)
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &tag, &msg))
return;
- if (*tag != '\0' && strcmp(tag, "*") != 0)
+ if (*tag != '\0' && g_strcmp0(tag, "*") != 0)
server = server_find_tag(tag);
if (server != NULL) {
@@ -416,8 +423,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-setup.c b/src/core/servers-setup.c
index 0819ff1a..9492c58c 100644
--- a/src/core/servers-setup.c
+++ b/src/core/servers-setup.c
@@ -122,6 +122,9 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn,
conn->address = g_strdup(address);
if (port > 0) conn->port = port;
+ if (strchr(address, '/') != NULL)
+ conn->unix_socket = TRUE;
+
if (!conn->nick) conn->nick = g_strdup(settings_get_str("nick"));
conn->username = g_strdup(settings_get_str("user_name"));
conn->realname = g_strdup(settings_get_str("real_name"));
@@ -164,18 +167,24 @@ static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
if (sserver->port > 0 && conn->port <= 0)
conn->port = sserver->port;
- conn->use_ssl = sserver->use_ssl;
- if (conn->ssl_cert == NULL && sserver->ssl_cert != NULL && sserver->ssl_cert[0] != '\0')
- conn->ssl_cert = g_strdup(sserver->ssl_cert);
- if (conn->ssl_pkey == NULL && sserver->ssl_pkey != NULL && sserver->ssl_pkey[0] != '\0')
- conn->ssl_pkey = g_strdup(sserver->ssl_pkey);
- if (conn->ssl_pass == NULL && sserver->ssl_pass != NULL && sserver->ssl_pass[0] != '\0')
- conn->ssl_pass = g_strdup(sserver->ssl_pass);
- conn->ssl_verify = sserver->ssl_verify;
- if (conn->ssl_cafile == NULL && sserver->ssl_cafile != NULL && sserver->ssl_cafile[0] != '\0')
- conn->ssl_cafile = g_strdup(sserver->ssl_cafile);
- if (conn->ssl_capath == NULL && sserver->ssl_capath != NULL && sserver->ssl_capath[0] != '\0')
- conn->ssl_capath = g_strdup(sserver->ssl_capath);
+ conn->use_tls = sserver->use_tls;
+ if (conn->tls_cert == NULL && sserver->tls_cert != NULL && sserver->tls_cert[0] != '\0')
+ conn->tls_cert = g_strdup(sserver->tls_cert);
+ if (conn->tls_pkey == NULL && sserver->tls_pkey != NULL && sserver->tls_pkey[0] != '\0')
+ conn->tls_pkey = g_strdup(sserver->tls_pkey);
+ if (conn->tls_pass == NULL && sserver->tls_pass != NULL && sserver->tls_pass[0] != '\0')
+ conn->tls_pass = g_strdup(sserver->tls_pass);
+ conn->tls_verify = sserver->tls_verify;
+ if (conn->tls_cafile == NULL && sserver->tls_cafile != NULL && sserver->tls_cafile[0] != '\0')
+ conn->tls_cafile = g_strdup(sserver->tls_cafile);
+ if (conn->tls_capath == NULL && sserver->tls_capath != NULL && sserver->tls_capath[0] != '\0')
+ conn->tls_capath = g_strdup(sserver->tls_capath);
+ if (conn->tls_ciphers == NULL && sserver->tls_ciphers != NULL && sserver->tls_ciphers[0] != '\0')
+ conn->tls_ciphers = g_strdup(sserver->tls_ciphers);
+ if (conn->tls_pinned_cert == NULL && sserver->tls_pinned_cert != NULL && sserver->tls_pinned_cert[0] != '\0')
+ conn->tls_pinned_cert = g_strdup(sserver->tls_pinned_cert);
+ if (conn->tls_pinned_pubkey == NULL && sserver->tls_pinned_pubkey != NULL && sserver->tls_pinned_pubkey[0] != '\0')
+ conn->tls_pinned_pubkey = g_strdup(sserver->tls_pinned_pubkey);
server_setup_fill_reconn(conn, sserver);
@@ -316,8 +325,8 @@ server_create_conn(int chat_type, const char *dest, int port,
chatrec = chatnet_find(dest);
if (chatrec != NULL) {
rec = create_chatnet_conn(chatrec->name, port, password, nick);
- if (rec != NULL)
- return rec;
+ /* If rec is NULL the chatnet has no url to connect to */
+ return rec;
}
chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
@@ -357,9 +366,10 @@ SERVER_SETUP_REC *server_setup_find(const char *address, int port,
static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
{
SERVER_SETUP_REC *rec;
- CHATNET_REC *chatnetrec;
+ CHATNET_REC *chatnetrec;
char *server, *chatnet, *family;
int port;
+ char *value = NULL;
g_return_val_if_fail(node != NULL, NULL);
@@ -385,7 +395,7 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
chatnet_create(chatnetrec);
}
- family = config_node_get_str(node, "family", "");
+ family = config_node_get_str(node, "family", "");
rec = CHAT_PROTOCOL(chatnetrec)->create_server_setup();
rec->type = module_get_uniq_id("SERVER SETUP", 0);
@@ -395,17 +405,55 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
(g_ascii_strcasecmp(family, "inet") == 0 ? AF_INET : 0);
rec->address = g_strdup(server);
rec->password = g_strdup(config_node_get_str(node, "password", NULL));
- rec->use_ssl = config_node_get_bool(node, "use_ssl", FALSE);
- rec->ssl_cert = g_strdup(config_node_get_str(node, "ssl_cert", NULL));
- rec->ssl_pkey = g_strdup(config_node_get_str(node, "ssl_pkey", NULL));
- rec->ssl_pass = g_strdup(config_node_get_str(node, "ssl_pass", NULL));
- rec->ssl_verify = config_node_get_bool(node, "ssl_verify", FALSE);
- rec->ssl_cafile = g_strdup(config_node_get_str(node, "ssl_cafile", NULL));
- rec->ssl_capath = g_strdup(config_node_get_str(node, "ssl_capath", NULL));
- if (rec->ssl_cafile || rec->ssl_capath)
- rec->ssl_verify = TRUE;
- if (rec->ssl_cert != NULL || rec->ssl_verify)
- rec->use_ssl = TRUE;
+
+ rec->use_tls = config_node_get_bool(node, "use_tls", FALSE) || config_node_get_bool(node, "use_ssl", FALSE);
+ rec->tls_verify = config_node_get_bool(node, "tls_verify", FALSE) || config_node_get_bool(node, "ssl_verify", FALSE);
+
+ value = config_node_get_str(node, "tls_cert", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_cert", NULL);
+ rec->tls_cert = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_pkey", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_pkey", NULL);
+ rec->tls_pkey = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_pass", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_pass", NULL);
+ rec->tls_pass = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_cafile", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_cafile", NULL);
+ rec->tls_cafile = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_capath", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_capath", NULL);
+ rec->tls_capath = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_ciphers", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_ciphers", NULL);
+ rec->tls_ciphers = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_pinned_cert", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_pinned_cert", NULL);
+ rec->tls_pinned_cert = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_pinned_pubkey", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_pinned_pubkey", NULL);
+ rec->tls_pinned_pubkey = g_strdup(value);
+
+ if (rec->tls_cafile || rec->tls_capath)
+ rec->tls_verify = TRUE;
+ if (rec->tls_cert != NULL || rec->tls_verify)
+ rec->use_tls = TRUE;
+
rec->port = port;
rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE);
@@ -417,17 +465,39 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
return rec;
}
+static int compare_server_setup (CONFIG_NODE *node, SERVER_SETUP_REC *server)
+{
+ char *address, *chatnet;
+ int port;
+
+ address = config_node_get_str(node, "address", NULL);
+ chatnet = config_node_get_str(node, "chatnet", NULL);
+ port = config_node_get_int(node, "port", 0);
+
+ if (g_strcmp0(address, server->address) != 0 ||
+ g_strcmp0(chatnet, server->chatnet) != 0 ||
+ port != server->port)
+ return 1;
+
+ return 0;
+}
+
static void server_setup_save(SERVER_SETUP_REC *rec)
{
- CONFIG_NODE *parentnode, *node;
- int index;
+ CONFIG_NODE *parent_node, *node;
+ GSList *config_node;
- index = g_slist_index(setupservers, rec);
+ parent_node = iconfig_node_traverse("(servers", TRUE);
- parentnode = iconfig_node_traverse("(servers", TRUE);
- node = config_node_nth(parentnode, index);
- if (node == NULL)
- node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
+ /* Try to find this channel in the configuration */
+ config_node = g_slist_find_custom(parent_node->value, rec,
+ (GCompareFunc)compare_server_setup);
+ if (config_node != NULL)
+ /* Let's update this server record */
+ node = config_node->data;
+ else
+ /* Create a brand-new server record */
+ node = iconfig_node_section(parent_node, NULL, NODE_TYPE_BLOCK);
iconfig_node_clear(node);
iconfig_node_set_str(node, "address", rec->address);
@@ -435,13 +505,18 @@ static void server_setup_save(SERVER_SETUP_REC *rec)
iconfig_node_set_int(node, "port", rec->port);
iconfig_node_set_str(node, "password", rec->password);
- iconfig_node_set_bool(node, "use_ssl", rec->use_ssl);
- iconfig_node_set_str(node, "ssl_cert", rec->ssl_cert);
- iconfig_node_set_str(node, "ssl_pkey", rec->ssl_pkey);
- iconfig_node_set_str(node, "ssl_pass", rec->ssl_pass);
- iconfig_node_set_bool(node, "ssl_verify", rec->ssl_verify);
- iconfig_node_set_str(node, "ssl_cafile", rec->ssl_cafile);
- iconfig_node_set_str(node, "ssl_capath", rec->ssl_capath);
+
+ iconfig_node_set_bool(node, "use_tls", rec->use_tls);
+ iconfig_node_set_str(node, "tls_cert", rec->tls_cert);
+ iconfig_node_set_str(node, "tls_pkey", rec->tls_pkey);
+ iconfig_node_set_str(node, "tls_pass", rec->tls_pass);
+ iconfig_node_set_bool(node, "tls_verify", rec->tls_verify);
+ iconfig_node_set_str(node, "tls_cafile", rec->tls_cafile);
+ iconfig_node_set_str(node, "tls_capath", rec->tls_capath);
+ iconfig_node_set_str(node, "tls_ciphers", rec->tls_ciphers);
+ iconfig_node_set_str(node, "tls_pinned_cert", rec->tls_pinned_cert);
+ iconfig_node_set_str(node, "tls_pinned_pubkey", rec->tls_pinned_pubkey);
+
iconfig_node_set_str(node, "own_host", rec->own_host);
iconfig_node_set_str(node, "family",
@@ -458,14 +533,21 @@ static void server_setup_save(SERVER_SETUP_REC *rec)
static void server_setup_remove_config(SERVER_SETUP_REC *rec)
{
- CONFIG_NODE *node;
- int index;
+ CONFIG_NODE *parent_node;
+ GSList *config_node;
- node = iconfig_node_traverse("servers", FALSE);
- if (node != NULL) {
- index = g_slist_index(setupservers, rec);
- iconfig_node_list_remove(node, index);
- }
+ parent_node = iconfig_node_traverse("servers", FALSE);
+
+ if (parent_node == NULL)
+ return;
+
+ /* Try to find this server in the configuration */
+ config_node = g_slist_find_custom(parent_node->value, rec,
+ (GCompareFunc)compare_server_setup);
+
+ if (config_node != NULL)
+ /* Delete the server from the configuration */
+ iconfig_node_remove(parent_node, config_node->data);
}
static void server_setup_destroy(SERVER_SETUP_REC *rec)
@@ -478,11 +560,14 @@ static void server_setup_destroy(SERVER_SETUP_REC *rec)
g_free_not_null(rec->own_ip6);
g_free_not_null(rec->chatnet);
g_free_not_null(rec->password);
- g_free_not_null(rec->ssl_cert);
- g_free_not_null(rec->ssl_pkey);
- g_free_not_null(rec->ssl_pass);
- g_free_not_null(rec->ssl_cafile);
- g_free_not_null(rec->ssl_capath);
+ g_free_not_null(rec->tls_cert);
+ g_free_not_null(rec->tls_pkey);
+ g_free_not_null(rec->tls_pass);
+ g_free_not_null(rec->tls_cafile);
+ g_free_not_null(rec->tls_capath);
+ g_free_not_null(rec->tls_ciphers);
+ g_free_not_null(rec->tls_pinned_cert);
+ g_free_not_null(rec->tls_pinned_pubkey);
g_free(rec->address);
g_free(rec);
}
@@ -497,6 +582,21 @@ void server_setup_add(SERVER_SETUP_REC *rec)
signal_emit("server setup updated", 1, rec);
}
+void server_setup_remove_chatnet(const char *chatnet)
+{
+ GSList *tmp, *next;
+
+ g_return_if_fail(chatnet != NULL);
+
+ for (tmp = setupservers; tmp != NULL; tmp = next) {
+ SERVER_SETUP_REC *rec = tmp->data;
+
+ next = tmp->next;
+ if (g_ascii_strcasecmp(rec->chatnet, chatnet) == 0)
+ server_setup_remove(rec);
+ }
+}
+
void server_setup_remove(SERVER_SETUP_REC *rec)
{
server_setup_remove_config(rec);
@@ -523,7 +623,7 @@ static void read_servers(void)
static void read_settings(void)
{
if (old_source_host == NULL ||
- strcmp(old_source_host, settings_get_str("hostname")) != 0) {
+ g_strcmp0(old_source_host, settings_get_str("hostname")) != 0) {
g_free_not_null(old_source_host);
old_source_host = g_strdup(settings_get_str("hostname"));
diff --git a/src/core/servers-setup.h b/src/core/servers-setup.h
index f7601a68..e7ff7abf 100644
--- a/src/core/servers-setup.h
+++ b/src/core/servers-setup.h
@@ -39,6 +39,9 @@ SERVER_SETUP_REC *server_setup_find(const char *address, int port,
void server_setup_add(SERVER_SETUP_REC *rec);
void server_setup_remove(SERVER_SETUP_REC *rec);
+/* Remove servers attached to chatne */
+void server_setup_remove_chatnet(const char *chatnet);
+
void servers_setup_init(void);
void servers_setup_deinit(void);
diff --git a/src/core/servers.c b/src/core/servers.c
index 06f82d4d..b9faab81 100644
--- a/src/core/servers.c
+++ b/src/core/servers.c
@@ -167,7 +167,6 @@ static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle)
server_connect_finished(server);
}
-#ifdef HAVE_OPENSSL
static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *handle)
{
int error;
@@ -198,7 +197,6 @@ static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *han
server_connect_finished(server);
}
-#endif
static void server_real_connect(SERVER_REC *server, IPADDR *ip,
const char *unix_socket)
@@ -218,12 +216,10 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
return;
if (ip != NULL) {
- own_ip = ip == NULL ? NULL :
- (IPADDR_IS_V6(ip) ? server->connrec->own_ip6 :
- server->connrec->own_ip4);
+ own_ip = IPADDR_IS_V6(ip) ? server->connrec->own_ip6 : server->connrec->own_ip4;
port = server->connrec->proxy != NULL ?
server->connrec->proxy_port : server->connrec->port;
- handle = server->connrec->use_ssl ?
+ handle = server->connrec->use_tls ?
net_connect_ip_ssl(ip, port, own_ip, server) : net_connect_ip(ip, port, own_ip);
} else {
handle = net_connect_unix(unix_socket);
@@ -241,7 +237,7 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
}
server->no_reconnect = TRUE;
}
- if (server->connrec->use_ssl && errno == ENOSYS)
+ if (server->connrec->use_tls && errno == ENOSYS)
server->no_reconnect = TRUE;
server->connection_lost = TRUE;
@@ -249,11 +245,9 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
g_free(errmsg2);
} else {
server->handle = net_sendbuffer_create(handle, 0);
-#ifdef HAVE_OPENSSL
- if (server->connrec->use_ssl)
+ if (server->connrec->use_tls)
server_connect_callback_init_ssl(server, handle);
else
-#endif
server->connect_tag =
g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
(GInputFunction)
@@ -628,21 +622,24 @@ void server_connect_unref(SERVER_CONNECT_REC *conn)
g_free_not_null(conn->own_ip4);
g_free_not_null(conn->own_ip6);
- g_free_not_null(conn->password);
- g_free_not_null(conn->nick);
- g_free_not_null(conn->username);
+ g_free_not_null(conn->password);
+ g_free_not_null(conn->nick);
+ g_free_not_null(conn->username);
g_free_not_null(conn->realname);
- g_free_not_null(conn->ssl_cert);
- g_free_not_null(conn->ssl_pkey);
- g_free_not_null(conn->ssl_pass);
- g_free_not_null(conn->ssl_cafile);
- g_free_not_null(conn->ssl_capath);
+ g_free_not_null(conn->tls_cert);
+ g_free_not_null(conn->tls_pkey);
+ g_free_not_null(conn->tls_pass);
+ g_free_not_null(conn->tls_cafile);
+ g_free_not_null(conn->tls_capath);
+ g_free_not_null(conn->tls_ciphers);
+ g_free_not_null(conn->tls_pinned_cert);
+ g_free_not_null(conn->tls_pinned_pubkey);
g_free_not_null(conn->channels);
- g_free_not_null(conn->away_reason);
+ g_free_not_null(conn->away_reason);
- conn->type = 0;
+ conn->type = 0;
g_free(conn);
}
@@ -685,21 +682,11 @@ SERVER_REC *cmd_options_get_server(const char *cmd,
SERVER_REC *defserver)
{
SERVER_REC *server;
- GSList *list, *tmp, *next;
+ GList *list;
/* get all the options, then remove the known ones. there should
be only one left - the server tag. */
- list = hashtable_get_keys(optlist);
- if (cmd != NULL) {
- for (tmp = list; tmp != NULL; tmp = next) {
- char *option = tmp->data;
- next = tmp->next;
-
- if (command_have_option(cmd, option))
- list = g_slist_remove(list, option);
- }
- }
-
+ list = optlist_remove_known(cmd, optlist);
if (list == NULL)
return defserver;
@@ -714,7 +701,7 @@ SERVER_REC *cmd_options_get_server(const char *cmd,
server = NULL;
}
- g_slist_free(list);
+ g_list_free(list);
return server;
}
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/core/session.c b/src/core/session.c
index b3002632..34190c52 100644
--- a/src/core/session.c
+++ b/src/core/session.c
@@ -92,7 +92,7 @@ static void cmd_upgrade(const char *data)
static void session_save_nick(CHANNEL_REC *channel, NICK_REC *nick,
CONFIG_REC *config, CONFIG_NODE *node)
{
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ node = config_node_section(config, node, NULL, NODE_TYPE_BLOCK);
config_node_set_str(config, node, "nick", nick->nick);
config_node_set_bool(config, node, "op", nick->op);
@@ -109,7 +109,7 @@ static void session_save_channel_nicks(CHANNEL_REC *channel, CONFIG_REC *config,
{
GSList *tmp, *nicks;
- node = config_node_section(node, "nicks", NODE_TYPE_LIST);
+ node = config_node_section(config, node, "nicks", NODE_TYPE_LIST);
nicks = nicklist_getnicks(channel);
for (tmp = nicks; tmp != NULL; tmp = tmp->next)
session_save_nick(channel, tmp->data, config, node);
@@ -119,7 +119,7 @@ static void session_save_channel_nicks(CHANNEL_REC *channel, CONFIG_REC *config,
static void session_save_channel(CHANNEL_REC *channel, CONFIG_REC *config,
CONFIG_NODE *node)
{
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ node = config_node_section(config, node, NULL, NODE_TYPE_BLOCK);
config_node_set_str(config, node, "name", channel->name);
config_node_set_str(config, node, "visible_name", channel->visible_name);
@@ -138,7 +138,7 @@ static void session_save_server_channels(SERVER_REC *server,
GSList *tmp;
/* save channels */
- node = config_node_section(node, "channels", NODE_TYPE_LIST);
+ node = config_node_section(config, node, "channels", NODE_TYPE_LIST);
for (tmp = server->channels; tmp != NULL; tmp = tmp->next)
session_save_channel(tmp->data, config, node);
}
@@ -148,10 +148,9 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
{
int handle;
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ node = config_node_section(config, node, NULL, NODE_TYPE_BLOCK);
- config_node_set_str(config, node, "chat_type",
- chat_protocol_find_id(server->chat_type)->name);
+ config_node_set_str(config, node, "chat_type", chat_protocol_find_id(server->chat_type)->name);
config_node_set_str(config, node, "address", server->connrec->address);
config_node_set_int(config, node, "port", server->connrec->port);
config_node_set_str(config, node, "chatnet", server->connrec->chatnet);
@@ -159,12 +158,15 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
config_node_set_str(config, node, "nick", server->nick);
config_node_set_str(config, node, "version", server->version);
- config_node_set_bool(config, node, "use_ssl", server->connrec->use_ssl);
- config_node_set_str(config, node, "ssl_cert", server->connrec->ssl_cert);
- config_node_set_str(config, node, "ssl_pkey", server->connrec->ssl_pkey);
- config_node_set_bool(config, node, "ssl_verify", server->connrec->ssl_verify);
- config_node_set_str(config, node, "ssl_cafile", server->connrec->ssl_cafile);
- config_node_set_str(config, node, "ssl_capath", server->connrec->ssl_capath);
+ config_node_set_bool(config, node, "use_tls", server->connrec->use_tls);
+ config_node_set_str(config, node, "tls_cert", server->connrec->tls_cert);
+ config_node_set_str(config, node, "tls_pkey", server->connrec->tls_pkey);
+ config_node_set_bool(config, node, "tls_verify", server->connrec->tls_verify);
+ config_node_set_str(config, node, "tls_cafile", server->connrec->tls_cafile);
+ config_node_set_str(config, node, "tls_capath", server->connrec->tls_capath);
+ config_node_set_str(config, node, "tls_ciphers", server->connrec->tls_ciphers);
+ config_node_set_str(config, node, "tls_pinned_cert", server->connrec->tls_pinned_cert);
+ config_node_set_str(config, node, "tls_pinned_pubkey", server->connrec->tls_pinned_pubkey);
handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
config_node_set_int(config, node, "handle", handle);
@@ -187,7 +189,7 @@ static void session_restore_channel_nicks(CHANNEL_REC *channel,
GSList *tmp;
/* restore nicks */
- node = config_node_section(node, "nicks", -1);
+ node = config_node_section(NULL, node, "nicks", -1);
if (node != NULL && node->type == NODE_TYPE_LIST) {
tmp = config_node_first(node->value);
for (; tmp != NULL; tmp = config_node_next(tmp)) {
@@ -223,7 +225,7 @@ static void session_restore_server_channels(SERVER_REC *server,
GSList *tmp;
/* restore channels */
- node = config_node_section(node, "channels", -1);
+ node = config_node_section(NULL, node, "channels", -1);
if (node != NULL && node->type == NODE_TYPE_LIST) {
tmp = config_node_first(node->value);
for (; tmp != NULL; tmp = config_node_next(tmp))
diff --git a/src/core/settings.c b/src/core/settings.c
index 2296909e..4e0717cd 100644
--- a/src/core/settings.c
+++ b/src/core/settings.c
@@ -59,7 +59,7 @@ static SETTINGS_REC *settings_get(const char *key, SettingType type)
g_warning("settings_get(%s) : not found", key);
return NULL;
}
- if (type != -1 && rec->type != type) {
+ if (type != SETTING_TYPE_ANY && rec->type != type) {
g_warning("settings_get(%s) : invalid type", key);
return NULL;
}
@@ -77,7 +77,7 @@ settings_get_str_type(const char *key, SettingType type)
if (rec == NULL) return NULL;
node = iconfig_node_traverse("settings", FALSE);
- node = node == NULL ? NULL : config_node_section(node, rec->module, -1);
+ node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
return node == NULL ? rec->default_value.v_string :
config_node_get_str(node, key, rec->default_value.v_string);
@@ -85,7 +85,7 @@ settings_get_str_type(const char *key, SettingType type)
const char *settings_get_str(const char *key)
{
- return settings_get_str_type(key, -1);
+ return settings_get_str_type(key, SETTING_TYPE_ANY);
}
int settings_get_int(const char *key)
@@ -97,7 +97,7 @@ int settings_get_int(const char *key)
if (rec == NULL) return 0;
node = iconfig_node_traverse("settings", FALSE);
- node = node == NULL ? NULL : config_node_section(node, rec->module, -1);
+ node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
return node == NULL ? rec->default_value.v_int :
config_node_get_int(node, key, rec->default_value.v_int);
@@ -112,7 +112,7 @@ int settings_get_bool(const char *key)
if (rec == NULL) return FALSE;
node = iconfig_node_traverse("settings", FALSE);
- node = node == NULL ? NULL : config_node_section(node, rec->module, -1);
+ node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
return node == NULL ? rec->default_value.v_bool :
config_node_get_bool(node, key, rec->default_value.v_bool);
@@ -148,11 +148,36 @@ int settings_get_size(const char *key)
return str == NULL ? 0 : bytes;
}
+int settings_get_choice(const char *key)
+{
+ SETTINGS_REC *rec;
+ CONFIG_NODE *node;
+ char *str;
+ int index;
+
+ rec = settings_get(key, SETTING_TYPE_CHOICE);
+ if (rec == NULL) return -1;
+
+ node = iconfig_node_traverse("settings", FALSE);
+ node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
+
+ str = node == NULL ? rec->default_value.v_string :
+ config_node_get_str(node, key, rec->default_value.v_string);
+
+ if (str == NULL || (index = strarray_find(rec->choices, str)) < 0)
+ return rec->default_value.v_int;
+
+ return index;
+}
+
char *settings_get_print(SETTINGS_REC *rec)
{
char *value = NULL;
switch(rec->type) {
+ case SETTING_TYPE_CHOICE:
+ value = g_strdup(rec->choices[settings_get_choice(rec->key)]);
+ break;
case SETTING_TYPE_BOOLEAN:
value = g_strdup(settings_get_bool(rec->key) ? "ON" : "OFF");
break;
@@ -163,6 +188,7 @@ char *settings_get_print(SETTINGS_REC *rec)
case SETTING_TYPE_TIME:
case SETTING_TYPE_LEVEL:
case SETTING_TYPE_SIZE:
+ case SETTING_TYPE_ANY:
value = g_strdup(settings_get_str(rec->key));
break;
}
@@ -171,13 +197,31 @@ char *settings_get_print(SETTINGS_REC *rec)
static void settings_add(const char *module, const char *section,
const char *key, SettingType type,
- const SettingValue *default_value)
+ const SettingValue *default_value,
+ const char *choices)
{
SETTINGS_REC *rec;
+ char **choices_vec = NULL;
g_return_if_fail(key != NULL);
g_return_if_fail(section != NULL);
+ if (type == SETTING_TYPE_CHOICE) {
+ if (choices == NULL) {
+ g_warning("Trying to add setting '%s' with no choices.", key);
+ return;
+ }
+
+ choices_vec = g_strsplit(choices, ";", -1);
+
+ /* validate the default value */
+ if (default_value->v_int < 0 || default_value->v_int >= g_strv_length(choices_vec)) {
+ g_warning("Trying to add setting '%s' with an invalid default value.", key);
+ g_strfreev(choices_vec);
+ return;
+ }
+ }
+
rec = g_hash_table_lookup(settings, key);
if (rec != NULL) {
/* Already exists, make sure it's correct type */
@@ -196,6 +240,7 @@ static void settings_add(const char *module, const char *section,
rec->type = type;
rec->default_value = *default_value;
+ rec->choices = choices_vec;
g_hash_table_insert(settings, rec->key, rec);
}
}
@@ -207,7 +252,17 @@ void settings_add_str_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
- settings_add(module, section, key, SETTING_TYPE_STRING, &default_value);
+ settings_add(module, section, key, SETTING_TYPE_STRING, &default_value, NULL);
+}
+
+void settings_add_choice_module(const char *module, const char *section,
+ const char *key, int def, const char *choices)
+{
+ SettingValue default_value;
+
+ memset(&default_value, 0, sizeof(default_value));
+ default_value.v_int = def;
+ settings_add(module, section, key, SETTING_TYPE_CHOICE, &default_value, choices);
}
void settings_add_int_module(const char *module, const char *section,
@@ -217,7 +272,7 @@ void settings_add_int_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_int = def;
- settings_add(module, section, key, SETTING_TYPE_INT, &default_value);
+ settings_add(module, section, key, SETTING_TYPE_INT, &default_value, NULL);
}
void settings_add_bool_module(const char *module, const char *section,
@@ -227,8 +282,7 @@ void settings_add_bool_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_bool = def;
- settings_add(module, section, key, SETTING_TYPE_BOOLEAN,
- &default_value);
+ settings_add(module, section, key, SETTING_TYPE_BOOLEAN, &default_value, NULL);
}
void settings_add_time_module(const char *module, const char *section,
@@ -238,7 +292,7 @@ void settings_add_time_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
- settings_add(module, section, key, SETTING_TYPE_TIME, &default_value);
+ settings_add(module, section, key, SETTING_TYPE_TIME, &default_value, NULL);
}
void settings_add_level_module(const char *module, const char *section,
@@ -248,7 +302,7 @@ void settings_add_level_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
- settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value);
+ settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value, NULL);
}
void settings_add_size_module(const char *module, const char *section,
@@ -258,14 +312,16 @@ void settings_add_size_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
- settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value);
+ settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value, NULL);
}
static void settings_destroy(SETTINGS_REC *rec)
{
if (rec->type != SETTING_TYPE_INT &&
- rec->type != SETTING_TYPE_BOOLEAN)
+ rec->type != SETTING_TYPE_BOOLEAN &&
+ rec->type != SETTING_TYPE_CHOICE)
g_free(rec->default_value.v_string);
+ g_strfreev(rec->choices);
g_free(rec->module);
g_free(rec->section);
g_free(rec->key);
@@ -295,7 +351,7 @@ void settings_remove(const char *key)
static int settings_remove_hash(const char *key, SETTINGS_REC *rec,
const char *module)
{
- if (strcmp(rec->module, module) == 0) {
+ if (g_strcmp0(rec->module, module) == 0) {
settings_unref(rec, FALSE);
return TRUE;
}
@@ -324,7 +380,21 @@ static CONFIG_NODE *settings_get_node(const char *key)
}
node = iconfig_node_traverse("settings", TRUE);
- return config_node_section(node, rec->module, NODE_TYPE_BLOCK);
+ return iconfig_node_section(node, rec->module, NODE_TYPE_BLOCK);
+}
+
+gboolean settings_set_choice(const char *key, const char *value)
+{
+ SETTINGS_REC *rec;
+
+ rec = settings_get_record(key);
+
+ if (rec != NULL && strarray_find(rec->choices, value) < 0)
+ return FALSE;
+
+ settings_set_str(key, value);
+
+ return TRUE;
}
void settings_set_str(const char *key, const char *value)
@@ -342,7 +412,7 @@ void settings_set_bool(const char *key, int value)
iconfig_node_set_bool(settings_get_node(key), key, value);
}
-int settings_set_time(const char *key, const char *value)
+gboolean settings_set_time(const char *key, const char *value)
{
int msecs;
@@ -353,7 +423,7 @@ int settings_set_time(const char *key, const char *value)
return TRUE;
}
-int settings_set_level(const char *key, const char *value)
+gboolean settings_set_level(const char *key, const char *value)
{
int iserror;
@@ -365,7 +435,7 @@ int settings_set_level(const char *key, const char *value)
return TRUE;
}
-int settings_set_size(const char *key, const char *value)
+gboolean settings_set_size(const char *key, const char *value)
{
int size;
@@ -380,10 +450,10 @@ SettingType settings_get_type(const char *key)
{
SETTINGS_REC *rec;
- g_return_val_if_fail(key != NULL, -1);
+ g_return_val_if_fail(key != NULL, SETTING_TYPE_ANY);
rec = g_hash_table_lookup(settings, key);
- return rec == NULL ? -1 : rec->type;
+ return rec == NULL ? SETTING_TYPE_ANY : rec->type;
}
/* Get the record of the setting */
@@ -420,7 +490,7 @@ static void settings_clean_invalid_module(const char *module)
node = iconfig_node_traverse("settings", FALSE);
if (node == NULL) return;
- node = config_node_section(node, module, -1);
+ node = iconfig_node_section(node, module, -1);
if (node == NULL) return;
for (tmp = config_node_first(node->value); tmp != NULL; tmp = next) {
@@ -428,7 +498,7 @@ static void settings_clean_invalid_module(const char *module)
next = config_node_next(tmp);
set = g_hash_table_lookup(settings, subnode->key);
- if (set == NULL || strcmp(set->module, module) != 0)
+ if (set == NULL || g_strcmp0(set->module, module) != 0)
iconfig_node_remove(node, subnode);
}
}
@@ -458,7 +528,7 @@ static int backwards_compatibility(const char *module, CONFIG_NODE *node,
new_value = NULL; new_key = NULL; new_module = NULL;
/* fe-text term_type -> fe-common/core term_charset - for 0.8.10-> */
- if (strcmp(module, "fe-text") == 0) {
+ if (g_strcmp0(module, "fe-text") == 0) {
if (g_ascii_strcasecmp(node->key, "term_type") == 0 ||
/* kludge for cvs-version where term_charset was in fe-text */
g_ascii_strcasecmp(node->key, "term_charset") == 0) {
@@ -468,7 +538,7 @@ static int backwards_compatibility(const char *module, CONFIG_NODE *node,
g_strdup(node->value);
new_node = iconfig_node_traverse("settings", FALSE);
new_node = new_node == NULL ? NULL :
- config_node_section(new_node, new_module, -1);
+ iconfig_node_section(new_node, new_module, -1);
config_node_set_str(mainconfig, new_node,
new_key, new_value);
@@ -502,7 +572,7 @@ void settings_check_module(const char *module)
g_return_if_fail(module != NULL);
node = iconfig_node_traverse("settings", FALSE);
- node = node == NULL ? NULL : config_node_section(node, module, -1);
+ node = node == NULL ? NULL : iconfig_node_section(node, module, -1);
if (node == NULL) return;
errors = g_string_new(NULL);
@@ -515,12 +585,13 @@ void settings_check_module(const char *module)
for (; tmp != NULL; tmp = next) {
node = tmp->data;
next = config_node_next(tmp);
+ if (node->key == NULL) continue;
set = g_hash_table_lookup(settings, node->key);
if (backwards_compatibility(module, node, parent))
continue;
- if (set == NULL || strcmp(set->module, module) != 0) {
+ if (set == NULL || g_strcmp0(set->module, module) != 0) {
g_string_append_printf(errors, " %s", node->key);
count++;
}
@@ -548,9 +619,9 @@ void settings_check_module(const char *module)
static int settings_compare(SETTINGS_REC *v1, SETTINGS_REC *v2)
{
- int cmp = strcmp(v1->section, v2->section);
+ int cmp = g_strcmp0(v1->section, v2->section);
if (!cmp)
- cmp = strcmp(v1->key, v2->key);
+ cmp = g_strcmp0(v1->key, v2->key);
return cmp;
}
@@ -682,7 +753,7 @@ static void init_configfile(void)
if (stat(get_irssi_dir(), &statbuf) != 0) {
/* ~/.irssi not found, create it. */
- if (mkpath(get_irssi_dir(), 0700) != 0) {
+ if (g_mkdir_with_parents(get_irssi_dir(), 0700) != 0) {
g_error("Couldn't create %s directory", get_irssi_dir());
}
} else if (!S_ISDIR(statbuf.st_mode)) {
diff --git a/src/core/settings.h b/src/core/settings.h
index f8d9f68f..d174f250 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -7,7 +7,9 @@ typedef enum {
SETTING_TYPE_BOOLEAN,
SETTING_TYPE_TIME,
SETTING_TYPE_LEVEL,
- SETTING_TYPE_SIZE
+ SETTING_TYPE_SIZE,
+ SETTING_TYPE_CHOICE,
+ SETTING_TYPE_ANY
} SettingType;
typedef struct {
@@ -25,6 +27,7 @@ typedef struct {
SettingType type;
SettingValue default_value;
+ char **choices;
} SETTINGS_REC;
/* macros for handling the default Irssi configuration */
@@ -36,6 +39,8 @@ typedef struct {
#define iconfig_set_int(a, b, c) config_set_int(mainconfig, a, b, c)
#define iconfig_set_bool(a, b, c) config_set_bool(mainconfig, a, b, c)
+#define iconfig_node_section(a, b, c) config_node_section(mainconfig, a, b, c)
+#define iconfig_node_section_index(a, b, c, d) config_node_section_index(mainconfig, a, b, c, d)
#define iconfig_node_traverse(a, b) config_node_traverse(mainconfig, a, b)
#define iconfig_node_set_str(a, b, c) config_node_set_str(mainconfig, a, b, c)
#define iconfig_node_set_int(a, b, c) config_node_set_int(mainconfig, a, b, c)
@@ -55,6 +60,7 @@ int settings_get_bool(const char *key);
int settings_get_time(const char *key); /* as milliseconds */
int settings_get_level(const char *key);
int settings_get_size(const char *key); /* as bytes */
+int settings_get_choice(const char *key);
char *settings_get_print(SETTINGS_REC *rec);
/* Functions to add/remove settings */
@@ -70,6 +76,8 @@ void settings_add_level_module(const char *module, const char *section,
const char *key, const char *def);
void settings_add_size_module(const char *module, const char *section,
const char *key, const char *def);
+void settings_add_choice_module(const char *module, const char *section,
+ const char *key, int def, const char *choices);
void settings_remove(const char *key);
void settings_remove_module(const char *module);
@@ -85,13 +93,16 @@ void settings_remove_module(const char *module);
settings_add_level_module(MODULE_NAME, section, key, def)
#define settings_add_size(section, key, def) \
settings_add_size_module(MODULE_NAME, section, key, def)
+#define settings_add_choice(section, key, def, choices) \
+ settings_add_choice_module(MODULE_NAME, section, key, def, choices)
void settings_set_str(const char *key, const char *value);
void settings_set_int(const char *key, int value);
void settings_set_bool(const char *key, int value);
-int settings_set_time(const char *key, const char *value);
-int settings_set_level(const char *key, const char *value);
-int settings_set_size(const char *key, const char *value);
+gboolean settings_set_time(const char *key, const char *value);
+gboolean settings_set_level(const char *key, const char *value);
+gboolean settings_set_size(const char *key, const char *value);
+gboolean settings_set_choice(const char *key, const char *value);
/* Get the type (SETTING_TYPE_xxx) of `key' */
SettingType settings_get_type(const char *key);
diff --git a/src/core/special-vars.c b/src/core/special-vars.c
index 4dcc3d2f..6ca080fc 100644
--- a/src/core/special-vars.c
+++ b/src/core/special-vars.c
@@ -25,10 +25,7 @@
#include "settings.h"
#include "servers.h"
#include "misc.h"
-
-#define ALIGN_RIGHT 0x01
-#define ALIGN_CUT 0x02
-#define ALIGN_PAD 0x04
+#include "utf8.h"
#define isvarchar(c) \
(i_isalnum(c) || (c) == '_')
@@ -47,7 +44,7 @@ static char *get_argument(char **cmd, char **arglist)
arg = 0;
max = -1;
- argcount = arglist == NULL ? 0 : strarray_length(arglist);
+ argcount = arglist == NULL ? 0 : g_strv_length(arglist);
if (**cmd == '*') {
/* get all arguments */
@@ -316,22 +313,28 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad)
}
/* return the aligned text */
-static char *get_alignment(const char *text, int align, int flags, char pad)
+char *get_alignment(const char *text, int align, int flags, char pad)
{
GString *str;
char *ret;
+ int policy;
+ unsigned int cut_bytes;
g_return_val_if_fail(text != NULL, NULL);
+ policy = string_policy(text);
+
str = g_string_new(text);
/* cut */
- if ((flags & ALIGN_CUT) && align > 0 && str->len > align)
- g_string_truncate(str, align);
+ if ((flags & ALIGN_CUT) && align > 0 && string_width(text, policy) > align) {
+ string_chars_for_width(text, policy, align, &cut_bytes);
+ g_string_truncate(str, cut_bytes);
+ }
/* add pad characters */
if (flags & ALIGN_PAD) {
- while (str->len < align) {
+ while (string_width(str->str, policy) < align) {
if (flags & ALIGN_RIGHT)
g_string_prepend_c(str, pad);
else
@@ -340,7 +343,7 @@ static char *get_alignment(const char *text, int align, int flags, char pad)
}
ret = str->str;
- g_string_free(str, FALSE);
+ g_string_free(str, FALSE);
return ret;
}
diff --git a/src/core/special-vars.h b/src/core/special-vars.h
index 11262dad..300dae0e 100644
--- a/src/core/special-vars.h
+++ b/src/core/special-vars.h
@@ -9,9 +9,16 @@
#define PARSE_FLAG_ESCAPE_THEME 0x08 /* if any arguments/variables contain { or } chars, escape them with % */
#define PARSE_FLAG_ONLY_ARGS 0x10 /* expand only arguments ($0 $1 etc.) but no other $variables */
+#define ALIGN_RIGHT 0x01
+#define ALIGN_CUT 0x02
+#define ALIGN_PAD 0x04
+
typedef char* (*SPECIAL_HISTORY_FUNC)
(const char *text, void *item, int *free_ret);
+/* Cut and/or pad text so it takes exactly "align" characters on the screen */
+char *get_alignment(const char *text, int align, int flags, char pad);
+
/* Parse and expand text after '$' character. return value has to be
g_free()'d if `free_ret' is TRUE. */
char *parse_special(char **cmd, SERVER_REC *server, void *item,
diff --git a/src/core/tls.c b/src/core/tls.c
new file mode 100644
index 00000000..3bddd773
--- /dev/null
+++ b/src/core/tls.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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 "tls.h"
+
+TLS_REC *tls_create_rec()
+{
+ TLS_REC *rec = g_new0(TLS_REC, 1);
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ return rec;
+}
+
+void tls_rec_free(TLS_REC *tls_rec)
+{
+ if (tls_rec == NULL)
+ return;
+
+ g_free_and_null(tls_rec->protocol_version);
+ g_free_and_null(tls_rec->cipher);
+ g_free_and_null(tls_rec->public_key_algorithm);
+ g_free_and_null(tls_rec->public_key_fingerprint);
+ g_free_and_null(tls_rec->public_key_fingerprint_algorithm);
+ g_free_and_null(tls_rec->certificate_fingerprint);
+ g_free_and_null(tls_rec->certificate_fingerprint_algorithm);
+ g_free_and_null(tls_rec->not_after);
+ g_free_and_null(tls_rec->not_before);
+ g_free_and_null(tls_rec->ephemeral_key_algorithm);
+
+ if (tls_rec->certs != NULL) {
+ g_slist_foreach(tls_rec->certs, (GFunc)tls_cert_rec_free, NULL);
+ g_slist_free(tls_rec->certs);
+ tls_rec->certs = NULL;
+ }
+
+ g_free(tls_rec);
+}
+
+void tls_rec_set_protocol_version(TLS_REC *tls_rec, const char *protocol_version)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->protocol_version = g_strdup(protocol_version);
+}
+
+void tls_rec_set_cipher(TLS_REC *tls_rec, const char *cipher)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->cipher = g_strdup(cipher);
+}
+
+void tls_rec_set_cipher_size(TLS_REC *tls_rec, size_t size)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->cipher_size = size;
+}
+
+void tls_rec_set_public_key_algorithm(TLS_REC *tls_rec, const char *algorithm)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->public_key_algorithm = g_strdup(algorithm);
+}
+
+void tls_rec_set_public_key_fingerprint(TLS_REC *tls_rec, const char *fingerprint)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->public_key_fingerprint = g_strdup(fingerprint);
+}
+
+void tls_rec_set_public_key_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->public_key_fingerprint_algorithm = g_strdup(algorithm);
+}
+
+void tls_rec_set_public_key_size(TLS_REC *tls_rec, size_t size)
+{
+ g_return_if_fail(tls_rec != NULL);
+ tls_rec->public_key_size = size;
+}
+
+void tls_rec_set_certificate_fingerprint(TLS_REC *tls_rec, const char *fingerprint)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->certificate_fingerprint = g_strdup(fingerprint);
+}
+
+void tls_rec_set_certificate_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->certificate_fingerprint_algorithm = g_strdup(algorithm);
+}
+
+void tls_rec_set_not_after(TLS_REC *tls_rec, const char *not_after)
+{
+ g_return_if_fail(tls_rec != NULL);
+ tls_rec->not_after = g_strdup(not_after);
+}
+
+void tls_rec_set_not_before(TLS_REC *tls_rec, const char *not_before)
+{
+ g_return_if_fail(tls_rec != NULL);
+ tls_rec->not_before = g_strdup(not_before);
+}
+
+void tls_rec_set_ephemeral_key_algorithm(TLS_REC *tls_rec, const char *algorithm)
+{
+ g_return_if_fail(tls_rec != NULL);
+ tls_rec->ephemeral_key_algorithm = g_strdup(algorithm);
+}
+
+void tls_rec_set_ephemeral_key_size(TLS_REC *tls_rec, size_t size)
+{
+ g_return_if_fail(tls_rec != NULL);
+ tls_rec->ephemeral_key_size = size;
+}
+
+void tls_rec_append_cert(TLS_REC *tls_rec, TLS_CERT_REC *tls_cert_rec)
+{
+ g_return_if_fail(tls_rec != NULL);
+ g_return_if_fail(tls_cert_rec != NULL);
+
+ tls_rec->certs = g_slist_append(tls_rec->certs, tls_cert_rec);
+}
+
+TLS_CERT_REC *tls_cert_create_rec()
+{
+ TLS_CERT_REC *rec = g_new0(TLS_CERT_REC, 1);
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ return rec;
+}
+
+void tls_cert_rec_append_subject_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec)
+{
+ g_return_if_fail(tls_cert_rec != NULL);
+ g_return_if_fail(tls_cert_entry_rec != NULL);
+
+ tls_cert_rec->subject = g_slist_append(tls_cert_rec->subject, tls_cert_entry_rec);
+}
+
+void tls_cert_rec_append_issuer_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec)
+{
+ g_return_if_fail(tls_cert_rec != NULL);
+ g_return_if_fail(tls_cert_entry_rec != NULL);
+
+ tls_cert_rec->issuer = g_slist_append(tls_cert_rec->issuer, tls_cert_entry_rec);
+}
+
+void tls_cert_rec_free(TLS_CERT_REC *tls_cert_rec)
+{
+ if (tls_cert_rec == NULL)
+ return;
+
+ if (tls_cert_rec->subject != NULL) {
+ g_slist_foreach(tls_cert_rec->subject, (GFunc)tls_cert_entry_rec_free, NULL);
+ g_slist_free(tls_cert_rec->subject);
+ tls_cert_rec->subject = NULL;
+ }
+
+ if (tls_cert_rec->issuer != NULL) {
+ g_slist_foreach(tls_cert_rec->issuer, (GFunc)tls_cert_entry_rec_free, NULL);
+ g_slist_free(tls_cert_rec->issuer);
+ tls_cert_rec->issuer = NULL;
+ }
+
+ g_free(tls_cert_rec);
+}
+
+TLS_CERT_ENTRY_REC *tls_cert_entry_create_rec(const char *name, const char *value)
+{
+ TLS_CERT_ENTRY_REC *rec = g_new0(TLS_CERT_ENTRY_REC, 1);
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ rec->name = g_strdup(name);
+ rec->value = g_strdup(value);
+
+ return rec;
+}
+
+void tls_cert_entry_rec_free(TLS_CERT_ENTRY_REC *tls_cert_entry)
+{
+ if (tls_cert_entry == NULL)
+ return;
+
+ g_free_and_null(tls_cert_entry->name);
+ g_free_and_null(tls_cert_entry->value);
+
+ g_free(tls_cert_entry);
+}
diff --git a/src/core/tls.h b/src/core/tls.h
new file mode 100644
index 00000000..9ba4ac47
--- /dev/null
+++ b/src/core/tls.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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 __TLS_H
+#define __TLS_H
+
+#include <openssl/ssl.h>
+
+#include <stdbool.h>
+
+typedef struct _TLS_REC TLS_REC;
+typedef struct _TLS_CERT_REC TLS_CERT_REC;
+typedef struct _TLS_CERT_ENTRY_REC TLS_CERT_ENTRY_REC;
+
+struct _TLS_REC {
+ char *protocol_version;
+ char *cipher;
+ size_t cipher_size;
+
+ char *public_key_algorithm;
+ char *public_key_fingerprint;
+ char *public_key_fingerprint_algorithm;
+ size_t public_key_size;
+
+ char *certificate_fingerprint;
+ char *certificate_fingerprint_algorithm;
+
+ char *not_after;
+ char *not_before;
+
+ char *ephemeral_key_algorithm;
+ size_t ephemeral_key_size;
+
+ GSList *certs;
+};
+
+struct _TLS_CERT_REC {
+ GSList *subject;
+ GSList *issuer;
+};
+
+struct _TLS_CERT_ENTRY_REC {
+ char *name;
+ char *value;
+};
+
+TLS_REC *tls_create_rec();
+void tls_rec_free(TLS_REC *tls_rec);
+
+void tls_rec_set_protocol_version(TLS_REC *tls_rec, const char *protocol_version);
+void tls_rec_set_cipher(TLS_REC *tls_rec, const char *cipher);
+void tls_rec_set_cipher_size(TLS_REC *tls_rec, size_t size);
+void tls_rec_set_public_key_algorithm(TLS_REC *tls_rec, const char *algorithm);
+void tls_rec_set_public_key_fingerprint(TLS_REC *tls_rec, const char *fingerprint);
+void tls_rec_set_public_key_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm);
+void tls_rec_set_public_key_size(TLS_REC *tls_rec, size_t size);
+void tls_rec_set_certificate_fingerprint(TLS_REC *tls_rec, const char *fingerprint);
+void tls_rec_set_certificate_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm);
+void tls_rec_set_not_after(TLS_REC *tls_rec, const char *not_after);
+void tls_rec_set_not_before(TLS_REC *tls_rec, const char *not_before);
+void tls_rec_set_ephemeral_key_algorithm(TLS_REC *tls_rec, const char *algorithm);
+void tls_rec_set_ephemeral_key_size(TLS_REC *tls_rec, size_t size);
+
+void tls_rec_append_cert(TLS_REC *tls_rec, TLS_CERT_REC *tls_cert_rec);
+
+TLS_CERT_REC *tls_cert_create_rec();
+void tls_cert_rec_free(TLS_CERT_REC *tls_cert_rec);
+
+void tls_cert_rec_append_subject_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec);
+void tls_cert_rec_append_issuer_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec);
+
+TLS_CERT_ENTRY_REC *tls_cert_entry_create_rec(const char *name, const char *value);
+void tls_cert_entry_rec_free(TLS_CERT_ENTRY_REC *tls_cert_entry);
+
+#endif
diff --git a/src/core/utf8.c b/src/core/utf8.c
new file mode 100644
index 00000000..c53d8816
--- /dev/null
+++ b/src/core/utf8.c
@@ -0,0 +1,135 @@
+/* utf8.c - Operations on UTF-8 strings.
+ *
+ * Copyright (C) 2002 Timo Sirainen
+ *
+ * Based on GLib code by
+ *
+ * Copyright (C) 1999 Tom Tromey
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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 "utf8.h"
+#include "module.h"
+
+/* Provide is_utf8(): */
+#include "recode.h"
+
+int string_advance(char const **str, int policy)
+{
+ if (policy == TREAT_STRING_AS_UTF8) {
+ gunichar c;
+
+ c = g_utf8_get_char(*str);
+ *str = g_utf8_next_char(*str);
+
+ return unichar_isprint(c) ? mk_wcwidth(c) : 1;
+ } else {
+ /* Assume TREAT_STRING_AS_BYTES: */
+ *str += 1;
+
+ return 1;
+ }
+}
+
+int string_policy(const char *str)
+{
+ if (is_utf8()) {
+ if (str == NULL || g_utf8_validate(str, -1, NULL)) {
+ /* No string provided or valid UTF-8 string: treat as UTF-8: */
+ return TREAT_STRING_AS_UTF8;
+ }
+ }
+ return TREAT_STRING_AS_BYTES;
+}
+
+int string_length(const char *str, int policy)
+{
+ g_return_val_if_fail(str != NULL, 0);
+
+ if (policy == -1) {
+ policy = string_policy(str);
+ }
+
+ if (policy == TREAT_STRING_AS_UTF8) {
+ return g_utf8_strlen(str, -1);
+ }
+ else {
+ /* Assume TREAT_STRING_AS_BYTES: */
+ return strlen(str);
+ }
+}
+
+int string_width(const char *str, int policy)
+{
+ int len;
+
+ g_return_val_if_fail(str != NULL, 0);
+
+ if (policy == -1) {
+ policy = string_policy(str);
+ }
+
+ len = 0;
+ while (*str != '\0') {
+ len += string_advance(&str, policy);
+ }
+ return len;
+}
+
+int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned int *bytes)
+{
+ const char *c, *previous_c;
+ int str_width, char_width, char_count;
+
+ g_return_val_if_fail(str != NULL, -1);
+
+ /* Handle the dummy case where n is 0: */
+ if (n == 0) {
+ if (bytes != NULL) {
+ *bytes = 0;
+ }
+ return 0;
+ }
+
+ if (policy == -1) {
+ policy = string_policy(str);
+ }
+
+ /* Iterate over characters until we reach n: */
+ char_count = 0;
+ str_width = 0;
+ c = str;
+ while (*c != '\0') {
+ previous_c = c;
+ char_width = string_advance(&c, policy);
+ if (str_width + char_width > n) {
+ /* We stepped beyond n, get one step back and stop there: */
+ c = previous_c;
+ break;
+ }
+ ++ char_count;
+ str_width += char_width;
+ }
+ /* At this point, we know that char_count characters reach str_width
+ * columns, which is less than or equal to n. */
+
+ /* Optionally provide the equivalent amount of bytes: */
+ if (bytes != NULL) {
+ *bytes = c - str;
+ }
+ return char_count;
+}
diff --git a/src/core/utf8.h b/src/core/utf8.h
new file mode 100644
index 00000000..5bb53193
--- /dev/null
+++ b/src/core/utf8.h
@@ -0,0 +1,56 @@
+#ifndef __UTF8_H
+#define __UTF8_H
+
+/* XXX I didn't check the encoding range of big5+. This is standard big5. */
+#define is_big5_los(lo) (0x40 <= (lo) && (lo) <= 0x7E) /* standard */
+#define is_big5_lox(lo) (0x80 <= (lo) && (lo) <= 0xFE) /* extended */
+#define is_big5_lo(lo) ((is_big5_los(lo) || is_big5_lox(lo)))
+#define is_big5_hi(hi) (0x81 <= (hi) && (hi) <= 0xFE)
+#define is_big5(hi,lo) (is_big5_hi(hi) && is_big5_lo(lo))
+
+#include <glib.h>
+typedef guint32 unichar;
+
+/* Returns width for character (0-2). */
+int mk_wcwidth(unichar c);
+
+/* Advance the str pointer one character further; return the number of columns
+ * occupied by the skipped character.
+ */
+int string_advance(char const **str, int policy);
+
+/* TREAT_STRING_AS_BYTES means strings are to be treated using strncpy,
+ * strnlen, etc.
+ * TREAT_STRING_AS_UTF8 means strings are to be treated using g_utf8_*
+ * functions.
+ */
+enum str_policy {
+ TREAT_STRING_AS_BYTES,
+ TREAT_STRING_AS_UTF8
+};
+
+/* Return how the str string ought to be treated: TREAT_STRING_AS_UTF8 if the
+ * terminal handles UTF-8 and if the string appears to be a valid UTF-8 string;
+ * TREAT_STRING_AS_BYTES otherwise.
+ */
+int string_policy(const char *str);
+
+/* Return the length of the str string according to the given policy; if policy
+ * is -1, this function will call string_policy().
+ */
+int string_length(const char *str, int policy);
+/* Return the screen width of the str string according to the given policy; if
+ * policy is -1, this function will call string_policy().
+ */
+int string_width(const char *str, int policy);
+
+/* Return the amount of characters from str it takes to reach n columns, or -1 if
+ * str is NULL. Optionally return the equivalent amount of bytes.
+ * If policy is -1, this function will call string_policy().
+ */
+int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned int *bytes);
+
+#define unichar_isprint(c) (((c) & ~0x80) >= 32)
+#define is_utf8_leading(c) (((c) & 0xc0) != 0x80)
+
+#endif
diff --git a/src/fe-common/core/wcwidth.c b/src/core/wcwidth.c
index 4ebfaca9..711c4646 100644
--- a/src/fe-common/core/wcwidth.c
+++ b/src/core/wcwidth.c
@@ -59,7 +59,7 @@
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*/
-#include "module.h"
+#include "utf8.h"
struct interval {
int first;
@@ -187,7 +187,7 @@ int mk_wcwidth(unichar ucs)
/* if we arrive here, ucs is not a combining or C0/C1 control character */
- return 1 +
+ return 1 +
(ucs >= 0x1100 &&
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
ucs == 0x2329 || ucs == 0x232a ||
diff --git a/src/fe-common/core/Makefile.am b/src/fe-common/core/Makefile.am
index e755b510..6efff411 100644
--- a/src/fe-common/core/Makefile.am
+++ b/src/fe-common/core/Makefile.am
@@ -24,8 +24,7 @@ libfe_common_core_a_SOURCES = \
fe-queries.c \
fe-server.c \
fe-settings.c \
- utf8.c \
- wcwidth.c \
+ fe-tls.c \
formats.c \
hilight-text.c \
keyboard.c \
@@ -50,6 +49,7 @@ pkginc_fe_common_core_HEADERS = \
fe-exec.h \
fe-messages.h \
fe-queries.h \
+ fe-tls.h \
formats.h \
hilight-text.h \
keyboard.h \
@@ -62,6 +62,3 @@ pkginc_fe_common_core_HEADERS = \
window-items.h \
windows-layout.h \
fe-windows.h
-
-noinst_HEADERS = \
- utf8.h
diff --git a/src/fe-common/core/chat-completion.c b/src/fe-common/core/chat-completion.c
index d3e018ad..1f00feaf 100644
--- a/src/fe-common/core/chat-completion.c
+++ b/src/fe-common/core/chat-completion.c
@@ -38,11 +38,18 @@
#include "chat-completion.h"
#include "window-items.h"
+enum {
+ COMPLETE_MCASE_NEVER = 0,
+ COMPLETE_MCASE_ALWAYS,
+ COMPLETE_MCASE_AUTO,
+};
+
static int keep_privates_count, keep_publics_count;
static int completion_lowercase;
-static const char *completion_char, *cmdchars;
+static char *completion_char, *cmdchars;
static GSList *global_lastmsgs;
-static int completion_auto, completion_strict;
+static int completion_auto, completion_strict, completion_empty_line;
+static int completion_match_case;
#define SERVER_LAST_MSG_ADD(server, nick) \
last_msg_add(&((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs, \
@@ -52,6 +59,18 @@ static int completion_auto, completion_strict;
last_msg_add(&((MODULE_CHANNEL_REC *) MODULE_DATA(channel))->lastmsgs, \
nick, own, keep_publics_count)
+static gboolean contains_uppercase(const char *s1)
+{
+ const char *ch;
+
+ for (ch = s1; *ch != '\0'; ch++) {
+ if (g_ascii_isupper(*ch))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static LAST_MSG_REC *last_msg_find(GSList *list, const char *nick)
{
while (list != NULL) {
@@ -336,7 +355,8 @@ GList *completion_msg(SERVER_REC *win_server,
}
static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
- const char *nick, const char *suffix)
+ const char *nick, const char *suffix,
+ const int match_case)
{
MODULE_CHANNEL_REC *mchannel;
GSList *tmp;
@@ -352,8 +372,10 @@ static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
for (tmp = mchannel->lastmsgs; tmp != NULL; tmp = tmp->next) {
LAST_MSG_REC *rec = tmp->data;
- if (g_ascii_strncasecmp(rec->nick, nick, len) == 0 &&
- glist_find_icase_string(*outlist, rec->nick) == NULL) {
+ if ((match_case? strncmp(rec->nick, nick, len)
+ : g_ascii_strncasecmp(rec->nick, nick, len)) == 0 &&
+ (match_case? glist_find_string(*outlist, rec->nick)
+ : glist_find_icase_string(*outlist, rec->nick)) == NULL) {
str = g_strconcat(rec->nick, suffix, NULL);
if (completion_lowercase) ascii_strdown(str);
if (rec->own)
@@ -368,7 +390,8 @@ static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
static GList *completion_nicks_nonstrict(CHANNEL_REC *channel,
const char *nick,
- const char *suffix)
+ const char *suffix,
+ const int match_case)
{
GSList *nicks, *tmp;
GList *list;
@@ -404,7 +427,8 @@ static GList *completion_nicks_nonstrict(CHANNEL_REC *channel,
*out = '\0';
/* add to list if 'cleaned' nick matches */
- if (g_ascii_strncasecmp(str, nick, len) == 0) {
+ if ((match_case? strncmp(str, nick, len)
+ : g_ascii_strncasecmp(str, nick, len)) == 0) {
tnick = g_strconcat(rec->nick, suffix, NULL);
if (completion_lowercase)
ascii_strdown(tnick);
@@ -428,7 +452,7 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
GSList *nicks, *tmp;
GList *list;
char *str;
- int len;
+ int len, match_case;
g_return_val_if_fail(channel != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
@@ -437,9 +461,12 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
if (suffix != NULL && *suffix == '\0')
suffix = NULL;
+ match_case = completion_match_case == COMPLETE_MCASE_ALWAYS ||
+ (completion_match_case == COMPLETE_MCASE_AUTO && contains_uppercase(nick));
+
/* put first the nicks who have recently said something */
list = NULL;
- complete_from_nicklist(&list, channel, nick, suffix);
+ complete_from_nicklist(&list, channel, nick, suffix, match_case);
/* and add the rest of the nicks too */
len = strlen(nick);
@@ -447,7 +474,8 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
NICK_REC *rec = tmp->data;
- if (g_ascii_strncasecmp(rec->nick, nick, len) == 0 &&
+ if ((match_case? strncmp(rec->nick, nick, len)
+ : g_ascii_strncasecmp(rec->nick, nick, len)) == 0 &&
rec != channel->ownnick) {
str = g_strconcat(rec->nick, suffix, NULL);
if (completion_lowercase)
@@ -463,7 +491,7 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
/* remove non alphanum chars from nick and search again in case
list is still NULL ("foo<tab>" would match "_foo_" f.e.) */
if (!completion_strict)
- list = g_list_concat(list, completion_nicks_nonstrict(channel, nick, suffix));
+ list = g_list_concat(list, completion_nicks_nonstrict(channel, nick, suffix, match_case));
return list;
}
@@ -574,7 +602,7 @@ GList *completion_get_aliases(const char *word)
list = g_list_append(list, g_strdup(node->key));
}
-
+
return list;
}
@@ -641,6 +669,8 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
return;
if (*linestart == '\0' && *word == '\0') {
+ if (!completion_empty_line)
+ return;
/* pressed TAB at the start of line - add /MSG */
prefix = g_strdup_printf("%cmsg", *cmdchars);
*list = completion_msg(server, NULL, "", prefix);
@@ -781,7 +811,7 @@ GList *completion_get_servers(const char *word)
for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
SERVER_SETUP_REC *rec = tmp->data;
- if (g_ascii_strncasecmp(rec->address, word, len) == 0)
+ if (g_ascii_strncasecmp(rec->address, word, len) == 0)
list = g_list_append(list, g_strdup(rec->address));
}
@@ -814,12 +844,12 @@ GList *completion_get_targets(const char *word)
list = g_list_append(list, g_strdup(node->key));
}
-
+
return list;
}
static void sig_complete_connect(GList **list, WINDOW_REC *window,
- const char *word, const char *line,
+ const char *word, const char *line,
int *want_space)
{
g_return_if_fail(list != NULL);
@@ -895,7 +925,7 @@ static void sig_complete_alias(GList **list, WINDOW_REC *window,
int *want_space)
{
const char *definition;
-
+
g_return_if_fail(list != NULL);
g_return_if_fail(word != NULL);
g_return_if_fail(line != NULL);
@@ -905,7 +935,7 @@ static void sig_complete_alias(GList **list, WINDOW_REC *window,
*list = g_list_append(NULL, g_strdup(definition));
signal_stop();
}
- } else {
+ } else {
*list = completion_get_aliases(word);
if (*list != NULL) signal_stop();
}
@@ -965,7 +995,7 @@ static void sig_complete_target(GList **list, WINDOW_REC *window,
int *want_space)
{
const char *definition;
-
+
g_return_if_fail(list != NULL);
g_return_if_fail(word != NULL);
g_return_if_fail(line != NULL);
@@ -975,7 +1005,7 @@ static void sig_complete_target(GList **list, WINDOW_REC *window,
*list = g_list_append(NULL, g_strdup(definition));
signal_stop();
}
- } else {
+ } else {
*list = completion_get_targets(word);
if (*list != NULL) signal_stop();
}
@@ -1126,10 +1156,18 @@ static void read_settings(void)
keep_privates_count = settings_get_int("completion_keep_privates");
keep_publics_count = settings_get_int("completion_keep_publics");
completion_lowercase = settings_get_bool("completion_nicks_lowercase");
- completion_char = settings_get_str("completion_char");
- cmdchars = settings_get_str("cmdchars");
+
completion_auto = settings_get_bool("completion_auto");
completion_strict = settings_get_bool("completion_strict");
+ completion_empty_line = settings_get_bool("completion_empty_line");
+
+ completion_match_case = settings_get_choice("completion_nicks_match_case");
+
+ g_free_not_null(completion_char);
+ completion_char = g_strdup(settings_get_str("completion_char"));
+
+ g_free_not_null(cmdchars);
+ cmdchars = g_strdup(settings_get_str("cmdchars"));
if (*completion_char == '\0') {
/* this would break.. */
@@ -1145,6 +1183,8 @@ void chat_completion_init(void)
settings_add_int("completion", "completion_keep_privates", 10);
settings_add_bool("completion", "completion_nicks_lowercase", FALSE);
settings_add_bool("completion", "completion_strict", FALSE);
+ settings_add_bool("completion", "completion_empty_line", TRUE);
+ settings_add_choice("completion", "completion_nicks_match_case", COMPLETE_MCASE_AUTO, "never;always;auto");
settings_add_bool("lookandfeel", "expand_escapes", FALSE);
@@ -1220,4 +1260,7 @@ void chat_completion_deinit(void)
signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+
+ g_free_not_null(completion_char);
+ g_free_not_null(cmdchars);
}
diff --git a/src/fe-common/core/command-history.c b/src/fe-common/core/command-history.c
index afe19aa7..1060744e 100644
--- a/src/fe-common/core/command-history.c
+++ b/src/fe-common/core/command-history.c
@@ -42,10 +42,10 @@ void command_history_add(HISTORY_REC *history, const char *text)
g_return_if_fail(text != NULL);
link = g_list_last(history->list);
- if (link != NULL && strcmp(link->data, text) == 0)
+ if (link != NULL && g_strcmp0(link->data, text) == 0)
return; /* same as previous entry */
- if (settings_get_int("max_command_history") < 1 ||
+ if (settings_get_int("max_command_history") < 1 ||
history->lines < settings_get_int("max_command_history"))
history->lines++;
else {
@@ -78,12 +78,12 @@ HISTORY_REC *command_history_find_name(const char *name)
for (tmp = histories; tmp != NULL; tmp = tmp->next) {
HISTORY_REC *rec = tmp->data;
-
+
if (rec->name != NULL &&
g_ascii_strcasecmp(rec->name, name) == 0)
return rec;
}
-
+
return NULL;
}
@@ -94,13 +94,13 @@ HISTORY_REC *command_history_current(WINDOW_REC *window)
if (window == NULL)
return global_history;
- if (window_history)
- return window->history;
-
rec = command_history_find_name(window->history_name);
if (rec != NULL)
return rec;
+ if (window_history)
+ return window->history;
+
return global_history;
}
@@ -121,7 +121,7 @@ const char *command_history_prev(WINDOW_REC *window, const char *text)
}
if (*text != '\0' &&
- (pos == NULL || strcmp(pos->data, text) != 0)) {
+ (pos == NULL || g_strcmp0(pos->data, text) != 0)) {
/* save the old entry to history */
command_history_add(history, text);
}
@@ -135,7 +135,7 @@ const char *command_history_next(WINDOW_REC *window, const char *text)
GList *pos;
history = command_history_current(window);
- pos = history->pos;
+ pos = history->pos;
if (pos != NULL)
history->pos = history->pos->next;
@@ -145,7 +145,7 @@ const char *command_history_next(WINDOW_REC *window, const char *text)
}
if (*text != '\0' &&
- (pos == NULL || strcmp(pos->data, text) != 0)) {
+ (pos == NULL || g_strcmp0(pos->data, text) != 0)) {
/* save the old entry to history */
command_history_add(history, text);
}
@@ -160,24 +160,35 @@ void command_history_clear_pos_func(HISTORY_REC *history, gpointer user_data)
void command_history_clear_pos(WINDOW_REC *window)
{
- g_slist_foreach(histories,
+ g_slist_foreach(histories,
(GFunc) command_history_clear_pos_func, NULL);
}
HISTORY_REC *command_history_create(const char *name)
{
HISTORY_REC *rec;
-
+
rec = g_new0(HISTORY_REC, 1);
-
+
if (name != NULL)
rec->name = g_strdup(name);
histories = g_slist_append(histories, rec);
-
+
return rec;
}
+void command_history_clear(HISTORY_REC *history)
+{
+ g_return_if_fail(history != NULL);
+
+ command_history_clear_pos_func(history, NULL);
+ g_list_foreach(history->list, (GFunc) g_free, NULL);
+ g_list_free(history->list);
+ history->list = NULL;
+ history->lines = 0;
+}
+
void command_history_destroy(HISTORY_REC *history)
{
g_return_if_fail(history != NULL);
@@ -186,9 +197,7 @@ void command_history_destroy(HISTORY_REC *history)
g_return_if_fail(history->refcount == 0);
histories = g_slist_remove(histories, history);
-
- g_list_foreach(history->list, (GFunc) g_free, NULL);
- g_list_free(history->list);
+ command_history_clear(history);
g_free_not_null(history->name);
g_free(history);
@@ -229,6 +238,18 @@ static void sig_window_destroyed(WINDOW_REC *window)
g_free_not_null(window->history_name);
}
+static void sig_window_history_cleared(WINDOW_REC *window, const char *name) {
+ HISTORY_REC *history;
+
+ if (name == NULL || *name == '\0') {
+ history = command_history_current(window);
+ } else {
+ history = command_history_find_name(name);
+ }
+
+ command_history_clear(history);
+}
+
static void sig_window_history_changed(WINDOW_REC *window, const char *oldname)
{
command_history_link(window->history_name);
@@ -279,6 +300,7 @@ void command_history_init(void)
signal_add("window created", (SIGNAL_FUNC) sig_window_created);
signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
signal_add("window history changed", (SIGNAL_FUNC) sig_window_history_changed);
+ signal_add_last("window history cleared", (SIGNAL_FUNC) sig_window_history_cleared);
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
}
@@ -287,6 +309,7 @@ void command_history_deinit(void)
signal_remove("window created", (SIGNAL_FUNC) sig_window_created);
signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
signal_remove("window history changed", (SIGNAL_FUNC) sig_window_history_changed);
+ signal_remove("window history cleared", (SIGNAL_FUNC) sig_window_history_cleared);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
command_history_destroy(global_history);
diff --git a/src/fe-common/core/command-history.h b/src/fe-common/core/command-history.h
index 7b76246b..a572216b 100644
--- a/src/fe-common/core/command-history.h
+++ b/src/fe-common/core/command-history.h
@@ -28,6 +28,7 @@ const char *command_history_next(WINDOW_REC *window, const char *text);
void command_history_clear_pos(WINDOW_REC *window);
HISTORY_REC *command_history_create(const char *name);
+void command_history_clear(HISTORY_REC *history);
void command_history_destroy(HISTORY_REC *history);
void command_history_link(const char *name);
void command_history_unlink(const char *name);
diff --git a/src/fe-common/core/completion.c b/src/fe-common/core/completion.c
index 31d62e10..914ba80b 100644
--- a/src/fe-common/core/completion.c
+++ b/src/fe-common/core/completion.c
@@ -37,8 +37,11 @@ static int last_want_space, last_line_pos;
#define isseparator_notspace(c) \
((c) == ',')
+#define isseparator_space(c) \
+ ((c) == ' ')
+
#define isseparator(c) \
- ((c) == ' ' || isseparator_notspace(c))
+ (isseparator_space(c) || isseparator_notspace(c))
void chat_completion_init(void);
void chat_completion_deinit(void);
@@ -51,7 +54,7 @@ static const char *completion_find(const char *key, int automatic)
if (node == NULL || node->type != NODE_TYPE_BLOCK)
return NULL;
- node = config_node_section(node, key, -1);
+ node = iconfig_node_section(node, key, -1);
if (node == NULL)
return NULL;
@@ -141,7 +144,7 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
g_return_val_if_fail(pos != NULL, NULL);
continue_complete = complist != NULL && *pos == last_line_pos &&
- strcmp(line, last_line) == 0;
+ g_strcmp0(line, last_line) == 0;
if (erase && !continue_complete)
return NULL;
@@ -153,20 +156,23 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
word = NULL;
linestart = NULL;
} else {
+ char* old_wordstart;
+
/* get the word we want to complete */
word = get_word_at(line, *pos, &wordstart);
+ old_wordstart = wordstart;
+
startpos = (int) (wordstart-line);
wordlen = strlen(word);
- /* get the start of line until the word we're completing */
- if (isseparator(*line)) {
- /* empty space at the start of line */
- if (wordstart == line)
- wordstart += strlen(wordstart);
- } else {
- while (wordstart > line && isseparator(wordstart[-1]))
- wordstart--;
- }
+ /* remove trailing spaces from linestart */
+ while (wordstart > line && isseparator_space(wordstart[-1]))
+ wordstart--;
+
+ /* unless everything was spaces */
+ if (old_wordstart > line && wordstart == line)
+ wordstart = old_wordstart - 1;
+
linestart = g_strndup(line, (int) (wordstart-line));
/* completions usually add space after the word, that makes
@@ -175,19 +181,24 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
BUT if we start completion with "/msg "<tab>, we don't
want to complete the /msg word, but instead complete empty
word with /msg being in linestart. */
- if (!erase && *pos > 0 && line[*pos-1] == ' ' &&
- (*linestart == '\0' || wordstart[-1] != ' ')) {
+ if (!erase && *pos > 0 && isseparator_space(line[*pos-1]) &&
+ (*linestart == '\0' || !isseparator_space(wordstart[-1]))) {
char *old;
- old = linestart;
+ old = linestart;
linestart = *linestart == '\0' ?
g_strdup(word) :
- g_strconcat(linestart, " ", word, NULL);
+ g_strdup_printf("%s%c%s",
+ /* do not accidentally duplicate the word separator */
+ line == wordstart - 1 ? "" : linestart,
+ old_wordstart[-1], word);
g_free(old);
g_free(word);
word = g_strdup("");
- startpos = strlen(linestart)+1;
+
+ startpos = *linestart == '\0' ? 0 :
+ strlen(linestart)+1;
wordlen = 0;
}
@@ -217,6 +228,11 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
want_space = TRUE;
signal_emit("complete word", 5, &complist, window, word, linestart, &want_space);
last_want_space = want_space;
+
+ if (complist != NULL) {
+ /* Remove all nulls (from the signal) before doing further processing */
+ complist = g_list_remove_all(g_list_first(complist), NULL);
+ }
}
g_free(linestart);
@@ -329,7 +345,9 @@ GList *filename_complete(const char *path, const char *default_path)
(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
continue; /* skip . and .. */
- if (basename[0] != '.')
+ /* Skip the dotfiles unless the user explicitly asked us
+ * to do so. Basename might be './', beware of that */
+ if (basename[0] != '.' || basename[1] == '\0')
continue;
}
@@ -362,8 +380,7 @@ static GList *completion_get_settings(const char *key, SettingType type)
for (tmp = sets; tmp != NULL; tmp = tmp->next) {
SETTINGS_REC *rec = tmp->data;
- if ((type == -1 || rec->type == type) &&
- g_ascii_strncasecmp(rec->key, key, len) == 0)
+ if ((type == SETTING_TYPE_ANY || rec->type == type) && g_ascii_strncasecmp(rec->key, key, len) == 0)
complist = g_list_insert_sorted(complist, g_strdup(rec->key), (GCompareFunc) g_istr_cmp);
}
g_slist_free(sets);
@@ -393,7 +410,8 @@ static GList *completion_get_aliases(const char *alias, char cmdchar)
continue;
if (g_ascii_strncasecmp(node->key, alias, len) == 0) {
- word = g_strdup_printf("%c%s", cmdchar, node->key);
+ word = cmdchar == '\0' ? g_strdup(node->key) :
+ g_strdup_printf("%c%s", cmdchar, node->key);
/* add matching alias to completion list, aliases will
be appended after command completions and kept in
uppercase to show it's an alias */
@@ -585,13 +603,19 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
/* command completion? */
cmdchars = settings_get_str("cmdchars");
- if (*word != '\0' && *linestart == '\0' && strchr(cmdchars, *word)) {
+ if (*word != '\0' && ((*linestart == '\0' && strchr(cmdchars, *word)) ||
+ (*linestart != '\0' && linestart[1] == '\0' &&
+ strchr(cmdchars, *linestart)))) {
+ gboolean skip = *linestart == '\0' ? TRUE : FALSE;
+
/* complete /command */
- *list = completion_get_commands(word+1, *word);
+ *list = completion_get_commands(word + (skip ? 1 : 0),
+ skip ? *word : '\0');
/* complete aliases, too */
*list = g_list_concat(*list,
- completion_get_aliases(word+1, *word));
+ completion_get_aliases(word + (skip ? 1 : 0),
+ skip ? *word : '\0'));
if (*list != NULL) signal_stop();
return;
@@ -681,14 +705,26 @@ static void sig_complete_set(GList **list, WINDOW_REC *window,
g_return_if_fail(line != NULL);
if (*line == '\0' ||
- !strcmp("-clear", line) || !strcmp("-default", line))
- *list = completion_get_settings(word, -1);
+ !g_strcmp0("-clear", line) || !g_strcmp0("-default", line))
+ *list = completion_get_settings(word, SETTING_TYPE_ANY);
else if (*line != '\0' && *word == '\0') {
SETTINGS_REC *rec = settings_get_record(line);
if (rec != NULL) {
char *value = settings_get_print(rec);
+
+ /* show the current option first */
if (value != NULL)
*list = g_list_append(*list, value);
+
+ /* show the whole list of valid options */
+ if (rec->type == SETTING_TYPE_CHOICE) {
+ char **tmp;
+
+ for (tmp = rec->choices; *tmp; tmp++) {
+ if (g_ascii_strcasecmp(*tmp, value) != 0)
+ *list = g_list_append(*list, g_strdup(*tmp));
+ }
+ }
}
}
@@ -785,7 +821,7 @@ static void cmd_completion(const char *data)
} else if (*key != '\0' && *value != '\0') {
int automatic = g_hash_table_lookup(optlist, "auto") != NULL;
- node = config_node_section(node, key, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, key, NODE_TYPE_BLOCK);
iconfig_node_set_str(node, "value", value);
if (automatic)
iconfig_node_set_bool(node, "auto", TRUE);
diff --git a/src/fe-common/core/fe-channels.c b/src/fe-common/core/fe-channels.c
index 75147fdb..00aac885 100644
--- a/src/fe-common/core/fe-channels.c
+++ b/src/fe-common/core/fe-channels.c
@@ -26,6 +26,8 @@
#include "levels.h"
#include "misc.h"
#include "settings.h"
+#include "special-vars.h"
+#include "utf8.h"
#include "chat-protocols.h"
#include "chatnets.h"
@@ -122,7 +124,8 @@ static void cmd_join(const char *data, SERVER_REC *server)
void *free_arg;
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+ PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS,
"join", &optlist, &pdata))
return;
@@ -245,9 +248,7 @@ static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
}
}
-/* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
- <channel> <network> [<password>] */
-static void cmd_channel_add(const char *data)
+static void cmd_channel_add_modify(const char *data, gboolean add)
{
GHashTable *optlist;
CHATNET_REC *chatnetrec;
@@ -256,18 +257,19 @@ static void cmd_channel_add(const char *data)
void *free_arg;
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
- "channel add", &optlist, &channel, &chatnet, &password))
+ "channel add", &optlist, &channel, &chatnet, &password))
return;
- if (*chatnet == '\0' || *channel == '\0')
+ if (*chatnet == '\0' || *channel == '\0') {
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+ }
chatnetrec = chatnet_find(chatnet);
if (chatnetrec == NULL) {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
- TXT_UNKNOWN_CHATNET, chatnet);
+ TXT_UNKNOWN_CHATNET, chatnet);
cmd_params_free(free_arg);
- return;
+ return;
}
botarg = g_hash_table_lookup(optlist, "bots");
@@ -275,6 +277,13 @@ static void cmd_channel_add(const char *data)
rec = channel_setup_find(channel, chatnet);
if (rec == NULL) {
+ if (add == FALSE) {
+ cmd_params_free(free_arg);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+ TXT_CHANSETUP_NOT_FOUND, channel, chatnet);
+ return;
+ }
+
rec = CHAT_PROTOCOL(chatnetrec)->create_channel_setup();
rec->name = g_strdup(channel);
rec->chatnet = g_strdup(chatnet);
@@ -287,7 +296,7 @@ static void cmd_channel_add(const char *data)
if (g_hash_table_lookup(optlist, "noauto")) rec->autojoin = FALSE;
if (botarg != NULL && *botarg != '\0') rec->botmasks = g_strdup(botarg);
if (botcmdarg != NULL && *botcmdarg != '\0') rec->autosendcmd = g_strdup(botcmdarg);
- if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
+ if (*password != '\0' && g_strcmp0(password, "-") != 0) rec->password = g_strdup(password);
signal_emit("channel add fill", 2, rec, optlist);
@@ -298,6 +307,18 @@ static void cmd_channel_add(const char *data)
cmd_params_free(free_arg);
}
+/* SYNTAX: CHANNEL ADD|MODIFY [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
+ <channel> <network> [<password>] */
+static void cmd_channel_add(const char *data)
+{
+ cmd_channel_add_modify(data, TRUE);
+}
+
+static void cmd_channel_modify(const char *data)
+{
+ cmd_channel_add_modify(data, FALSE);
+}
+
/* SYNTAX: CHANNEL REMOVE <channel> <network> */
static void cmd_channel_remove(const char *data)
{
@@ -322,40 +343,40 @@ static void cmd_channel_remove(const char *data)
static int get_nick_length(void *data)
{
- return strlen(((NICK_REC *) data)->nick);
+ return string_width(((NICK_REC *) data)->nick, -1);
}
static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
{
- WINDOW_REC *window;
+ WINDOW_REC *window;
TEXT_DEST_REC dest;
GString *str;
GSList *tmp;
- char *format, *stripped, *prefix_format;
- char *linebuf, nickmode[2] = { 0, 0 };
+ char *format, *stripped, *prefix_format;
+ char *aligned_nick, nickmode[2] = { 0, 0 };
int *columns, cols, rows, last_col_rows, col, row, max_width;
- int item_extra, linebuf_size, formatnum;
+ int item_extra, formatnum;
window = window_find_closest(channel->server, channel->visible_name,
- MSGLEVEL_CLIENTCRAP);
- max_width = window->width;
+ MSGLEVEL_CLIENTCRAP);
+ max_width = window->width;
- /* get the length of item extra stuff ("[ ] ") */
+ /* get the length of item extra stuff ("[ ] ") */
format = format_get_text(MODULE_NAME, NULL,
- channel->server, channel->visible_name,
- TXT_NAMES_NICK, " ", "");
+ channel->server, channel->visible_name,
+ TXT_NAMES_NICK, " ", "");
stripped = strip_codes(format);
item_extra = strlen(stripped);
- g_free(stripped);
+ g_free(stripped);
g_free(format);
if (settings_get_int("names_max_width") > 0 &&
settings_get_int("names_max_width") < max_width)
max_width = settings_get_int("names_max_width");
- /* remove width of the timestamp from max_width */
+ /* remove width of the timestamp from max_width */
format_create_dest(&dest, channel->server, channel->visible_name,
- MSGLEVEL_CLIENTCRAP, NULL);
+ MSGLEVEL_CLIENTCRAP, NULL);
format = format_get_line_start(current_theme, &dest, time(NULL));
if (format != NULL) {
stripped = strip_codes(format);
@@ -364,11 +385,11 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
g_free(format);
}
- /* remove width of the prefix from max_width */
+ /* remove width of the prefix from max_width */
prefix_format = format_get_text(MODULE_NAME, NULL,
- channel->server, channel->visible_name,
- TXT_NAMES_PREFIX,
- channel->visible_name);
+ channel->server, channel->visible_name,
+ TXT_NAMES_PREFIX,
+ channel->visible_name);
if (prefix_format != NULL) {
stripped = strip_codes(prefix_format);
max_width -= strlen(stripped);
@@ -383,19 +404,18 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
/* calculate columns */
cols = get_max_column_count(nicklist, get_nick_length, max_width,
- settings_get_int("names_max_columns"),
- item_extra, 3, &columns, &rows);
+ settings_get_int("names_max_columns"),
+ item_extra, 3, &columns, &rows);
nicklist = columns_sort_list(nicklist, rows);
- /* rows in last column */
+ /* rows in last column */
last_col_rows = rows-(cols*rows-g_slist_length(nicklist));
if (last_col_rows == 0)
- last_col_rows = rows;
+ last_col_rows = rows;
str = g_string_new(prefix_format);
- linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size);
- col = 0; row = 0;
+ col = 0; row = 0;
for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
NICK_REC *rec = tmp->data;
@@ -403,49 +423,45 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
nickmode[0] = rec->prefixes[0];
else
nickmode[0] = ' ';
-
- if (linebuf_size < columns[col]-item_extra+1) {
- linebuf_size = (columns[col]-item_extra+1)*2;
- linebuf = g_realloc(linebuf, linebuf_size);
- }
- memset(linebuf, ' ', columns[col]-item_extra);
- linebuf[columns[col]-item_extra] = '\0';
- memcpy(linebuf, rec->nick, strlen(rec->nick));
-
- formatnum = rec->op ? TXT_NAMES_NICK_OP :
- rec->halfop ? TXT_NAMES_NICK_HALFOP :
- rec->voice ? TXT_NAMES_NICK_VOICE :
- TXT_NAMES_NICK;
+
+ aligned_nick = get_alignment(rec->nick,
+ columns[col]-item_extra,
+ ALIGN_PAD, ' ');
+
+ formatnum = rec->op ? TXT_NAMES_NICK_OP :
+ rec->halfop ? TXT_NAMES_NICK_HALFOP :
+ rec->voice ? TXT_NAMES_NICK_VOICE :
+ TXT_NAMES_NICK;
format = format_get_text(MODULE_NAME, NULL,
- channel->server,
- channel->visible_name,
- formatnum, nickmode, linebuf);
+ channel->server,
+ channel->visible_name,
+ formatnum, nickmode, aligned_nick);
g_string_append(str, format);
+ g_free(aligned_nick);
g_free(format);
if (++col == cols) {
printtext(channel->server, channel->visible_name,
- MSGLEVEL_CLIENTCRAP, "%s", str->str);
+ MSGLEVEL_CLIENTCRAP, "%s", str->str);
g_string_truncate(str, 0);
if (prefix_format != NULL)
- g_string_assign(str, prefix_format);
+ g_string_assign(str, prefix_format);
col = 0; row++;
if (row == last_col_rows)
- cols--;
+ cols--;
}
}
if (str->len > strlen(prefix_format)) {
printtext(channel->server, channel->visible_name,
- MSGLEVEL_CLIENTCRAP, "%s", str->str);
+ MSGLEVEL_CLIENTCRAP, "%s", str->str);
}
g_slist_free(nicklist);
g_string_free(str, TRUE);
g_free_not_null(columns);
g_free_not_null(prefix_format);
- g_free(linebuf);
}
void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
@@ -523,7 +539,7 @@ static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
"names", &optlist, &channel))
return;
- if (strcmp(channel, "*") == 0 || *channel == '\0') {
+ if (g_strcmp0(channel, "*") == 0 || *channel == '\0') {
if (!IS_CHANNEL(item))
cmd_param_error(CMDERR_NOT_JOINED);
@@ -561,7 +577,7 @@ static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
if (unknowns->len > 1)
g_string_truncate(unknowns, unknowns->len-1);
- if (unknowns->len > 0 && strcmp(channel, unknowns->str) != 0)
+ if (unknowns->len > 0 && g_strcmp0(channel, unknowns->str) != 0)
signal_emit("command names", 3, unknowns->str, server, item);
g_string_free(unknowns, TRUE);
@@ -621,12 +637,14 @@ void fe_channels_init(void)
command_bind("join", NULL, (SIGNAL_FUNC) cmd_join);
command_bind("channel", NULL, (SIGNAL_FUNC) cmd_channel);
command_bind("channel add", NULL, (SIGNAL_FUNC) cmd_channel_add);
+ command_bind("channel modify", NULL, (SIGNAL_FUNC) cmd_channel_modify);
command_bind("channel remove", NULL, (SIGNAL_FUNC) cmd_channel_remove);
command_bind("channel list", NULL, (SIGNAL_FUNC) cmd_channel_list);
command_bind("names", NULL, (SIGNAL_FUNC) cmd_names);
command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle);
command_set_options("channel add", "auto noauto -bots -botcmd");
+ command_set_options("channel modify", "auto noauto -bots -botcmd");
command_set_options("names", "count ops halfops voices normal");
command_set_options("join", "invite window");
}
@@ -642,6 +660,7 @@ void fe_channels_deinit(void)
command_unbind("join", (SIGNAL_FUNC) cmd_join);
command_unbind("channel", (SIGNAL_FUNC) cmd_channel);
command_unbind("channel add", (SIGNAL_FUNC) cmd_channel_add);
+ command_unbind("channel modify", (SIGNAL_FUNC) cmd_channel_modify);
command_unbind("channel remove", (SIGNAL_FUNC) cmd_channel_remove);
command_unbind("channel list", (SIGNAL_FUNC) cmd_channel_list);
command_unbind("names", (SIGNAL_FUNC) cmd_names);
diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c
index a475f056..512fc84c 100644
--- a/src/fe-common/core/fe-common-core.c
+++ b/src/fe-common/core/fe-common-core.c
@@ -88,6 +88,9 @@ void fe_server_deinit(void);
void fe_settings_init(void);
void fe_settings_deinit(void);
+void fe_tls_init(void);
+void fe_tls_deinit(void);
+
void window_commands_init(void);
void window_commands_deinit(void);
@@ -147,7 +150,6 @@ void fe_common_core_init(void)
settings_add_level("lookandfeel", "timestamp_level", "ALL");
settings_add_time("lookandfeel", "timestamp_timeout", "0");
- settings_add_bool("lookandfeel", "bell_beeps", FALSE);
settings_add_level("lookandfeel", "beep_msg_level", "");
settings_add_bool("lookandfeel", "beep_when_window_active", TRUE);
settings_add_bool("lookandfeel", "beep_when_away", TRUE);
@@ -168,9 +170,7 @@ void fe_common_core_init(void)
keyboard_init();
printtext_init();
formats_init();
-#ifndef WIN32
fe_exec_init();
-#endif
fe_expandos_init();
fe_help_init();
fe_ignore_init();
@@ -178,6 +178,7 @@ void fe_common_core_init(void)
fe_modules_init();
fe_server_init();
fe_settings_init();
+ fe_tls_init();
windows_init();
window_activity_init();
window_commands_init();
@@ -211,9 +212,7 @@ void fe_common_core_deinit(void)
keyboard_deinit();
printtext_deinit();
formats_deinit();
-#ifndef WIN32
fe_exec_deinit();
-#endif
fe_expandos_deinit();
fe_help_deinit();
fe_ignore_deinit();
@@ -221,6 +220,7 @@ void fe_common_core_deinit(void)
fe_modules_deinit();
fe_server_deinit();
fe_settings_deinit();
+ fe_tls_deinit();
windows_deinit();
window_activity_deinit();
window_commands_deinit();
@@ -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);
@@ -471,15 +463,24 @@ gboolean strarray_find_dest(char **array, const TEXT_DEST_REC *dest)
{
g_return_val_if_fail(array != NULL, FALSE);
+ if (strarray_find(array, "*") != -1)
+ return TRUE;
+
if (strarray_find(array, dest->target) != -1)
return TRUE;
if (dest->server_tag != NULL) {
- char *tagtarget = g_strdup_printf("%s/%s", dest->server_tag, dest->target);
+ char *tagtarget = g_strdup_printf("%s/%s", dest->server_tag, "*");
int ret = strarray_find(array, tagtarget);
g_free(tagtarget);
if (ret != -1)
return TRUE;
+
+ tagtarget = g_strdup_printf("%s/%s", dest->server_tag, dest->target);
+ ret = strarray_find(array, tagtarget);
+ g_free(tagtarget);
+ if (ret != -1)
+ return TRUE;
}
return FALSE;
}
diff --git a/src/fe-common/core/fe-core-commands.c b/src/fe-common/core/fe-core-commands.c
index 7b1d7b94..97a246ec 100644
--- a/src/fe-common/core/fe-core-commands.c
+++ b/src/fe-common/core/fe-core-commands.c
@@ -51,7 +51,8 @@ static int ret_texts[] = {
TXT_INVALID_TIME,
TXT_INVALID_CHARSET,
TXT_EVAL_MAX_RECURSE,
- TXT_PROGRAM_NOT_FOUND
+ TXT_PROGRAM_NOT_FOUND,
+ TXT_NO_SERVER_DEFINED,
};
int command_hide_output;
diff --git a/src/fe-common/core/fe-exec.c b/src/fe-common/core/fe-exec.c
index 9249f432..36990866 100644
--- a/src/fe-common/core/fe-exec.c
+++ b/src/fe-common/core/fe-exec.c
@@ -161,7 +161,7 @@ static PROCESS_REC *process_find(const char *name, int verbose)
for (tmp = processes; tmp != NULL; tmp = tmp->next) {
PROCESS_REC *rec = tmp->data;
- if (rec->name != NULL && strcmp(rec->name, name) == 0)
+ if (rec->name != NULL && g_strcmp0(rec->name, name) == 0)
return rec;
}
@@ -237,22 +237,13 @@ static int signal_name_to_id(const char *name)
static int cmd_options_get_signal(const char *cmd,
GHashTable *optlist)
{
- GSList *list, *tmp, *next;
+ GList *list;
char *signame;
int signum;
/* get all the options, then remove the known ones. there should
be only one left - the signal */
- list = hashtable_get_keys(optlist);
- if (cmd != NULL) {
- for (tmp = list; tmp != NULL; tmp = next) {
- char *option = tmp->data;
- next = tmp->next;
-
- if (command_have_option(cmd, option))
- list = g_slist_remove(list, option);
- }
- }
+ list = optlist_remove_known(cmd, optlist);
if (list == NULL)
return -1;
@@ -272,7 +263,7 @@ static int cmd_options_get_signal(const char *cmd,
return -2;
}
- g_slist_free(list);
+ g_list_free(list);
return signum;
}
diff --git a/src/fe-common/core/fe-help.c b/src/fe-common/core/fe-help.c
index 4ea7c89f..23a6e701 100644
--- a/src/fe-common/core/fe-help.c
+++ b/src/fe-common/core/fe-help.c
@@ -37,12 +37,12 @@ static int commands_equal(COMMAND_REC *rec, COMMAND_REC *rec2)
if (rec2->category == NULL && rec->category != NULL)
return 1;
if (rec->category != NULL && rec2->category != NULL) {
- i = strcmp(rec->category, rec2->category);
+ i = g_strcmp0(rec->category, rec2->category);
if (i != 0)
return i;
}
- return strcmp(rec->cmd, rec2->cmd);
+ return g_strcmp0(rec->cmd, rec2->cmd);
}
static int get_cmd_length(void *data)
@@ -176,7 +176,7 @@ static void show_help(const char *data)
if (last != NULL && rec->category != NULL &&
(last->category == NULL ||
- strcmp(rec->category, last->category) != 0)) {
+ g_strcmp0(rec->category, last->category) != 0)) {
/* category changed */
if (items > 0) {
if (!header) {
diff --git a/src/fe-common/core/fe-ignore.c b/src/fe-common/core/fe-ignore.c
index 96242f9e..800e881d 100644
--- a/src/fe-common/core/fe-ignore.c
+++ b/src/fe-common/core/fe-ignore.c
@@ -58,14 +58,17 @@ static void ignore_print(int index, IGNORE_REC *rec)
g_string_append(options, "-regexp ");
if (rec->pattern == NULL)
g_string_append(options, "[INVALID! -pattern missing] ");
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ else if (rec->preg == NULL)
+ g_string_append(options, "[INVALID!] ");
+#else
else if (!rec->regexp_compiled)
g_string_append(options, "[INVALID!] ");
#endif
}
if (rec->fullword) g_string_append(options, "-full ");
if (rec->replies) g_string_append(options, "-replies ");
- if (rec->servertag != NULL)
+ if (rec->servertag != NULL)
g_string_append_printf(options, "-network %s ", rec->servertag);
if (rec->pattern != NULL)
g_string_append_printf(options, "-pattern %s ", rec->pattern);
@@ -127,7 +130,8 @@ static void cmd_ignore(const char *data)
return;
}
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"ignore", &optlist, &mask, &levels))
return;
@@ -137,7 +141,7 @@ static void cmd_ignore(const char *data)
/* Allow -ircnet for backwards compatibility */
if (!servertag)
servertag = g_hash_table_lookup(optlist, "ircnet");
-
+
if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (*levels == '\0') levels = "ALL";
level = level2bits(levels, NULL);
@@ -157,15 +161,15 @@ static void cmd_ignore(const char *data)
channels = (chanarg == NULL || *chanarg == '\0') ? NULL :
g_strsplit(chanarg, ",", -1);
- rec = patternarg != NULL ? NULL: ignore_find_noact(servertag, mask, channels,
- (level & MSGLEVEL_NO_ACT));
+ rec = ignore_find_full(servertag, mask, patternarg, channels,
+ IGNORE_FIND_PATTERN | ((level & MSGLEVEL_NO_ACT) ? IGNORE_FIND_NOACT : 0));
new_ignore = rec == NULL;
if (rec == NULL) {
rec = g_new0(IGNORE_REC, 1);
rec->mask = mask == NULL || *mask == '\0' ||
- strcmp(mask, "*") == 0 ? NULL : g_strdup(mask);
+ g_strcmp0(mask, "*") == 0 ? NULL : g_strdup(mask);
rec->channels = channels;
} else {
g_free_and_null(rec->pattern);
@@ -214,7 +218,7 @@ static void cmd_unignore(const char *data)
{
IGNORE_REC *rec;
GSList *tmp;
- char *mask;
+ char *mask, *mask_orig;
void *free_arg;
if (!cmd_get_params(data, &free_arg, 1, &mask))
@@ -223,6 +227,10 @@ static void cmd_unignore(const char *data)
if (*mask == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+ /* Save the mask string here since it might be modified in the code
+ * below and we need it to print meaningful error messages. */
+ mask_orig = mask;
+
if (is_numeric(mask, ' ')) {
/* with index number */
tmp = g_slist_nth(ignores, atoi(mask)-1);
@@ -236,9 +244,9 @@ static void cmd_unignore(const char *data)
chans[0] = mask;
mask = NULL;
}
- rec = ignore_find_noact("*", mask, (char **) chans, 0);
+ rec = ignore_find_full("*", mask, NULL, (char **) chans, 0);
if (rec == NULL) {
- rec = ignore_find_noact("*", mask, (char **) chans, 1);
+ rec = ignore_find_full("*", mask, NULL, (char **) chans, IGNORE_FIND_NOACT);
}
}
@@ -247,7 +255,7 @@ static void cmd_unignore(const char *data)
ignore_update_rec(rec);
} else {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
- TXT_IGNORE_NOT_FOUND, mask);
+ TXT_IGNORE_NOT_FOUND, mask_orig);
}
cmd_params_free(free_arg);
}
diff --git a/src/fe-common/core/fe-log.c b/src/fe-common/core/fe-log.c
index 5ee72d8b..5bc5c4e1 100644
--- a/src/fe-common/core/fe-log.c
+++ b/src/fe-common/core/fe-log.c
@@ -43,11 +43,11 @@
static int autolog_level;
static int autoremove_tag;
-static const char *autolog_path;
+static char *autolog_path;
static THEME_REC *log_theme;
static int skip_next_printtext;
-static const char *log_theme_name;
+static char *log_theme_name;
static int log_dir_create_mode;
@@ -87,8 +87,9 @@ static void cmd_log_open(const char *data)
int level;
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
- PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_OPTIONS,
- "log open", &optlist, &fname, &levels))
+ PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_OPTIONS |
+ PARAM_FLAG_STRIP_TRAILING_WS, "log open", &optlist,
+ &fname, &levels))
return;
if (*fname == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -452,7 +453,7 @@ static void autolog_open(SERVER_REC *server, const char *server_tag,
log_item_add(log, LOG_ITEM_TARGET, target, server_tag);
dir = g_path_get_dirname(log->real_fname);
- mkpath(dir, log_dir_create_mode);
+ g_mkdir_with_parents(dir, log_dir_create_mode);
g_free(dir);
log->temp = TRUE;
@@ -485,7 +486,7 @@ static void autolog_open_check(TEXT_DEST_REC *dest)
return;
if (target != NULL)
- autolog_open(server, server_tag, strcmp(target, "*") ? target : deftarget);
+ autolog_open(server, server_tag, g_strcmp0(target, "*") ? target : deftarget);
}
static void log_single_line(WINDOW_REC *window, const char *server_tag,
@@ -616,7 +617,7 @@ static void sig_window_item_remove(WINDOW_REC *window, WI_ITEM_REC *item)
static void sig_log_locked(LOG_REC *log)
{
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
- TXT_LOG_LOCKED, log->fname);
+ TXT_LOG_LOCKED, log->real_fname);
}
static void sig_log_create_failed(LOG_REC *log)
@@ -629,7 +630,7 @@ static void sig_log_create_failed(LOG_REC *log)
static void sig_log_new(LOG_REC *log)
{
if (!settings_get_bool("awaylog_colors") &&
- strcmp(log->fname, settings_get_str("awaylog_file")) == 0)
+ g_strcmp0(log->fname, settings_get_str("awaylog_file")) == 0)
log->colorizer = log_colorizer_strip;
}
@@ -656,11 +657,11 @@ static void sig_awaylog_show(LOG_REC *log, gpointer pmsgs, gpointer pfilepos)
filepos = GPOINTER_TO_INT(pfilepos);
if (msgs == 0)
- printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_NO_AWAY_MSGS, log->fname);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_NO_AWAY_MSGS, log->real_fname);
else {
- printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_AWAY_MSGS, log->fname, msgs);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_AWAY_MSGS, log->real_fname, msgs);
- str = g_strdup_printf("\"%s\" %d", log->fname, filepos);
+ str = g_strdup_printf("\"%s\" %d", log->real_fname, filepos);
signal_emit("command cat", 1, str);
g_free(str);
}
@@ -675,9 +676,11 @@ static void sig_theme_destroyed(THEME_REC *theme)
static void read_settings(void)
{
int old_autolog = autolog_level;
- int log_file_create_mode;
+ int log_file_create_mode;
+
+ g_free_not_null(autolog_path);
+ autolog_path = g_strdup(settings_get_str("autolog_path"));
- autolog_path = settings_get_str("autolog_path");
autolog_level = !settings_get_bool("autolog") ? 0 :
settings_get_level("autolog_level");
@@ -687,9 +690,14 @@ static void read_settings(void)
/* write to log files with different theme? */
if (log_theme_name != NULL)
signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
- log_theme_name = settings_get_str("log_theme");
- if (*log_theme_name == '\0')
+
+ g_free_not_null(log_theme_name);
+ log_theme_name = g_strdup(settings_get_str("log_theme"));
+
+ if (*log_theme_name == '\0') {
+ g_free(log_theme_name);
log_theme_name = NULL;
+ }
else
signal_add("print format", (SIGNAL_FUNC) sig_print_format);
@@ -752,7 +760,7 @@ void fe_log_deinit(void)
{
g_source_remove(autoremove_tag);
if (log_theme_name != NULL)
- signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
+ signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
command_unbind("log", (SIGNAL_FUNC) cmd_log);
command_unbind("log open", (SIGNAL_FUNC) cmd_log_open);
@@ -776,4 +784,7 @@ void fe_log_deinit(void)
if (autolog_ignore_targets != NULL)
g_strfreev(autolog_ignore_targets);
+
+ g_free_not_null(autolog_path);
+ g_free_not_null(log_theme_name);
}
diff --git a/src/fe-common/core/fe-messages.c b/src/fe-common/core/fe-messages.c
index 95d9a9d8..487a5754 100644
--- a/src/fe-common/core/fe-messages.c
+++ b/src/fe-common/core/fe-messages.c
@@ -92,7 +92,7 @@ char *expand_emphasis(WI_ITEM_REC *item, const char *text)
use emphasis on them. */
int found;
char c;
- char *end2;
+ char *end2;
/* check if _foo_ is a nick */
c = end[1];
@@ -175,6 +175,7 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
int for_me, print_channel, level;
char *nickmode, *color, *freemsg = NULL;
HILIGHT_REC *hilight;
+ TEXT_DEST_REC dest;
/* NOTE: this may return NULL if some channel is just closed with
/WINDOW CLOSE and server still sends the few last messages */
@@ -183,7 +184,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);
@@ -212,10 +215,11 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
if (printnick == NULL)
printnick = nick;
+ format_create_dest(&dest, server, target, level, NULL);
+ dest.address = address;
+ dest.nick = nick;
if (color != NULL) {
/* highlighted nick */
- TEXT_DEST_REC dest;
- format_create_dest(&dest, server, target, level, NULL);
hilight_update_text_dest(&dest,hilight);
if (!print_channel) /* message to active channel in window */
printformat_dest(&dest, TXT_PUBMSG_HILIGHT, color,
@@ -226,15 +230,15 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
nickmode);
} else {
if (!print_channel)
- printformat(server, target, level,
+ printformat_dest(&dest,
for_me ? TXT_PUBMSG_ME : TXT_PUBMSG,
printnick, msg, nickmode);
else
- printformat(server, target, level,
+ printformat_dest(&dest,
for_me ? TXT_PUBMSG_ME_CHANNEL :
TXT_PUBMSG_CHANNEL,
printnick, target, msg, nickmode);
- }
+ }
g_free_not_null(nickmode);
g_free_not_null(freemsg);
@@ -242,13 +246,16 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
}
static void sig_message_private(SERVER_REC *server, const char *msg,
- const char *nick, const char *address)
+ const char *nick, const char *address, const char *target)
{
QUERY_REC *query;
char *freemsg = NULL;
int level = MSGLEVEL_MSGS;
- query = query_find(server, nick);
+ /* own message returned by bouncer? */
+ int own = (!g_strcmp0(nick, server->nick));
+
+ query = query_find(server, own ? target : nick);
if (settings_get_bool("emphasis"))
msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg);
@@ -256,9 +263,15 @@ static void sig_message_private(SERVER_REC *server, const char *msg,
if (ignore_check(server, nick, address, NULL, msg, level | MSGLEVEL_NO_ACT))
level |= MSGLEVEL_NO_ACT;
- printformat(server, nick, level,
- query == NULL ? TXT_MSG_PRIVATE :
- TXT_MSG_PRIVATE_QUERY, nick, address, msg);
+ if (own) {
+ printformat(server, target, level,
+ query == NULL ? TXT_OWN_MSG_PRIVATE :
+ TXT_OWN_MSG_PRIVATE_QUERY, target, msg, server->nick);
+ } else {
+ printformat(server, nick, level,
+ query == NULL ? TXT_MSG_PRIVATE :
+ TXT_MSG_PRIVATE_QUERY, nick, address, msg);
+ }
g_free_not_null(freemsg);
}
@@ -314,8 +327,8 @@ static void sig_message_own_private(SERVER_REC *server, const char *msg,
/* this should only happen if some special target failed and
we should display some error message. currently the special
targets are only ',' and '.'. */
- g_return_if_fail(strcmp(origtarget, ",") == 0 ||
- strcmp(origtarget, ".") == 0);
+ g_return_if_fail(g_strcmp0(origtarget, ",") == 0 ||
+ g_strcmp0(origtarget, ".") == 0);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
*origtarget == ',' ? TXT_NO_MSGS_GOT :
@@ -383,8 +396,9 @@ static void sig_message_quit(SERVER_REC *server, const char *nick,
count = 0; windows = NULL;
chans = g_string_new(NULL);
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+ CHANNEL_REC *rec;
level = MSGLEVEL_QUITS;
- CHANNEL_REC *rec = tmp->data;
+ rec = tmp->data;
if (!nicklist_find(rec, nick))
continue;
@@ -560,7 +574,7 @@ static int printnick_exists(NICK_REC *first, NICK_REC *ignore,
while (first != NULL) {
if (first != ignore) {
printnick = g_hash_table_lookup(printnicks, first);
- if (printnick != NULL && strcmp(printnick, nick) == 0)
+ if (printnick != NULL && g_strcmp0(printnick, nick) == 0)
return TRUE;
}
@@ -589,9 +603,6 @@ static void sig_nicklist_new(CHANNEL_REC *channel, NICK_REC *nick)
char *nickhost, *p;
int n;
- if (nick->host == NULL)
- return;
-
firstnick = g_hash_table_lookup(channel->nicks, nick->nick);
if (firstnick->next == NULL)
return;
@@ -604,6 +615,9 @@ static void sig_nicklist_new(CHANNEL_REC *channel, NICK_REC *nick)
return; /* nope, we have it */
}
+ if (nick->host == NULL)
+ return;
+
/* identical nick already exists, have to change it somehow.. */
p = strchr(nick->host, '@');
if (p == NULL) p = nick->host; else p++;
@@ -685,6 +699,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/fe-modules.c b/src/fe-common/core/fe-modules.c
index df97ceb1..27b6b4c1 100644
--- a/src/fe-common/core/fe-modules.c
+++ b/src/fe-common/core/fe-modules.c
@@ -43,6 +43,10 @@ static void sig_module_error(void *number, const char *data,
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
TXT_MODULE_LOAD_ERROR, rootmodule, submodule, data);
break;
+ case MODULE_ERROR_VERSION_MISMATCH:
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ TXT_MODULE_VERSION_MISMATCH, rootmodule, submodule, data);
+ break;
case MODULE_ERROR_INVALID:
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
TXT_MODULE_INVALID, rootmodule, submodule);
diff --git a/src/fe-common/core/fe-queries.c b/src/fe-common/core/fe-queries.c
index 7599fb23..121417e4 100644
--- a/src/fe-common/core/fe-queries.c
+++ b/src/fe-common/core/fe-queries.c
@@ -326,12 +326,15 @@ static int sig_query_autoclose(void)
}
static void sig_message_private(SERVER_REC *server, const char *msg,
- const char *nick, const char *address)
+ const char *nick, const char *address, const char *target)
{
QUERY_REC *query;
+ /* own message returned by bouncer? */
+ int own = (!g_strcmp0(nick, server->nick));
+
/* create query window if needed */
- query = privmsg_get_query(server, nick, FALSE, MSGLEVEL_MSGS);
+ query = privmsg_get_query(server, own ? target : nick, FALSE, MSGLEVEL_MSGS);
/* reset the query's last_unread_msg timestamp */
if (query != NULL)
diff --git a/src/fe-common/core/fe-recode.c b/src/fe-common/core/fe-recode.c
index dbc43574..829c89e7 100644
--- a/src/fe-common/core/fe-recode.c
+++ b/src/fe-common/core/fe-recode.c
@@ -45,7 +45,7 @@ static const char *fe_recode_get_target (WI_ITEM_REC *witem)
static int fe_recode_compare_func (CONFIG_NODE *node1, CONFIG_NODE *node2)
{
- return strcmp(node1->key, node2->key);
+ return g_strcmp0(node1->key, node2->key);
}
/* SYNTAX: RECODE */
diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c
index 2dec1d8a..f4c1d3ee 100644
--- a/src/fe-common/core/fe-server.c
+++ b/src/fe-common/core/fe-server.c
@@ -104,7 +104,7 @@ static SERVER_SETUP_REC *create_server_setup(GHashTable *optlist)
return server;
}
-static void cmd_server_add(const char *data)
+static void cmd_server_add_modify(const char *data, gboolean add)
{
GHashTable *optlist;
SERVER_SETUP_REC *rec;
@@ -113,7 +113,7 @@ static void cmd_server_add(const char *data)
int port;
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
- "server add", &optlist, &addr, &portstr, &password))
+ "server add", &optlist, &addr, &portstr, &password))
return;
if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -124,6 +124,13 @@ static void cmd_server_add(const char *data)
rec = server_setup_find(addr, port, chatnet);
if (rec == NULL) {
+ if (add == FALSE) {
+ cmd_params_free(free_arg);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+ TXT_SETUPSERVER_NOT_FOUND, addr, port);
+ return;
+ }
+
rec = create_server_setup(optlist);
if (rec == NULL) {
cmd_params_free(free_arg);
@@ -147,45 +154,73 @@ static void cmd_server_add(const char *data)
else if (g_hash_table_lookup(optlist, "4"))
rec->family = AF_INET;
- if (g_hash_table_lookup(optlist, "ssl"))
- rec->use_ssl = TRUE;
+ if (g_hash_table_lookup(optlist, "tls") || g_hash_table_lookup(optlist, "ssl"))
+ rec->use_tls = TRUE;
+
+ value = g_hash_table_lookup(optlist, "tls_cert");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_cert");
+ if (value != NULL && *value != '\0')
+ rec->tls_cert = g_strdup(value);
+
+ value = g_hash_table_lookup(optlist, "tls_pkey");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_pkey");
+ if (value != NULL && *value != '\0')
+ rec->tls_pkey = g_strdup(value);
- value = g_hash_table_lookup(optlist, "ssl_cert");
+ value = g_hash_table_lookup(optlist, "tls_pass");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_pass");
if (value != NULL && *value != '\0')
- rec->ssl_cert = g_strdup(value);
+ rec->tls_pass = g_strdup(value);
- value = g_hash_table_lookup(optlist, "ssl_pkey");
+ if (g_hash_table_lookup(optlist, "tls_verify") || g_hash_table_lookup(optlist, "ssl_verify"))
+ rec->tls_verify = TRUE;
+
+ value = g_hash_table_lookup(optlist, "tls_cafile");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_cafile");
if (value != NULL && *value != '\0')
- rec->ssl_pkey = g_strdup(value);
+ rec->tls_cafile = g_strdup(value);
- value = g_hash_table_lookup(optlist, "ssl_pass");
+ value = g_hash_table_lookup(optlist, "tls_capath");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_capath");
if (value != NULL && *value != '\0')
- rec->ssl_pass = g_strdup(value);
+ rec->tls_capath = g_strdup(value);
- if (g_hash_table_lookup(optlist, "ssl_verify"))
- rec->ssl_verify = TRUE;
+ value = g_hash_table_lookup(optlist, "tls_ciphers");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_ciphers");
+ if (value != NULL && *value != '\0')
+ rec->tls_ciphers = g_strdup(value);
- value = g_hash_table_lookup(optlist, "ssl_cafile");
+ value = g_hash_table_lookup(optlist, "tls_pinned_cert");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_pinned_cert");
if (value != NULL && *value != '\0')
- rec->ssl_cafile = g_strdup(value);
+ rec->tls_pinned_cert = g_strdup(value);
- value = g_hash_table_lookup(optlist, "ssl_capath");
+ value = g_hash_table_lookup(optlist, "tls_pinned_pubkey");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_pinned_pubkey");
if (value != NULL && *value != '\0')
- rec->ssl_capath = g_strdup(value);
+ rec->tls_pinned_pubkey = g_strdup(value);
- if ((rec->ssl_cafile != NULL && rec->ssl_cafile[0] != '\0')
- || (rec->ssl_capath != NULL && rec->ssl_capath[0] != '\0'))
- rec->ssl_verify = TRUE;
+ if ((rec->tls_cafile != NULL && rec->tls_cafile[0] != '\0')
+ || (rec->tls_capath != NULL && rec->tls_capath[0] != '\0'))
+ rec->tls_verify = TRUE;
- if ((rec->ssl_cert != NULL && rec->ssl_cert[0] != '\0') || rec->ssl_verify == TRUE)
- rec->use_ssl = TRUE;
+ if ((rec->tls_cert != NULL && rec->tls_cert[0] != '\0') || rec->tls_verify == TRUE)
+ rec->use_tls = TRUE;
if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE;
if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE;
if (g_hash_table_lookup(optlist, "proxy")) rec->no_proxy = FALSE;
if (g_hash_table_lookup(optlist, "noproxy")) rec->no_proxy = TRUE;
- if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
+ if (*password != '\0' && g_strcmp0(password, "-") != 0) rec->password = g_strdup(password);
value = g_hash_table_lookup(optlist, "host");
if (value != NULL && *value != '\0') {
rec->own_host = g_strdup(value);
@@ -201,6 +236,16 @@ static void cmd_server_add(const char *data)
cmd_params_free(free_arg);
}
+static void cmd_server_add(const char *data)
+{
+ cmd_server_add_modify(data, TRUE);
+}
+
+static void cmd_server_modify(const char *data)
+{
+ cmd_server_add_modify(data, FALSE);
+}
+
/* SYNTAX: SERVER REMOVE <address> [<port>] [<network>] */
static void cmd_server_remove(const char *data)
{
@@ -264,7 +309,7 @@ static void cmd_server_connect(const char *data)
"connect", &optlist, &addr))
return;
- if (*addr == '\0' || strcmp(addr, "+") == 0)
+ if (*addr == '\0' || g_strcmp0(addr, "+") == 0)
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (*addr == '+') window_create(NULL, FALSE);
@@ -384,10 +429,13 @@ void fe_server_init(void)
command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect);
command_bind("server add", NULL, (SIGNAL_FUNC) cmd_server_add);
+ command_bind("server modify", NULL, (SIGNAL_FUNC) cmd_server_modify);
command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove);
command_bind_first("server", NULL, (SIGNAL_FUNC) server_command);
command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command);
- command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath auto noauto proxy noproxy -host -port noautosendcmd");
+
+ command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd");
+ command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd");
signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);
@@ -408,6 +456,7 @@ void fe_server_deinit(void)
command_unbind("server", (SIGNAL_FUNC) cmd_server);
command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect);
command_unbind("server add", (SIGNAL_FUNC) cmd_server_add);
+ command_unbind("server modify", (SIGNAL_FUNC) cmd_server_modify);
command_unbind("server remove", (SIGNAL_FUNC) cmd_server_remove);
command_unbind("server", (SIGNAL_FUNC) server_command);
command_unbind("disconnect", (SIGNAL_FUNC) server_command);
diff --git a/src/fe-common/core/fe-settings.c b/src/fe-common/core/fe-settings.c
index 96e03ceb..abbd45a8 100644
--- a/src/fe-common/core/fe-settings.c
+++ b/src/fe-common/core/fe-settings.c
@@ -53,7 +53,7 @@ static void set_print_pattern(const char *pattern)
if (stristr(rec->key, pattern) == NULL)
continue;
- if (strcmp(last_section, rec->section) != 0) {
+ if (g_strcmp0(last_section, rec->section) != 0) {
/* print section */
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
TXT_SET_TITLE, rec->section);
@@ -67,6 +67,7 @@ static void set_print_pattern(const char *pattern)
static void set_boolean(const char *key, const char *value)
{
char *stripped_value;
+
stripped_value = g_strdup(value);
g_strstrip(stripped_value);
@@ -79,7 +80,7 @@ static void set_boolean(const char *key, const char *value)
else
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_TOGGLE);
- g_free(stripped_value);
+ g_free(stripped_value);
}
static void set_int(const char *key, const char *value)
@@ -99,6 +100,24 @@ static void set_int(const char *key, const char *value)
settings_set_int(key, (int)longval);
}
+static void set_choice(const char *key, const char *value)
+{
+ char *stripped_value;
+
+ stripped_value = g_strdup(value);
+ g_strstrip(stripped_value);
+
+ if (settings_set_choice(key, stripped_value) == FALSE) {
+ SETTINGS_REC *rec = settings_get_record(key);
+ char *msg = g_strjoinv(", ", rec->choices);
+
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_CHOICE, msg);
+ g_free(msg);
+ }
+
+ g_free(stripped_value);
+}
+
/* SYNTAX: SET [-clear | -default] [<key> [<value>]] */
static void cmd_set(char *data)
{
@@ -108,7 +127,8 @@ static void cmd_set(char *data)
int clear, set_default;
SETTINGS_REC *rec;
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS,
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
+ PARAM_FLAG_OPTIONS,
"set", &optlist, &key, &value))
return;
@@ -126,7 +146,7 @@ static void cmd_set(char *data)
/* change the setting */
switch (rec->type) {
case SETTING_TYPE_BOOLEAN:
- if (clear)
+ if (clear)
settings_set_bool(key, FALSE);
else if (set_default)
settings_set_bool(key, rec->default_value.v_bool);
@@ -141,6 +161,12 @@ static void cmd_set(char *data)
else
set_int(key, value);
break;
+ case SETTING_TYPE_CHOICE:
+ if (clear || set_default)
+ settings_set_choice(key, rec->choices[rec->default_value.v_int]);
+ else
+ set_choice(key, value);
+ break;
case SETTING_TYPE_STRING:
settings_set_str(key, clear ? "" :
set_default ? rec->default_value.v_string :
@@ -149,32 +175,30 @@ static void cmd_set(char *data)
case SETTING_TYPE_TIME:
if (!settings_set_time(key, clear ? "0" :
set_default ? rec->default_value.v_string : value))
- printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
- TXT_INVALID_TIME);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_TIME);
break;
case SETTING_TYPE_LEVEL:
if (!settings_set_level(key, clear ? "" :
set_default ? rec->default_value.v_string : value))
- printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
- TXT_INVALID_LEVEL);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_LEVEL);
break;
case SETTING_TYPE_SIZE:
if (!settings_set_size(key, clear ? "0" :
set_default ? rec->default_value.v_string : value))
- printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
- TXT_INVALID_SIZE);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_SIZE);
+ break;
+ case SETTING_TYPE_ANY:
+ /* Unpossible! */
break;
}
signal_emit("setup changed", 0);
- printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
- TXT_SET_TITLE, rec->section);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_SET_TITLE, rec->section);
set_print(rec);
} else
- printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
- TXT_SET_UNKNOWN, key);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_UNKNOWN, key);
}
- cmd_params_free(free_arg);
+ cmd_params_free(free_arg);
}
/* SYNTAX: TOGGLE <key> [on|off|toggle] */
@@ -184,23 +208,24 @@ static void cmd_toggle(const char *data)
void *free_arg;
int type;
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &key, &value))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, &key, &value))
return;
- if (*key == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+ if (*key == '\0')
+ cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
type = settings_get_type(key);
- if (type == -1)
+ if (type == SETTING_TYPE_ANY)
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_UNKNOWN, key);
else if (type != SETTING_TYPE_BOOLEAN)
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_NOT_BOOLEAN, key);
else {
set_boolean(key, *value != '\0' ? value : "TOGGLE");
- set_print(settings_get_record(key));
+ set_print(settings_get_record(key));
signal_emit("setup changed", 0);
}
- cmd_params_free(free_arg);
+ cmd_params_free(free_arg);
}
static int config_key_compare(CONFIG_NODE *node1, CONFIG_NODE *node2)
diff --git a/src/fe-common/core/fe-tls.c b/src/fe-common/core/fe-tls.c
new file mode 100644
index 00000000..ed206d18
--- /dev/null
+++ b/src/fe-common/core/fe-tls.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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 "settings.h"
+#include "levels.h"
+#include "tls.h"
+
+#include "module-formats.h"
+#include "printtext.h"
+
+#include "fe-tls.h"
+
+static void tls_handshake_finished(SERVER_REC *server, TLS_REC *tls)
+{
+ GSList *certs = NULL;
+ GSList *subject = NULL;
+ GSList *issuer = NULL;
+ GString *str = NULL;
+ TLS_CERT_ENTRY_REC *data = NULL;
+
+ if (! settings_get_bool("tls_verbose_connect"))
+ return;
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_HEADER);
+
+ for (certs = tls->certs; certs != NULL; certs = certs->next) {
+ TLS_CERT_REC *tls_cert_rec = certs->data;
+ str = g_string_new(NULL);
+
+ for (subject = tls_cert_rec->subject; subject != NULL; subject = subject->next) {
+ data = subject->data;
+ g_string_append_printf(str, "%s: %s, ", data->name, data->value);
+ }
+
+ if (str->len > 1)
+ g_string_truncate(str, str->len - 2);
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_SUBJECT, str->str);
+ g_string_free(str, TRUE);
+
+ str = g_string_new(NULL);
+
+ for (issuer = tls_cert_rec->issuer; issuer != NULL; issuer = issuer->next) {
+ data = issuer->data;
+ g_string_append_printf(str, "%s: %s, ", data->name, data->value);
+ }
+
+ if (str->len > 1)
+ g_string_truncate(str, str->len - 2);
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_ISSUER, str->str);
+ g_string_free(str, TRUE);
+ }
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PROTOCOL_VERSION, tls->protocol_version, tls->cipher_size, tls->cipher);
+
+ if (tls->ephemeral_key_algorithm != NULL)
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY, tls->ephemeral_key_size, tls->ephemeral_key_algorithm);
+ else
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE);
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY, tls->public_key_size, tls->public_key_algorithm, tls->not_before, tls->not_after);
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY_FINGERPRINT, tls->public_key_fingerprint, tls->public_key_fingerprint_algorithm);
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_FINGERPRINT, tls->certificate_fingerprint, tls->certificate_fingerprint_algorithm);
+}
+
+void fe_tls_init(void)
+{
+ settings_add_bool("lookandfeel", "tls_verbose_connect", TRUE);
+
+ signal_add("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished);
+}
+
+void fe_tls_deinit(void)
+{
+ signal_remove("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished);
+}
diff --git a/src/fe-common/core/fe-tls.h b/src/fe-common/core/fe-tls.h
new file mode 100644
index 00000000..f0082477
--- /dev/null
+++ b/src/fe-common/core/fe-tls.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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 __FE_TLS_H
+#define __FE_TLS_H
+
+void fe_tls_init(void);
+void fe_tls_deinit(void);
+
+#endif
diff --git a/src/fe-common/core/fe-windows.c b/src/fe-common/core/fe-windows.c
index bf9d7154..0afa2914 100644
--- a/src/fe-common/core/fe-windows.c
+++ b/src/fe-common/core/fe-windows.c
@@ -35,30 +35,83 @@
GSList *windows; /* first in the list is the active window,
next is the last active, etc. */
+GSequence *windows_seq;
WINDOW_REC *active_win;
static int daytag;
static int daycheck; /* 0 = don't check, 1 = time is 00:00, check,
2 = time is 00:00, already checked */
+static int window_refnum_lookup(WINDOW_REC *window, void *refnum_p)
+{
+ int refnum = GPOINTER_TO_INT(refnum_p);
+ return window->refnum == refnum ? 0 : window->refnum < refnum ? -1 : 1;
+}
+
+static GSequenceIter *windows_seq_begin(void)
+{
+ return g_sequence_get_begin_iter(windows_seq);
+}
+
+static GSequenceIter *windows_seq_end(void)
+{
+ return g_sequence_get_end_iter(windows_seq);
+}
+
+static GSequenceIter *windows_seq_insert(WINDOW_REC *rec)
+{
+ return g_sequence_insert_sorted(windows_seq, rec, (GCompareDataFunc)window_refnum_cmp, NULL);
+}
+
+static GSequenceIter *windows_seq_refnum_lookup(int refnum)
+{
+ return g_sequence_lookup(windows_seq, GINT_TO_POINTER(refnum), (GCompareDataFunc)window_refnum_lookup, NULL);
+}
+
+static void windows_seq_changed(GSequenceIter *iter)
+{
+ g_sequence_sort_changed(iter, (GCompareDataFunc)window_refnum_cmp, NULL);
+}
+
+static GSequenceIter *windows_seq_window_lookup(WINDOW_REC *rec)
+{
+ return g_sequence_lookup(windows_seq, rec, (GCompareDataFunc)window_refnum_cmp, NULL);
+}
+
+/* search to the numerically right iterator of refnum */
+static GSequenceIter *windows_seq_refnum_search_right(int refnum)
+{
+ return g_sequence_search(windows_seq, GINT_TO_POINTER(refnum), (GCompareDataFunc)window_refnum_lookup, NULL);
+}
+
+/* we want to find the numerically left iterator of refnum, so we
+ search the right of the previous refnum. but we need to figure out
+ the case where the iterator is already at the beginning, i.e
+ iter->refnum >= refnum */
+static GSequenceIter *windows_seq_refnum_search_left(int refnum)
+{
+ GSequenceIter *iter = windows_seq_refnum_search_right(refnum - 1);
+ return iter == windows_seq_begin() ? NULL : g_sequence_iter_prev(iter);
+}
+
static int window_get_new_refnum(void)
{
WINDOW_REC *win;
- GSList *tmp;
+ GSequenceIter *iter, *end;
int refnum;
refnum = 1;
- tmp = windows;
- while (tmp != NULL) {
- win = tmp->data;
+ iter = windows_seq_begin();
+ end = windows_seq_end();
- if (refnum != win->refnum) {
- tmp = tmp->next;
- continue;
- }
+ while (iter != end) {
+ win = g_sequence_get(iter);
+
+ if (refnum != win->refnum)
+ return refnum;
refnum++;
- tmp = windows;
+ iter = g_sequence_iter_next(iter);
}
return refnum;
@@ -73,6 +126,7 @@ WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic)
rec->level = settings_get_level("window_default_level");
windows = g_slist_prepend(windows, rec);
+ windows_seq_insert(rec);
signal_emit("window created", 2, rec, GINT_TO_POINTER(automatic));
if (item != NULL) window_item_add(rec, item, automatic);
@@ -84,6 +138,19 @@ WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic)
return rec;
}
+static void window_set_refnum0(WINDOW_REC *window, int refnum)
+{
+ int old_refnum;
+
+ g_return_if_fail(window != NULL);
+ g_return_if_fail(refnum >= 1);
+ if (window->refnum == refnum) return;
+
+ old_refnum = window->refnum;
+ window->refnum = refnum;
+ signal_emit("window refnum changed", 2, window, GINT_TO_POINTER(old_refnum));
+}
+
/* removed_refnum was removed from the windows list, pack the windows so
there won't be any holes. If there is any holes after removed_refnum,
leave the windows behind it alone. */
@@ -91,23 +158,37 @@ static void windows_pack(int removed_refnum)
{
WINDOW_REC *window;
int refnum;
+ GSequenceIter *iter, *end;
+
+ refnum = removed_refnum + 1;
+ end = windows_seq_end();
+ iter = windows_seq_refnum_lookup(refnum);
+ if (iter == NULL) return;
- for (refnum = removed_refnum+1;; refnum++) {
- window = window_find_refnum(refnum);
- if (window == NULL || window->sticky_refnum)
+ while (iter != end) {
+ window = g_sequence_get(iter);
+
+ if (window == NULL || window->sticky_refnum || window->refnum != refnum)
break;
- window_set_refnum(window, refnum-1);
+ window_set_refnum0(window, refnum - 1);
+ windows_seq_changed(iter);
+
+ refnum++;
+ iter = g_sequence_iter_next(iter);
}
}
void window_destroy(WINDOW_REC *window)
{
+ GSequenceIter *iter;
g_return_if_fail(window != NULL);
if (window->destroying) return;
window->destroying = TRUE;
windows = g_slist_remove(windows, window);
+ iter = windows_seq_window_lookup(window);
+ if (iter != NULL) g_sequence_remove(iter);
if (active_win == window) {
active_win = NULL; /* it's corrupted */
@@ -184,31 +265,37 @@ void window_change_server(WINDOW_REC *window, void *server)
if (window->active_server != active) {
window->active_server = active;
signal_emit("window server changed", 2, window, active);
- }
+ }
}
void window_set_refnum(WINDOW_REC *window, int refnum)
{
- GSList *tmp;
+ GSequenceIter *other_iter, *window_iter;
int old_refnum;
g_return_if_fail(window != NULL);
g_return_if_fail(refnum >= 1);
if (window->refnum == refnum) return;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ other_iter = windows_seq_refnum_lookup(refnum);
+ window_iter = windows_seq_refnum_lookup(window->refnum);
- if (rec->refnum == refnum) {
- rec->refnum = window->refnum;
- signal_emit("window refnum changed", 2, rec, GINT_TO_POINTER(refnum));
- break;
- }
+ if (other_iter != NULL) {
+ WINDOW_REC *rec = g_sequence_get(other_iter);
+
+ rec->refnum = window->refnum;
+ signal_emit("window refnum changed", 2, rec, GINT_TO_POINTER(refnum));
}
old_refnum = window->refnum;
window->refnum = refnum;
signal_emit("window refnum changed", 2, window, GINT_TO_POINTER(old_refnum));
+
+ if (window_iter != NULL && other_iter != NULL) {
+ g_sequence_swap(other_iter, window_iter);
+ } else {
+ windows_seq_changed(window_iter);
+ }
}
void window_set_name(WINDOW_REC *window, const char *name)
@@ -229,11 +316,16 @@ void window_set_history(WINDOW_REC *window, const char *name)
else
window->history_name = g_strdup(name);
- signal_emit("window history changed", 1, window, oldname);
+ signal_emit("window history changed", 2, window, oldname);
g_free_not_null(oldname);
}
+void window_clear_history(WINDOW_REC *window, const char *name)
+{
+ signal_emit("window history cleared", 2, window, name);
+}
+
void window_set_level(WINDOW_REC *window, int level)
{
g_return_if_fail(window != NULL);
@@ -341,13 +433,13 @@ WINDOW_REC *window_find_closest(void *server, const char *name, int level)
WINDOW_REC *window_find_refnum(int refnum)
{
- GSList *tmp;
+ GSequenceIter *iter;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ iter = windows_seq_refnum_lookup(refnum);
+ if (iter != NULL) {
+ WINDOW_REC *rec = g_sequence_get(iter);
- if (rec->refnum == refnum)
- return rec;
+ return rec;
}
return NULL;
@@ -395,71 +487,86 @@ WINDOW_REC *window_find_item(SERVER_REC *server, const char *name)
int window_refnum_prev(int refnum, int wrap)
{
- GSList *tmp;
- int prev, max;
+ WINDOW_REC *rec;
+ GSequenceIter *iter, *end;
- max = prev = -1;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ iter = windows_seq_refnum_search_left(refnum);
+ end = windows_seq_end();
- if (rec->refnum < refnum && (prev == -1 || rec->refnum > prev))
- prev = rec->refnum;
- if (wrap && (max == -1 || rec->refnum > max))
- max = rec->refnum;
+ if (iter != NULL) {
+ rec = g_sequence_get(iter);
+ return rec->refnum;
+ }
+
+ if (wrap) {
+ iter = g_sequence_iter_prev(end);
+ if (iter != end) {
+ rec = g_sequence_get(iter);
+ return rec->refnum;
+ }
}
- return prev != -1 ? prev : max;
+ return -1;
}
int window_refnum_next(int refnum, int wrap)
{
- GSList *tmp;
- int min, next;
+ WINDOW_REC *rec;
+ GSequenceIter *iter, *end;
- min = next = -1;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ iter = windows_seq_refnum_search_right(refnum);
+ end = windows_seq_end();
- if (rec->refnum > refnum && (next == -1 || rec->refnum < next))
- next = rec->refnum;
- if (wrap && (min == -1 || rec->refnum < min))
- min = rec->refnum;
+ if (iter != end) {
+ rec = g_sequence_get(iter);
+ return rec->refnum;
}
- return next != -1 ? next : min;
+ if (wrap) {
+ iter = windows_seq_begin();
+ if (iter != end) {
+ rec = g_sequence_get(iter);
+ return rec->refnum;
+ }
+ }
+
+ return -1;
}
int windows_refnum_last(void)
{
- GSList *tmp;
- int max;
-
- max = -1;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ WINDOW_REC *rec;
+ GSequenceIter *end, *iter;
- if (rec->refnum > max)
- max = rec->refnum;
+ end = windows_seq_end();
+ iter = g_sequence_iter_prev(end);
+ if (iter != end) {
+ rec = g_sequence_get(iter);
+ return rec->refnum;
}
- return max;
+ return -1;
}
int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2)
{
- return w1->refnum < w2->refnum ? -1 : 1;
+ return w1 == w2 ? 0 : w1->refnum < w2->refnum ? -1 : 1;
}
GSList *windows_get_sorted(void)
{
- GSList *tmp, *sorted;
+ GSequenceIter *iter, *begin;
+ GSList *sorted;
sorted = NULL;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ iter = windows_seq_end();
+ begin = windows_seq_begin();
+
+ while (iter != begin) {
+ iter = g_sequence_iter_prev(iter);
+ WINDOW_REC *rec = g_sequence_get(iter);
- sorted = g_slist_insert_sorted(sorted, rec, (GCompareFunc)
- window_refnum_cmp);
+ sorted = g_slist_prepend(sorted, rec);
}
return sorted;
@@ -704,6 +811,7 @@ static void read_settings(void)
void windows_init(void)
{
active_win = NULL;
+ windows_seq = g_sequence_new(NULL);
daycheck = 0; daytag = -1;
settings_add_bool("lookandfeel", "window_auto_change", FALSE);
settings_add_bool("lookandfeel", "windows_auto_renumber", TRUE);
@@ -728,4 +836,6 @@ void windows_deinit(void)
signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
signal_remove("server connect failed", (SIGNAL_FUNC) sig_server_disconnected);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+ g_sequence_free(windows_seq);
+ windows_seq = NULL;
}
diff --git a/src/fe-common/core/fe-windows.h b/src/fe-common/core/fe-windows.h
index 613f15f8..32d6cfcd 100644
--- a/src/fe-common/core/fe-windows.h
+++ b/src/fe-common/core/fe-windows.h
@@ -66,6 +66,7 @@ void window_change_server(WINDOW_REC *window, void *server);
void window_set_refnum(WINDOW_REC *window, int refnum);
void window_set_name(WINDOW_REC *window, const char *name);
void window_set_history(WINDOW_REC *window, const char *name);
+void window_clear_history(WINDOW_REC *window, const char *name);
void window_set_level(WINDOW_REC *window, int level);
void window_set_immortal(WINDOW_REC *window, int immortal);
diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c
index 375b00eb..17c13a97 100644
--- a/src/fe-common/core/formats.c
+++ b/src/fe-common/core/formats.c
@@ -68,7 +68,7 @@ static void format_expand_code(const char **format, GString *out, int *flags)
if (flags == NULL) {
/* flags are being ignored - skip the code */
- while (**format != ']')
+ while (**format != ']' && **format != '\0')
(*format)++;
return;
}
@@ -131,6 +131,8 @@ void unformat_24bit_color(char **ptr, int off, int *fgcolor, int *bgcolor, int *
unsigned char rgbx[4];
unsigned int i;
for (i = 0; i < 4; ++i) {
+ if ((*ptr)[i + off] == '\0')
+ return;
rgbx[i] = (*ptr)[i + off];
}
rgbx[3] -= 0x20;
@@ -244,6 +246,10 @@ int format_expand_styles(GString *out, const char **format, int *flags)
case '[':
/* code */
format_expand_code(format, out, flags);
+ if ((*format)[0] == '\0')
+ /* oops, reached end prematurely */
+ (*format)--;
+
break;
case 'x':
case 'X':
@@ -420,33 +426,17 @@ void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
window_find_closest(server, target, level);
}
-static int advance (char const **str, gboolean utf8)
-{
- if (utf8) {
- gunichar c;
-
- c = g_utf8_get_char(*str);
- *str = g_utf8_next_char(*str);
-
- return unichar_isprint(c) ? mk_wcwidth(c) : 1;
- } else {
- *str += 1;
-
- return 1;
- }
-}
-
/* Return length of text part in string (ie. without % codes) */
int format_get_length(const char *str)
{
GString *tmp;
int len;
- gboolean utf8;
+ int utf8;
int adv = 0;
g_return_val_if_fail(str != NULL, 0);
- utf8 = is_utf8() && g_utf8_validate(str, -1, NULL);
+ utf8 = string_policy(str);
tmp = g_string_new(NULL);
len = 0;
@@ -465,7 +455,7 @@ int format_get_length(const char *str)
len++;
}
- len += advance(&str, utf8);
+ len += string_advance(&str, utf8);
}
g_string_free(tmp, TRUE);
@@ -480,12 +470,12 @@ int format_real_length(const char *str, int len)
GString *tmp;
const char *start;
const char *oldstr;
- gboolean utf8;
+ int utf8;
int adv = 0;
g_return_val_if_fail(str != NULL, 0);
g_return_val_if_fail(len >= 0, 0);
- utf8 = is_utf8() && g_utf8_validate(str, -1, NULL);
+ utf8 = string_policy(str);
start = str;
tmp = g_string_new(NULL);
@@ -507,7 +497,7 @@ int format_real_length(const char *str, int len)
}
oldstr = str;
- len -= advance(&str, utf8);
+ len -= string_advance(&str, utf8);
if (len < 0)
str = oldstr;
}
@@ -905,10 +895,13 @@ static const char *get_ansi_color(THEME_REC *theme, const char *str,
switch (num) {
case 0:
- /* reset colors back to default */
+ /* reset colors and attributes back to default */
fg = theme->default_color;
bg = -1;
- flags &= ~(GUI_PRINT_FLAG_COLOR_24_FG | GUI_PRINT_FLAG_COLOR_24_BG | GUI_PRINT_FLAG_INDENT);
+ flags &= ~(GUI_PRINT_FLAG_INDENT |
+ GUI_PRINT_FLAG_BOLD | GUI_PRINT_FLAG_ITALIC | GUI_PRINT_FLAG_UNDERLINE |
+ GUI_PRINT_FLAG_BLINK | GUI_PRINT_FLAG_REVERSE |
+ GUI_PRINT_FLAG_COLOR_24_FG | GUI_PRINT_FLAG_COLOR_24_BG);
break;
case 1:
/* hilight */
@@ -967,6 +960,7 @@ static const char *get_ansi_color(THEME_REC *theme, const char *str,
str++;
for (num2 = 0; i_isdigit(*str); str++)
num2 = num2*10 + (*str-'0');
+ if (*str == '\0') return start;
switch (num2) {
case 2:
@@ -984,6 +978,8 @@ static const char *get_ansi_color(THEME_REC *theme, const char *str,
for (; i_isdigit(*str); str++)
num2 = (num2&~0xff) |
(((num2&0xff) * 10 + (*str-'0'))&0xff);
+
+ if (*str == '\0') return start;
}
if (i == -1) break;
@@ -1012,6 +1008,7 @@ static const char *get_ansi_color(THEME_REC *theme, const char *str,
str++;
for (num2 = 0; i_isdigit(*str); str++)
num2 = num2*10 + (*str-'0');
+ if (*str == '\0') return start;
if (num == 38) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
@@ -1235,11 +1232,7 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
}
}
- if (type == 7) {
- /* bell */
- if (settings_get_bool("bell_beeps"))
- signal_emit("beep", 0);
- } else if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
+ if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
/* clear to end of line */
flags |= GUI_PRINT_FLAG_CLRTOEOL;
}
@@ -1354,6 +1347,9 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
bgcolor = *ptr==(char)0xff ? -1 : *ptr-'0';
}
}
+ if (*ptr == '\0')
+ break;
+
ptr++;
break;
case 6:
diff --git a/src/fe-common/core/formats.h b/src/fe-common/core/formats.h
index 07e1832c..8efd204c 100644
--- a/src/fe-common/core/formats.h
+++ b/src/fe-common/core/formats.h
@@ -45,11 +45,15 @@ struct _FORMAT_REC {
#define PRINT_FLAG_SET_SERVERTAG 0x0010
#define PRINT_FLAG_UNSET_SERVERTAG 0x0020
+typedef struct _HILIGHT_REC HILIGHT_REC;
+
typedef struct _TEXT_DEST_REC {
WINDOW_REC *window;
SERVER_REC *server;
const char *server_tag; /* if server is non-NULL, must be server->tag */
const char *target;
+ const char *nick;
+ const char *address;
int level;
int hilight_priority;
diff --git a/src/fe-common/core/hilight-text.c b/src/fe-common/core/hilight-text.c
index 4b914517..037cde5c 100644
--- a/src/fe-common/core/hilight-text.c
+++ b/src/fe-common/core/hilight-text.c
@@ -49,7 +49,7 @@ static void reset_level_cache(void)
HILIGHT_REC *rec = tmp->data;
if (never_hilight_level & rec->level)
- never_hilight_level &= ~rec->level;
+ never_hilight_level &= ~rec->level;
}
}
@@ -66,22 +66,23 @@ static void hilight_add_config(HILIGHT_REC *rec)
g_return_if_fail(rec != NULL);
node = iconfig_node_traverse("(hilights", TRUE);
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
-
- iconfig_node_set_str(node, "text", rec->text);
- if (rec->level > 0) iconfig_node_set_int(node, "level", rec->level);
- if (rec->color) iconfig_node_set_str(node, "color", rec->color);
- if (rec->act_color) iconfig_node_set_str(node, "act_color", rec->act_color);
- if (rec->priority > 0) iconfig_node_set_int(node, "priority", rec->priority);
- iconfig_node_set_bool(node, "nick", rec->nick);
- iconfig_node_set_bool(node, "word", rec->word);
- if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE);
- if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
- if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
- if (rec->servertag) iconfig_node_set_str(node, "servertag", rec->servertag);
+ node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+ iconfig_node_set_str(node, "text", rec->text);
+ if (rec->level > 0) iconfig_node_set_int(node, "level", rec->level);
+ if (rec->color) iconfig_node_set_str(node, "color", rec->color);
+ if (rec->act_color) iconfig_node_set_str(node, "act_color", rec->act_color);
+ if (rec->priority > 0) iconfig_node_set_int(node, "priority", rec->priority);
+ iconfig_node_set_bool(node, "nick", rec->nick);
+ iconfig_node_set_bool(node, "word", rec->word);
+ if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE);
+ if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
+ if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
+ if (rec->case_sensitive) iconfig_node_set_bool(node, "matchcase", TRUE);
+ if (rec->servertag) iconfig_node_set_str(node, "servertag", rec->servertag);
if (rec->channels != NULL && *rec->channels != NULL) {
- node = config_node_section(node, "channels", NODE_TYPE_LIST);
+ node = iconfig_node_section(node, "channels", NODE_TYPE_LIST);
iconfig_node_add_list(node, rec->channels);
}
}
@@ -100,7 +101,9 @@ static void hilight_destroy(HILIGHT_REC *rec)
{
g_return_if_fail(rec != NULL);
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg != NULL) g_regex_unref(rec->preg);
+#else
if (rec->regexp_compiled) regfree(&rec->preg);
#endif
if (rec->channels != NULL) g_strfreev(rec->channels);
@@ -119,10 +122,18 @@ static void hilights_destroy_all(void)
static void hilight_init_rec(HILIGHT_REC *rec)
{
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg != NULL)
+ g_regex_unref(rec->preg);
+
+ rec->preg = g_regex_new(rec->text, G_REGEX_OPTIMIZE | G_REGEX_RAW | G_REGEX_CASELESS, 0, NULL);
+#else
if (rec->regexp_compiled) regfree(&rec->preg);
- rec->regexp_compiled = !rec->regexp ? FALSE :
- regcomp(&rec->preg, rec->text, REG_EXTENDED|REG_ICASE) == 0;
+ if (!rec->regexp)
+ rec->regexp_compiled = FALSE;
+ else
+ rec->regexp_compiled = regcomp(&rec->preg, rec->text,
+ rec->case_sensitive ? REG_EXTENDED : (REG_EXTENDED|REG_ICASE)) == 0;
#endif
}
@@ -168,13 +179,13 @@ static HILIGHT_REC *hilight_find(const char *text, char **channels)
if ((channels == NULL && rec->channels == NULL))
return rec; /* no channels - ok */
- if (channels != NULL && strcmp(*channels, "*") == 0)
+ if (channels != NULL && g_strcmp0(*channels, "*") == 0)
return rec; /* ignore channels */
if (channels == NULL || rec->channels == NULL)
continue; /* other doesn't have channels */
- if (strarray_length(channels) != strarray_length(rec->channels))
+ if (g_strv_length(channels) != g_strv_length(rec->channels))
continue; /* different amount of channels */
/* check that channels match */
@@ -190,39 +201,58 @@ static HILIGHT_REC *hilight_find(const char *text, char **channels)
return NULL;
}
-static int hilight_match_text(HILIGHT_REC *rec, const char *text,
- int *match_beg, int *match_end)
+static gboolean hilight_match_text(HILIGHT_REC *rec, const char *text,
+ int *match_beg, int *match_end)
{
- char *match;
+ gboolean ret = FALSE;
if (rec->regexp) {
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg != NULL) {
+ GMatchInfo *match;
+
+ g_regex_match (rec->preg, text, 0, &match);
+
+ if (g_match_info_matches(match))
+ ret = g_match_info_fetch_pos(match, 0, match_beg, match_end);
+
+ g_match_info_free(match);
+ }
+#else
regmatch_t rmatch[1];
if (rec->regexp_compiled &&
- regexec(&rec->preg, text, 1, rmatch, 0) == 0) {
+ regexec(&rec->preg, text, 1, rmatch, 0) == 0) {
if (rmatch[0].rm_so > 0 &&
- match_beg != NULL && match_end != NULL) {
+ match_beg != NULL && match_end != NULL) {
*match_beg = rmatch[0].rm_so;
*match_end = rmatch[0].rm_eo;
}
- return TRUE;
+ ret = TRUE;
}
#endif
} else {
- match = rec->fullword ?
- stristr_full(text, rec->text) :
- stristr(text, rec->text);
+ char *match;
+
+ if (rec->case_sensitive) {
+ match = rec->fullword ?
+ strstr_full(text, rec->text) :
+ strstr(text, rec->text);
+ } else {
+ match = rec->fullword ?
+ stristr_full(text, rec->text) :
+ stristr(text, rec->text);
+ }
if (match != NULL) {
if (match_beg != NULL && match_end != NULL) {
*match_beg = (int) (match-text);
*match_end = *match_beg + strlen(rec->text);
}
- return TRUE;
+ ret = TRUE;
}
}
- return FALSE;
+ return ret;
}
#define hilight_match_level(rec, level) \
@@ -267,14 +297,14 @@ HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel,
HILIGHT_REC *rec = tmp->data;
if (!rec->nickmask && hilight_match_level(rec, level) &&
- hilight_match_channel(rec, channel) &&
- (rec->servertag == NULL ||
- (server != NULL && g_ascii_strcasecmp(rec->servertag, server->tag) == 0)) &&
- hilight_match_text(rec, str, match_beg, match_end))
+ hilight_match_channel(rec, channel) &&
+ (rec->servertag == NULL ||
+ (server != NULL && g_ascii_strcasecmp(rec->servertag, server->tag) == 0)) &&
+ hilight_match_text(rec, str, match_beg, match_end))
return rec;
}
- return NULL;
+ return NULL;
}
static char *hilight_get_act_color(HILIGHT_REC *rec)
@@ -306,9 +336,9 @@ void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
dest->hilight_priority = rec->priority;
g_free_and_null(dest->hilight_color);
- if (rec->act_color != NULL && strcmp(rec->act_color, "%n") == 0)
+ if (rec->act_color != NULL && g_strcmp0(rec->act_color, "%n") == 0)
dest->level |= MSGLEVEL_NO_ACT;
- else
+ else
dest->hilight_color = hilight_get_act_color(rec);
}
@@ -325,11 +355,11 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
if (dest->level & MSGLEVEL_NOHILIGHT)
return;
- hilight_start = hilight_end = 0;
- hilight = hilight_match(dest->server, dest->target,
- NULL, NULL, dest->level, stripped,
- &hilight_start,
- &hilight_end);
+ hilight_start = hilight_end = 0;
+ hilight = hilight_match(dest->server, dest->target, dest->nick,
+ dest->address, dest->level, stripped,
+ &hilight_start, &hilight_end);
+
if (hilight == NULL)
return;
@@ -337,14 +367,14 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
old_level = dest->level;
if (!nick_match || (dest->level & MSGLEVEL_HILIGHT)) {
- /* update the level / hilight info */
- hilight_update_text_dest(dest, hilight);
/* Remove NO_ACT, this means explicitly defined hilights will bypass
* /IGNORE ... NO_ACT.
* (It's still possible to use /hilight -actcolor %n to hide
* hilight/beep).
*/
dest->level &= ~MSGLEVEL_NO_ACT;
+ /* update the level / hilight info */
+ hilight_update_text_dest(dest, hilight);
}
if (nick_match)
@@ -362,29 +392,29 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
/* hilight whole line */
char *tmp = strip_codes(text);
newstr = g_strconcat(color, tmp, NULL);
- g_free(tmp);
+ g_free(tmp);
} else {
/* hilight part of the line */
- GString *tmp;
- char *middle;
+ GString *tmp;
+ char *middle;
int pos, color_pos, color_len;
- tmp = g_string_new(NULL);
+ tmp = g_string_new(NULL);
- /* start of the line */
+ /* start of the line */
pos = strip_real_length(text, hilight_start, NULL, NULL);
g_string_append(tmp, text);
- g_string_truncate(tmp, pos);
+ g_string_truncate(tmp, pos);
/* color */
- g_string_append(tmp, color);
+ g_string_append(tmp, color);
/* middle of the line, stripped */
middle = strip_codes(text+pos);
- pos = tmp->len;
+ pos = tmp->len;
g_string_append(tmp, middle);
- g_string_truncate(tmp, pos+hilight_len);
- g_free(middle);
+ g_string_truncate(tmp, pos+hilight_len);
+ g_free(middle);
/* end of the line */
pos = strip_real_length(text, hilight_end,
@@ -398,8 +428,8 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
}
g_string_append(tmp, text+pos);
- newstr = tmp->str;
- g_string_free(tmp, FALSE);
+ newstr = tmp->str;
+ g_string_free(tmp, FALSE);
}
signal_emit("print text", 3, dest, newstr, stripped);
@@ -417,7 +447,7 @@ HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel,
HILIGHT_REC *rec;
rec = hilight_match(server, channel, nick, address,
- level, msg, NULL, NULL);
+ level, msg, NULL, NULL);
return (rec == NULL || !rec->nick) ? NULL : rec;
}
@@ -464,6 +494,7 @@ static void read_hilight_config(void)
rec->priority = config_node_get_int(node, "priority", 0);
rec->nick = config_node_get_bool(node, "nick", TRUE);
rec->word = config_node_get_bool(node, "word", TRUE);
+ rec->case_sensitive = config_node_get_bool(node, "matchcase", FALSE);
rec->nickmask = config_node_get_bool(node, "mask", FALSE);
rec->fullword = config_node_get_bool(node, "fullword", FALSE);
@@ -471,11 +502,11 @@ static void read_hilight_config(void)
rec->servertag = config_node_get_str(node, "servertag", NULL);
hilight_init_rec(rec);
- node = config_node_section(node, "channels", -1);
+ node = iconfig_node_section(node, "channels", -1);
if (node != NULL) rec->channels = config_node_get_list(node);
}
- reset_cache();
+ reset_cache();
}
static void hilight_print(int index, HILIGHT_REC *rec)
@@ -484,16 +515,24 @@ static void hilight_print(int index, HILIGHT_REC *rec)
GString *options;
options = g_string_new(NULL);
- if (!rec->nick || !rec->word) {
- if (rec->nick) g_string_append(options, "-nick ");
- if (rec->word) g_string_append(options, "-word ");
- }
+
+ if (rec->nick && rec->word) { /* default case, no option */ }
+ else if (rec->nick)
+ g_string_append(options, "-nick ");
+ else if (rec->word)
+ g_string_append(options, "-word ");
+ else
+ g_string_append(options, "-line ");
if (rec->nickmask) g_string_append(options, "-mask ");
if (rec->fullword) g_string_append(options, "-full ");
+ if (rec->case_sensitive) g_string_append(options, "-matchcase ");
if (rec->regexp) {
g_string_append(options, "-regexp ");
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg == NULL)
+ g_string_append(options, "[INVALID!] ");
+#else
if (!rec->regexp_compiled)
g_string_append(options, "[INVALID!] ");
#endif
@@ -515,10 +554,10 @@ static void hilight_print(int index, HILIGHT_REC *rec)
if (levelstr != NULL)
levelstr = g_strconcat(levelstr, " ", NULL);
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
- TXT_HILIGHT_LINE, index, rec->text,
- chans != NULL ? chans : "",
- levelstr != NULL ? levelstr : "",
- options->str);
+ TXT_HILIGHT_LINE, index, rec->text,
+ chans != NULL ? chans : "",
+ levelstr != NULL ? levelstr : "",
+ options->str);
g_free_not_null(chans);
g_free_not_null(levelstr);
g_string_free(options, TRUE);
@@ -539,12 +578,12 @@ static void cmd_hilight_show(void)
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_HILIGHT_FOOTER);
}
-/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -regexp]
+/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -matchcase | -regexp]
[-color <color>] [-actcolor <color>] [-level <level>]
[-network <network>] [-channels <channels>] <text> */
static void cmd_hilight(const char *data)
{
- GHashTable *optlist;
+ GHashTable *optlist;
HILIGHT_REC *rec;
char *colorarg, *actcolorarg, *levelarg, *priorityarg, *chanarg, *text, *servertag;
char **channels;
@@ -558,7 +597,7 @@ static void cmd_hilight(const char *data)
}
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "hilight", &optlist, &text))
+ PARAM_FLAG_GETREST, "hilight", &optlist, &text))
return;
chanarg = g_hash_table_lookup(optlist, "channels");
@@ -578,7 +617,7 @@ static void cmd_hilight(const char *data)
rec = g_new0(HILIGHT_REC, 1);
/* default to nick/word hilighting */
- rec->nick = TRUE;
+ rec->nick = TRUE;
rec->word = TRUE;
rec->text = g_strdup(text);
@@ -598,15 +637,16 @@ static void cmd_hilight(const char *data)
if (g_hash_table_lookup(optlist, "word") != NULL) {
rec->word = TRUE;
- rec->nick = FALSE;
+ rec->nick = FALSE;
}
if (g_hash_table_lookup(optlist, "nick") != NULL)
- rec->nick = TRUE;
+ rec->nick = TRUE;
rec->nickmask = g_hash_table_lookup(optlist, "mask") != NULL;
rec->fullword = g_hash_table_lookup(optlist, "full") != NULL;
rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL;
+ rec->case_sensitive = g_hash_table_lookup(optlist, "matchcase") != NULL;
if (colorarg != NULL) {
g_free_and_null(rec->color);
@@ -627,7 +667,7 @@ static void cmd_hilight(const char *data)
hilight_create(rec);
hilight_print(g_slist_index(hilights, rec)+1, rec);
- cmd_params_free(free_arg);
+ cmd_params_free(free_arg);
reset_cache();
}
@@ -645,7 +685,7 @@ static void cmd_dehilight(const char *data)
} else {
/* with mask */
char *chans[2] = { "*", NULL };
- rec = hilight_find(data, chans);
+ rec = hilight_find(data, chans);
}
if (rec == NULL)
@@ -653,20 +693,20 @@ static void cmd_dehilight(const char *data)
else {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_HILIGHT_REMOVED, rec->text);
hilight_remove(rec);
- reset_cache();
+ reset_cache();
}
}
static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
- NICK_REC *nick)
+ NICK_REC *nick)
{
GSList *tmp;
HILIGHT_REC *match;
- char *nickmask;
+ char *nickmask;
int len, best_match;
if (nick->host == NULL)
- return; /* don't check until host is known */
+ return; /* don't check until host is known */
nickmask = g_strconcat(nick->nick, "!", nick->host, NULL);
@@ -675,8 +715,8 @@ static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
HILIGHT_REC *rec = tmp->data;
if (rec->nickmask &&
- hilight_match_channel(rec, channel->name) &&
- match_wildcards(rec->text, nickmask)) {
+ hilight_match_channel(rec, channel->name) &&
+ match_wildcards(rec->text, nickmask)) {
len = strlen(rec->text);
if (best_match < len) {
best_match = len;
@@ -687,7 +727,7 @@ static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
g_free_not_null(nickmask);
if (match != NULL)
- g_hash_table_insert(list, nick, match);
+ g_hash_table_insert(list, nick, match);
}
static void read_settings(void)
@@ -702,28 +742,28 @@ void hilight_text_init(void)
settings_add_str("lookandfeel", "hilight_act_color", "%M");
settings_add_level("lookandfeel", "hilight_level", "PUBLIC DCCMSGS");
- read_settings();
+ read_settings();
nickmatch = nickmatch_init(hilight_nick_cache);
read_hilight_config();
signal_add_first("print text", (SIGNAL_FUNC) sig_print_text);
- signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config);
- signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+ signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config);
+ signal_add("setup changed", (SIGNAL_FUNC) read_settings);
command_bind("hilight", NULL, (SIGNAL_FUNC) cmd_hilight);
command_bind("dehilight", NULL, (SIGNAL_FUNC) cmd_dehilight);
- command_set_options("hilight", "-color -actcolor -level -priority -network -channels nick word line mask full regexp");
+ command_set_options("hilight", "-color -actcolor -level -priority -network -channels nick word line mask full regexp matchcase");
}
void hilight_text_deinit(void)
{
hilights_destroy_all();
- nickmatch_deinit(nickmatch);
+ nickmatch_deinit(nickmatch);
signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
- signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config);
- signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+ signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config);
+ signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
command_unbind("hilight", (SIGNAL_FUNC) cmd_hilight);
command_unbind("dehilight", (SIGNAL_FUNC) cmd_dehilight);
diff --git a/src/fe-common/core/hilight-text.h b/src/fe-common/core/hilight-text.h
index d54ec4b5..76beec1f 100644
--- a/src/fe-common/core/hilight-text.h
+++ b/src/fe-common/core/hilight-text.h
@@ -1,14 +1,12 @@
#ifndef __HILIGHT_TEXT_H
#define __HILIGHT_TEXT_H
-#ifdef HAVE_REGEX_H
+#ifndef USE_GREGEX
# include <regex.h>
#endif
#include "formats.h"
-typedef struct _HILIGHT_REC HILIGHT_REC;
-
struct _HILIGHT_REC {
char *text;
@@ -25,7 +23,10 @@ struct _HILIGHT_REC {
unsigned int nickmask:1; /* `text' is a nick mask */
unsigned int fullword:1; /* match `text' only for full words */
unsigned int regexp:1; /* `text' is a regular expression */
-#ifdef HAVE_REGEX_H
+ unsigned int case_sensitive:1;/* `text' must match case */
+#ifdef USE_GREGEX
+ GRegex *preg;
+#else
unsigned int regexp_compiled:1; /* should always be TRUE, unless regexp is invalid */
regex_t preg;
#endif
@@ -42,7 +43,7 @@ HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel,
HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel,
const char *nick, const char *address,
int level, const char *msg);
-
+
char *hilight_get_color(HILIGHT_REC *rec);
void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec);
diff --git a/src/fe-common/core/keyboard.c b/src/fe-common/core/keyboard.c
index 00454401..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;
@@ -118,7 +120,7 @@ static CONFIG_NODE *key_config_find(const char *key)
for (; tmp != NULL; tmp = config_node_next(tmp)) {
node = tmp->data;
- if (strcmp(config_node_get_str(node, "key", ""), key) == 0)
+ if (g_strcmp0(config_node_get_str(node, "key", ""), key) == 0)
return node;
}
@@ -135,7 +137,7 @@ static void keyconfig_save(const char *id, const char *key, const char *data)
node = key_config_find(key);
if (node == NULL) {
node = iconfig_node_traverse("(keyboard", TRUE);
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
}
iconfig_node_set_str(node, "key", key);
@@ -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);
@@ -211,7 +217,7 @@ static int expand_combo(const char *start, const char *end, GSList **out)
for (tmp = info->keys; tmp != NULL; tmp = tmp->next) {
KEY_REC *rec = tmp->data;
- if (strcmp(rec->data, str) == 0)
+ if (g_strcmp0(rec->data, str) == 0)
list = g_slist_append(list, rec);
}
@@ -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, &copy)) {
+ if (!expand_key(rec->key, &copy, 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;
@@ -292,12 +310,18 @@ static int expand_key(const char *key, GSList **out)
}
last_hyphen = !last_hyphen;
} else if (*key == '^') {
+ expand_out_char(*out, '^');
+
/* ctrl-code */
- if (key[1] != '\0')
+ if (key[1] != '\0' && key[1] != '-') {
key++;
+ expand_out_char(*out, *key);
+ }
+ else {
+ /* escaped syntax for ^, see gui-readline.c */
+ expand_out_char(*out, '-');
+ }
- expand_out_char(*out, '^');
- expand_out_char(*out, *key);
expand_out_char(*out, '-');
last_hyphen = FALSE; /* optional */
} else if (last_hyphen && i_isalpha(*key)) {
@@ -326,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;
@@ -340,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 (strcmp(rec->info->id, "key") == 0)
+ 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;
@@ -377,7 +402,7 @@ static void key_states_rescan(void)
g_tree_foreach(key_states, (GTraverseFunc) key_state_destroy,
NULL);
g_tree_destroy(key_states);
- key_states = g_tree_new((GCompareFunc) strcmp);
+ key_states = g_tree_new((GCompareFunc) g_strcmp0);
temp = g_string_new(NULL);
g_hash_table_foreach(keys, (GHFunc) key_states_scan_key, temp);
@@ -854,7 +879,7 @@ void keyboard_init(void)
default_keys = g_hash_table_new((GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal);
keyinfos = NULL;
- key_states = g_tree_new((GCompareFunc) strcmp);
+ key_states = g_tree_new((GCompareFunc) g_strcmp0);
key_config_frozen = 0;
memset(used_keys, 0, sizeof(used_keys));
diff --git a/src/fe-common/core/module-formats.c b/src/fe-common/core/module-formats.c
index db11fae1..da9705be 100644
--- a/src/fe-common/core/module-formats.c
+++ b/src/fe-common/core/module-formats.c
@@ -42,8 +42,8 @@ FORMAT_REC fecommon_core_formats[] = {
{ "window_set_immortal", "Window is now immortal", 0 },
{ "window_unset_immortal", "Window isn't immortal anymore", 0 },
{ "window_immortal_error", "Window is immortal, if you really want to close it, say /WINDOW IMMORTAL OFF", 0 },
- { "windowlist_header", "%#Ref Name Active item Server Level", 0 },
- { "windowlist_line", "%#$[3]0 %|$[20]1 $[15]2 $[15]3 $4", 5, { 1, 0, 0, 0, 0 } },
+ { "windowlist_header", "%#Ref Name Active item Server Level", 0 },
+ { "windowlist_line", "%#$[4]0 %|$[20]1 $[15]2 $[15]3 $4", 5, { 1, 0, 0, 0, 0 } },
{ "windowlist_footer", "", 0 },
{ "windows_layout_saved", "Layout of windows is now remembered", 0 },
{ "windows_layout_reset", "Layout of windows reset to defaults", 0 },
@@ -196,6 +196,7 @@ FORMAT_REC fecommon_core_formats[] = {
{ "module_already_loaded", "Module {hilight $0/$1} already loaded", 2, { 0, 0 } },
{ "module_not_loaded", "Module {hilight $0/$1} is not loaded", 2, { 0, 0 } },
{ "module_load_error", "Error loading module {hilight $0/$1}: $2", 3, { 0, 0, 0 } },
+ { "module_version_mismatch", "{hilight $0/$1} is ABI version $2 but Irssi is version $abiversion, cannot load", 3, { 0, 0, 0 } },
{ "module_invalid", "{hilight $0/$1} isn't Irssi module", 2, { 0, 0 } },
{ "module_loaded", "Loaded module {hilight $0/$1}", 2, { 0, 0 } },
{ "module_unloaded", "Unloaded module {hilight $0/$1}", 2, { 0, 0 } },
@@ -220,8 +221,10 @@ FORMAT_REC fecommon_core_formats[] = {
{ "invalid_level", "Invalid message level", 0 },
{ "invalid_size", "Invalid size", 0 },
{ "invalid_charset", "Invalid charset: $0", 1, { 0 } },
+ { "invalid_choice", "Invalid choice, must be one of $0", 1, { 0 } },
{ "eval_max_recurse", "/eval hit maximum recursion limit", 0 },
{ "program_not_found", "Could not find file or file is not executable", 0 },
+ { "no_server_defined", "No servers defined for this network, see /help server for how to add one", 0 },
/* ---- */
{ NULL, "Themes", 0 },
@@ -288,5 +291,18 @@ FORMAT_REC fecommon_core_formats[] = {
{ "completion_line", "%#$[10]0 $[!40]1 $2", 3, { 0, 0, 0 } },
{ "completion_footer", "", 0 },
+ /* ---- */
+ { NULL, "TLS", 0 },
+
+ { "tls_ephemeral_key", "EDH Key: {hilight $0} bit {hilight $1}", 2, { 1, 0 } },
+ { "tls_ephemeral_key_unavailable", "EDH Key: {error N/A}", 0 },
+ { "tls_pubkey", "Public Key: {hilight $0} bit {hilight $1}, valid from {hilight $2} to {hilight $3}", 4, { 1, 0, 0, 0 } },
+ { "tls_cert_header", "Certificate Chain:", 0 },
+ { "tls_cert_subject", " Subject: {hilight $0}", 1, { 0 } },
+ { "tls_cert_issuer", " Issuer: {hilight $0}", 1, { 0 } },
+ { "tls_pubkey_fingerprint", "Public Key Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } },
+ { "tls_cert_fingerprint", "Certificate Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } },
+ { "tls_protocol_version", "Protocol: {hilight $0} ({hilight $1} bit, {hilight $2})", 3, { 0, 1, 0 } },
+
{ NULL, NULL, 0 }
};
diff --git a/src/fe-common/core/module-formats.h b/src/fe-common/core/module-formats.h
index 18bf91f5..a9ed28c5 100644
--- a/src/fe-common/core/module-formats.h
+++ b/src/fe-common/core/module-formats.h
@@ -166,6 +166,7 @@ enum {
TXT_MODULE_ALREADY_LOADED,
TXT_MODULE_NOT_LOADED,
TXT_MODULE_LOAD_ERROR,
+ TXT_MODULE_VERSION_MISMATCH,
TXT_MODULE_INVALID,
TXT_MODULE_LOADED,
TXT_MODULE_UNLOADED,
@@ -189,8 +190,10 @@ enum {
TXT_INVALID_LEVEL,
TXT_INVALID_SIZE,
TXT_INVALID_CHARSET,
+ TXT_INVALID_CHOICE,
TXT_EVAL_MAX_RECURSE,
TXT_PROGRAM_NOT_FOUND,
+ TXT_NO_SERVER_DEFINED,
TXT_FILL_11,
@@ -251,7 +254,19 @@ enum {
TXT_COMPLETION_REMOVED,
TXT_COMPLETION_HEADER,
TXT_COMPLETION_LINE,
- TXT_COMPLETION_FOOTER
+ TXT_COMPLETION_FOOTER,
+
+ TLS_FILL_15,
+
+ TXT_TLS_EPHEMERAL_KEY,
+ TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE,
+ TXT_TLS_PUBKEY,
+ TXT_TLS_CERT_HEADER,
+ TXT_TLS_CERT_SUBJECT,
+ TXT_TLS_CERT_ISSUER,
+ TXT_TLS_PUBKEY_FINGERPRINT,
+ TXT_TLS_CERT_FINGERPRINT,
+ TXT_TLS_PROTOCOL_VERSION
};
extern FORMAT_REC fecommon_core_formats[];
diff --git a/src/fe-common/core/module.h b/src/fe-common/core/module.h
index 51b61b3e..db712ec7 100644
--- a/src/fe-common/core/module.h
+++ b/src/fe-common/core/module.h
@@ -2,7 +2,7 @@
#define MODULE_NAME "fe-common/core"
-typedef guint32 unichar;
+#include "utf8.h"
typedef struct {
time_t time;
char *nick;
diff --git a/src/fe-common/core/printtext.c b/src/fe-common/core/printtext.c
index a5eaa38f..01ef2dcd 100644
--- a/src/fe-common/core/printtext.c
+++ b/src/fe-common/core/printtext.c
@@ -170,13 +170,13 @@ static void print_line(TEXT_DEST_REC *dest, const char *text)
g_return_if_fail(dest != NULL);
g_return_if_fail(text != NULL);
-
+
theme = window_get_theme(dest->window);
tmp = format_get_level_tag(theme, dest);
str = !theme->info_eol ? format_add_linestart(text, tmp) :
format_add_lineend(text, tmp);
g_free_not_null(tmp);
-
+
/* send both the formatted + stripped (for logging etc.) */
stripped = strip_codes(str);
signal_emit_id(signal_print_text, 3, dest, str, stripped);
@@ -413,6 +413,18 @@ void printtext_gui(const char *text)
g_free(str);
}
+/* Like printtext_gui(), but don't expand % codes. */
+void printtext_gui_internal(const char *str)
+{
+ TEXT_DEST_REC dest;
+
+ g_return_if_fail(str != NULL);
+
+ memset(&dest, 0, sizeof(dest));
+
+ format_send_to_gui(&dest, str);
+}
+
static void msg_beep_check(TEXT_DEST_REC *dest)
{
if (dest->level != 0 && (dest->level & MSGLEVEL_NO_ACT) == 0 &&
@@ -434,7 +446,9 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text)
if (dest->window == NULL) {
str = strip_codes(text);
+#ifndef SUPPRESS_PRINTF_FALLBACK
printf("NO WINDOWS: %s\n", str);
+#endif
g_free(str);
return;
}
diff --git a/src/fe-common/core/printtext.h b/src/fe-common/core/printtext.h
index 66ba28f4..ab8dfac1 100644
--- a/src/fe-common/core/printtext.h
+++ b/src/fe-common/core/printtext.h
@@ -23,6 +23,7 @@ void printtext_dest(TEXT_DEST_REC *dest, const char *text, ...);
/* only GUI should call these - used for printing text to somewhere else
than windows */
void printtext_gui(const char *text);
+void printtext_gui_internal(const char *str);
void printformat_module_gui(const char *module, int formatnum, ...);
void printformat_module_gui_args(const char *module, int formatnum, va_list va);
diff --git a/src/fe-common/core/themes.c b/src/fe-common/core/themes.c
index d92d23fe..2b1459be 100644
--- a/src/fe-common/core/themes.c
+++ b/src/fe-common/core/themes.c
@@ -365,9 +365,9 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
GString *str;
char *ret;
theme_rm_col dummy, reset;
+ int braces = 1; /* we start with one brace opened */
dummy.m[0] = '\0';
strcpy(reset.m, "n");
- int braces = 1; /* we start with one brace opened */
str = g_string_new(NULL);
while (**format != '\0' && braces != 0) {
@@ -385,7 +385,7 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
&dummy, &dummy, 0);
continue;
}
-
+
if (braces == 0) {
(*format)++;
break;
@@ -739,7 +739,7 @@ static void theme_read_formats(THEME_REC *theme, const char *module,
node = config_node_traverse(config, "formats", FALSE);
if (node == NULL) return;
- node = config_node_section(node, module, -1);
+ node = config_node_section(config, node, module, -1);
if (node == NULL) return;
for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
@@ -895,7 +895,7 @@ THEME_REC *theme_load(const char *setname)
name = g_strdup(setname);
p = strrchr(name, '.');
- if (p != NULL && strcmp(p, ".theme") == 0) {
+ if (p != NULL && g_strcmp0(p, ".theme") == 0) {
/* remove the trailing .theme */
*p = '\0';
}
@@ -1177,7 +1177,7 @@ static void module_save(const char *module, MODULE_THEME_REC *rec,
fnode = config_node_traverse(data->config, "formats", TRUE);
- node = config_node_section(fnode, rec->name, NODE_TYPE_BLOCK);
+ node = config_node_section(data->config, fnode, rec->name, NODE_TYPE_BLOCK);
for (n = 1; formats[n].def != NULL; n++) {
if (rec->formats[n] != NULL) {
config_node_set_str(data->config, node, formats[n].tag,
@@ -1358,9 +1358,9 @@ static void read_settings(void)
theme = settings_get_str("theme");
len = strlen(current_theme->name);
- if (strcmp(current_theme->name, theme) != 0 &&
+ if (g_strcmp0(current_theme->name, theme) != 0 &&
(strncmp(current_theme->name, theme, len) != 0 ||
- strcmp(theme+len, ".theme") != 0))
+ g_strcmp0(theme+len, ".theme") != 0))
change_theme(theme, TRUE);
}
diff --git a/src/fe-common/core/utf8.c b/src/fe-common/core/utf8.c
deleted file mode 100644
index 2d07ea8e..00000000
--- a/src/fe-common/core/utf8.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/* utf8.c - Operations on UTF-8 strings.
- *
- * Copyright (C) 2002 Timo Sirainen
- *
- * Based on GLib code by
- *
- * Copyright (C) 1999 Tom Tromey
- * Copyright (C) 2000 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser 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"
-
diff --git a/src/fe-common/core/utf8.h b/src/fe-common/core/utf8.h
deleted file mode 100644
index 3c15dc7d..00000000
--- a/src/fe-common/core/utf8.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef __UTF8_H
-#define __UTF8_H
-
-/* XXX I didn't check the encoding range of big5+. This is standard big5. */
-#define is_big5_los(lo) (0x40 <= (lo) && (lo) <= 0x7E) /* standard */
-#define is_big5_lox(lo) (0x80 <= (lo) && (lo) <= 0xFE) /* extended */
-#define is_big5_lo(lo) ((is_big5_los(lo) || is_big5_lox(lo)))
-#define is_big5_hi(hi) (0x81 <= (hi) && (hi) <= 0xFE)
-#define is_big5(hi,lo) (is_big5_hi(hi) && is_big5_lo(lo))
-
-/* Returns width for character (0-2). */
-int mk_wcwidth(unichar c);
-
-#define unichar_isprint(c) (((c) & ~0x80) >= 32)
-#define is_utf8_leading(c) (((c) & 0xc0) != 0x80)
-
-#endif
diff --git a/src/fe-common/core/window-commands.c b/src/fe-common/core/window-commands.c
index a975fe5c..9e4aab3a 100644
--- a/src/fe-common/core/window-commands.c
+++ b/src/fe-common/core/window-commands.c
@@ -33,6 +33,7 @@
#include "window-items.h"
#include "windows-layout.h"
#include "printtext.h"
+#include "command-history.h"
static void window_print_binds(WINDOW_REC *win)
{
@@ -127,7 +128,7 @@ static void cmd_window_info(WINDOW_REC *win)
win->active_server->tag : "NONE");
} else {
if (win->active_server != NULL &&
- strcmp(win->active_server->tag, win->servertag) != 0)
+ g_strcmp0(win->active_server->tag, win->servertag) != 0)
g_warning("Active server isn't the sticky server!");
printformat_window(win, MSGLEVEL_CLIENTCRAP,
@@ -529,7 +530,7 @@ static void cmd_window_item_goto(const char *data, SERVER_REC *server)
GSList *tmp;
void *free_arg;
char *target;
-
+
if (!cmd_get_params(data, &free_arg, 1, &target))
return;
@@ -609,16 +610,31 @@ static void cmd_window_name(const char *data)
if (win == NULL || win == active_win)
window_set_name(active_win, data);
else if (active_win->name == NULL ||
- strcmp(active_win->name, data) != 0) {
+ g_strcmp0(active_win->name, data) != 0) {
printformat_window(active_win, MSGLEVEL_CLIENTERROR,
TXT_WINDOW_NAME_NOT_UNIQUE, data);
}
}
-/* SYNTAX: WINDOW HISTORY <name> */
+/* SYNTAX: WINDOW HISTORY [-clear] <name> */
void cmd_window_history(const char *data)
{
- window_set_history(active_win, data);
+ GHashTable *optlist;
+ char *name;
+ void *free_arg;
+
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | PARAM_FLAG_STRIP_TRAILING_WS,
+ "window history", &optlist, &name))
+ return;
+
+ if (g_hash_table_lookup(optlist, "clear") != NULL) {
+ signal_continue(1, data);
+ window_clear_history(active_win, name);
+ } else {
+ window_set_history(active_win, name);
+ }
+
+ cmd_params_free(free_arg);
}
/* we're moving the first window to last - move the first contiguous block
@@ -883,6 +899,7 @@ void window_commands_init(void)
command_set_options("window number", "sticky");
command_set_options("window server", "sticky unsticky");
command_set_options("window theme", "delete");
+ command_set_options("window history", "clear");
}
void window_commands_deinit(void)
diff --git a/src/fe-common/core/windows-layout.c b/src/fe-common/core/windows-layout.c
index 65741a73..ce6b9910 100644
--- a/src/fe-common/core/windows-layout.c
+++ b/src/fe-common/core/windows-layout.c
@@ -68,7 +68,7 @@ static void sig_layout_restore_item(WINDOW_REC *window, const char *type,
(SIGNAL_FUNC) signal_query_created_curwin);
restore_win = window;
-
+
protocol = chat_protocol_find(chat_type);
if (protocol == NULL)
window_bind_add(window, tag, name);
@@ -127,6 +127,7 @@ static void sig_layout_restore(void)
for (; tmp != NULL; tmp = config_node_next(tmp)) {
CONFIG_NODE *node = tmp->data;
+ if (node->key == NULL) continue;
window = window_find_refnum(atoi(node->key));
if (window == NULL)
window = window_create(NULL, TRUE);
@@ -143,7 +144,7 @@ static void sig_layout_restore(void)
if (window->theme_name != NULL)
window->theme = theme_load(window->theme_name);
- window_add_items(window, config_node_section(node, "items", -1));
+ window_add_items(window, iconfig_node_section(node, "items", -1));
signal_emit("layout restore window", 2, window, node);
}
}
@@ -160,19 +161,19 @@ static void sig_layout_save_item(WINDOW_REC *window, WI_ITEM_REC *item,
if (type == NULL)
return;
- subnode = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ subnode = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
iconfig_node_set_str(subnode, "type", type);
proto = item->chat_type == 0 ? NULL :
chat_protocol_find_id(item->chat_type);
if (proto != NULL)
iconfig_node_set_str(subnode, "chat_type", proto->name);
- iconfig_node_set_str(subnode, "name", item->visible_name);
+ iconfig_node_set_str(subnode, "name", item->name);
if (item->server != NULL) {
iconfig_node_set_str(subnode, "tag", item->server->tag);
if (IS_CHANNEL(item)) {
- rec = window_bind_add(window, item->server->tag, item->visible_name);
+ rec = window_bind_add(window, item->server->tag, item->name);
if (rec != NULL)
rec->sticky = TRUE;
}
@@ -185,7 +186,7 @@ static void window_save_items(WINDOW_REC *window, CONFIG_NODE *node)
{
GSList *tmp;
- node = config_node_section(node, "items", NODE_TYPE_LIST);
+ node = iconfig_node_section(node, "items", NODE_TYPE_LIST);
for (tmp = window->items; tmp != NULL; tmp = tmp->next)
signal_emit("layout save item", 3, window, tmp->data, node);
}
@@ -195,7 +196,7 @@ static void window_save(WINDOW_REC *window, CONFIG_NODE *node)
char refnum[MAX_INT_STRLEN];
ltoa(refnum, window->refnum);
- node = config_node_section(node, refnum, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, refnum, NODE_TYPE_BLOCK);
if (window->sticky_refnum)
iconfig_node_set_bool(node, "sticky_refnum", TRUE);
diff --git a/src/fe-common/irc/Makefile.am b/src/fe-common/irc/Makefile.am
index 463f145c..bf88f5cd 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
@@ -36,6 +37,7 @@ libfe_common_irc_a_SOURCES = \
pkginc_fe_common_ircdir=$(pkgincludedir)/src/fe-common/irc
pkginc_fe_common_irc_HEADERS = \
fe-irc-server.h \
+ fe-irc-channels.h \
module.h \
module-formats.h
diff --git a/src/fe-common/irc/dcc/fe-dcc-chat-messages.c b/src/fe-common/irc/dcc/fe-dcc-chat-messages.c
index 93e10943..45791f56 100644
--- a/src/fe-common/irc/dcc/fe-dcc-chat-messages.c
+++ b/src/fe-common/irc/dcc/fe-dcc-chat-messages.c
@@ -60,7 +60,7 @@ static void sig_message_dcc_own_action(CHAT_DCC_REC *dcc, const char *msg)
format_create_dest_tag(&dest, dcc->server, dcc->servertag, tag,
MSGLEVEL_DCCMSGS | MSGLEVEL_ACTIONS |
MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT, NULL);
-
+
printformat_dest(&dest, query != NULL ? IRCTXT_OWN_DCC_ACTION_QUERY :
IRCTXT_OWN_DCC_ACTION, dcc->mynick, dcc->id, msg);
g_free(tag);
diff --git a/src/fe-common/irc/dcc/fe-dcc-chat.c b/src/fe-common/irc/dcc/fe-dcc-chat.c
index 101aeb31..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);
}
@@ -243,7 +243,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
return;
/* handle only DCC messages */
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
dcc = item_get_dcc(item);
else if (*target == '=')
dcc = dcc_chat_find_id(target+1);
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 d1cd12d4..3eb124fc 100644
--- a/src/fe-common/irc/fe-events-numeric.c
+++ b/src/fe-common/irc/fe-events-numeric.c
@@ -118,7 +118,7 @@ static void event_who(IRC_SERVER_REC *server, const char *data)
while (*realname != '\0' && *realname != ' ') realname++;
if (*realname == ' ')
*realname++ = '\0';
-
+
recoded = recode_in(SERVER(server), realname, nick);
printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_WHO,
channel, nick, stat, hops, user, host, recoded, serv);
@@ -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);
@@ -427,7 +427,7 @@ static void event_no_such_nick(IRC_SERVER_REC *server, const char *data,
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &unick);
- if (!strcmp(unick, "*"))
+ if (!g_strcmp0(unick, "*"))
/* more information will be in the description,
* e.g. * :Target left IRC. Failed to deliver: [hi] */
print_event_received(server, data, nick, FALSE);
@@ -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;
@@ -605,7 +605,7 @@ static void print_event_received(IRC_SERVER_REC *server, const char *data,
recoded = recode_in(SERVER(server), args, NULL);
format = nick == NULL || server->real_address == NULL ||
- strcmp(nick, server->real_address) == 0 ?
+ g_strcmp0(nick, server->real_address) == 0 ?
IRCTXT_DEFAULT_EVENT : IRCTXT_DEFAULT_EVENT_SERVER;
printformat(server, target, MSGLEVEL_CRAP, format,
nick, recoded, current_server_event);
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..0ec30003 100644
--- a/src/fe-common/irc/fe-irc-channels.c
+++ b/src/fe-common/irc/fe-irc-channels.c
@@ -31,6 +31,48 @@
#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 */
+ 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 +88,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-commands.c b/src/fe-common/irc/fe-irc-commands.c
index 765b5340..11a911d2 100644
--- a/src/fe-common/irc/fe-irc-commands.c
+++ b/src/fe-common/irc/fe-irc-commands.c
@@ -110,14 +110,14 @@ static void cmd_notice(const char *data, IRC_SERVER_REC *server,
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
&target, &msg))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? "" : window_item_get_target(item);
if (*target == '\0' || *msg == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
-
+
signal_emit("message irc own_notice", 3, server, msg, target);
-
+
cmd_params_free(free_arg);
}
@@ -133,7 +133,7 @@ static void cmd_ctcp(const char *data, IRC_SERVER_REC *server,
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
&target, &ctcpcmd, &ctcpdata))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? "" : window_item_get_target(item);
if (*target == '\0' || *ctcpcmd == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -162,7 +162,7 @@ static void cmd_nctcp(const char *data, IRC_SERVER_REC *server,
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
&target, &text))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? "" : window_item_get_target(item);
if (*target == '\0' || *text == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -246,8 +246,8 @@ static void cmd_ban(const char *data, IRC_SERVER_REC *server,
CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 2 |
- PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN |
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
item, &channel, &nicks))
return;
@@ -262,7 +262,7 @@ static void cmd_ban(const char *data, IRC_SERVER_REC *server,
if (chanrec == NULL && *channel == '\0')
cmd_param_error(CMDERR_NOT_JOINED);
- if (*channel != '\0' && strcmp(channel, "*") != 0)
+ if (*channel != '\0' && g_strcmp0(channel, "*") != 0)
chanrec = irc_channel_find(server, channel);
if (chanrec == NULL || !chanrec->synced) {
diff --git a/src/fe-common/irc/fe-irc-messages.c b/src/fe-common/irc/fe-irc-messages.c
index a8d52745..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 */
@@ -87,7 +63,7 @@ static void sig_message_own_public(SERVER_REC *server, const char *msg,
g_free(nickmode);
signal_stop();
}
-
+
}
/* received msg to all ops 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);
@@ -160,12 +147,13 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
const char *oldtarget;
char *freemsg = NULL;
int level;
+ 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;
@@ -174,15 +162,17 @@ 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
- item = privmsg_get_query(SERVER(server), nick, FALSE, level);
+ } else {
+ own = (!g_strcmp0(nick, server->nick));
+ item = privmsg_get_query(SERVER(server), own ? target : nick, FALSE, level);
+ }
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 */
@@ -195,20 +185,28 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
nick, oldtarget, msg);
}
} else {
- /* private action */
- printformat(server, nick, MSGLEVEL_ACTIONS | MSGLEVEL_MSGS,
- item == NULL ? IRCTXT_ACTION_PRIVATE :
- IRCTXT_ACTION_PRIVATE_QUERY,
- nick, address == NULL ? "" : address, msg);
+ if (own) {
+ /* own action bounced */
+ printformat(server, target,
+ MSGLEVEL_ACTIONS | MSGLEVEL_MSGS,
+ item != NULL && oldtarget == target ? IRCTXT_OWN_ACTION : IRCTXT_OWN_ACTION_TARGET,
+ server->nick, msg, oldtarget);
+ } else {
+ /* private action */
+ printformat(server, nick, MSGLEVEL_ACTIONS | MSGLEVEL_MSGS,
+ item == NULL ? IRCTXT_ACTION_PRIVATE :
+ IRCTXT_ACTION_PRIVATE_QUERY,
+ nick, address == NULL ? "" : address, msg);
+ }
}
-
+
g_free_not_null(freemsg);
}
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);
}
@@ -219,9 +217,9 @@ static void sig_message_irc_notice(SERVER_REC *server, const char *msg,
{
const char *oldtarget;
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 */
@@ -234,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);
@@ -259,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);
}
@@ -271,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..c928a94a 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;
@@ -78,6 +78,13 @@ static void event_privmsg(SERVER_REC *server, const char *data,
if (!server_has_nick(server, query->name))
query_change_nick(query, nick);
}
+ } else {
+ /* process the changes to the query structure now, before the
+ * privmsg is dispatched. */
+ if (g_strcmp0(query->name, nick) != 0)
+ query_change_nick(query, nick);
+ if (address != NULL && g_strcmp0(query->address, address) != 0)
+ query_change_address(query, address);
}
}
diff --git a/src/fe-common/irc/fe-irc-server.c b/src/fe-common/irc/fe-irc-server.c
index fbfe4d9d..c4435d8f 100644
--- a/src/fe-common/irc/fe-irc-server.c
+++ b/src/fe-common/irc/fe-irc-server.c
@@ -50,11 +50,13 @@ const char *get_visible_target(IRC_SERVER_REC *server, const char *target)
return target;
}
-/* SYNTAX: SERVER ADD [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>]
- [-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
- [-auto | -noauto] [-network <network>] [-host <hostname>]
- [-cmdspeed <ms>] [-cmdmax <count>] [-port <port>]
- <address> [<port> [<password>]] */
+
+/* SYNTAX: SERVER ADD|MODIFY [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>]
+ [-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
+ [-ssl_ciphers <list>]
+ [-auto | -noauto] [-network <network>] [-host <hostname>]
+ [-cmdspeed <ms>] [-cmdmax <count>] [-port <port>]
+ <address> [<port> [<password>]] */
/* NOTE: -network replaces the old -ircnet flag. */
static void sig_server_add_fill(IRC_SERVER_SETUP_REC *rec,
GHashTable *optlist)
@@ -63,7 +65,7 @@ static void sig_server_add_fill(IRC_SERVER_SETUP_REC *rec,
char *value;
value = g_hash_table_lookup(optlist, "network");
- /* For backwards compatibility, also allow the old name 'ircnet'.
+ /* For backwards compatibility, also allow the old name 'ircnet'.
But of course only if -network was not given. */
if (!value)
value = g_hash_table_lookup(optlist, "ircnet");
@@ -106,22 +108,28 @@ static void cmd_server_list(const char *data)
g_string_append(str, "autoconnect, ");
if (rec->no_proxy)
g_string_append(str, "noproxy, ");
- if (rec->use_ssl) {
- g_string_append(str, "ssl, ");
- if (rec->ssl_cert) {
- g_string_append_printf(str, "ssl_cert: %s, ", rec->ssl_cert);
- if (rec->ssl_pkey)
- g_string_append_printf(str, "ssl_pkey: %s, ", rec->ssl_pkey);
- if (rec->ssl_pass)
+ if (rec->use_tls) {
+ g_string_append(str, "tls, ");
+ if (rec->tls_cert) {
+ g_string_append_printf(str, "tls_cert: %s, ", rec->tls_cert);
+ if (rec->tls_pkey)
+ g_string_append_printf(str, "tls_pkey: %s, ", rec->tls_pkey);
+ if (rec->tls_pass)
g_string_append_printf(str, "(pass), ");
}
- if (rec->ssl_verify)
- g_string_append(str, "ssl_verify, ");
- if (rec->ssl_cafile)
- g_string_append_printf(str, "ssl_cafile: %s, ", rec->ssl_cafile);
- if (rec->ssl_capath)
- g_string_append_printf(str, "ssl_capath: %s, ", rec->ssl_capath);
-
+ if (rec->tls_verify)
+ g_string_append(str, "tls_verify, ");
+ if (rec->tls_cafile)
+ g_string_append_printf(str, "tls_cafile: %s, ", rec->tls_cafile);
+ if (rec->tls_capath)
+ g_string_append_printf(str, "tls_capath: %s, ", rec->tls_capath);
+ if (rec->tls_ciphers)
+ g_string_append_printf(str, "tls_ciphers: %s, ", rec->tls_ciphers);
+ if (rec->tls_pinned_cert)
+ g_string_append_printf(str, "tls_pinned_cert: %s, ", rec->tls_pinned_cert);
+ if (rec->tls_pinned_pubkey)
+ g_string_append_printf(str, "tls_pinned_pubkey: %s, ", rec->tls_pinned_pubkey);
+
}
if (rec->max_cmds_at_once > 0)
g_string_append_printf(str, "cmdmax: %d, ", rec->max_cmds_at_once);
diff --git a/src/fe-common/irc/fe-ircnet.c b/src/fe-common/irc/fe-ircnet.c
index 6618edd7..b70a9ea7 100644
--- a/src/fe-common/irc/fe-ircnet.c
+++ b/src/fe-common/irc/fe-ircnet.c
@@ -29,6 +29,8 @@
#include "irc-servers.h"
#include "irc-chatnets.h"
#include "printtext.h"
+#include "servers-setup.h"
+#include "channels-setup.h"
static void cmd_network_list(void)
{
@@ -56,7 +58,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)
@@ -81,12 +88,7 @@ static void cmd_network_list(void)
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETWORK_FOOTER);
}
-/* SYNTAX: NETWORK ADD [-nick <nick>] [-user <user>] [-realname <name>]
- [-host <host>] [-usermode <mode>] [-autosendcmd <cmd>]
- [-querychans <count>] [-whois <count>] [-msgs <count>]
- [-kicks <count>] [-modes <count>]
- [-cmdspeed <ms>] [-cmdmax <count>] <name> */
-static void cmd_network_add(const char *data)
+static void cmd_network_add_modify(const char *data, gboolean add)
{
GHashTable *optlist;
char *name, *value;
@@ -94,12 +96,20 @@ static void cmd_network_add(const char *data)
IRC_CHATNET_REC *rec;
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
- "network add", &optlist, &name))
+ "network add", &optlist, &name))
return;
+
if (*name == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
rec = ircnet_find(name);
if (rec == NULL) {
+ if (add == FALSE) {
+ cmd_params_free(free_arg);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+ IRCTXT_NETWORK_NOT_FOUND, name);
+ return;
+ }
+
rec = g_new0(IRC_CHATNET_REC, 1);
rec->name = g_strdup(name);
} else {
@@ -112,6 +122,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,12 +161,37 @@ 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);
cmd_params_free(free_arg);
}
+/* SYNTAX: NETWORK ADD|MODIFY [-nick <nick>] [-user <user>] [-realname <name>]
+ [-host <host>] [-usermode <mode>] [-autosendcmd <cmd>]
+ [-querychans <count>] [-whois <count>] [-msgs <count>]
+ [-kicks <count>] [-modes <count>] [-cmdspeed <ms>]
+ [-cmdmax <count>] [-sasl_mechanism <mechanism>]
+ [-sasl_username <username>] [-sasl_password <password>]
+ <name> */
+static void cmd_network_add(const char *data)
+{
+ cmd_network_add_modify(data, TRUE);
+}
+
+static void cmd_network_modify(const char *data)
+{
+ cmd_network_add_modify(data, FALSE);
+}
+
/* SYNTAX: NETWORK REMOVE <network> */
static void cmd_network_remove(const char *data)
{
@@ -165,6 +203,8 @@ static void cmd_network_remove(const char *data)
if (rec == NULL)
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NETWORK_NOT_FOUND, data);
else {
+ server_setup_remove_chatnet(data);
+ channel_setup_remove_chatnet(data);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NETWORK_REMOVED, data);
chatnet_remove(CHATNET(rec));
}
@@ -184,9 +224,13 @@ void fe_ircnet_init(void)
command_bind("network", NULL, (SIGNAL_FUNC) cmd_network);
command_bind("network list", NULL, (SIGNAL_FUNC) cmd_network_list);
command_bind("network add", NULL, (SIGNAL_FUNC) cmd_network_add);
+ command_bind("network modify", NULL, (SIGNAL_FUNC) cmd_network_modify);
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");
+ command_set_options("network modify", "-kicks -msgs -modes -whois -cmdspeed "
+ "-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
}
void fe_ircnet_deinit(void)
@@ -195,5 +239,6 @@ void fe_ircnet_deinit(void)
command_unbind("network", (SIGNAL_FUNC) cmd_network);
command_unbind("network list", (SIGNAL_FUNC) cmd_network_list);
command_unbind("network add", (SIGNAL_FUNC) cmd_network_add);
+ command_unbind("network modify", (SIGNAL_FUNC) cmd_network_modify);
command_unbind("network remove", (SIGNAL_FUNC) cmd_network_remove);
}
diff --git a/src/fe-common/irc/fe-modes.c b/src/fe-common/irc/fe-modes.c
index 027d7a76..53e56c97 100644
--- a/src/fe-common/irc/fe-modes.c
+++ b/src/fe-common/irc/fe-modes.c
@@ -135,7 +135,7 @@ static void msg_multi_mode(IRC_CHANNEL_REC *channel, const char *sender,
signal_add("print starting", (SIGNAL_FUNC) sig_print_starting);
rec = mode_find_channel(channel);
- if (rec != NULL && strcmp(rec->mode, mode) != 0) {
+ if (rec != NULL && g_strcmp0(rec->mode, mode) != 0) {
/* different mode than last time, show and remove the old */
print_mode(rec);
mode_destroy(rec);
@@ -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..4eb388c0 100644
--- a/src/fe-common/irc/fe-netjoin.c
+++ b/src/fe-common/irc/fe-netjoin.c
@@ -164,11 +164,11 @@ static void print_channel_netjoins(char *channel, TEMP_PRINT_REC *rec,
g_free(channel);
}
-static void print_netjoins(NETJOIN_SERVER_REC *server)
+static void print_netjoins(NETJOIN_SERVER_REC *server, const char *filter_channel)
{
TEMP_PRINT_REC *temp;
GHashTable *channels;
- GSList *tmp, *next, *old;
+ GSList *tmp, *tmp2, *next, *next2, *old;
g_return_if_fail(server != NULL);
@@ -181,11 +181,19 @@ static void print_netjoins(NETJOIN_SERVER_REC *server)
for (tmp = server->netjoins; tmp != NULL; tmp = next) {
NETJOIN_REC *rec = tmp->data;
- next = tmp->next;
- while (rec->now_channels != NULL) {
- char *channel = rec->now_channels->data;
+ next = g_slist_next(tmp);
+
+ for (tmp2 = rec->now_channels; tmp2 != NULL; tmp2 = next2) {
+ char *channel = tmp2->data;
char *realchannel = channel + 1;
+ next2 = g_slist_next(tmp2);
+
+ /* Filter the results by channel if asked to do so */
+ if (filter_channel != NULL &&
+ strcasecmp(realchannel, filter_channel) != 0)
+ continue;
+
temp = g_hash_table_lookup(channels, realchannel);
if (temp == NULL) {
temp = g_new0(TEMP_PRINT_REC, 1);
@@ -214,8 +222,8 @@ static void print_netjoins(NETJOIN_SERVER_REC *server)
g_free(data);
}
- rec->now_channels =
- g_slist_remove(rec->now_channels, channel);
+ /* drop tmp2 from the list */
+ rec->now_channels = g_slist_delete_link(rec->now_channels, tmp2);
g_free(channel);
}
@@ -235,20 +243,25 @@ static void print_netjoins(NETJOIN_SERVER_REC *server)
/* something is going to be printed to screen, print our current netsplit
message before it. */
-static void sig_print_starting(void)
+static void sig_print_starting(TEXT_DEST_REC *dest)
{
- GSList *tmp, *next;
+ NETJOIN_SERVER_REC *rec;
if (printing_joins)
return;
- for (tmp = joinservers; tmp != NULL; tmp = next) {
- NETJOIN_SERVER_REC *server = tmp->data;
+ if (!IS_IRC_SERVER(dest->server))
+ return;
- next = tmp->next;
- if (server->netjoins != NULL)
- print_netjoins(server);
- }
+ if (!(dest->level & MSGLEVEL_PUBLIC))
+ return;
+
+ if (!server_ischannel(dest->server, dest->target))
+ return;
+
+ rec = netjoin_find_server(IRC_SERVER(dest->server));
+ if (rec != NULL && rec->netjoins != NULL)
+ print_netjoins(rec, dest->target);
}
static int sig_check_netjoins(void)
@@ -272,7 +285,7 @@ static int sig_check_netjoins(void)
}
if (server->netjoins != NULL)
- print_netjoins(server);
+ print_netjoins(server, NULL);
}
/* now remove all netjoins which haven't had any new joins
@@ -400,7 +413,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-netsplit.c b/src/fe-common/irc/fe-netsplit.c
index 3eb30796..17fb1994 100644
--- a/src/fe-common/irc/fe-netsplit.c
+++ b/src/fe-common/irc/fe-netsplit.c
@@ -142,7 +142,7 @@ static void get_server_splits(void *key, NETSPLIT_REC *split,
}
}
-static void print_server_splits(IRC_SERVER_REC *server, TEMP_SPLIT_REC *rec)
+static void print_server_splits(IRC_SERVER_REC *server, TEMP_SPLIT_REC *rec, const char *filter_channel)
{
GString *destservers;
char *sourceserver;
@@ -168,6 +168,10 @@ static void print_server_splits(IRC_SERVER_REC *server, TEMP_SPLIT_REC *rec)
for (tmp = rec->channels; tmp != NULL; tmp = tmp->next) {
TEMP_SPLIT_CHAN_REC *chan = tmp->data;
+ if (filter_channel != NULL &&
+ strcasecmp(chan->name, filter_channel) != 0)
+ continue;
+
g_string_truncate(chan->nicks, chan->nicks->len-2);
if (netsplit_max_nicks > 0 &&
@@ -193,7 +197,7 @@ static void temp_split_chan_free(TEMP_SPLIT_CHAN_REC *rec)
g_free(rec);
}
-static void print_splits(IRC_SERVER_REC *server)
+static void print_splits(IRC_SERVER_REC *server, const char *channel)
{
TEMP_SPLIT_REC temp;
GSList *servers;
@@ -212,7 +216,7 @@ static void print_splits(IRC_SERVER_REC *server)
g_hash_table_foreach(server->splits,
(GHFunc) get_server_splits, &temp);
- print_server_splits(server, &temp);
+ print_server_splits(server, &temp, channel);
g_slist_foreach(temp.channels,
(GFunc) temp_split_chan_free, NULL);
@@ -233,25 +237,31 @@ static int check_server_splits(IRC_SERVER_REC *server)
if (time(NULL)-last < SPLIT_WAIT_TIME)
return FALSE;
- print_splits(server);
+ print_splits(server, NULL);
return TRUE;
}
/* something is going to be printed to screen, print our current netsplit
message before it. */
-static void sig_print_starting(void)
+static void sig_print_starting(TEXT_DEST_REC *dest)
{
- GSList *tmp;
+ IRC_SERVER_REC *rec;
if (printing_splits)
return;
- for (tmp = servers; tmp != NULL; tmp = tmp->next) {
- IRC_SERVER_REC *rec = tmp->data;
+ if (!IS_IRC_SERVER(dest->server))
+ return;
- if (IS_IRC_SERVER(rec) && rec->split_servers != NULL)
- print_splits(rec);
- }
+ if (!(dest->level & MSGLEVEL_PUBLIC))
+ return;
+
+ if (!server_ischannel(dest->server, dest->target))
+ return;
+
+ rec = IRC_SERVER(dest->server);
+ if (rec->split_servers != NULL)
+ print_splits(rec, dest->target);
}
static int sig_check_splits(void)
diff --git a/src/fe-common/irc/fe-sasl.c b/src/fe-common/irc/fe-sasl.c
new file mode 100644
index 00000000..fc8105fc
--- /dev/null
+++ b/src/fe-common/irc/fe-sasl.c
@@ -0,0 +1,75 @@
+/*
+ fe-sasl.c : irssi
+
+ Copyright (C) 2015-2017 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 "misc.h"
+#include "sasl.h"
+
+#include "irc-servers.h"
+#include "settings.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);
+}
+
+static void sig_cap_end(IRC_SERVER_REC *server)
+{
+ /* The negotiation has now been terminated, if we didn't manage to
+ * authenticate successfully with the server just disconnect. */
+ if (!server->sasl_success &&
+ server->connrec->sasl_mechanism != SASL_MECHANISM_NONE &&
+ settings_get_bool("sasl_disconnect_on_failure")) {
+ /* We can't use server_disconnect() here because we'd end up
+ * freeing the 'server' object and be guilty of a slew of UaF. */
+ server->connection_lost = TRUE;
+ /* By setting connection_lost we make sure the communication is
+ * halted and when the control goes back to irc_parse_incoming
+ * the server object is safely destroyed. */
+ signal_stop();
+ }
+
+}
+
+void fe_sasl_init(void)
+{
+ settings_add_bool("server", "sasl_disconnect_on_failure", TRUE);
+
+ signal_add("server sasl success", (SIGNAL_FUNC) sig_sasl_success);
+ signal_add("server sasl failure", (SIGNAL_FUNC) sig_sasl_failure);
+ signal_add_first("server cap end", (SIGNAL_FUNC) sig_cap_end);
+}
+
+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);
+ signal_remove("server cap end", (SIGNAL_FUNC) sig_cap_end);
+}
diff --git a/src/fe-common/irc/fe-whois.c b/src/fe-common/irc/fe-whois.c
index c5726124..117df120 100644
--- a/src/fe-common/irc/fe-whois.c
+++ b/src/fe-common/irc/fe-whois.c
@@ -92,7 +92,7 @@ static void event_whois_oper(IRC_SERVER_REC *server, const char *data)
params = event_get_params(data, 3, NULL, &nick, &type);
- /* Bugfix: http://bugs.irssi.org/?do=details&id=99
+ /* Bugfix: http://bugs.irssi.org/?do=details&task_id=99
* Author: Geert Hauwaerts <geert@irssi.org>
* Date: Wed Sep 15 20:17:24 CEST 2004
*/
@@ -134,8 +134,8 @@ static void event_whois_realhost(IRC_SERVER_REC *server, const char *data)
/* <yournick> real hostname <nick> <hostname> */
params = event_get_params(data, 5, NULL, &nick, &txt_real,
&txt_hostname, &hostname);
- if (strcmp(txt_real, "real") != 0 ||
- strcmp(txt_hostname, "hostname") != 0) {
+ if (g_strcmp0(txt_real, "real") != 0 ||
+ g_strcmp0(txt_hostname, "hostname") != 0) {
/* <yournick> <nick> :... from <hostname> */
g_free(params);
params = event_get_params(data, 3, NULL, &nick, &hostname);
@@ -219,7 +219,7 @@ static void event_whois_usermode(IRC_SERVER_REC *server, const char *data)
params = event_get_params(data, 4, NULL, &txt_usermodes,
&nick, &usermode);
- if (strcmp(txt_usermodes, "usermodes") == 0) {
+ if (g_strcmp0(txt_usermodes, "usermodes") == 0) {
/* <yournick> usermodes <nick> usermode */
printformat(server, nick, MSGLEVEL_CRAP,
IRCTXT_WHOIS_USERMODE, nick, usermode);
@@ -409,8 +409,8 @@ void fe_whois_init(void)
signal_add("event 311", (SIGNAL_FUNC) event_whois);
signal_add("event 312", (SIGNAL_FUNC) event_whois_server);
- /* readding this events fixes the printing of /whois -yes *
- Bug http://bugs.irssi.org/?do=details&id=123 */
+ /* readding this events fixes the printing of /whois -yes *
+ Bug http://bugs.irssi.org/?do=details&task_id=123 */
signal_add("event 317", (SIGNAL_FUNC) event_whois_idle);
signal_add("event 319", (SIGNAL_FUNC) event_whois_channels);
signal_add("event 313", (SIGNAL_FUNC) event_whois_oper);
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-fuzz/Makefile.am b/src/fe-fuzz/Makefile.am
new file mode 100644
index 00000000..3a547c66
--- /dev/null
+++ b/src/fe-fuzz/Makefile.am
@@ -0,0 +1,25 @@
+bin_PROGRAMS = irssi-fuzz
+
+# Force link with clang++ for libfuzzer support
+CCLD=clang++ $(CXXFLAGS)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/core/ \
+ -I$(top_srcdir)/src/irc/core/ \
+ -I$(top_srcdir)/src/fe-common/core/ \
+ $(GLIB_CFLAGS)
+
+irssi_fuzz_DEPENDENCIES = @COMMON_LIBS@
+
+irssi_fuzz_LDADD = \
+ @COMMON_LIBS@ \
+ @PROG_LIBS@ \
+ $(FUZZER_LIBS)
+
+irssi_fuzz_SOURCES = \
+ irssi.c \
+ $(top_srcdir)/src/fe-text/module-formats.c
+
+noinst_HEADERS = \
+ $(top_srcdir)/src/fe-text/module-formats.h
diff --git a/src/fe-fuzz/irssi.c b/src/fe-fuzz/irssi.c
new file mode 100644
index 00000000..77892aaf
--- /dev/null
+++ b/src/fe-fuzz/irssi.c
@@ -0,0 +1,57 @@
+/*
+ irssi.c : irssi
+
+ Copyright (C) 2017 Joseph Bisch
+
+ 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 "modules-load.h"
+#include "levels.h"
+#include "../fe-text/module-formats.h" // need to explicitly grab from fe-text
+#include "themes.h"
+#include "core.h"
+#include "fe-common-core.h"
+#include "args.h"
+#include "printtext.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+int LLVMFuzzerInitialize(int *argc, char ***argv) {
+ core_register_options();
+ fe_common_core_register_options();
+ /* no args */
+ args_execute(0, NULL);
+ core_preinit((*argv)[0]);
+ core_init();
+ fe_common_core_init();
+ theme_register(gui_text_formats);
+ module_register("core", "fe-fuzz");
+ printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, "init");
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ char *copy = (char *)malloc(sizeof(char)*(size+1));
+ memcpy(copy, data, size);
+ copy[size] = '\0';
+ printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, copy);
+ free(copy);
+ return 0;
+}
diff --git a/src/fe-fuzz/tokens.txt b/src/fe-fuzz/tokens.txt
new file mode 100644
index 00000000..e337b6e9
--- /dev/null
+++ b/src/fe-fuzz/tokens.txt
@@ -0,0 +1,143 @@
+"@%+"
+"*@*!*"
+"001"
+"002"
+"003"
+"004"
+"005"
+"221"
+"254"
+"271"
+"272"
+"281"
+"301"
+"302"
+"303"
+"305"
+"306"
+"311"
+"312"
+"313"
+"314"
+"315"
+"317"
+"318"
+"319"
+"324"
+"326"
+"327"
+"328"
+"329"
+"330"
+"332"
+"333"
+"338"
+"341"
+"344"
+"345"
+"346"
+"347"
+"348"
+"349"
+"352"
+"353"
+"364"
+"365"
+"366"
+"367"
+"368"
+"369"
+"372"
+"375"
+"376"
+"377"
+"378"
+"379"
+"381"
+"386"
+"387"
+"388"
+"389"
+"396"
+"401"
+"403"
+"404"
+"405"
+"407"
+"408"
+"410"
+"421"
+"422"
+"433"
+"436"
+"437"
+"438"
+"439"
+"442"
+"465"
+"470"
+"471"
+"472"
+"473"
+"474"
+"475"
+"476"
+"477"
+"478"
+"479"
+"482"
+"486"
+"489"
+"494"
+"506"
+"707"
+"716"
+"717"
+"728"
+"729"
+"902"
+"903"
+"904"
+"905"
+"906"
+"907"
+":a"
+"+a"
+"ACK"
+"authenticate"
+"away"
+"-b"
+"+b"
+"cap"
+"#chan"
+"connected"
+"empty"
+"error"
+"invite"
+"join"
+"kick"
+"kill"
+"LS"
+"mode"
+"multi-prefix"
+"NAK"
+"network"
+"nick"
+"nicklen"
+"notice"
+"-o"
+"+o"
+"part"
+"ping"
+"pong"
+"prefix"
+"privmsg"
+"quit"
+"sasl"
+"topic"
+"wallops"
+"watch"
+":\x01"
+":\x01ACTION"
+":\x01PING"
+":\x01VERSION"
diff --git a/src/fe-text/Makefile.am b/src/fe-text/Makefile.am
index b9538e60..bdd3df22 100644
--- a/src/fe-text/Makefile.am
+++ b/src/fe-text/Makefile.am
@@ -4,8 +4,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/core/ \
-I$(top_srcdir)/src/fe-common/core/ \
- $(GLIB_CFLAGS) \
- $(CURSES_INCLUDEDIR)
+ $(GLIB_CFLAGS)
irssi_DEPENDENCIES = \
@COMMON_LIBS@ \
@@ -22,25 +21,11 @@ irssi_LDADD = \
@PROG_LIBS@ \
@TEXTUI_LIBS@
-tparm_sources = \
- tparm.c
-
terminfo_sources = \
term-terminfo.c \
terminfo-core.c
-curses_sources = \
- term-curses.c
-
-if NEED_TPARM
-use_tparm_sources = $(tparm_sources)
-endif
-
-if USE_CURSES
-use_term_sources = $(curses_sources)
-else
use_term_sources = $(terminfo_sources)
-endif
irssi_SOURCES = \
gui-entry.c \
@@ -56,8 +41,6 @@ irssi_SOURCES = \
statusbar-config.c \
statusbar-items.c \
term.c \
- term-dummy.c \
- $(use_tparm_sources) \
$(use_term_sources) \
textbuffer.c \
textbuffer-commands.c \
@@ -85,6 +68,4 @@ noinst_HEADERS = \
module-formats.h
EXTRA_DIST = \
- $(tparm_sources) \
- $(terminfo_sources) \
- $(curses_sources)
+ $(terminfo_sources)
diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c
index f123ce4c..f05decd2 100644
--- a/src/fe-text/gui-entry.c
+++ b/src/fe-text/gui-entry.c
@@ -26,30 +26,33 @@
#include "gui-entry.h"
#include "gui-printtext.h"
#include "term.h"
+#include "recode.h"
#undef i_toupper
#undef i_tolower
#undef i_isalnum
+#define KILL_RING_MAX 10
+
static unichar i_toupper(unichar c)
{
if (term_type == TERM_TYPE_UTF8)
return g_unichar_toupper(c);
- return (c >= 0 && c <= 255) ? toupper(c) : c;
+ return c <= 255 ? toupper(c) : c;
}
static unichar i_tolower(unichar c)
{
if (term_type == TERM_TYPE_UTF8)
return g_unichar_tolower(c);
- return (c >= 0 && c <= 255) ? tolower(c) : c;
+ return c <= 255 ? tolower(c) : c;
}
static int i_isalnum(unichar c)
{
if (term_type == TERM_TYPE_UTF8)
return (g_unichar_isalnum(c) || mk_wcwidth(c) == 0);
- return (c >= 0 && c <= 255) ? isalnum(c) : 0;
+ return c <= 255 ? isalnum(c) : 0;
}
GUI_ENTRY_REC *active_entry;
@@ -81,11 +84,22 @@ GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
void gui_entry_destroy(GUI_ENTRY_REC *entry)
{
+ GSList *tmp;
+
g_return_if_fail(entry != NULL);
if (active_entry == entry)
gui_entry_set_active(NULL);
+ for (tmp = entry->kill_ring; tmp != NULL; tmp = tmp->next) {
+ GUI_ENTRY_CUTBUFFER_REC *rec = tmp->data;
+ if (rec != NULL) {
+ g_free(rec->cutbuffer);
+ g_free(rec);
+ }
+ }
+ g_slist_free(entry->kill_ring);
+
g_free(entry->text);
g_free(entry->prompt);
g_free(entry);
@@ -226,8 +240,8 @@ static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
int i;
int xpos, end_xpos;
- xpos = entry->xpos + entry->promptlen +
- pos2scrpos(entry, pos + entry->scrstart) -
+ xpos = entry->xpos + entry->promptlen +
+ pos2scrpos(entry, pos + entry->scrstart) -
pos2scrpos(entry, entry->scrstart);
end_xpos = entry->xpos + entry->width;
@@ -345,6 +359,19 @@ void gui_entry_set_active(GUI_ENTRY_REC *entry)
}
}
+/* Return screen length of plain string */
+static int scrlen_str(const char *str)
+{
+ int len = 0;
+ char *stripped;
+ g_return_val_if_fail(str != NULL, 0);
+
+ stripped = strip_codes(str);
+ len = string_width(stripped, -1);
+ g_free(stripped);
+ return len;
+}
+
void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
{
int oldlen;
@@ -355,11 +382,11 @@ void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
if (str != NULL) {
g_free_not_null(entry->prompt);
entry->prompt = g_strdup(str);
- entry->promptlen = format_get_length(str);
+ entry->promptlen = scrlen_str(str);
}
if (entry->prompt != NULL)
- gui_printtext(entry->xpos, entry->ypos, entry->prompt);
+ gui_printtext_internal(entry->xpos, entry->ypos, entry->prompt);
if (entry->promptlen != oldlen) {
gui_entry_fix_cursor(entry);
@@ -513,28 +540,51 @@ void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr)
char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry)
{
+ GUI_ENTRY_CUTBUFFER_REC *tmp;
char *buf;
int i;
g_return_val_if_fail(entry != NULL, NULL);
- if (entry->cutbuffer == NULL)
+ if (entry->kill_ring == NULL || entry->kill_ring->data == NULL)
+ return NULL;
+
+ tmp = entry->kill_ring->data;
+
+ if (tmp->cutbuffer == NULL)
return NULL;
if (entry->utf8)
- buf = g_ucs4_to_utf8(entry->cutbuffer, -1, NULL, NULL, NULL);
+ buf = g_ucs4_to_utf8(tmp->cutbuffer, -1, NULL, NULL, NULL);
else {
- buf = g_malloc(entry->cutbuffer_len*6 + 1);
+ buf = g_malloc(tmp->cutbuffer_len*6 + 1);
if (term_type == TERM_TYPE_BIG5)
- unichars_to_big5(entry->cutbuffer, buf);
+ unichars_to_big5(tmp->cutbuffer, buf);
else
- for (i = 0; i <= entry->cutbuffer_len; i++)
- buf[i] = entry->cutbuffer[i];
+ for (i = 0; i <= tmp->cutbuffer_len; i++)
+ buf[i] = tmp->cutbuffer[i];
}
return buf;
}
-void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
+char *gui_entry_get_next_cutbuffer(GUI_ENTRY_REC *entry)
+{
+ GUI_ENTRY_CUTBUFFER_REC *tmp;
+
+ g_return_val_if_fail(entry != NULL, NULL);
+
+ if (entry->kill_ring == NULL)
+ return NULL;
+
+ tmp = entry->kill_ring->data;
+
+ entry->kill_ring = g_slist_remove(entry->kill_ring, tmp);
+ entry->kill_ring = g_slist_append(entry->kill_ring, tmp);
+
+ return gui_entry_get_cutbuffer(entry);
+}
+
+void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, CUTBUFFER_UPDATE_OP update_cutbuffer)
{
int newpos, size = 0;
@@ -545,7 +595,41 @@ void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
gui_entry_erase(entry, size, update_cutbuffer);
}
-void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
+static GUI_ENTRY_CUTBUFFER_REC *get_cutbuffer_rec(GUI_ENTRY_REC *entry, CUTBUFFER_UPDATE_OP update_cutbuffer)
+{
+ GUI_ENTRY_CUTBUFFER_REC *tmp;
+
+ g_return_val_if_fail(entry != NULL, NULL);
+
+ if (entry->kill_ring == NULL) {
+ /* no kill ring exists */
+ entry->kill_ring = g_slist_prepend(entry->kill_ring, (void *)NULL);
+ } else {
+ tmp = entry->kill_ring->data;
+
+ if (tmp != NULL && tmp->cutbuffer_len > 0
+ && (!entry->previous_append_next_kill
+ || update_cutbuffer == CUTBUFFER_UPDATE_REPLACE)) {
+ /* a cutbuffer exists and should be replaced */
+ entry->kill_ring = g_slist_prepend(entry->kill_ring, (void *)NULL);
+ }
+ }
+
+ if (g_slist_length(entry->kill_ring) > KILL_RING_MAX) {
+ GUI_ENTRY_CUTBUFFER_REC *rec = g_slist_last(entry->kill_ring)->data;
+ entry->kill_ring = g_slist_remove(entry->kill_ring, rec);
+ if (rec != NULL) g_free(rec->cutbuffer);
+ g_free(rec);
+ }
+
+ if (entry->kill_ring->data == NULL) {
+ entry->kill_ring->data = g_new0(GUI_ENTRY_CUTBUFFER_REC, 1);
+ }
+
+ return entry->kill_ring->data;
+}
+
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer)
{
size_t w = 0;
@@ -554,17 +638,59 @@ void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
if (size == 0 || entry->pos < size)
return;
- if (update_cutbuffer) {
- /* put erased text to cutbuffer */
- if (entry->cutbuffer_len < size) {
- g_free(entry->cutbuffer);
- entry->cutbuffer = g_new(unichar, size+1);
+ if (update_cutbuffer != CUTBUFFER_UPDATE_NOOP) {
+ int cutbuffer_new_size;
+ unichar *tmpcutbuffer;
+ GUI_ENTRY_CUTBUFFER_REC *tmp = get_cutbuffer_rec(entry, update_cutbuffer);
+
+ if (tmp->cutbuffer_len == 0) {
+ update_cutbuffer = CUTBUFFER_UPDATE_REPLACE;
}
- entry->cutbuffer_len = size;
- entry->cutbuffer[size] = '\0';
- memcpy(entry->cutbuffer, entry->text + entry->pos - size,
- size * sizeof(unichar));
+ cutbuffer_new_size = tmp->cutbuffer_len + size;
+ tmpcutbuffer = tmp->cutbuffer;
+ entry->append_next_kill = TRUE;
+ switch (update_cutbuffer) {
+ case CUTBUFFER_UPDATE_APPEND:
+ tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1);
+ memcpy(tmp->cutbuffer, tmpcutbuffer,
+ tmp->cutbuffer_len * sizeof(unichar));
+ memcpy(tmp->cutbuffer + tmp->cutbuffer_len,
+ entry->text + entry->pos - size, size * sizeof(unichar));
+
+ tmp->cutbuffer_len = cutbuffer_new_size;
+ tmp->cutbuffer[cutbuffer_new_size] = '\0';
+ g_free(tmpcutbuffer);
+ break;
+
+ case CUTBUFFER_UPDATE_PREPEND:
+ tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1);
+ memcpy(tmp->cutbuffer, entry->text + entry->pos - size,
+ size * sizeof(unichar));
+ memcpy(tmp->cutbuffer + size, tmpcutbuffer,
+ tmp->cutbuffer_len * sizeof(unichar));
+
+ tmp->cutbuffer_len = cutbuffer_new_size;
+ tmp->cutbuffer[cutbuffer_new_size] = '\0';
+ g_free(tmpcutbuffer);
+ break;
+
+ case CUTBUFFER_UPDATE_REPLACE:
+ /* put erased text to cutbuffer */
+ if (tmp->cutbuffer_len < size) {
+ g_free(tmp->cutbuffer);
+ tmp->cutbuffer = g_new(unichar, size+1);
+ }
+
+ tmp->cutbuffer_len = size;
+ tmp->cutbuffer[size] = '\0';
+ memcpy(tmp->cutbuffer, entry->text + entry->pos - size, size * sizeof(unichar));
+ break;
+
+ case CUTBUFFER_UPDATE_NOOP:
+ /* cannot happen, handled in "if" */
+ break;
+ }
}
if (entry->utf8)
@@ -601,7 +727,7 @@ void gui_entry_erase_cell(GUI_ENTRY_REC *entry)
gui_entry_draw(entry);
}
-void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
+void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op)
{
int to;
@@ -624,10 +750,10 @@ void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
}
if (to > 0) to++;
- gui_entry_erase(entry, entry->pos-to, TRUE);
+ gui_entry_erase(entry, entry->pos-to, cutbuffer_op);
}
-void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
+void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op)
{
int to, size;
@@ -650,7 +776,7 @@ void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
size = to-entry->pos;
entry->pos = to;
- gui_entry_erase(entry, size, TRUE);
+ gui_entry_erase(entry, size, cutbuffer_op);
}
void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)
@@ -730,7 +856,7 @@ void gui_entry_transpose_words(GUI_ENTRY_REC *entry)
g_free(second);
}
-
+
gui_entry_redraw_from(entry, spos1);
gui_entry_fix_cursor(entry);
gui_entry_draw(entry);
diff --git a/src/fe-text/gui-entry.h b/src/fe-text/gui-entry.h
index 29d8dea2..8777f083 100644
--- a/src/fe-text/gui-entry.h
+++ b/src/fe-text/gui-entry.h
@@ -2,11 +2,15 @@
#define __GUI_ENTRY_H
typedef struct {
+ int cutbuffer_len;
+ unichar *cutbuffer;
+} GUI_ENTRY_CUTBUFFER_REC;
+
+typedef struct {
int text_len, text_alloc; /* as shorts, not chars */
unichar *text;
- int cutbuffer_len;
- unichar *cutbuffer;
+ GSList *kill_ring;
/* all as shorts, not chars */
int xpos, ypos, width; /* entry position in screen */
@@ -18,8 +22,19 @@ typedef struct {
int redraw_needed_from;
unsigned int utf8:1;
+
+ unsigned int previous_append_next_kill:1;
+ unsigned int append_next_kill:1;
+ unsigned int yank_preceded:1;
} GUI_ENTRY_REC;
+typedef enum {
+ CUTBUFFER_UPDATE_NOOP,
+ CUTBUFFER_UPDATE_REPLACE,
+ CUTBUFFER_UPDATE_APPEND,
+ CUTBUFFER_UPDATE_PREPEND
+} CUTBUFFER_UPDATE_OP;
+
extern GUI_ENTRY_REC *active_entry;
GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8);
@@ -40,11 +55,12 @@ void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str);
void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr);
char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry);
-void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer);
-void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer);
+char *gui_entry_get_next_cutbuffer(GUI_ENTRY_REC *entry);
+void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, CUTBUFFER_UPDATE_OP update_cutbuffer);
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer);
void gui_entry_erase_cell(GUI_ENTRY_REC *entry);
-void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space);
-void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space);
+void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op);
+void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op);
void gui_entry_transpose_chars(GUI_ENTRY_REC *entry);
void gui_entry_transpose_words(GUI_ENTRY_REC *entry);
@@ -60,4 +76,5 @@ void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space);
void gui_entry_redraw(GUI_ENTRY_REC *entry);
+
#endif
diff --git a/src/fe-text/gui-printtext.c b/src/fe-text/gui-printtext.c
index 547d39c9..a07451fa 100644
--- a/src/fe-text/gui-printtext.c
+++ b/src/fe-text/gui-printtext.c
@@ -109,6 +109,16 @@ void gui_printtext(int xpos, int ypos, const char *str)
next_xpos = next_ypos = -1;
}
+void gui_printtext_internal(int xpos, int ypos, const char *str)
+{
+ next_xpos = xpos;
+ next_ypos = ypos;
+
+ printtext_gui_internal(str);
+
+ next_xpos = next_ypos = -1;
+}
+
void gui_printtext_after_time(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str, time_t time)
{
GUI_WINDOW_REC *gui;
@@ -159,7 +169,8 @@ static void get_colors(int flags, int *fg, int *bg, int *attr)
if (*bg >= 0) {
*bg = mirc_colors[*bg % 100];
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
- if (settings_get_bool("mirc_blink_fix")) {
+ /* ignore mirc color 99 = -1 (reset) */
+ if (*bg != -1 && settings_get_bool("mirc_blink_fix")) {
if (*bg < 16) /* ansi bit flip :-( */
*bg = (*bg&8) | (*bg&4)>>2 | (*bg&2) | (*bg&1)<<2;
*bg = term_color256map[*bg&0xff] & 7;
@@ -220,16 +231,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-printtext.h b/src/fe-text/gui-printtext.h
index 33b7ce6f..d2671497 100644
--- a/src/fe-text/gui-printtext.h
+++ b/src/fe-text/gui-printtext.h
@@ -17,6 +17,7 @@ void gui_set_default_indent(const char *name);
INDENT_FUNC get_default_indent_func(void);
void gui_printtext(int xpos, int ypos, const char *str);
+void gui_printtext_internal(int xpos, int ypos, const char *str);
void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str);
void gui_printtext_after_time(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str, time_t time);
diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c
index 476a798b..2c2eac21 100644
--- a/src/fe-text/gui-readline.c
+++ b/src/fe-text/gui-readline.c
@@ -37,8 +37,12 @@
#include "gui-windows.h"
#include "utf8.h"
+#include <string.h>
#include <signal.h>
+/* After LINE_SPLIT_LIMIT characters, the message will be split into multiple lines */
+#define LINE_SPLIT_LIMIT 400
+
typedef void (*ENTRY_REDIRECT_KEY_FUNC) (int key, void *data, SERVER_REC *server, WI_ITEM_REC *item);
typedef void (*ENTRY_REDIRECT_ENTRY_FUNC) (const char *line, void *data, SERVER_REC *server, WI_ITEM_REC *item);
@@ -60,11 +64,22 @@ static int paste_detect_time, paste_verify_line_count;
static char *paste_entry;
static int paste_entry_pos;
static GArray *paste_buffer;
+static GArray *paste_buffer_rest;
static char *paste_old_prompt;
static int paste_prompt, paste_line_count;
static int paste_join_multiline;
static int paste_timeout_id;
+static int paste_use_bracketed_mode;
+static int paste_bracketed_mode;
+static int paste_was_bracketed_mode;
+static int previous_yank_preceded;
+
+/* Terminal sequences that surround the input when the terminal has the
+ * bracketed paste mode active. Fror more details see
+ * https://cirw.in/blog/bracketed-paste */
+static const unichar bp_start[] = { 0x1b, '[', '2', '0', '0', '~' };
+static const unichar bp_end[] = { 0x1b, '[', '2', '0', '1', '~' };
static void sig_input(void);
@@ -147,7 +162,6 @@ static void window_next_page(void)
static void paste_buffer_join_lines(GArray *buf)
{
-#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
unsigned int i, count, indent, line_len;
unichar *arr, *dest, *last_lf_pos;
int last_lf;
@@ -177,15 +191,15 @@ static void paste_buffer_join_lines(GArray *buf)
if (buf->len == 0)
return;
- arr = (unichar *) paste_buffer->data;
+ arr = (unichar *)buf->data;
/* first line */
- if (IS_WHITE(arr[0]))
+ if (isblank(arr[0]))
return;
/* find the first beginning of indented line */
for (i = 1; i < buf->len; i++) {
- if (arr[i-1] == '\n' && IS_WHITE(arr[i]))
+ if (arr[i-1] == '\n' && isblank(arr[i]))
break;
}
if (i == buf->len)
@@ -193,7 +207,7 @@ static void paste_buffer_join_lines(GArray *buf)
/* get how much indentation we have.. */
for (indent = 0; i < buf->len; i++, indent++) {
- if (!IS_WHITE(arr[i]))
+ if (!isblank(arr[i]))
break;
}
if (i == buf->len)
@@ -203,7 +217,7 @@ static void paste_buffer_join_lines(GArray *buf)
count = indent; last_lf = TRUE;
for (; i < buf->len; i++) {
if (last_lf) {
- if (IS_WHITE(arr[i]))
+ if (isblank(arr[i]))
count++;
else {
last_lf = FALSE;
@@ -217,14 +231,14 @@ static void paste_buffer_join_lines(GArray *buf)
}
/* all looks fine - now remove the whitespace, but don't let lines
- get longer than 400 chars */
+ get longer than LINE_SPLIT_LIMIT chars */
dest = arr; last_lf = TRUE; last_lf_pos = NULL; line_len = 0;
for (i = 0; i < buf->len; i++) {
- if (last_lf && IS_WHITE(arr[i])) {
+ if (last_lf && isblank(arr[i])) {
/* whitespace, ignore */
} else if (arr[i] == '\n') {
if (!last_lf && i+1 != buf->len &&
- IS_WHITE(arr[i+1])) {
+ isblank(arr[i+1])) {
last_lf_pos = dest;
*dest++ = ' ';
} else {
@@ -235,9 +249,9 @@ static void paste_buffer_join_lines(GArray *buf)
last_lf = TRUE;
} else {
last_lf = FALSE;
- if (++line_len >= 400 && last_lf_pos != NULL) {
+ if (++line_len >= LINE_SPLIT_LIMIT && 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++;
@@ -248,9 +262,16 @@ static void paste_buffer_join_lines(GArray *buf)
g_array_set_size(buf, dest - arr);
}
+static void paste_send_line(char *text)
+{
+ /* we need to get the current history every time because it might change between calls */
+ command_history_add(command_history_current(active_win), text);
+
+ signal_emit("send command", 3, text, active_win->active_server, active_win->active);
+}
+
static void paste_send(void)
{
- HISTORY_REC *history;
unichar *arr;
GString *str;
char out[10], *text;
@@ -275,11 +296,7 @@ static void paste_send(void)
}
text = gui_entry_get_text(active_entry);
- history = command_history_current(active_win);
- command_history_add(history, text);
-
- signal_emit("send command", 3, text,
- active_win->active_server, active_win->active);
+ paste_send_line(text);
g_free(text);
}
@@ -287,12 +304,7 @@ static void paste_send(void)
str = g_string_new(NULL);
for (; i < paste_buffer->len; i++) {
if (arr[i] == '\r' || arr[i] == '\n') {
- history = command_history_current(active_win);
- command_history_add(history, str->str);
-
- signal_emit("send command", 3, str->str,
- active_win->active_server,
- active_win->active);
+ paste_send_line(str->str);
g_string_truncate(str, 0);
} else if (active_entry->utf8) {
out[g_unichar_to_utf8(arr[i], out)] = '\0';
@@ -306,7 +318,14 @@ static void paste_send(void)
}
}
- gui_entry_set_text(active_entry, str->str);
+ if (paste_was_bracketed_mode) {
+ /* the text before the bracket end should be sent along with the rest */
+ paste_send_line(str->str);
+ gui_entry_set_text(active_entry, "");
+ } else {
+ gui_entry_set_text(active_entry, str->str);
+ }
+
g_string_free(str, TRUE);
}
@@ -322,6 +341,12 @@ static void paste_flush(int send)
paste_send();
g_array_set_size(paste_buffer, 0);
+ /* re-add anything that may have been after the bracketed paste end */
+ if (paste_buffer_rest->len > 0) {
+ g_array_append_vals(paste_buffer, paste_buffer_rest->data, paste_buffer_rest->len);
+ g_array_set_size(paste_buffer_rest, 0);
+ }
+
gui_entry_set_prompt(active_entry,
paste_old_prompt == NULL ? "" : paste_old_prompt);
g_free(paste_old_prompt); paste_old_prompt = NULL;
@@ -336,11 +361,24 @@ static void insert_paste_prompt(void)
{
char *str;
+ /* The actual number of lines that will show up post-line-split */
+ int actual_line_count = paste_line_count;
+ int split_lines = paste_buffer->len / LINE_SPLIT_LIMIT;
+
+ /* in case this prompt is happening due to line-splitting, calculate the
+ number of lines obtained from this. The number isn't entirely accurate;
+ we just choose the greater of the two since the exact value isn't
+ important */
+ if (split_lines > paste_verify_line_count &&
+ split_lines > paste_line_count) {
+ actual_line_count = split_lines;
+ }
+
paste_prompt = TRUE;
paste_old_prompt = g_strdup(active_entry->prompt);
printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
TXT_PASTE_WARNING,
- paste_line_count,
+ actual_line_count,
active_win->active == NULL ? "window" :
active_win->active->visible_name);
@@ -392,9 +430,9 @@ static void sig_gui_key_pressed(gpointer keyp)
str[g_unichar_to_utf8(key, str)] = '\0';
}
- if (strcmp(str, "^") == 0) {
- /* change it as ^^ */
- str[1] = '^';
+ if (g_strcmp0(str, "^") == 0) {
+ /* change it as ^-, that is an invalid control char */
+ str[1] = '-';
str[2] = '\0';
}
@@ -403,11 +441,20 @@ static void sig_gui_key_pressed(gpointer keyp)
gui_entry_insert_char(active_entry, key);
ret = 1;
} else {
+ previous_yank_preceded = active_entry->yank_preceded;
+ active_entry->yank_preceded = FALSE;
+ active_entry->previous_append_next_kill = active_entry->append_next_kill;
+ active_entry->append_next_kill = FALSE;
ret = key_pressed(keyboard, str);
if (ret < 0) {
/* key wasn't used for anything, print it */
gui_entry_insert_char(active_entry, key);
}
+ if (ret == 0) {
+ /* combo not complete, ignore append_next_kill and yank_preceded */
+ active_entry->append_next_kill = active_entry->previous_append_next_kill;
+ active_entry->yank_preceded = previous_yank_preceded;
+ }
}
/* ret = 0 : some key create multiple characters - we're in the middle
@@ -435,22 +482,21 @@ static void key_send_line(void)
add_history = *str != '\0';
history = command_history_current(active_win);
+ if (redir != NULL && redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
+ add_history = 0;
+
+ if (add_history && history != NULL) {
+ command_history_add(history, str);
+ }
+
if (redir == NULL) {
signal_emit("send command", 3, str,
active_win->active_server,
active_win->active);
} else {
- if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
- add_history = 0;
handle_entry_redirect(str);
}
- if (add_history) {
- history = command_history_find(history);
- if (history != NULL)
- command_history_add(history, str);
- }
-
if (active_entry != NULL)
gui_entry_set_text(active_entry, "");
command_history_clear_pos(active_win);
@@ -527,7 +573,7 @@ static void key_forward_to_space(void)
static void key_erase_line(void)
{
gui_entry_set_pos(active_entry, active_entry->text_len);
- gui_entry_erase(active_entry, active_entry->text_len, TRUE);
+ gui_entry_erase(active_entry, active_entry->text_len, CUTBUFFER_UPDATE_REPLACE);
}
static void key_erase_to_beg_of_line(void)
@@ -535,7 +581,7 @@ static void key_erase_to_beg_of_line(void)
int pos;
pos = gui_entry_get_pos(active_entry);
- gui_entry_erase(active_entry, pos, TRUE);
+ gui_entry_erase(active_entry, pos, CUTBUFFER_UPDATE_PREPEND);
}
static void key_erase_to_end_of_line(void)
@@ -544,7 +590,7 @@ static void key_erase_to_end_of_line(void)
pos = gui_entry_get_pos(active_entry);
gui_entry_set_pos(active_entry, active_entry->text_len);
- gui_entry_erase(active_entry, active_entry->text_len - pos, TRUE);
+ gui_entry_erase(active_entry, active_entry->text_len - pos, CUTBUFFER_UPDATE_APPEND);
}
static void key_yank_from_cutbuffer(void)
@@ -554,7 +600,32 @@ static void key_yank_from_cutbuffer(void)
cutbuffer = gui_entry_get_cutbuffer(active_entry);
if (cutbuffer != NULL) {
gui_entry_insert_text(active_entry, cutbuffer);
- g_free(cutbuffer);
+ active_entry->yank_preceded = TRUE;
+ g_free(cutbuffer);
+ }
+}
+
+static void key_yank_next_cutbuffer(void)
+{
+ GUI_ENTRY_CUTBUFFER_REC *rec;
+ guint length = 0;
+ char *cutbuffer;
+
+ if (!previous_yank_preceded)
+ return;
+
+ if (active_entry->kill_ring == NULL)
+ return;
+
+ rec = active_entry->kill_ring->data;
+ if (rec != NULL) length = rec->cutbuffer_len;
+
+ cutbuffer = gui_entry_get_next_cutbuffer(active_entry);
+ if (cutbuffer != NULL) {
+ gui_entry_erase(active_entry, length, CUTBUFFER_UPDATE_NOOP);
+ gui_entry_insert_text(active_entry, cutbuffer);
+ active_entry->yank_preceded = TRUE;
+ g_free(cutbuffer);
}
}
@@ -591,32 +662,44 @@ static void key_delete_character(void)
static void key_backspace(void)
{
- gui_entry_erase(active_entry, 1, FALSE);
+ gui_entry_erase(active_entry, 1, CUTBUFFER_UPDATE_NOOP);
}
static void key_delete_previous_word(void)
{
- gui_entry_erase_word(active_entry, FALSE);
+ gui_entry_erase_word(active_entry, FALSE, CUTBUFFER_UPDATE_PREPEND);
}
static void key_delete_next_word(void)
{
- gui_entry_erase_next_word(active_entry, FALSE);
+ gui_entry_erase_next_word(active_entry, FALSE, CUTBUFFER_UPDATE_APPEND);
}
static void key_delete_to_previous_space(void)
{
- gui_entry_erase_word(active_entry, TRUE);
+ gui_entry_erase_word(active_entry, TRUE, CUTBUFFER_UPDATE_PREPEND);
}
static void key_delete_to_next_space(void)
{
- gui_entry_erase_next_word(active_entry, TRUE);
+ gui_entry_erase_next_word(active_entry, TRUE, CUTBUFFER_UPDATE_APPEND);
+}
+
+static void key_append_next_kill(void)
+{
+ active_entry->append_next_kill = TRUE;
}
static gboolean paste_timeout(gpointer data)
{
- if (paste_line_count == 0) {
+ int split_lines;
+ paste_was_bracketed_mode = paste_bracketed_mode;
+
+ /* number of lines after splitting extra-long messages */
+ split_lines = paste_buffer->len / LINE_SPLIT_LIMIT;
+
+ /* Take into account the fact that a line may be split every LINE_SPLIT_LIMIT characters */
+ if (paste_line_count == 0 && split_lines <= paste_verify_line_count) {
int i;
for (i = 0; i < paste_buffer->len; i++) {
@@ -625,8 +708,9 @@ static gboolean paste_timeout(gpointer data)
}
g_array_set_size(paste_buffer, 0);
} else if (paste_verify_line_count > 0 &&
- paste_line_count >= paste_verify_line_count &&
- active_win->active != NULL)
+ (paste_line_count >= paste_verify_line_count ||
+ split_lines > paste_verify_line_count) &&
+ active_win->active != NULL)
insert_paste_prompt();
else
paste_flush(TRUE);
@@ -634,6 +718,70 @@ static gboolean paste_timeout(gpointer data)
return FALSE;
}
+static void paste_bracketed_end(int i, gboolean rest)
+{
+ unichar last_char;
+
+ /* if there's stuff after the end bracket, save it for later */
+ if (rest) {
+ unichar *start = ((unichar *) paste_buffer->data) + i + G_N_ELEMENTS(bp_end);
+ int len = paste_buffer->len - i - G_N_ELEMENTS(bp_end);
+
+ g_array_set_size(paste_buffer_rest, 0);
+ g_array_append_vals(paste_buffer_rest, start, len);
+ }
+
+ /* remove the rest, including the trailing sequence chars */
+ g_array_set_size(paste_buffer, i);
+
+ last_char = g_array_index(paste_buffer, unichar, i - 1);
+
+ if (paste_line_count > 0 && last_char != '\n' && last_char != '\r') {
+ /* there are newlines, but there's also stuff after the newline
+ * adjust line count to reflect this */
+ paste_line_count++;
+ }
+
+ /* decide what to do with the buffer */
+ paste_timeout(NULL);
+
+ paste_bracketed_mode = FALSE;
+}
+
+static void paste_bracketed_middle()
+{
+ int i;
+ int marklen = G_N_ELEMENTS(bp_end);
+ int len = paste_buffer->len - marklen;
+ unichar *ptr = (unichar *) paste_buffer->data;
+
+ if (len < 0) {
+ return;
+ }
+
+ for (i = 0; i <= len; i++, ptr++) {
+ if (ptr[0] == bp_end[0] && memcmp(ptr, bp_end, sizeof(bp_end)) == 0) {
+
+ /* if there are at least 6 bytes after the end,
+ * check for another start marker right afterwards */
+ if (i <= (len - marklen) &&
+ memcmp(ptr + marklen, bp_start, sizeof(bp_start)) == 0) {
+
+ /* remove both markers*/
+ g_array_remove_range(paste_buffer, i, marklen * 2);
+ len -= marklen * 2;
+
+ /* go one step back */
+ i--;
+ ptr--;
+ continue;
+ }
+ paste_bracketed_end(i, i != len);
+ break;
+ }
+ }
+}
+
static void sig_input(void)
{
if (!active_entry) {
@@ -647,21 +795,37 @@ static void sig_input(void)
unichar key;
term_gets(buffer, &line_count);
key = g_array_index(buffer, unichar, 0);
+ /* Either Ctrl-k or Ctrl-c is pressed */
if (key == 11 || key == 3)
paste_flush(key == 11);
g_array_free(buffer, TRUE);
} else {
term_gets(paste_buffer, &paste_line_count);
- if (paste_detect_time > 0 && paste_buffer->len >= 3) {
+
+ /* use the bracketed paste mode to detect when the user pastes
+ * some text into the entry */
+ if (paste_bracketed_mode) {
+ paste_bracketed_middle();
+
+ } else if (!paste_use_bracketed_mode && paste_detect_time > 0 && paste_buffer->len >= 3) {
if (paste_timeout_id != -1)
g_source_remove(paste_timeout_id);
paste_timeout_id = g_timeout_add(paste_detect_time, paste_timeout, NULL);
- } else {
+ } else if (!paste_bracketed_mode) {
int i;
for (i = 0; i < paste_buffer->len; i++) {
unichar key = g_array_index(paste_buffer, unichar, i);
signal_emit("gui key pressed", 1, GINT_TO_POINTER(key));
+
+ if (paste_bracketed_mode) {
+ /* just enabled by the signal, remove what was processed so far */
+ g_array_remove_range(paste_buffer, 0, i + 1);
+
+ /* handle single-line / small pastes here */
+ paste_bracketed_middle();
+ return;
+ }
}
g_array_set_size(paste_buffer, 0);
paste_line_count = 0;
@@ -669,6 +833,11 @@ static void sig_input(void)
}
}
+static void key_paste_start(void)
+{
+ paste_bracketed_mode = TRUE;
+}
+
time_t get_idle_time(void)
{
return last_keypress.tv_sec;
@@ -930,6 +1099,11 @@ static void setup_changed(void)
paste_verify_line_count = settings_get_int("paste_verify_line_count");
paste_join_multiline = settings_get_bool("paste_join_multiline");
+ paste_use_bracketed_mode = settings_get_bool("paste_use_bracketed_mode");
+
+ term_set_appkey_mode(settings_get_bool("term_appkey_mode"));
+ /* Enable the bracketed paste mode on demand */
+ term_set_bracketed_paste_mode(paste_use_bracketed_mode);
}
void gui_readline_init(void)
@@ -943,13 +1117,17 @@ void gui_readline_init(void)
paste_entry = NULL;
paste_entry_pos = 0;
paste_buffer = g_array_new(FALSE, FALSE, sizeof(unichar));
+ paste_buffer_rest = g_array_new(FALSE, FALSE, sizeof(unichar));
paste_old_prompt = NULL;
paste_timeout_id = -1;
+ paste_bracketed_mode = FALSE;
g_get_current_time(&last_keypress);
input_listen_init(STDIN_FILENO);
+ settings_add_bool("lookandfeel", "term_appkey_mode", TRUE);
settings_add_str("history", "scroll_page_count", "/2");
settings_add_time("misc", "paste_detect_time", "5msecs");
+ settings_add_bool("misc", "paste_use_bracketed_mode", FALSE);
/* NOTE: function keys can generate at least 5 characters long
keycodes. this must be larger to allow them to work. */
settings_add_int("misc", "paste_verify_line_count", 5);
@@ -1020,6 +1198,10 @@ void gui_readline_init(void)
key_bind("key", NULL, "meta2-5F", "cend", (SIGNAL_FUNC) key_combo);
key_bind("key", NULL, "meta2-1;5F", "cend", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta-O-M", "return", (SIGNAL_FUNC) key_combo);
+
+ key_bind("paste_start", "Bracketed paste start", "meta2-200~", "paste_start", (SIGNAL_FUNC) key_paste_start);
+
/* cursor movement */
key_bind("backward_character", "Move the cursor a character backward", "left", NULL, (SIGNAL_FUNC) key_backward_character);
key_bind("forward_character", "Move the cursor a character forward", "right", NULL, (SIGNAL_FUNC) key_forward_character);
@@ -1050,6 +1232,8 @@ void gui_readline_init(void)
key_bind("erase_to_beg_of_line", "Erase everything before the cursor", NULL, NULL, (SIGNAL_FUNC) key_erase_to_beg_of_line);
key_bind("erase_to_end_of_line", "Erase everything after the cursor", "^K", NULL, (SIGNAL_FUNC) key_erase_to_end_of_line);
key_bind("yank_from_cutbuffer", "\"Undelete\", paste the last deleted text", "^Y", NULL, (SIGNAL_FUNC) key_yank_from_cutbuffer);
+ key_bind("yank_next_cutbuffer", "Revert to the previous last deleted text", NULL, NULL, (SIGNAL_FUNC) key_yank_next_cutbuffer);
+ key_bind("append_next_kill", "Append next deletion", NULL, NULL, (SIGNAL_FUNC) key_append_next_kill);
key_bind("transpose_characters", "Swap current and previous character", "^T", NULL, (SIGNAL_FUNC) key_transpose_characters);
key_bind("transpose_words", "Swap current and previous word", NULL, NULL, (SIGNAL_FUNC) key_transpose_words);
key_bind("capitalize_word", "Capitalize the current word", NULL, NULL, (SIGNAL_FUNC) key_capitalize_word);
@@ -1115,6 +1299,8 @@ void gui_readline_deinit(void)
key_configure_freeze();
+ key_unbind("paste_start", (SIGNAL_FUNC) key_paste_start);
+
key_unbind("backward_character", (SIGNAL_FUNC) key_backward_character);
key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character);
key_unbind("backward_word", (SIGNAL_FUNC) key_backward_word);
@@ -1137,6 +1323,8 @@ void gui_readline_deinit(void)
key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line);
key_unbind("erase_to_end_of_line", (SIGNAL_FUNC) key_erase_to_end_of_line);
key_unbind("yank_from_cutbuffer", (SIGNAL_FUNC) key_yank_from_cutbuffer);
+ key_unbind("yank_next_cutbuffer", (SIGNAL_FUNC) key_yank_next_cutbuffer);
+ key_unbind("append_next_kill", (SIGNAL_FUNC) key_append_next_kill);
key_unbind("transpose_characters", (SIGNAL_FUNC) key_transpose_characters);
key_unbind("transpose_words", (SIGNAL_FUNC) key_transpose_words);
@@ -1172,6 +1360,7 @@ void gui_readline_deinit(void)
key_unbind("stop_irc", (SIGNAL_FUNC) key_sig_stop);
keyboard_destroy(keyboard);
g_array_free(paste_buffer, TRUE);
+ g_array_free(paste_buffer_rest, TRUE);
key_configure_thaw();
diff --git a/src/fe-text/gui-windows.c b/src/fe-text/gui-windows.c
index 4213149d..c63c495c 100644
--- a/src/fe-text/gui-windows.c
+++ b/src/fe-text/gui-windows.c
@@ -49,6 +49,7 @@ static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window,
settings_get_int("indent"),
!settings_get_bool("indent_always"),
get_default_indent_func());
+ textbuffer_view_set_break_wide(gui->view, settings_get_bool("break_wide"));
if (parent->active == window)
textbuffer_view_set_window(gui->view, parent->screen_win);
return gui;
@@ -201,12 +202,14 @@ void gui_windows_reset_settings(void)
for (tmp = windows; tmp != NULL; tmp = tmp->next) {
WINDOW_REC *rec = tmp->data;
- GUI_WINDOW_REC *gui = WINDOW_GUI(rec);
+ GUI_WINDOW_REC *gui = WINDOW_GUI(rec);
- textbuffer_view_set_default_indent(gui->view,
+ textbuffer_view_set_break_wide(gui->view, settings_get_bool("break_wide"));
+
+ textbuffer_view_set_default_indent(gui->view,
settings_get_int("indent"),
!settings_get_bool("indent_always"),
- get_default_indent_func());
+ get_default_indent_func());
textbuffer_view_set_scroll(gui->view,
gui->use_scroll ? gui->scroll :
@@ -281,6 +284,7 @@ void gui_windows_init(void)
settings_add_bool("lookandfeel", "autostick_split_windows", TRUE);
settings_add_int("lookandfeel", "indent", 10);
settings_add_bool("lookandfeel", "indent_always", FALSE);
+ settings_add_bool("lookandfeel", "break_wide", FALSE);
settings_add_bool("lookandfeel", "scroll", TRUE);
window_create_override = -1;
diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c
index c0524247..ad79e0c4 100644
--- a/src/fe-text/irssi.c
+++ b/src/fe-text/irssi.c
@@ -74,10 +74,7 @@ void mainwindow_activity_deinit(void);
void mainwindows_layout_init(void);
void mainwindows_layout_deinit(void);
-void term_dummy_init(void);
-void term_dummy_deinit(void);
-
-static int dirty, full_redraw, dummy;
+static int dirty, full_redraw;
static GMainLoop *main_loop;
int quitting;
@@ -122,7 +119,7 @@ void irssi_set_dirty(void)
static void dirty_check(void)
{
- if (!dirty || dummy)
+ if (!dirty)
return;
term_resize_dirty();
@@ -171,27 +168,22 @@ static void textui_finish_init(void)
{
quitting = FALSE;
- if (dummy)
- term_dummy_init();
- else {
- term_refresh_freeze();
- textbuffer_init();
- textbuffer_view_init();
- textbuffer_commands_init();
- gui_expandos_init();
- gui_printtext_init();
- gui_readline_init();
- lastlog_init();
- mainwindows_init();
- mainwindow_activity_init();
- mainwindows_layout_init();
- gui_windows_init();
- statusbar_init();
- term_refresh_thaw();
-
- /* don't check settings with dummy mode */
- settings_check();
- }
+ term_refresh_freeze();
+ textbuffer_init();
+ textbuffer_view_init();
+ textbuffer_commands_init();
+ gui_expandos_init();
+ gui_printtext_init();
+ gui_readline_init();
+ lastlog_init();
+ mainwindows_init();
+ mainwindow_activity_init();
+ mainwindows_layout_init();
+ gui_windows_init();
+ statusbar_init();
+ term_refresh_thaw();
+
+ settings_check();
module_register("core", "fe-text");
@@ -233,25 +225,21 @@ static void textui_deinit(void)
dirty_check(); /* one last time to print any quit messages */
signal_remove("gui exit", (SIGNAL_FUNC) sig_exit);
- if (dummy)
- term_dummy_deinit();
- else {
- lastlog_deinit();
- statusbar_deinit();
- gui_printtext_deinit();
- gui_readline_deinit();
- gui_windows_deinit();
- mainwindows_layout_deinit();
- mainwindow_activity_deinit();
- mainwindows_deinit();
- gui_expandos_deinit();
- textbuffer_commands_deinit();
- textbuffer_view_deinit();
- textbuffer_deinit();
-
- term_refresh_thaw();
- term_deinit();
- }
+ lastlog_deinit();
+ statusbar_deinit();
+ gui_printtext_deinit();
+ gui_readline_deinit();
+ gui_windows_deinit();
+ mainwindows_layout_deinit();
+ mainwindow_activity_deinit();
+ mainwindows_deinit();
+ gui_expandos_deinit();
+ textbuffer_commands_deinit();
+ textbuffer_view_deinit();
+ textbuffer_deinit();
+
+ term_refresh_thaw();
+ term_deinit();
theme_unregister();
@@ -271,49 +259,15 @@ static void check_files(void)
}
}
-#ifdef WIN32
-static void winsock_init(void)
-{
- WORD wVersionRequested;
- WSADATA wsaData;
-
- wVersionRequested = MAKEWORD(2, 2);
-
- if (WSAStartup(wVersionRequested, &wsaData) != 0) {
- printf("Error initializing winsock\n");
- exit(1);
- }
-}
-#endif
-
-#ifdef USE_GC
-#ifdef HAVE_GC_H
-# include <gc.h>
-#else
-# include <gc/gc.h>
-#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;
static GOptionEntry options[] = {
- { "dummy", 'd', 0, G_OPTION_ARG_NONE, &dummy, "Use the dummy terminal mode", NULL },
{ "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Display irssi version", NULL },
{ NULL }
};
-
-#ifdef USE_GC
- g_mem_set_vtable(&gc_mem_table);
-#endif
+ int loglev;
core_register_options();
fe_common_core_register_options();
@@ -328,15 +282,11 @@ int main(int argc, char **argv)
srand(time(NULL));
- dummy = FALSE;
quitting = FALSE;
core_preinit(argv[0]);
check_files();
-#ifdef WIN32
- winsock_init();
-#endif
#ifdef HAVE_SOCKS
SOCKSinit(argv[0]);
#endif
@@ -346,32 +296,30 @@ int main(int argc, char **argv)
before this call.
locales aren't actually used for anything else than autodetection
- of UTF-8 currently..
+ of UTF-8 currently..
- furthermore to get the users's charset with g_get_charset() properly
+ furthermore to get the users's charset with g_get_charset() properly
you have to call setlocale(LC_ALL, "") */
setlocale(LC_ALL, "");
+ loglev = g_log_set_always_fatal(G_LOG_FATAL_MASK | G_LOG_LEVEL_CRITICAL);
textui_init();
- if (!dummy && !term_init()) {
- fprintf(stderr, "Can't initialize screen handling, quitting.\n");
- fprintf(stderr, "You can still use the dummy mode with -d parameter\n");
+ if (!term_init()) {
+ fprintf(stderr, "Can't initialize screen handling.\n");
return 1;
}
+ g_log_set_always_fatal(loglev);
textui_finish_init();
main_loop = g_main_new(TRUE);
/* 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();
+ term_refresh_freeze();
g_main_iteration(TRUE);
- if (!dummy) term_refresh_thaw();
+ term_refresh_thaw();
if (reload_config) {
/* SIGHUP received, do /RELOAD */
diff --git a/src/fe-text/lastlog.c b/src/fe-text/lastlog.c
index 417eb484..c0b1dde8 100644
--- a/src/fe-text/lastlog.c
+++ b/src/fe-text/lastlog.c
@@ -39,21 +39,10 @@
Returns -1 if unknown option was given. */
int cmd_options_get_level(const char *cmd, GHashTable *optlist)
{
- GSList *list, *tmp, *next;
+ GList *list;
int level, retlevel;
- /* get all the options, then remove the known ones. there should
- be only one left - the server tag. */
- list = hashtable_get_keys(optlist);
- if (cmd != NULL) {
- for (tmp = list; tmp != NULL; tmp = next) {
- char *option = tmp->data;
- next = tmp->next;
-
- if (command_have_option(cmd, option))
- list = g_slist_remove(list, option);
- }
- }
+ list = optlist_remove_known(cmd, optlist);
retlevel = 0;
while (list != NULL) {
@@ -68,12 +57,31 @@ int cmd_options_get_level(const char *cmd, GHashTable *optlist)
}
retlevel |= level;
- list = g_slist_remove(list, list->data);
+ list = g_list_remove(list, list->data);
}
return retlevel;
}
+static void prepend_date(WINDOW_REC *window, LINE_REC *rec, GString *line)
+{
+ THEME_REC *theme = NULL;
+ TEXT_DEST_REC dest = {0};
+ char *format = NULL, datestamp[20] = {0};
+ struct tm *tm = localtime(&rec->info.time);
+ int ret = 0;
+
+ theme = window->theme != NULL ? window->theme : current_theme;
+ format_create_dest(&dest, NULL, NULL, MSGLEVEL_LASTLOG, window);
+ format = format_get_text_theme(theme, MODULE_NAME, &dest, TXT_LASTLOG_DATE);
+
+ ret = strftime(datestamp, sizeof(datestamp), format, tm);
+ g_free(format);
+ if (ret <= 0) return;
+
+ g_string_prepend(line, datestamp);
+}
+
static void show_lastlog(const char *searchtext, GHashTable *optlist,
int start, int count, FILE *fhandle)
{
@@ -82,7 +90,7 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
GList *list, *tmp;
GString *line;
char *str;
- int level, before, after, len;
+ int level, before, after, len, date = FALSE;
level = cmd_options_get_level("lastlog", optlist);
if (level == -1) return; /* error in options */
@@ -132,6 +140,9 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
atoi(str) : DEFAULT_LASTLOG_AFTER;
}
+ if (g_hash_table_lookup(optlist, "date") != NULL)
+ date = TRUE;
+
list = textbuffer_find_text(WINDOW_GUI(window)->view->buffer, startline,
level, MSGLEVEL_LASTLOG,
searchtext, before, after,
@@ -199,6 +210,9 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
g_string_prepend(line, timestamp);
}
+ if (date == TRUE)
+ prepend_date(window, rec, line);
+
/* write to file/window */
if (fhandle != NULL) {
fwrite(line->str, line->len, 1, fhandle);
@@ -223,7 +237,7 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
}
/* SYNTAX: LASTLOG [-] [-file <filename>] [-window <ref#|name>] [-new | -away]
- [-<level> -<level...>] [-clear] [-count] [-case]
+ [-<level> -<level...>] [-clear] [-count] [-case] [-date]
[-regexp | -word] [-before [<#>]] [-after [<#>]]
[-<# before+after>] [<pattern>] [<count> [<start>]] */
static void cmd_lastlog(const char *data)
@@ -285,7 +299,7 @@ void lastlog_init(void)
{
command_bind("lastlog", NULL, (SIGNAL_FUNC) cmd_lastlog);
- command_set_options("lastlog", "!- # force clear -file -window new away word regexp case count @a @after @before");
+ command_set_options("lastlog", "!- # force clear -file -window new away word regexp case count date @a @after @before");
}
void lastlog_deinit(void)
diff --git a/src/fe-text/mainwindows-layout.c b/src/fe-text/mainwindows-layout.c
index 28fae924..020969e6 100644
--- a/src/fe-text/mainwindows-layout.c
+++ b/src/fe-text/mainwindows-layout.c
@@ -70,7 +70,7 @@ static void main_window_save(MAIN_WINDOW_REC *window, CONFIG_NODE *node)
char num[MAX_INT_STRLEN];
ltoa(num, window->active->refnum);
- node = config_node_section(node, num, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, num, NODE_TYPE_BLOCK);
iconfig_node_set_int(node, "first_line", window->first_line);
iconfig_node_set_int(node, "lines", window->height);
@@ -179,6 +179,7 @@ static void sig_layout_restore(void)
lower_window = NULL; lower_size = 0;
for (i = 0, tmp = sorted_config; i < windows_count; tmp = tmp->next, i++) {
CONFIG_NODE *node = tmp->data;
+ if (node->key == NULL) continue;
/* create a new window + mainwindow */
signal_emit("gui window create override", 1,
diff --git a/src/fe-text/module-formats.c b/src/fe-text/module-formats.c
index 1d905095..899827c2 100644
--- a/src/fe-text/module-formats.c
+++ b/src/fe-text/module-formats.c
@@ -33,6 +33,7 @@ FORMAT_REC gui_text_formats[] =
{ "lastlog_start", "{hilight Lastlog}:", 0 },
{ "lastlog_end", "{hilight End of Lastlog}", 0 },
{ "lastlog_separator", "--", 0 },
+ { "lastlog_date", "%%F ", 0 },
/* ---- */
{ NULL, "Windows", 0 },
diff --git a/src/fe-text/module-formats.h b/src/fe-text/module-formats.h
index 4eebfc3e..3fa8c511 100644
--- a/src/fe-text/module-formats.h
+++ b/src/fe-text/module-formats.h
@@ -10,6 +10,7 @@ enum {
TXT_LASTLOG_START,
TXT_LASTLOG_END,
TXT_LASTLOG_SEPARATOR,
+ TXT_LASTLOG_DATE,
TXT_FILL_2,
diff --git a/src/fe-text/statusbar-config.c b/src/fe-text/statusbar-config.c
index deaa1b5d..48f4aa61 100644
--- a/src/fe-text/statusbar-config.c
+++ b/src/fe-text/statusbar-config.c
@@ -95,7 +95,7 @@ statusbar_config_find(STATUSBAR_GROUP_REC *group, const char *name)
for (tmp = group->config_bars; tmp != NULL; tmp = tmp->next) {
STATUSBAR_CONFIG_REC *config = tmp->data;
- if (strcmp(config->name, name) == 0)
+ if (g_strcmp0(config->name, name) == 0)
return config;
}
@@ -137,7 +137,7 @@ static void statusbar_read_item(STATUSBAR_CONFIG_REC *bar, CONFIG_NODE *node)
int priority, right_alignment;
priority = config_node_get_int(node, "priority", 0);
- right_alignment = strcmp(config_node_get_str(node, "alignment", ""), "right") == 0;
+ right_alignment = g_strcmp0(config_node_get_str(node, "alignment", ""), "right") == 0;
statusbar_item_config_create(bar, node->key,
priority, right_alignment);
}
@@ -177,7 +177,7 @@ static void statusbar_read(STATUSBAR_GROUP_REC *group, CONFIG_NODE *node)
bar->placement = STATUSBAR_TOP;
bar->position = config_node_get_int(node, "position", 0);
- node = config_node_section(node, "items", -1);
+ node = iconfig_node_section(node, "items", -1);
if (node != NULL) {
/* we're overriding the items - destroy the old */
while (bar->items != NULL)
@@ -194,6 +194,8 @@ static void statusbar_read_group(CONFIG_NODE *node)
STATUSBAR_GROUP_REC *group;
GSList *tmp;
+ g_return_if_fail(is_node_list(node));
+
group = statusbar_group_find(node->key);
if (group == NULL) {
group = statusbar_group_create(node->key);
@@ -227,7 +229,7 @@ static void read_statusbar_config_from_node(CONFIG_NODE *node)
CONFIG_NODE *items;
GSList *tmp;
- items = config_node_section(node, "items", -1);
+ items = iconfig_node_section(node, "items", -1);
if (items != NULL)
statusbar_read_items(items);
@@ -369,7 +371,7 @@ static void cmd_statusbar_reset(const char *data, void *server,
CONFIG_NODE *parent;
parent = iconfig_node_traverse("statusbar", TRUE);
- parent = config_node_section(parent, active_statusbar_group->name,
+ parent = iconfig_node_section(parent, active_statusbar_group->name,
NODE_TYPE_BLOCK);
iconfig_node_set_str(parent, node->key, NULL);
@@ -432,7 +434,7 @@ static CONFIG_NODE *statusbar_items_section(CONFIG_NODE *parent)
CONFIG_NODE *node;
GSList *tmp;
- node = config_node_section(parent, "items", -1);
+ node = iconfig_node_section(parent, "items", -1);
if (node != NULL)
return node;
@@ -446,11 +448,11 @@ static CONFIG_NODE *statusbar_items_section(CONFIG_NODE *parent)
/* since items list in config file overrides defaults,
we'll need to copy the whole list. */
- parent = config_node_section(parent, "items", NODE_TYPE_BLOCK);
+ parent = iconfig_node_section(parent, "items", NODE_TYPE_BLOCK);
for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
SBAR_ITEM_CONFIG_REC *rec = tmp->data;
- node = config_node_section(parent, rec->name,
+ node = iconfig_node_section(parent, rec->name,
NODE_TYPE_BLOCK);
if (rec->priority != 0)
iconfig_node_set_int(node, "priority", rec->priority);
@@ -487,7 +489,7 @@ static void cmd_statusbar_add(const char *data, void *server,
if (value != NULL) index = config_node_index(node, value)+1;
/* create/move item */
- node = config_node_section_index(node, name, index, NODE_TYPE_BLOCK);
+ node = iconfig_node_section_index(node, name, index, NODE_TYPE_BLOCK);
/* set the options */
value = g_hash_table_lookup(optlist, "priority");
@@ -511,7 +513,7 @@ static void cmd_statusbar_remove(const char *data, void *server,
if (node == NULL)
return;
- if (config_node_section(node, data, -1) != NULL)
+ if (iconfig_node_section(node, data, -1) != NULL)
iconfig_node_set_str(node, data, NULL);
else {
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
@@ -545,9 +547,9 @@ static void cmd_statusbar(const char *data)
/* lookup/create the statusbar node */
node = iconfig_node_traverse("statusbar", TRUE);
- node = config_node_section(node, active_statusbar_group->name,
+ node = iconfig_node_section(node, active_statusbar_group->name,
NODE_TYPE_BLOCK);
- node = config_node_section(node, name, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, name, NODE_TYPE_BLOCK);
/* call the subcommand */
signal = g_strconcat("command statusbar ", cmd, NULL);
diff --git a/src/fe-text/statusbar-items.c b/src/fe-text/statusbar-items.c
index 044c2fa0..de4499b4 100644
--- a/src/fe-text/statusbar-items.c
+++ b/src/fe-text/statusbar-items.c
@@ -143,16 +143,34 @@ static char *get_activity_list(MAIN_WINDOW_REC *window, int normal, int hilight)
static void item_act(SBAR_ITEM_REC *item, int get_size_only)
{
char *actlist;
+ int max_size;
+
+ if (get_size_only) {
+ if (activity_list == NULL)
+ item->min_size = item->max_size = 0;
+ /* Skip activity calculation on regular trigger, only
+ set dirty */
+ return;
+ }
actlist = get_activity_list(item->bar->parent_window, TRUE, TRUE);
if (actlist == NULL) {
- if (get_size_only)
- item->min_size = item->max_size = 0;
return;
}
- statusbar_item_default_handler(item, get_size_only,
+ max_size = item->max_size;
+ statusbar_item_default_handler(item, TRUE,
+ NULL, actlist, FALSE);
+ statusbar_item_default_handler(item, FALSE,
NULL, actlist, FALSE);
+ if (max_size != item->max_size) {
+ /* Due to above hack of skipping the calculation, we
+ need to manually trigger the redraw process now or
+ we won't see the item */
+ item->bar->dirty = item->dirty = TRUE;
+ statusbar_redraw(item->bar, TRUE);
+ statusbar_redraw_dirty();
+ }
g_free_not_null(actlist);
}
@@ -289,6 +307,10 @@ static void sig_statusbar_more_updated(void)
{
int visible;
+ /* no active window, for example during /window hide */
+ if (active_win == NULL)
+ return;
+
visible = g_slist_find(more_visible, WINDOW_MAIN(active_win)) != NULL;
if (WINDOW_GUI(active_win)->view->more_text != visible)
statusbar_items_redraw("more");
@@ -347,10 +369,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/statusbar.c b/src/fe-text/statusbar.c
index ef5abc55..f0dff828 100644
--- a/src/fe-text/statusbar.c
+++ b/src/fe-text/statusbar.c
@@ -129,7 +129,7 @@ STATUSBAR_GROUP_REC *statusbar_group_find(const char *name)
for (tmp = statusbar_groups; tmp != NULL; tmp = tmp->next) {
STATUSBAR_GROUP_REC *rec = tmp->data;
- if (strcmp(rec->name, name) == 0)
+ if (g_strcmp0(rec->name, name) == 0)
return rec;
}
@@ -617,7 +617,7 @@ STATUSBAR_REC *statusbar_find(STATUSBAR_GROUP_REC *group, const char *name,
STATUSBAR_REC *rec = tmp->data;
if (rec->parent_window == window &&
- strcmp(rec->config->name, name) == 0)
+ g_strcmp0(rec->config->name, name) == 0)
return rec;
}
@@ -668,11 +668,11 @@ void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
int escape_vars)
{
SERVER_REC *server;
- WI_ITEM_REC *wiitem;
+ WI_ITEM_REC *wiitem;
char *tmpstr, *tmpstr2;
theme_rm_col reset;
- strcpy(reset.m, "n");
int len;
+ strcpy(reset.m, "n");
if (str == NULL)
str = statusbar_item_get_value(item);
diff --git a/src/fe-text/term-curses.c b/src/fe-text/term-curses.c
deleted file mode 100644
index 752edd7f..00000000
--- a/src/fe-text/term-curses.c
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- term-curses.c : irssi
-
- Copyright (C) 1999-2001 Timo Sirainen
-
- 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 "settings.h"
-
-#include "term.h"
-#include "mainwindows.h"
-
-#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
-# include <ncurses.h>
-#else
-# include <curses.h>
-#endif
-#include <termios.h>
-#include <signal.h>
-
-#ifndef COLOR_PAIRS
-# define COLOR_PAIRS 64
-#endif
-
-#if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM)
-# define USE_RESIZE_TERM
-#endif
-
-#ifndef _POSIX_VDISABLE
-# define _POSIX_VDISABLE 0
-#endif
-
-struct _TERM_WINDOW {
- int x, y;
- int width, height;
- WINDOW *win;
-};
-
-TERM_WINDOW *root_window;
-
-static int curs_x, curs_y;
-static int freeze_refresh;
-static struct termios old_tio;
-
-static int init_curses(void)
-{
- char ansi_tab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
- int num;
- struct termios tio;
-
- if (!initscr())
- return FALSE;
-
- cbreak(); noecho(); idlok(stdscr, 1);
-#ifdef HAVE_CURSES_IDCOK
- /*idcok(stdscr, 1); - disabled currently, causes redrawing problems with NetBSD */
-#endif
- intrflush(stdscr, FALSE); nodelay(stdscr, TRUE);
-
- /* Disable INTR, QUIT, VDSUSP and SUSP keys */
- if (tcgetattr(0, &old_tio) == 0) {
- memcpy(&tio, &old_tio, sizeof(tio));
- tio.c_cc[VINTR] = _POSIX_VDISABLE;
- tio.c_cc[VQUIT] = _POSIX_VDISABLE;
-#ifdef VDSUSP
- tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
-#endif
-#ifdef VSUSP
- tio.c_cc[VSUSP] = _POSIX_VDISABLE;
-#endif
- tcsetattr(0, TCSADRAIN, &tio);
- }
-
- if (has_colors())
- start_color();
- else if (term_use_colors)
- term_use_colors = FALSE;
-
-#ifdef HAVE_NCURSES_USE_DEFAULT_COLORS
- /* this lets us to use the "default" background color for colors <= 7 so
- background pixmaps etc. show up right */
- use_default_colors();
-
- for (num = 1; num < COLOR_PAIRS; num++)
- init_pair(num, ansi_tab[num & 7], num <= 7 ? -1 : ansi_tab[num >> 3]);
-
- init_pair(63, 0, -1); /* hm.. not THAT good idea, but probably more
- people want dark grey than white on white.. */
-#else
- for (num = 1; num < COLOR_PAIRS; num++)
- init_pair(num, ansi_tab[num & 7], ansi_tab[num >> 3]);
- init_pair(63, 0, 0);
-#endif
-
- clear();
- return TRUE;
-}
-
-static int term_init_int(void)
-{
- int ret;
-
- ret = init_curses();
- if (!ret) return 0;
-
- curs_x = curs_y = 0;
- freeze_refresh = 0;
-
- root_window = g_new0(TERM_WINDOW, 1);
- root_window->win = stdscr;
-
- term_width = COLS;
- term_height = LINES;
- return ret;
-}
-
-static void term_deinit_int(void)
-{
- tcsetattr(0, TCSADRAIN, &old_tio);
-
- endwin();
- g_free_and_null(root_window);
-}
-
-int term_init(void)
-{
- if (!term_init_int())
- return FALSE;
-
- settings_add_int("lookandfeel", "default_color", 7);
- term_common_init();
- return TRUE;
-}
-
-void term_deinit(void)
-{
- term_common_deinit();
- term_deinit_int();
-}
-
-/* Resize terminal - if width or height is negative,
- the new size is unknown and should be figured out somehow */
-void term_resize(int width, int height)
-{
-#ifdef HAVE_CURSES_RESIZETERM
- if (width < 0 || height < 0) {
-#endif
- term_deinit_int();
- term_init_int();
-#ifdef HAVE_CURSES_RESIZETERM
- } else if (term_width != width || term_height != height) {
- term_width = width;
- term_height = height;
- resizeterm(term_height, term_width);
- }
-#endif
-}
-
-void term_resize_final(int width, int height)
-{
-#ifdef HAVE_CURSES_RESIZETERM
- if (width < 0 || height < 0)
- mainwindows_recreate();
-#else
- mainwindows_recreate();
-#endif
-}
-
-/* Returns TRUE if terminal has colors */
-int term_has_colors(void)
-{
- return has_colors();
-}
-
-/* Force the colors on any way you can */
-void term_force_colors(int set)
-{
- /* don't do anything with curses */
-}
-
-/* Clear screen */
-void term_clear(void)
-{
- term_set_color(root_window, 0);
- clear();
-}
-
-/* Beep */
-void term_beep(void)
-{
- beep();
-}
-
-/* Create a new window in terminal */
-TERM_WINDOW *term_window_create(int x, int y, int width, int height)
-{
- TERM_WINDOW *window;
-
- window = g_new0(TERM_WINDOW, 1);
- window->x = x; window->y = y;
- window->width = width; window->height = height;
- window->win = newwin(height, width, y, x);
- if (window->win == NULL)
- g_error("newwin() failed: %d,%d %d,%d", x, y, width, height);
- idlok(window->win, 1);
-
- return window;
-}
-
-/* Destroy a terminal window */
-void term_window_destroy(TERM_WINDOW *window)
-{
- delwin(window->win);
- g_free(window);
-}
-
-/* Move/resize a window */
-void term_window_move(TERM_WINDOW *window, int x, int y,
- int width, int height)
-{
- /* some checks to make sure the window is visible in screen,
- otherwise curses could get nasty and not show our window anymore. */
- if (width < 1) width = 1;
- if (height < 1) height = 1;
- if (x+width > term_width) x = term_width-width;
- if (y+height > term_height) y = term_height-height;
-
-#ifdef HAVE_CURSES_WRESIZE
- if (window->width != width || window->height != height)
- wresize(window->win, height, width);
- if (window->x != x || window->y != y)
- mvwin(window->win, y, x);
-#else
- if (window->width != width || window->height != height ||
- window->x != x || window->y != y) {
- delwin(window->win);
- window->win = newwin(height, width, y, x);
- idlok(window->win, 1);
- }
-#endif
- window->x = x; window->y = y;
- window->width = width; window->height = height;
-}
-
-/* Clear window */
-void term_window_clear(TERM_WINDOW *window)
-{
- werase(window->win);
-}
-
-/* Scroll window up/down */
-void term_window_scroll(TERM_WINDOW *window, int count)
-{
- scrollok(window->win, TRUE);
- wscrl(window->win, count);
- scrollok(window->win, FALSE);
-}
-
-static int get_attr(int color)
-{
- int attr;
-
- if ((color & FG_MASK) >> 4)
- color = (color & ~FG_MASK) | term_color256map[color & FG_MASK];
- if ((color & BG_MASK) >> (BG_SHIFT + 4))
- color = (color & ~BG_MASK) | (term_color256map[(color & BG_MASK) >> BG_SHIFT] << BG_SHIFT);
- if (!term_use_colors)
- attr = (color & (0x7 << BG_SHIFT)) ? A_REVERSE : 0;
- else if ((color & ((0xf << BG_SHIFT) | 0xf)) == 8 || (color & (FG_MASK | BG_MASK | ATTR_RESETFG)) == 0)
- attr = COLOR_PAIR(63);
- else if ((color & ((0x7 << BG_SHIFT) | 0x7)) == 0)
- attr = A_NORMAL;
- else {
- if (color & ATTR_RESETFG) {
- color &= ~FG_MASK;
- color |= settings_get_int("default_color");
- }
- attr = COLOR_PAIR((color&0x7) | ((color&(0x7<<BG_SHIFT))>>BG_SHIFT<<3));
- }
-
- if ((color & 0x8) || (color & ATTR_BOLD)) attr |= A_BOLD;
- if (color & ATTR_BLINK) attr |= A_BLINK;
-
- if (color & ATTR_UNDERLINE) attr |= A_UNDERLINE;
- if (color & ATTR_REVERSE) attr |= A_REVERSE;
-#ifdef A_ITALIC
- if (color & ATTR_ITALIC) attr |= A_ITALIC;
-#endif
- return attr;
-}
-
-/* Change active color */
-void term_set_color(TERM_WINDOW *window, int col)
-{
- wattrset(window->win, get_attr(col));
- wbkgdset(window->win, ' ' | get_attr(col));
-}
-
-void term_move(TERM_WINDOW *window, int x, int y)
-{
- wmove(window->win, y, x);
-}
-
-void term_addch(TERM_WINDOW *window, char chr)
-{
- waddch(window->win, chr);
-}
-
-void term_add_unichar(TERM_WINDOW *window, unichar chr)
-{
-#ifdef WIDEC_CURSES
- cchar_t wch;
- wchar_t temp[2];
- temp[0] = chr;
- temp[1] = 0;
- if (setcchar(&wch, temp, A_NORMAL, 0, NULL) == OK)
- wadd_wch(window->win, &wch);
- else
-#endif
- waddch(window->win, chr);
-}
-
-void term_addstr(TERM_WINDOW *window, const char *str)
-{
- waddstr(window->win, (const char *) str);
-}
-
-void term_clrtoeol(TERM_WINDOW *window)
-{
- wclrtoeol(window->win);
-}
-
-void term_move_cursor(int x, int y)
-{
- curs_x = x;
- curs_y = y;
-}
-
-void term_refresh_freeze(void)
-{
- freeze_refresh++;
-}
-
-void term_refresh_thaw(void)
-{
- if (freeze_refresh > 0) {
- freeze_refresh--;
- if (freeze_refresh == 0) term_refresh(NULL);
- }
-}
-
-void term_refresh(TERM_WINDOW *window)
-{
- if (window != NULL)
- wnoutrefresh(window->win);
-
- if (freeze_refresh == 0) {
- move(curs_y, curs_x);
- wnoutrefresh(stdscr);
- doupdate();
- }
-}
-
-void term_stop(void)
-{
- term_deinit_int();
- kill(getpid(), SIGTSTP);
- term_init_int();
- irssi_redraw();
-}
-
-void term_set_input_type(int type)
-{
-}
-
-void term_gets(GArray *buffer, int *line_count)
-{
-#ifdef WIDEC_CURSES
- wint_t key;
-#else
- int key;
-#endif
-
- for (;;) {
-#ifdef WIDEC_CURSES
- if (get_wch(&key) == ERR)
-#else
- if ((key = getch()) == ERR)
-#endif
- break;
-#ifdef KEY_RESIZE
- if (key == KEY_RESIZE)
- continue;
-#endif
-
- g_array_append_val(buffer, key);
- if (key == '\r' || key == '\n')
- (*line_count)++;
- }
-}
diff --git a/src/fe-text/term-dummy.c b/src/fe-text/term-dummy.c
deleted file mode 100644
index 6a56af88..00000000
--- a/src/fe-text/term-dummy.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- term-dummy.c : irssi
-
- Copyright (C) 2001 Timo Sirainen
-
- 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 "fe-windows.h"
-
-static int newline;
-
-static GIOChannel *stdin_channel;
-static int readtag;
-static GString *input;
-
-static void sig_gui_printtext(WINDOW_REC *window, void *fgcolor,
- void *bgcolor, void *pflags,
- char *str, void *level)
-{
- if (newline) {
- newline = FALSE;
- printf("\r");
- }
-
- printf("%s", str);
-}
-
-static void sig_gui_printtext_finished(WINDOW_REC *window)
-{
- printf("\n");
- newline = TRUE;
-}
-
-static void sig_window_created(WINDOW_REC *window)
-{
- window->width = 80;
- window->height = 25;
-}
-
-static void readline(void)
-{
- unsigned char buffer[128];
- char *p;
- int ret, i;
-
- ret = read(0, buffer, sizeof(buffer));
- if (ret == 0 || (ret == -1 && errno != EINTR)) {
- /* lost terminal */
- signal_emit("command quit", 1, "Lost terminal");
- return;
- }
-
- for (i = 0; i < ret; i++)
- g_string_append_c(input, buffer[i]);
-
- p = strchr(input->str, '\n');
- if (p != NULL) {
- *p = '\0';
- signal_emit("send command", 3, input->str,
- active_win->active_server, active_win->active);
- *p = '\n';
- g_string_erase(input, 0, (int) (p-input->str)+1);
- }
-}
-
-void term_dummy_init(void)
-{
- newline = TRUE;
- input = g_string_new(NULL);
-
- signal_add("gui print text", (SIGNAL_FUNC) sig_gui_printtext);
- signal_add("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
- signal_add("window created", (SIGNAL_FUNC) sig_window_created);
-
- stdin_channel = g_io_channel_unix_new(0);
- readtag = g_input_add_full(stdin_channel,
- G_PRIORITY_HIGH, G_INPUT_READ,
- (GInputFunction) readline, NULL);
- g_io_channel_unref(stdin_channel);
-}
-
-void term_dummy_deinit(void)
-{
- signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_printtext);
- signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
- signal_remove("window created", (SIGNAL_FUNC) sig_window_created);
-
- g_source_remove(readtag);
- g_string_free(input, TRUE);
-}
diff --git a/src/fe-text/term-terminfo.c b/src/fe-text/term-terminfo.c
index 29d3f7eb..3098a4e4 100644
--- a/src/fe-text/term-terminfo.c
+++ b/src/fe-text/term-terminfo.c
@@ -390,7 +390,8 @@ void term_set_color(TERM_WINDOW *window, int col)
}
/* set background color */
- if (window && (term_color256map[bg&0xff]&8) == window->term->TI_colors)
+ if (window && window->term->TI_colors &&
+ (term_color256map[bg&0xff]&8) == window->term->TI_colors)
col |= ATTR_BLINK;
if (col & ATTR_BLINK)
current_term->set_blink(current_term);
@@ -413,7 +414,8 @@ void term_set_color(TERM_WINDOW *window, int col)
terminfo_set_reverse();
/* bold */
- if (window && (term_color256map[fg&0xff]&8) == window->term->TI_colors)
+ if (window && window->term->TI_colors &&
+ (term_color256map[fg&0xff]&8) == window->term->TI_colors)
col |= ATTR_BOLD;
if (col & ATTR_BOLD)
terminfo_set_bold();
@@ -520,15 +522,43 @@ 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_validated(ptr, -1);
+ /* On utf8 error, treat as single byte and try to
+ continue interpretting rest of string as utf8 */
+ if (tmp == (gunichar)-1 || tmp == (gunichar)-2) {
+ len++;
+ ptr++;
+ } else {
+ 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)
@@ -588,8 +618,6 @@ void term_stop(void)
{
terminfo_stop(current_term);
kill(getpid(), SIGTSTP);
- terminfo_cont(current_term);
- irssi_redraw();
}
static int input_utf8(const unsigned char *buffer, int size, unichar *result)
diff --git a/src/fe-text/term.h b/src/fe-text/term.h
index cdcc787a..0c7847f6 100644
--- a/src/fe-text/term.h
+++ b/src/fe-text/term.h
@@ -27,7 +27,7 @@ typedef struct _TERM_WINDOW TERM_WINDOW;
#define TERM_TYPE_UTF8 1
#define TERM_TYPE_BIG5 2
-typedef guint32 unichar;
+#include "utf8.h"
extern TERM_WINDOW *root_window;
extern int term_width, term_height;
@@ -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);
@@ -94,6 +94,9 @@ void term_refresh(TERM_WINDOW *window);
void term_stop(void);
+void term_set_appkey_mode(int enable);
+void term_set_bracketed_paste_mode(int enable);
+
/* keyboard input handling */
void term_set_input_type(int type);
void term_gets(GArray *buffer, int *line_count);
diff --git a/src/fe-text/terminfo-core.c b/src/fe-text/terminfo-core.c
index d16987fe..9c9179a5 100644
--- a/src/fe-text/terminfo-core.c
+++ b/src/fe-text/terminfo-core.c
@@ -17,7 +17,6 @@ inline static int term_putchar(int c)
char *tparm();
int tputs();
-#ifdef HAVE_TERMINFO
int setupterm();
char *tigetstr();
int tigetnum();
@@ -25,15 +24,6 @@ int tigetflag();
#define term_getstr(x, buffer) tigetstr(x.ti_name)
#define term_getnum(x) tigetnum(x.ti_name);
#define term_getflag(x) tigetflag(x.ti_name);
-#else
-int tgetent();
-char *tgetstr();
-int tgetnum();
-int tgetflag();
-#define term_getstr(x, buffer) tgetstr(x.tc_name, &buffer)
-#define term_getnum(x) tgetnum(x.tc_name)
-#define term_getflag(x) tgetflag(x.tc_name)
-#endif
#define CAP_TYPE_FLAG 0
#define CAP_TYPE_INT 1
@@ -50,62 +40,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) },
-
- /* 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) },
+ /* Terminal size */
+ { "cols", "co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, width) },
+ { "lines", "li", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, height) },
- /* 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) },
+ /* 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) },
/* 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) },
-
- /* Beep */
- { "bel", "bl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bel) },
+ { "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) },
+
+ /* 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) */
@@ -391,15 +385,26 @@ static void _ignore_parm(TERM_REC *term, int param)
{
}
+static void terminfo_set_appkey_mode(TERM_REC *term, int set)
+{
+ if (term->TI_smkx && term->TI_rmkx)
+ tput(tparm(set ? term->TI_smkx : term->TI_rmkx));
+}
+
+static void term_dec_set_bracketed_paste_mode(int enable)
+{
+ if (enable)
+ tputs("\e[?2004h", 0, term_putchar);
+ else
+ tputs("\e[?2004l", 0, term_putchar);
+}
+
static void term_fill_capabilities(TERM_REC *term)
{
int i, ival;
char *sval;
void *ptr;
-#ifndef HAVE_TERMINFO
- char *tptr = term->buffer2;
-#endif
for (i = 0; i < sizeof(tcaps)/sizeof(tcaps[0]); i++) {
ptr = G_STRUCT_MEMBER_P(term, tcaps[i].offset);
@@ -492,12 +497,15 @@ void terminfo_setup_colors(TERM_REC *term, int force)
}
}
-static void terminfo_input_init(TERM_REC *term)
+static void terminfo_input_init0(TERM_REC *term)
{
tcgetattr(fileno(term->in), &term->old_tio);
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 */
@@ -511,8 +519,11 @@ static void terminfo_input_init(TERM_REC *term)
term->tio.c_cc[VSUSP] = _POSIX_VDISABLE;
#endif
- tcsetattr(fileno(term->in), TCSADRAIN, &term->tio);
+}
+static void terminfo_input_init(TERM_REC *term)
+{
+ tcsetattr(fileno(term->in), TCSADRAIN, &term->tio);
}
static void terminfo_input_deinit(TERM_REC *term)
@@ -523,7 +534,14 @@ 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->appkey_enabled)
+ terminfo_set_appkey_mode(term, TRUE);
+
+ if (term->bracketed_paste_enabled)
+ term_dec_set_bracketed_paste_mode(TRUE);
+
terminfo_input_init(term);
}
@@ -534,10 +552,16 @@ void terminfo_stop(TERM_REC *term)
/* move cursor to bottom of the screen */
terminfo_move(0, term->height-1);
+ if (term->bracketed_paste_enabled)
+ term_dec_set_bracketed_paste_mode(FALSE);
+
/* stop cup-mode */
if (term->TI_rmcup)
tput(tparm(term->TI_rmcup));
+ if (term->appkey_enabled)
+ terminfo_set_appkey_mode(term, FALSE);
+
/* reset input settings */
terminfo_input_deinit(term);
fflush(term->out);
@@ -546,9 +570,7 @@ void terminfo_stop(TERM_REC *term)
static int term_setup(TERM_REC *term)
{
GString *str;
-#ifdef HAVE_TERMINFO
int err;
-#endif
char *term_env;
term_env = getenv("TERM");
@@ -557,18 +579,10 @@ static int term_setup(TERM_REC *term)
return 0;
}
-#ifdef HAVE_TERMINFO
if (setupterm(term_env, 1, &err) != 0) {
fprintf(stderr, "setupterm() failed for TERM=%s: %d\n", term_env, err);
return 0;
}
-#else
- if (tgetent(term->buffer1, term_env) < 1)
- {
- fprintf(stderr, "Termcap not found for TERM=%s\n", term_env);
- return 0;
- }
-#endif
term_fill_capabilities(term);
@@ -646,11 +660,11 @@ static int term_setup(TERM_REC *term)
str = g_string_new(NULL);
if (term->TI_sgr0)
g_string_append(str, term->TI_sgr0);
- if (term->TI_rmul && (term->TI_sgr0 == NULL || strcmp(term->TI_rmul, term->TI_sgr0) != 0))
+ if (term->TI_rmul && (term->TI_sgr0 == NULL || g_strcmp0(term->TI_rmul, term->TI_sgr0) != 0))
g_string_append(str, term->TI_rmul);
- if (term->TI_rmso && (term->TI_sgr0 == NULL || strcmp(term->TI_rmso, term->TI_sgr0) != 0))
+ if (term->TI_rmso && (term->TI_sgr0 == NULL || g_strcmp0(term->TI_rmso, term->TI_sgr0) != 0))
g_string_append(str, term->TI_rmso);
- if (term->TI_ritm && (term->TI_sgr0 == NULL || strcmp(term->TI_ritm, term->TI_sgr0) != 0))
+ if (term->TI_ritm && (term->TI_sgr0 == NULL || g_strcmp0(term->TI_ritm, term->TI_sgr0) != 0))
g_string_append(str, term->TI_ritm);
term->TI_normal = str->str;
g_string_free(str, FALSE);
@@ -659,10 +673,29 @@ static int term_setup(TERM_REC *term)
term->beep = term->TI_bel ? _beep : _ignore;
terminfo_setup_colors(term, FALSE);
+ terminfo_input_init0(term);
terminfo_cont(term);
return 1;
}
+void term_set_appkey_mode(int enable)
+{
+ if (current_term->appkey_enabled == enable)
+ return;
+
+ current_term->appkey_enabled = enable;
+ terminfo_set_appkey_mode(current_term, enable);
+}
+
+void term_set_bracketed_paste_mode(int enable)
+{
+ if (current_term->bracketed_paste_enabled == enable)
+ return;
+
+ current_term->bracketed_paste_enabled = enable;
+ term_dec_set_bracketed_paste_mode(enable);
+}
+
TERM_REC *terminfo_core_init(FILE *in, FILE *out)
{
TERM_REC *old_term, *term;
diff --git a/src/fe-text/terminfo-core.h b/src/fe-text/terminfo-core.h
index 6ab4637d..1253fd9d 100644
--- a/src/fe-text/terminfo-core.h
+++ b/src/fe-text/terminfo-core.h
@@ -88,6 +88,14 @@ struct _TERM_REC {
/* Beep */
char *TI_bel;
+
+ /* Keyboard-transmit mode */
+ const char *TI_smkx;
+ const char *TI_rmkx;
+
+ /* Terminal mode states */
+ int appkey_enabled;
+ int bracketed_paste_enabled;
};
extern TERM_REC *current_term;
diff --git a/src/fe-text/textbuffer-view.c b/src/fe-text/textbuffer-view.c
index ad4c00b8..58bd36fb 100644
--- a/src/fe-text/textbuffer-view.c
+++ b/src/fe-text/textbuffer-view.c
@@ -310,7 +310,7 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
continue;
}
- if (!view->utf8 && char_width > 1) {
+ if (view->break_wide && char_width > 1) {
last_space = xpos;
last_space_ptr = next_ptr;
last_color = color; last_fg24 = fg24; last_bg24 = bg24;
@@ -668,6 +668,16 @@ void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
view->default_indent_func = indent_func;
}
+/* Enable breaking of wide chars */
+void textbuffer_view_set_break_wide(TEXT_BUFFER_VIEW_REC *view,
+ gboolean break_wide)
+{
+ if (view->break_wide != break_wide) {
+ view->break_wide = break_wide;
+ view_reset_cache(view);
+ }
+}
+
static void view_unregister_indent_func(TEXT_BUFFER_VIEW_REC *view,
INDENT_FUNC indent_func)
{
@@ -1231,7 +1241,7 @@ void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
void textbuffer_view_remove_lines_by_level(TEXT_BUFFER_VIEW_REC *view, int level)
{
LINE_REC *line, *next;
-
+
term_refresh_freeze();
line = textbuffer_view_get_lines(view);
diff --git a/src/fe-text/textbuffer-view.h b/src/fe-text/textbuffer-view.h
index ab6786e0..5e7a9d0a 100644
--- a/src/fe-text/textbuffer-view.h
+++ b/src/fe-text/textbuffer-view.h
@@ -59,6 +59,7 @@ struct _TEXT_BUFFER_VIEW_REC {
unsigned int longword_noindent:1;
unsigned int scroll:1; /* scroll down automatically when at bottom */
unsigned int utf8:1; /* use UTF8 in this view */
+ unsigned int break_wide:1; /* Break wide chars in this view */
TEXT_BUFFER_CACHE_REC *cache;
int ypos; /* cursor position - visible area is 0..height-1 */
@@ -72,7 +73,7 @@ struct _TEXT_BUFFER_VIEW_REC {
/* how many empty lines are in screen. a screenful when started
or used /CLEAR */
- int empty_linecount;
+ int empty_linecount;
/* window is at the bottom of the text buffer */
unsigned int bottom:1;
/* if !bottom - new text has been printed since we were at bottom */
@@ -97,6 +98,8 @@ void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
int longword_noindent,
INDENT_FUNC indent_func);
void textbuffer_views_unregister_indent_func(INDENT_FUNC indent_func);
+void textbuffer_view_set_break_wide(TEXT_BUFFER_VIEW_REC *view,
+ gboolean break_wide);
void textbuffer_view_set_scroll(TEXT_BUFFER_VIEW_REC *view, int scroll);
void textbuffer_view_set_utf8(TEXT_BUFFER_VIEW_REC *view, int utf8);
diff --git a/src/fe-text/textbuffer.c b/src/fe-text/textbuffer.c
index 561fdabd..3668f4c7 100644
--- a/src/fe-text/textbuffer.c
+++ b/src/fe-text/textbuffer.c
@@ -27,7 +27,7 @@
#include "textbuffer.h"
-#ifdef HAVE_REGEX_H
+#ifndef USE_GREGEX
# include <regex.h>
#endif
@@ -545,7 +545,9 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
int before, int after,
int regexp, int fullword, int case_sensitive)
{
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ GRegex *preg;
+#else
regex_t preg;
#endif
LINE_REC *line, *pre_line;
@@ -557,16 +559,23 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
g_return_val_if_fail(buffer != NULL, NULL);
g_return_val_if_fail(text != NULL, NULL);
+#ifdef USE_GREGEX
+ preg = NULL;
+
+ if (regexp) {
+ preg = g_regex_new(text, G_REGEX_RAW | (case_sensitive ? 0 : G_REGEX_CASELESS), 0, NULL);
+
+ if (preg == NULL)
+ return NULL;
+ }
+#else
if (regexp) {
-#ifdef HAVE_REGEX_H
int flags = REG_EXTENDED | REG_NOSUB |
(case_sensitive ? 0 : REG_ICASE);
if (regcomp(&preg, text, flags) != 0)
return NULL;
-#else
- return NULL;
-#endif
}
+#endif
matches = NULL; match_after = 0;
str = g_string_new(NULL);
@@ -585,12 +594,15 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
if (*text != '\0') {
textbuffer_line2text(line, FALSE, str);
- if (line_matched)
- line_matched =
-#ifdef HAVE_REGEX_H
- regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
+ if (line_matched) {
+ line_matched = regexp ?
+#ifdef USE_GREGEX
+ g_regex_match(preg, str->str, 0, NULL)
+#else
+ regexec(&preg, str->str, 0, NULL, 0) == 0
#endif
- match_func(str->str, text) != NULL;
+ : match_func(str->str, text) != NULL;
+ }
}
if (line_matched) {
@@ -618,7 +630,11 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
matches = g_list_append(matches, NULL);
}
}
-#ifdef HAVE_REGEX_H
+
+#ifdef USE_GREGEX
+ if (preg != NULL)
+ g_regex_unref(preg);
+#else
if (regexp) regfree(&preg);
#endif
g_string_free(str, TRUE);
diff --git a/src/fe-text/tparm.c b/src/fe-text/tparm.c
deleted file mode 100644
index 3f58e6f3..00000000
--- a/src/fe-text/tparm.c
+++ /dev/null
@@ -1,740 +0,0 @@
-/*
- * tparm.c
- *
- * By Ross Ridge
- * Public Domain
- * 92/02/01 07:30:36
- *
- */
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-#ifndef MAX_PUSHED
-#define MAX_PUSHED 32
-#endif
-
-#define ARG 1
-#define NUM 2
-
-#define INTEGER 1
-#define STRING 2
-
-#define MAX_LINE 640
-
-typedef void* anyptr;
-
-typedef struct stack_str {
- int type;
- int argnum;
- int value;
-} stack;
-
-static stack S[MAX_PUSHED];
-static stack vars['z'-'a'+1];
-static int pos = 0;
-
-static struct arg_str {
- int type;
- int integer;
- char *string;
-} arg_list[10];
-
-static int argcnt;
-
-static va_list tparm_args;
-
-static int pusharg(int arg)
-{
- if (pos == MAX_PUSHED)
- return 1;
- S[pos].type = ARG;
- S[pos++].argnum = arg;
- return 0;
-}
-
-static int pushnum(int num)
-{
- if (pos == MAX_PUSHED)
- return 1;
- S[pos].type = NUM;
- S[pos++].value = num;
- return 0;
-}
-
-/* VARARGS2 */
-static int getarg(int argnum, int type, anyptr p)
-{
- while (argcnt < argnum) {
- arg_list[argcnt].type = INTEGER;
- arg_list[argcnt++].integer = (int) va_arg(tparm_args, int);
- }
- if (argcnt > argnum) {
- if (arg_list[argnum].type != type)
- return 1;
- else if (type == STRING)
- *(char **)p = arg_list[argnum].string;
- else
- *(int *)p = arg_list[argnum].integer;
- } else {
- arg_list[argcnt].type = type;
- if (type == STRING)
- *(char **)p = arg_list[argcnt++].string
- = (char *) va_arg(tparm_args, char *);
- else
- *(int *)p = arg_list[argcnt++].integer = (int) va_arg(tparm_args, int);
- }
- return 0;
-}
-
-
-static int popstring(char **str)
-{
- if (pos-- == 0)
- return 1;
- if (S[pos].type != ARG)
- return 1;
- return(getarg(S[pos].argnum, STRING, (anyptr) str));
-}
-
-static int popnum(int *num)
-{
- if (pos-- == 0)
- return 1;
- switch (S[pos].type) {
- case ARG:
- return (getarg(S[pos].argnum, INTEGER, (anyptr) num));
- case NUM:
- *num = S[pos].value;
- return 0;
- }
- return 1;
-}
-
-static int cvtchar(const char *sp, char *c)
-{
- switch(*sp) {
- case '\\':
- switch(*++sp) {
- case '\'':
- case '$':
- case '\\':
- case '%':
- *c = *sp;
- return 2;
- case '\0':
- *c = '\\';
- return 1;
- case '0':
- if (sp[1] == '0' && sp[2] == '0') {
- *c = '\0';
- return 4;
- }
- *c = '\200'; /* '\0' ???? */
- return 2;
- default:
- *c = *sp;
- return 2;
- }
- default:
- *c = *sp;
- return 1;
- }
-}
-
-static int termcap;
-
-/* sigh... this has got to be the ugliest code I've ever written.
- Trying to handle everything has its cost, I guess.
-
- It actually isn't to hard to figure out if a given % code is supposed
- to be interpeted with its termcap or terminfo meaning since almost
- all terminfo codes are invalid unless something has been pushed on
- the stack and termcap strings will never push things on the stack
- (%p isn't used by termcap). So where we have a choice we make the
- decision by wether or not somthing has been pushed on the stack.
- The static variable termcap keeps track of this; it starts out set
- to 1 and is incremented as each argument processed by a termcap % code,
- however if something is pushed on the stack it's set to 0 and the
- rest of the % codes are interpeted as terminfo % codes. Another way
- of putting it is that if termcap equals one we haven't decided either
- way yet, if it equals zero we're looking for terminfo codes, and if
- its greater than 1 we're looking for termcap codes.
-
- Terminfo % codes:
-
- %% output a '%'
- %[[:][-+# ][width][.precision]][doxXs]
- output pop according to the printf format
- %c output pop as a char
- %'c' push character constant c.
- %{n} push decimal constant n.
- %p[1-9] push paramter [1-9]
- %g[a-z] push variable [a-z]
- %P[a-z] put pop in variable [a-z]
- %l push the length of pop (a string)
- %+ add pop to pop and push the result
- %- subtract pop from pop and push the result
- %* multiply pop and pop and push the result
- %& bitwise and pop and pop and push the result
- %| bitwise or pop and pop and push the result
- %^ bitwise xor pop and pop and push the result
- %~ push the bitwise not of pop
- %= compare if pop and pop are equal and push the result
- %> compare if pop is less than pop and push the result
- %< compare if pop is greater than pop and push the result
- %A logical and pop and pop and push the result
- %O logical or pop and pop and push the result
- %! push the logical not of pop
- %? condition %t if_true [%e if_false] %;
- if condtion evaulates as true then evaluate if_true,
- else evaluate if_false. elseif's can be done:
-%? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %;
- %i add one to parameters 1 and 2. (ANSI)
-
- Termcap Codes:
-
- %% output a %
- %. output parameter as a character
- %d output parameter as a decimal number
- %2 output parameter in printf format %02d
- %3 output parameter in printf format %03d
- %+x add the character x to parameter and output it as a character
-(UW) %-x subtract parameter FROM the character x and output it as a char
-(UW) %ax add the character x to parameter
-(GNU) %a[+*-/=][cp]x
- GNU arithmetic.
-(UW) %sx subtract parameter FROM the character x
- %>xy if parameter > character x then add character y to parameter
- %B convert to BCD (parameter = (parameter/10)*16 + parameter%16)
- %D Delta Data encode (parameter = parameter - 2*(paramter%16))
- %i increment the first two parameters by one
- %n xor the first two parameters by 0140
-(GNU) %m xor the first two parameters by 0177
- %r swap the first two parameters
-(GNU) %b backup to previous parameter
-(GNU) %f skip this parameter
-
- Note the two definitions of %a, the GNU defintion is used if the characters
- after the 'a' are valid, otherwise the UW definition is used.
-
- (GNU) used by GNU Emacs termcap libraries
- (UW) used by the University of Waterloo (MFCF) termcap libraries
-
-*/
-
-char *tparm(const char *str, ...) {
- static char OOPS[] = "OOPS";
- static char buf[MAX_LINE];
- register const char *sp;
- register char *dp;
- register char *fmt;
- char conv_char;
- char scan_for;
- int scan_depth = 0, if_depth;
- static int i, j;
- static char *s, c;
- char fmt_buf[MAX_LINE];
- char sbuf[MAX_LINE];
-
- va_start(tparm_args, str);
-
- sp = str;
- dp = buf;
- scan_for = 0;
- if_depth = 0;
- argcnt = 0;
- pos = 0;
- termcap = 1;
- while (*sp != '\0') {
- switch(*sp) {
- case '\\':
- if (scan_for) {
- if (*++sp != '\0')
- sp++;
- break;
- }
- *dp++ = *sp++;
- if (*sp != '\0')
- *dp++ = *sp++;
- break;
- case '%':
- sp++;
- if (scan_for) {
- if (*sp == scan_for && if_depth == scan_depth) {
- if (scan_for == ';')
- if_depth--;
- scan_for = 0;
- } else if (*sp == '?')
- if_depth++;
- else if (*sp == ';') {
- if (if_depth == 0)
- return OOPS;
- else
- if_depth--;
- }
- sp++;
- break;
- }
- fmt = NULL;
- switch(*sp) {
- case '%':
- *dp++ = *sp++;
- break;
- case '+':
- if (!termcap) {
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i += j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- }
- ;/* FALLTHROUGH */
- case 'C':
- if (*sp == 'C') {
- if (getarg(termcap - 1, INTEGER, &i))
- return OOPS;
- if (i >= 96) {
- i /= 96;
- if (i == '$')
- *dp++ = '\\';
- *dp++ = i;
- }
- }
- fmt = "%c";
- /* FALLTHROUGH */
- case 'a':
- if (!termcap)
- return OOPS;
- if (getarg(termcap - 1, INTEGER, (anyptr) &i))
- return OOPS;
- if (*++sp == '\0')
- return OOPS;
- if ((sp[1] == 'p' || sp[1] == 'c')
- && sp[2] != '\0' && fmt == NULL) {
- /* GNU aritmitic parameter, what they
- realy need is terminfo. */
- int val, lc;
- if (sp[1] == 'p'
- && getarg(termcap - 1 + sp[2] - '@',
- INTEGER, (anyptr) &val))
- return OOPS;
- if (sp[1] == 'c') {
- lc = cvtchar(sp + 2, &c) + 2;
- /* Mask out 8th bit so \200 can be
- used for \0 as per GNU doc's */
- val = c & 0177;
- } else
- lc = 2;
- switch(sp[0]) {
- case '=':
- break;
- case '+':
- val = i + val;
- break;
- case '-':
- val = i - val;
- break;
- case '*':
- val = i * val;
- break;
- case '/':
- val = i / val;
- break;
- default:
- /* Not really GNU's %a after all... */
- lc = cvtchar(sp, &c);
- val = c + i;
- break;
- }
- arg_list[termcap - 1].integer = val;
- sp += lc;
- break;
- }
- sp += cvtchar(sp, &c);
- arg_list[termcap - 1].integer = c + i;
- if (fmt == NULL)
- break;
- sp--;
- /* FALLTHROUGH */
- case '-':
- if (!termcap) {
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i -= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- }
- fmt = "%c";
- /* FALLTHROUGH */
- case 's':
- if (termcap && (fmt == NULL || *sp == '-')) {
- if (getarg(termcap - 1, INTEGER, &i))
- return OOPS;
- if (*++sp == '\0')
- return OOPS;
- sp += cvtchar(sp, &c);
- arg_list[termcap - 1].integer = c - i;
- if (fmt == NULL)
- break;
- sp--;
- }
- if (!termcap)
- return OOPS;
- ;/* FALLTHROUGH */
- case '.':
- if (termcap && fmt == NULL)
- fmt = "%c";
- ;/* FALLTHROUGH */
- case 'd':
- if (termcap && fmt == NULL)
- fmt = "%d";
- ;/* FALLTHROUGH */
- case '2':
- if (termcap && fmt == NULL)
- fmt = "%02d";
- ;/* FALLTHROUGH */
- case '3':
- if (termcap && fmt == NULL)
- fmt = "%03d";
- ;/* FALLTHROUGH */
- case ':': case ' ': case '#': case 'u':
- case 'x': case 'X': case 'o': case 'c':
- case '0': case '1': case '4': case '5':
- case '6': case '7': case '8': case '9':
- if (fmt == NULL) {
- if (termcap)
- return OOPS;
- if (*sp == ':')
- sp++;
- fmt = fmt_buf;
- *fmt++ = '%';
- while(*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' && *sp != 'o' && *sp != 'c' && *sp != 'u') {
- if (*sp == '\0')
- return OOPS;
- *fmt++ = *sp++;
- }
- *fmt++ = *sp;
- *fmt = '\0';
- fmt = fmt_buf;
- }
- conv_char = fmt[strlen(fmt) - 1];
- if (conv_char == 's') {
- if (popstring(&s))
- return OOPS;
- sprintf(sbuf, fmt, s);
- } else {
- if (termcap) {
- if (getarg(termcap++ - 1,
- INTEGER, &i))
- return OOPS;
- } else
- if (popnum(&i))
- return OOPS;
- if (i == 0 && conv_char == 'c')
- *sbuf = 0;
- else
- sprintf(sbuf, fmt, i);
- }
- sp++;
- fmt = sbuf;
- while(*fmt != '\0') {
- if (*fmt == '$')
- *dp++ = '\\';
- *dp++ = *fmt++;
- }
- break;
- case 'r':
- if (!termcap || getarg(1, INTEGER, &i))
- return OOPS;
- arg_list[1].integer = arg_list[0].integer;
- arg_list[0].integer = i;
- sp++;
- break;
- case 'i':
- if (getarg(1, INTEGER, &i)
- || arg_list[0].type != INTEGER)
- return OOPS;
- arg_list[1].integer++;
- arg_list[0].integer++;
- sp++;
- break;
- case 'n':
- if (!termcap || getarg(1, INTEGER, &i))
- return OOPS;
- arg_list[0].integer ^= 0140;
- arg_list[1].integer ^= 0140;
- sp++;
- break;
- case '>':
- if (!termcap) {
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i = (i > j);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- }
- if (getarg(termcap-1, INTEGER, &i))
- return OOPS;
- sp += cvtchar(sp, &c);
- if (i > c) {
- sp += cvtchar(sp, &c);
- arg_list[termcap-1].integer += c;
- } else
- sp += cvtchar(sp, &c);
- sp++;
- break;
- case 'B':
- if (!termcap || getarg(termcap-1, INTEGER, &i))
- return OOPS;
- arg_list[termcap-1].integer = 16*(i/10)+i%10;
- sp++;
- break;
- case 'D':
- if (!termcap || getarg(termcap-1, INTEGER, &i))
- return OOPS;
- arg_list[termcap-1].integer = i - 2 * (i % 16);
- sp++;
- break;
- case 'p':
- if (termcap > 1)
- return OOPS;
- if (*++sp == '\0')
- return OOPS;
- if (*sp == '0')
- i = 9;
- else
- i = *sp - '1';
- if (i < 0 || i > 9)
- return OOPS;
- if (pusharg(i))
- return OOPS;
- termcap = 0;
- sp++;
- break;
- case 'P':
- if (termcap || *++sp == '\0')
- return OOPS;
- i = *sp++ - 'a';
- if (i < 0 || i > 25)
- return OOPS;
- if (pos-- == 0)
- return OOPS;
- switch(vars[i].type = S[pos].type) {
- case ARG:
- vars[i].argnum = S[pos].argnum;
- break;
- case NUM:
- vars[i].value = S[pos].value;
- break;
- }
- break;
- case 'g':
- if (termcap || *++sp == '\0')
- return OOPS;
- i = *sp++ - 'a';
- if (i < 0 || i > 25)
- return OOPS;
- switch(vars[i].type) {
- case ARG:
- if (pusharg(vars[i].argnum))
- return OOPS;
- break;
- case NUM:
- if (pushnum(vars[i].value))
- return OOPS;
- break;
- }
- break;
- case '\'':
- if (termcap > 1)
- return OOPS;
- if (*++sp == '\0')
- return OOPS;
- sp += cvtchar(sp, &c);
- if (pushnum(c) || *sp++ != '\'')
- return OOPS;
- termcap = 0;
- break;
- case '{':
- if (termcap > 1)
- return OOPS;
- i = 0;
- sp++;
- while(isdigit((int) (unsigned char) *sp))
- i = 10 * i + *sp++ - '0';
- if (*sp++ != '}' || pushnum(i))
- return OOPS;
- termcap = 0;
- break;
- case 'l':
- if (termcap || popstring(&s))
- return OOPS;
- i = strlen(s);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '*':
- if (termcap || popnum(&j) || popnum(&i))
- return OOPS;
- i *= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '/':
- if (termcap || popnum(&j) || popnum(&i))
- return OOPS;
- i /= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case 'm':
- if (termcap) {
- if (getarg(1, INTEGER, &i))
- return OOPS;
- arg_list[0].integer ^= 0177;
- arg_list[1].integer ^= 0177;
- sp++;
- break;
- }
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i %= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '&':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i &= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '|':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i |= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '^':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i ^= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '=':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i = (i == j);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '<':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i = (i < j);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case 'A':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i = (i && j);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case 'O':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i = (i || j);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '!':
- if (popnum(&i))
- return OOPS;
- i = !i;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '~':
- if (popnum(&i))
- return OOPS;
- i = ~i;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '?':
- if (termcap > 1)
- return OOPS;
- termcap = 0;
- if_depth++;
- sp++;
- break;
- case 't':
- if (popnum(&i) || if_depth == 0)
- return OOPS;
- if (!i) {
- scan_for = 'e';
- scan_depth = if_depth;
- }
- sp++;
- break;
- case 'e':
- if (if_depth == 0)
- return OOPS;
- scan_for = ';';
- scan_depth = if_depth;
- sp++;
- break;
- case ';':
- if (if_depth-- == 0)
- return OOPS;
- sp++;
- break;
- case 'b':
- if (--termcap < 1)
- return OOPS;
- sp++;
- break;
- case 'f':
- if (!termcap++)
- return OOPS;
- sp++;
- break;
- }
- break;
- default:
- if (scan_for)
- sp++;
- else
- *dp++ = *sp++;
- break;
- }
- }
- va_end(tparm_args);
- *dp = '\0';
- return buf;
-}
diff --git a/src/irc/core/Makefile.am b/src/irc/core/Makefile.am
index 3db5cf0e..20caaeb1 100644
--- a/src/irc/core/Makefile.am
+++ b/src/irc/core/Makefile.am
@@ -26,6 +26,8 @@ libirc_core_a_SOURCES = \
irc-servers-reconnect.c \
irc-servers-setup.c \
irc-session.c \
+ irc-cap.c \
+ sasl.c \
lag.c \
massjoin.c \
modes.c \
@@ -48,6 +50,8 @@ pkginc_irc_core_HEADERS = \
irc-queries.h \
irc-servers.h \
irc-servers-setup.h \
+ irc-cap.h \
+ sasl.h \
modes.h \
mode-lists.h \
module.h \
diff --git a/src/irc/core/bans.c b/src/irc/core/bans.c
index de799812..198fdc4a 100644
--- a/src/irc/core/bans.c
+++ b/src/irc/core/bans.c
@@ -88,7 +88,7 @@ char *ban_get_masks(IRC_CHANNEL_REC *channel, const char *nicks, int ban_type)
str = g_string_new(NULL);
banlist = g_strsplit(nicks, " ", -1);
for (ban = banlist; *ban != NULL; ban++) {
- if (strchr(*ban, '!') != NULL) {
+ if (**ban == '$' || strchr(*ban, '!') != NULL) {
/* explicit ban */
g_string_append_printf(str, "%s ", *ban);
continue;
@@ -184,11 +184,11 @@ static void command_set_ban(const char *data, IRC_SERVER_REC *server,
if (server == NULL || !server->connected || !IS_IRC_SERVER(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
- item, &channel, &nicks)) return;
- if (!ischannel(*channel)) cmd_param_error(CMDERR_NOT_JOINED);
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS, item, &channel, &nicks)) return;
+ if (!server_ischannel(SERVER(server), channel)) cmd_param_error(CMDERR_NOT_JOINED);
if (*nicks == '\0') {
- if (strcmp(data, "*") != 0)
+ if (g_strcmp0(data, "*") != 0)
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
/* /BAN * or /UNBAN * - ban/unban everyone */
nicks = (char *) data;
@@ -262,8 +262,8 @@ static void cmd_ban(const char *data, IRC_SERVER_REC *server, void *item)
CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 1 |
- PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"ban", &optlist, &ban))
return;
@@ -297,8 +297,8 @@ static void cmd_unban(const char *data, IRC_SERVER_REC *server, void *item)
CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 1 |
- PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"unban", &optlist, &ban))
return;
@@ -309,7 +309,7 @@ static void cmd_unban(const char *data, IRC_SERVER_REC *server, void *item)
ban = g_strdup(BAN_LAST);
command_set_ban(ban ? ban : data, server, item, FALSE, 0);
-
+
g_free(ban);
cmd_params_free(free_arg);
@@ -318,7 +318,7 @@ static void cmd_unban(const char *data, IRC_SERVER_REC *server, void *item)
static void read_settings(void)
{
if (default_ban_type_str != NULL &&
- strcmp(default_ban_type_str, settings_get_str("ban_type")) == 0)
+ g_strcmp0(default_ban_type_str, settings_get_str("ban_type")) == 0)
return;
g_free_not_null(default_ban_type_str);
diff --git a/src/irc/core/channel-events.c b/src/irc/core/channel-events.c
index 9198ed82..6cb9b088 100644
--- a/src/irc/core/channel-events.c
+++ b/src/irc/core/channel-events.c
@@ -123,10 +123,10 @@ static void channel_change_topic(IRC_SERVER_REC *server, const char *channel,
{
CHANNEL_REC *chanrec;
char *recoded = NULL;
-
+
chanrec = channel_find(SERVER(server), channel);
if (chanrec == NULL) return;
- /* the topic may be send out encoded, so we need to
+ /* the topic may be send out encoded, so we need to
recode it back or /topic <tab> will not work properly */
recoded = recode_in(SERVER(server), topic, channel);
if (topic != NULL) {
@@ -137,7 +137,7 @@ static void channel_change_topic(IRC_SERVER_REC *server, const char *channel,
g_free_not_null(chanrec->topic_by);
chanrec->topic_by = g_strdup(setby);
-
+
chanrec->topic_time = settime;
signal_emit("channel topic changed", 1, chanrec);
@@ -270,7 +270,7 @@ static void event_join(IRC_SERVER_REC *server, const char *data, const char *nic
}
chanrec->joined = TRUE;
- if (strcmp(chanrec->name, channel) != 0) {
+ if (g_strcmp0(chanrec->name, channel) != 0) {
g_free(chanrec->name);
chanrec->name = g_strdup(channel);
}
diff --git a/src/irc/core/channel-rejoin.c b/src/irc/core/channel-rejoin.c
index d518f0c2..154035ae 100644
--- a/src/irc/core/channel-rejoin.c
+++ b/src/irc/core/channel-rejoin.c
@@ -84,7 +84,7 @@ static int channel_rejoin(IRC_SERVER_REC *server, const char *channel)
channel_destroy(CHANNEL(chanrec));
return 0;
}
-
+
rec = rejoin_find(server, channel);
if (rec != NULL) {
/* already exists */
@@ -149,7 +149,7 @@ static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
- if (ischannel(*channel)) {
+ if (server_ischannel(SERVER(server), channel)) {
chanrec = irc_channel_find(server, channel);
if (chanrec != NULL && chanrec->joined) {
/* dalnet event - can't change nick while
diff --git a/src/irc/core/channels-query.c b/src/irc/core/channels-query.c
index 48ba5703..857ebaf0 100644
--- a/src/irc/core/channels-query.c
+++ b/src/irc/core/channels-query.c
@@ -311,7 +311,7 @@ static void channel_checksync(IRC_CHANNEL_REC *channel)
signal_emit("channel sync", 1, channel);
}
-/* Error occured when trying to execute query - abort and try again. */
+/* Error occurred when trying to execute query - abort and try again. */
static void query_current_error(IRC_SERVER_REC *server)
{
SERVER_QUERY_REC *rec;
diff --git a/src/irc/core/irc-cap.c b/src/irc/core/irc-cap.c
new file mode 100644
index 00000000..5464e493
--- /dev/null
+++ b/src/irc/core/irc-cap.c
@@ -0,0 +1,192 @@
+/* irc-cap.c : irssi
+
+ Copyright (C) 2015 The Lemon Man
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+
+#include "irc-cap.h"
+#include "irc-servers.h"
+
+int cap_toggle (IRC_SERVER_REC *server, char *cap, int enable)
+{
+ if (cap == NULL || *cap == '\0')
+ return FALSE;
+
+ /* If the negotiation hasn't been completed yet just queue the requests */
+ if (!server->cap_complete) {
+ if (enable && !gslist_find_string(server->cap_queue, cap)) {
+ server->cap_queue = g_slist_prepend(server->cap_queue, g_strdup(cap));
+ return TRUE;
+ }
+ else if (!enable && gslist_find_string(server->cap_queue, cap)) {
+ server->cap_queue = gslist_remove_string(server->cap_queue, cap);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ if (enable && !gslist_find_string(server->cap_active, cap)) {
+ /* Make sure the required cap is supported by the server */
+ if (!gslist_find_string(server->cap_supported, cap))
+ return FALSE;
+
+ irc_send_cmdv(server, "CAP REQ %s", cap);
+ return TRUE;
+ }
+ else if (!enable && gslist_find_string(server->cap_active, cap)) {
+ irc_send_cmdv(server, "CAP REQ -%s", cap);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void cap_finish_negotiation (IRC_SERVER_REC *server)
+{
+ if (server->cap_complete)
+ return;
+
+ server->cap_complete = TRUE;
+ irc_send_cmd_now(server, "CAP END");
+
+ signal_emit("server cap end", 1, server);
+}
+
+static void cap_emit_signal (IRC_SERVER_REC *server, char *cmd, char *args)
+{
+ char *signal_name;
+
+ signal_name = g_strdup_printf("server cap %s %s", cmd, args? args: "");
+ signal_emit(signal_name, 1, server);
+ g_free(signal_name);
+}
+
+static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *address)
+{
+ GSList *tmp;
+ GString *cmd;
+ char *params, *evt, *list, **caps;
+ int i, caps_length, disable, avail_caps;
+
+ params = event_get_params(args, 3, NULL, &evt, &list);
+ if (params == NULL)
+ return;
+
+ /* Strip the trailing whitespaces before splitting the string, some servers send responses with
+ * superfluous whitespaces that g_strsplit the interprets as tokens */
+ caps = g_strsplit(g_strchomp(list), " ", -1);
+ caps_length = g_strv_length(caps);
+
+ if (!g_strcmp0(evt, "LS")) {
+ /* Create a list of the supported caps */
+ for (i = 0; i < caps_length; i++)
+ server->cap_supported = g_slist_prepend(server->cap_supported, g_strdup(caps[i]));
+
+ /* Request the required caps, if any */
+ if (server->cap_queue == NULL) {
+ cap_finish_negotiation(server);
+ }
+ else {
+ cmd = g_string_new("CAP REQ :");
+
+ avail_caps = 0;
+
+ /* Check whether the cap is supported by the server */
+ for (tmp = server->cap_queue; tmp != NULL; tmp = tmp->next) {
+ if (gslist_find_string(server->cap_supported, tmp->data)) {
+ if (avail_caps > 0)
+ g_string_append_c(cmd, ' ');
+ g_string_append(cmd, tmp->data);
+
+ avail_caps++;
+ }
+ }
+
+ /* Clear the queue here */
+ gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
+ server->cap_queue = NULL;
+
+ /* If the server doesn't support any cap we requested close the negotiation here */
+ if (avail_caps > 0)
+ irc_send_cmd_now(server, cmd->str);
+ else
+ cap_finish_negotiation(server);
+
+ g_string_free(cmd, TRUE);
+ }
+ }
+ else if (!g_strcmp0(evt, "ACK")) {
+ int got_sasl = FALSE;
+
+ /* Emit a signal for every ack'd cap */
+ for (i = 0; i < caps_length; i++) {
+ disable = (*caps[i] == '-');
+
+ if (disable)
+ server->cap_active = gslist_remove_string(server->cap_active, caps[i] + 1);
+ else
+ server->cap_active = g_slist_prepend(server->cap_active, g_strdup(caps[i]));
+
+ if (!g_strcmp0(caps[i], "sasl"))
+ got_sasl = TRUE;
+
+ cap_emit_signal(server, "ack", caps[i]);
+ }
+
+ /* Hopefully the server has ack'd all the caps requested and we're ready to terminate the
+ * negotiation, unless sasl was requested. In this case we must not terminate the negotiation
+ * until the sasl handshake is over. */
+ if (got_sasl == FALSE)
+ cap_finish_negotiation(server);
+ }
+ else if (!g_strcmp0(evt, "NAK")) {
+ g_warning("The server answered with a NAK to our CAP request, this should not happen");
+
+ /* A NAK'd request means that a required cap can't be enabled or disabled, don't update the
+ * list of active caps and notify the listeners. */
+ for (i = 0; i < caps_length; i++)
+ cap_emit_signal(server, "nak", caps[i]);
+ }
+
+ g_strfreev(caps);
+ g_free(params);
+}
+
+static void event_invalid_cap (IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ /* The server didn't understand one (or more) requested caps, terminate the negotiation.
+ * This could be handled in a graceful way but since it shouldn't really ever happen this seems a
+ * good way to deal with 410 errors. */
+ server->cap_complete = FALSE;
+ irc_send_cmd_now(server, "CAP END");
+}
+
+void cap_init (void)
+{
+ signal_add_first("event cap", (SIGNAL_FUNC) event_cap);
+ signal_add_first("event 410", (SIGNAL_FUNC) event_invalid_cap);
+}
+
+void cap_deinit (void)
+{
+ signal_remove("event cap", (SIGNAL_FUNC) event_cap);
+ signal_remove("event 410", (SIGNAL_FUNC) event_invalid_cap);
+}
diff --git a/src/irc/core/irc-cap.h b/src/irc/core/irc-cap.h
new file mode 100644
index 00000000..df957cd2
--- /dev/null
+++ b/src/irc/core/irc-cap.h
@@ -0,0 +1,9 @@
+#ifndef __IRC_CAP_H
+#define __IRC_CAP_H
+
+void cap_init(void);
+void cap_deinit(void);
+int cap_toggle (IRC_SERVER_REC *server, char *cap, int enable);
+void cap_finish_negotiation (IRC_SERVER_REC *server);
+
+#endif
diff --git a/src/irc/core/irc-channels-setup.c b/src/irc/core/irc-channels-setup.c
index 2320352d..bbbc095c 100644
--- a/src/irc/core/irc-channels-setup.c
+++ b/src/irc/core/irc-channels-setup.c
@@ -24,10 +24,12 @@
void irc_channels_setup_init(void)
{
- signal_add("channel wholist", (SIGNAL_FUNC) channel_send_autocommands);
+ signal_add("channel wholist", (SIGNAL_FUNC) channel_send_botcommands);
+ signal_add("channel joined", (SIGNAL_FUNC) channel_send_autocommands);
}
void irc_channels_setup_deinit(void)
{
- signal_remove("channel wholist", (SIGNAL_FUNC) channel_send_autocommands);
+ signal_remove("channel wholist", (SIGNAL_FUNC) channel_send_botcommands);
+ signal_remove("channel joined", (SIGNAL_FUNC) channel_send_autocommands);
}
diff --git a/src/irc/core/irc-channels.c b/src/irc/core/irc-channels.c
index dd6e29ce..e775f530 100644
--- a/src/irc/core/irc-channels.c
+++ b/src/irc/core/irc-channels.c
@@ -99,7 +99,7 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
tmp = chanlist;
for (;; tmp++) {
if (*tmp != NULL) {
- channel = ischannel(**tmp) ? g_strdup(*tmp) :
+ channel = server_ischannel(SERVER(server), *tmp) ? g_strdup(*tmp) :
g_strdup_printf("#%s", *tmp);
chanrec = irc_channel_find(server, channel);
@@ -114,7 +114,7 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
use_keys = TRUE;
key = schannel->password;
} else key = NULL;
-
+
g_string_append_printf(outkeys, "%s,", get_join_key(key));
channame = channel + (channel[0] == '!' &&
channel[1] == '!');
@@ -126,31 +126,33 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
if (*tmpkey != NULL)
tmpkey++;
-
+
tmpstr = tmp;
tmpstr++;
cmdlen = outchans->len-1;
-
+
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);
-
- /* don't try to send too long lines
+
+ /* don't try to send too long lines
make sure it's not longer than 510
so 510 - strlen("JOIN ") = 505 */
if (cmdlen < 505)
continue;
}
if (outchans->len > 0) {
- g_string_truncate(outchans, outchans->len-1);
- g_string_truncate(outkeys, outkeys->len-1);
- irc_send_cmdv(IRC_SERVER(server),
- use_keys ? "JOIN %s %s" : "JOIN %s",
- outchans->str, outkeys->str);
+ g_string_truncate(outchans, outchans->len - 1);
+ g_string_truncate(outkeys, outkeys->len - 1);
+
+ if (use_keys)
+ irc_send_cmdv(IRC_SERVER(server), "JOIN %s %s", outchans->str, outkeys->str);
+ else
+ irc_send_cmdv(IRC_SERVER(server), "JOIN %s", outchans->str);
}
cmdlen = 0;
g_string_truncate(outchans,0);
@@ -172,6 +174,13 @@ static CHANNEL_REC *irc_channel_find_server(SERVER_REC *server,
const char *channel)
{
GSList *tmp;
+ char *fmt_channel;
+
+ /* if 'channel' has no leading # this lookup is going to fail, add a
+ * octothorpe in front of it to handle this case. */
+ fmt_channel = server_ischannel(SERVER(server), channel) ?
+ g_strdup(channel) :
+ g_strdup_printf("#%s", channel);
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *rec = tmp->data;
@@ -180,13 +189,19 @@ static CHANNEL_REC *irc_channel_find_server(SERVER_REC *server,
continue;
/* check both !ABCDEchannel and !channel */
- if (IRC_SERVER(server)->nick_comp_func(channel, rec->name) == 0)
+ if (IRC_SERVER(server)->nick_comp_func(fmt_channel, rec->name) == 0) {
+ g_free(fmt_channel);
return rec;
+ }
- if (IRC_SERVER(server)->nick_comp_func(channel, rec->visible_name) == 0)
+ if (IRC_SERVER(server)->nick_comp_func(fmt_channel, rec->visible_name) == 0) {
+ g_free(fmt_channel);
return rec;
+ }
}
+ g_free(fmt_channel);
+
return NULL;
}
diff --git a/src/irc/core/irc-chatnets.c b/src/irc/core/irc-chatnets.c
index d757bf8d..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 239c5a64..32ee8845 100644
--- a/src/irc/core/irc-commands.c
+++ b/src/irc/core/irc-commands.c
@@ -61,18 +61,18 @@ static int knockout_tag;
/* SYNTAX: NOTICE <targets> <message> */
static void cmd_notice(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
const char *target, *msg;
char *recoded;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
- &target, &msg))
+ &target, &msg))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? NULL : window_item_get_target(item);
if (target == NULL || *target == '\0' || *msg == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -88,18 +88,18 @@ static void cmd_notice(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: CTCP <targets> <ctcp command> [<ctcp data>] */
static void cmd_ctcp(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
const char *target;
char *ctcpcmd, *ctcpdata;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
- &target, &ctcpcmd, &ctcpdata))
+ &target, &ctcpcmd, &ctcpdata))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? NULL : window_item_get_target(item);
if (target == NULL || *target == '\0' || *ctcpcmd == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -122,18 +122,18 @@ static void cmd_ctcp(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: NCTCP <targets> <ctcp command> [<ctcp data>] */
static void cmd_nctcp(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
const char *target;
char *ctcpcmd, *ctcpdata, *recoded;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
- &target, &ctcpcmd, &ctcpdata))
+ &target, &ctcpcmd, &ctcpdata))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? NULL : window_item_get_target(item);
if (target == NULL || *target == '\0' || *ctcpcmd == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -150,28 +150,31 @@ static void cmd_nctcp(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: PART [<channels>] [<message>] */
static void cmd_part(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
char *channame, *msg;
char *recoded = NULL;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
- PARAM_FLAG_OPTCHAN, item, &channame, &msg))
+ PARAM_FLAG_OPTCHAN, item, &channame, &msg))
return;
if (*channame == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (*msg == '\0') msg = (char *) settings_get_str("part_message");
- if (server->cmdcount > MAX_COMMANDS_ON_PART_UNTIL_PURGE)
+ if (server->cmdcount > MAX_COMMANDS_ON_PART_UNTIL_PURGE)
irc_server_purge_output(server, channame);
if (*msg != '\0')
recoded = recode_out(SERVER(server), msg, channame);
- irc_send_cmdv(server, ! recoded ? "PART %s" : "PART %s :%s",
- channame, recoded);
+
+ if (recoded == NULL)
+ irc_send_cmdv(server, "PART %s", channame);
+ else
+ irc_send_cmdv(server, "PART %s :%s", channame, recoded);
g_free(recoded);
cmd_params_free(free_arg);
@@ -183,15 +186,15 @@ static void cmd_kick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
char *channame, *nicks, *reason, *recoded;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST |
- PARAM_FLAG_OPTCHAN, item,
- &channame, &nicks, &reason))
+ PARAM_FLAG_OPTCHAN, item,
+ &channame, &nicks, &reason))
return;
if (*channame == '\0' || *nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
- if (!ischannel(*channame)) cmd_param_error(CMDERR_NOT_JOINED);
+ if (!server_ischannel(SERVER(server), channame)) cmd_param_error(CMDERR_NOT_JOINED);
recoded = recode_out(SERVER(server), reason, channame);
g_string_printf(tmpstr, "KICK %s %s :%s", channame, nicks, recoded);
@@ -210,17 +213,21 @@ static void cmd_topic(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *ite
char *recoded = NULL;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN |
- PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
- item, "topic", &optlist, &channame, &topic))
+ PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ item, "topic", &optlist, &channame, &topic))
return;
if (*topic != '\0' || g_hash_table_lookup(optlist, "delete") != NULL)
recoded = recode_out(SERVER(server), topic, channame);
- irc_send_cmdv(server, recoded == NULL ? "TOPIC %s" : "TOPIC %s :%s",
- channame, recoded);
+
+ if (recoded == NULL)
+ irc_send_cmdv(server, "TOPIC %s", channame);
+ else
+ irc_send_cmdv(server, "TOPIC %s :%s", channame, recoded);
+
g_free(recoded);
cmd_params_free(free_arg);
@@ -232,13 +239,13 @@ static void cmd_invite(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *it
char *nick, *channame;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2, &nick, &channame))
return;
if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
- if (*channame == '\0' || strcmp(channame, "*") == 0) {
+ if (*channame == '\0' || g_strcmp0(channame, "*") == 0) {
if (!IS_IRC_CHANNEL(item))
cmd_param_error(CMDERR_NOT_JOINED);
@@ -251,16 +258,17 @@ static void cmd_invite(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *it
/* SYNTAX: LIST [-yes] [<channel>] */
static void cmd_list(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
GHashTable *optlist;
char *str;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "list", &optlist, &str))
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ "list", &optlist, &str))
return;
if (*str == '\0' && g_hash_table_lookup(optlist, "yes") == NULL &&
@@ -274,55 +282,60 @@ static void cmd_list(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: WHO [<nicks> | <channels> | **] */
static void cmd_who(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
char *channel, *rest;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &channel, &rest))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS, &channel, &rest))
return;
- if (strcmp(channel, "*") == 0 || *channel == '\0') {
+ if (g_strcmp0(channel, "*") == 0 || *channel == '\0') {
if (!IS_IRC_CHANNEL(item))
- cmd_param_error(CMDERR_NOT_JOINED);
+ cmd_param_error(CMDERR_NOT_JOINED);
channel = IRC_CHANNEL(item)->name;
}
- if (strcmp(channel, "**") == 0) {
+ if (g_strcmp0(channel, "**") == 0) {
/* ** displays all nicks.. */
*channel = '\0';
}
- irc_send_cmdv(server, *rest == '\0' ? "WHO %s" : "WHO %s %s",
- channel, rest);
+ if (rest[0] == '\0')
+ irc_send_cmdv(server, "WHO %s", channel);
+ else
+ irc_send_cmdv(server, "WHO %s %s", channel, rest);
+
cmd_params_free(free_arg);
}
static void cmd_names(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
- GHashTable *optlist;
+ GHashTable *optlist;
char *channel;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "names", &optlist, &channel))
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ "names", &optlist, &channel))
return;
- if (strcmp(channel, "*") == 0 || *channel == '\0') {
+ if (g_strcmp0(channel, "*") == 0 || *channel == '\0') {
if (!IS_IRC_CHANNEL(item))
- cmd_param_error(CMDERR_NOT_JOINED);
+ cmd_param_error(CMDERR_NOT_JOINED);
channel = IRC_CHANNEL(item)->name;
}
- if (strcmp(channel, "**") == 0) {
+ if (g_strcmp0(channel, "**") == 0) {
/* ** displays all nicks.. */
- irc_send_cmd(server, "NAMES");
+ irc_send_cmd(server, "NAMES");
} else {
irc_send_cmdv(server, "NAMES %s", channel);
}
@@ -333,12 +346,12 @@ static void cmd_names(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: NICK <new nick> */
static void cmd_nick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
{
- char *nick;
+ char *nick;
void *free_arg;
g_return_if_fail(data != NULL);
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1, &nick))
return;
@@ -375,23 +388,23 @@ static char *get_redirect_nicklist(const char *nicks, int *free)
/* SYNTAX: WHOIS [-<server tag>] [<server>] [<nicks>] */
static void cmd_whois(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
GHashTable *optlist;
char *qserver, *query, *event_402, *str;
void *free_arg;
int free_nick;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_UNKNOWN_OPTIONS,
- "whois", &optlist, &qserver, &query))
+ PARAM_FLAG_UNKNOWN_OPTIONS,
+ "whois", &optlist, &qserver, &query))
return;
/* -<server tag> */
server = IRC_SERVER(cmd_options_get_server("whois", optlist,
- SERVER(server)));
+ SERVER(server)));
if (server == NULL) {
cmd_params_free(free_arg);
return;
@@ -409,7 +422,7 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server,
query = qserver = queryitem->name;
}
- if (strcmp(query, "*") == 0 &&
+ if (g_strcmp0(query, "*") == 0 &&
g_hash_table_lookup(optlist, "yes") == NULL)
cmd_param_error(CMDERR_NOT_GOOD_IDEA);
@@ -426,15 +439,15 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server,
str = g_strconcat(qserver, " ", query, NULL);
server_redirect_event(server, "whois", 1, str, TRUE,
- NULL,
- "event 318", "whois end",
- "event 402", event_402,
- "event 301", "whois away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
- "event 313", "whois oper",
- "event 401", (settings_get_bool("auto_whowas") ? "whois try whowas" : "whois event not found"),
- "event 311", "whois event",
- "", "whois default event", NULL);
- g_free(str);
+ NULL,
+ "event 318", "whois end",
+ "event 402", event_402,
+ "event 301", "whois away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
+ "event 313", "whois oper",
+ "event 401", (settings_get_bool("auto_whowas") ? "whois try whowas" : "whois event not found"),
+ "event 311", "whois event",
+ "", "whois default event", NULL);
+ g_free(str);
server->whois_found = FALSE;
irc_send_cmd_split(server, tmpstr->str, 2, server->max_whois_in_cmd);
@@ -444,7 +457,7 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server,
}
static void event_whois(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr)
+ const char *nick, const char *addr)
{
server->whois_found = TRUE;
signal_emit("event 311", 4, server, data, nick, addr);
@@ -460,23 +473,23 @@ static void sig_whois_try_whowas(IRC_SERVER_REC *server, const char *data)
server->whowas_found = FALSE;
server_redirect_event(server, "whowas", 1, nick, -1, NULL,
- "event 314", "whowas event",
- "event 369", "whowas event end",
- "event 406", "event empty", NULL);
+ "event 314", "whowas event",
+ "event 369", "whowas event end",
+ "event 406", "event empty", NULL);
irc_send_cmdv(server, "WHOWAS %s 1", nick);
g_free(params);
}
static void event_end_of_whois(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr)
+ const char *nick, const char *addr)
{
signal_emit("event 318", 4, server, data, nick, addr);
server->whois_found = FALSE;
}
static void event_whowas(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr)
+ const char *nick, const char *addr)
{
server->whowas_found = TRUE;
signal_emit("event 314", 4, server, data, nick, addr);
@@ -489,21 +502,25 @@ static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
void *free_arg;
int free_nick;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &nicks, &rest))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ &nicks, &rest))
return;
if (*nicks == '\0') nicks = server->nick;
nicks_redir = get_redirect_nicklist(nicks, &free_nick);
server_redirect_event(server, "whowas", 1, nicks_redir, -1, NULL,
- "event 301", "whowas away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
- "event 314", "whowas event", NULL);
+ "event 301", "whowas away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
+ "event 314", "whowas event", NULL);
if (free_nick) g_free(nicks_redir);
server->whowas_found = FALSE;
- irc_send_cmdv(server, *rest == '\0' ? "WHOWAS %s" :
- "WHOWAS %s %s", nicks, rest);
+
+ if (rest[0] == '\0')
+ irc_send_cmdv(server, "WHOWAS %s", nicks);
+ else
+ irc_send_cmdv(server, "WHOWAS %s %s", nicks, rest);
cmd_params_free(free_arg);
}
@@ -512,9 +529,9 @@ static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
static void cmd_ping(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
{
GTimeVal tv;
- char *str;
+ char *str;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (*data == '\0') {
if (!IS_QUERY(item))
@@ -537,7 +554,7 @@ static void cmd_away(const char *data, IRC_SERVER_REC *server)
void *free_arg;
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "away", &optlist, &reason)) return;
+ PARAM_FLAG_GETREST, "away", &optlist, &reason)) return;
if (g_hash_table_lookup(optlist, "one") != NULL)
irc_server_send_away(server, reason);
@@ -550,7 +567,7 @@ static void cmd_away(const char *data, IRC_SERVER_REC *server)
/* SYNTAX: SCONNECT <new server> [[<port>] <existing server>] */
static void cmd_sconnect(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "CONNECT %s", data);
@@ -583,11 +600,11 @@ static void cmd_wait(const char *data, IRC_SERVER_REC *server)
void *free_arg;
int n;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
- NULL, &optlist, &msecs))
+ PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+ NULL, &optlist, &msecs))
return;
if (*msecs == '\0')
@@ -595,7 +612,7 @@ static void cmd_wait(const char *data, IRC_SERVER_REC *server)
/* -<server tag> */
server = IRC_SERVER(cmd_options_get_server(NULL, optlist,
- SERVER(server)));
+ SERVER(server)));
n = atoi(msecs);
if (server != NULL && n > 0) {
@@ -618,10 +635,10 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
IRC_CHANNEL_REC *chanrec;
GSList *tmp, *nicks;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN |
- PARAM_FLAG_GETREST, item, &channame, &msg))
+ PARAM_FLAG_GETREST, item, &channame, &msg))
return;
if (*msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -637,11 +654,11 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
/* Fall back to manually noticing each op */
nicks = NULL;
g_hash_table_foreach(chanrec->nicks,
- (GHFunc) cmd_wall_hash, &nicks);
+ (GHFunc) cmd_wall_hash, &nicks);
args = g_strconcat(chanrec->name, " ", recoded, NULL);
msg = parse_special_string(settings_get_str("wall_format"),
- SERVER(server), item, args, NULL, 0);
+ SERVER(server), item, args, NULL, 0);
g_free(args);
for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
@@ -649,7 +666,7 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
if (rec != chanrec->ownnick) {
irc_send_cmdv(server, "NOTICE %s :%s",
- rec->nick, msg);
+ rec->nick, msg);
}
}
@@ -663,17 +680,17 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
/* SYNTAX: KICKBAN [<channel>] <nicks> <reason> */
static void cmd_kickban(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
- IRC_CHANNEL_REC *chanrec;
+ IRC_CHANNEL_REC *chanrec;
char *channel, *nicks, *reason, *kickcmd, *bancmd, *recoded;
- char **nicklist, *spacenicks;
+ char **nicklist, *spacenicks;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
- item, &channel, &nicks, &reason))
+ item, &channel, &nicks, &reason))
return;
if (*channel == '\0' || *nicks == '\0')
@@ -684,7 +701,7 @@ static void cmd_kickban(const char *data, IRC_SERVER_REC *server,
cmd_param_error(CMDERR_CHAN_NOT_FOUND);
nicklist = g_strsplit(nicks, ",", -1);
- spacenicks = g_strjoinv(" ", nicklist);
+ spacenicks = g_strjoinv(" ", nicklist);
g_strfreev(nicklist);
recoded = recode_out(SERVER(server), reason, channel);
@@ -692,9 +709,9 @@ static void cmd_kickban(const char *data, IRC_SERVER_REC *server,
g_free(recoded);
bancmd = g_strdup_printf("%s %s", chanrec->name, spacenicks);
- g_free(spacenicks);
+ g_free(spacenicks);
- if (settings_get_bool("kick_first_on_kickban")) {
+ if (settings_get_bool("kick_first_on_kickban")) {
signal_emit("command kick", 3, kickcmd, server, chanrec);
signal_emit("command ban", 3, bancmd, server, chanrec);
} else {
@@ -725,14 +742,14 @@ static void knockout_timeout_server(IRC_SERVER_REC *server)
if (!IS_IRC_SERVER(server))
return;
- now = time(NULL);
+ now = time(NULL);
for (tmp = server->knockoutlist; tmp != NULL; tmp = next) {
KNOCKOUT_REC *rec = tmp->data;
next = tmp->next;
if (rec->unban_time <= now) {
/* timeout, unban. */
- ban_remove(rec->channel, rec->ban);
+ signal_emit("command unban", 3, rec->ban, server, rec->channel);
knockout_destroy(server, rec);
}
}
@@ -746,16 +763,16 @@ static int knockout_timeout(void)
/* SYNTAX: KNOCKOUT [<time>] <nicks> <reason> */
static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
- IRC_CHANNEL_REC *channel)
+ IRC_CHANNEL_REC *channel)
{
KNOCKOUT_REC *rec;
char *nicks, *reason, *timeoutstr, *kickcmd, *bancmd, *recoded;
- char **nicklist, *spacenicks, *banmasks;
+ char **nicklist, *spacenicks, *banmasks;
void *free_arg;
int timeleft;
GSList *ptr;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!IS_IRC_CHANNEL(channel))
cmd_return_error(CMDERR_NOT_JOINED);
@@ -763,14 +780,14 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
if (i_isdigit(*data)) {
/* first argument is the timeout */
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
- &timeoutstr, &nicks, &reason))
+ &timeoutstr, &nicks, &reason))
return;
if (!parse_time_interval(timeoutstr, &timeleft))
cmd_param_error(CMDERR_INVALID_TIME);
} else {
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
- &nicks, &reason))
+ &nicks, &reason))
return;
timeleft = settings_get_time("knockout_time");
}
@@ -778,7 +795,7 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
if (*nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
nicklist = g_strsplit(nicks, ",", -1);
- spacenicks = g_strjoinv(" ", nicklist);
+ spacenicks = g_strjoinv(" ", nicklist);
g_strfreev(nicklist);
banmasks = ban_get_masks(channel, spacenicks, 0);
@@ -790,8 +807,8 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
bancmd = *banmasks == '\0'? NULL :
g_strdup_printf("%s %s", channel->name, banmasks);
-
- if (settings_get_bool("kick_first_on_kickban")) {
+
+ if (settings_get_bool("kick_first_on_kickban")) {
signal_emit("command kick", 3, kickcmd, server, channel);
if (bancmd != NULL)
signal_emit("command ban", 3, bancmd, server, channel);
@@ -810,7 +827,7 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
for (ptr = server->knockoutlist; ptr != NULL; ptr = ptr->next) {
rec = ptr->data;
if (channel == rec->channel &&
- !strcmp(rec->ban, banmasks))
+ !g_strcmp0(rec->ban, banmasks))
break;
}
if (ptr == NULL) {
@@ -828,10 +845,10 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: SERVER PURGE [<target>] */
static void cmd_server_purge(const char *data, IRC_SERVER_REC *server)
{
- char *target;
+ char *target;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1, &target))
return;
@@ -878,12 +895,12 @@ static void cmd_oper(const char *data, IRC_SERVER_REC *server)
char *nick, *password;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- /* asking for password is handled by fe-common */
+ /* asking for password is handled by fe-common */
if (!cmd_get_params(data, &free_arg, 2, &nick, &password))
return;
- if (*password == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+ if (*password == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "OPER %s %s", nick, password);
cmd_params_free(free_arg);
@@ -892,9 +909,9 @@ static void cmd_oper(const char *data, IRC_SERVER_REC *server)
/* SYNTAX: ACCEPT [[-]nick,...] */
static void cmd_accept(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- if (*data == '\0')
+ if (*data == '\0')
irc_send_cmd(server, "ACCEPT *");
else
irc_send_cmdv(server, "ACCEPT %s", data);
@@ -903,9 +920,9 @@ static void cmd_accept(const char *data, IRC_SERVER_REC *server)
/* SYNTAX: UNSILENCE <nick!user@host> */
static void cmd_unsilence(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- if (*data == '\0')
+ if (*data == '\0')
cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "SILENCE -%s", data);
@@ -913,9 +930,12 @@ static void cmd_unsilence(const char *data, IRC_SERVER_REC *server)
static void command_self(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- irc_send_cmdv(server, *data == '\0' ? "%s" : "%s %s", current_command, data);
+ if (data[0] == '\0')
+ irc_send_cmdv(server, "%s", current_command);
+ else
+ irc_send_cmdv(server, "%s %s", current_command, data);
}
static void command_1self(const char *data, IRC_SERVER_REC *server)
@@ -933,7 +953,7 @@ static void command_2self(const char *data, IRC_SERVER_REC *server)
char *target, *text;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &text))
return;
@@ -978,8 +998,8 @@ void irc_commands_init(void)
command_bind_irc("admin", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: INFO [<server>] */
command_bind_irc("info", NULL, (SIGNAL_FUNC) command_self);
- /* SYNTAX: KNOCK <channel> */
- command_bind_irc("knock", NULL, (SIGNAL_FUNC) command_self);
+ /* SYNTAX: KNOCK <channel> */
+ command_bind_irc("knock", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: LINKS [[<server>] <mask>] */
command_bind_irc("links", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: LUSERS [<server mask> [<remote server>]] */
@@ -998,11 +1018,15 @@ void irc_commands_init(void)
command_bind_irc("trace", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: VERSION [<server>|<nick>] */
command_bind_irc("version", NULL, (SIGNAL_FUNC) command_self);
+ /* SYNTAX: SERVLIST [<mask> [<type>]] */
+ command_bind_irc("servlist", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: SILENCE [[+|-]<nick!user@host>]
SILENCE [<nick>] */
command_bind_irc("silence", NULL, (SIGNAL_FUNC) command_self);
command_bind_irc("unsilence", NULL, (SIGNAL_FUNC) cmd_unsilence);
command_bind_irc("sconnect", NULL, (SIGNAL_FUNC) cmd_sconnect);
+ /* SYNTAX: SQUERY <service> [<message>] */
+ command_bind_irc("squery", NULL, (SIGNAL_FUNC) command_2self);
/* SYNTAX: DIE */
command_bind_irc("die", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: HASH */
@@ -1061,7 +1085,7 @@ void irc_commands_deinit(void)
command_unbind("accept", (SIGNAL_FUNC) cmd_accept);
command_unbind("admin", (SIGNAL_FUNC) command_self);
command_unbind("info", (SIGNAL_FUNC) command_self);
- command_unbind("knock", (SIGNAL_FUNC) command_self);
+ command_unbind("knock", (SIGNAL_FUNC) command_self);
command_unbind("links", (SIGNAL_FUNC) command_self);
command_unbind("lusers", (SIGNAL_FUNC) command_self);
command_unbind("map", (SIGNAL_FUNC) command_self);
@@ -1071,9 +1095,11 @@ void irc_commands_deinit(void)
command_unbind("time", (SIGNAL_FUNC) command_self);
command_unbind("trace", (SIGNAL_FUNC) command_self);
command_unbind("version", (SIGNAL_FUNC) command_self);
+ command_unbind("servlist", (SIGNAL_FUNC) command_self);
command_unbind("silence", (SIGNAL_FUNC) command_self);
command_unbind("unsilence", (SIGNAL_FUNC) cmd_unsilence);
command_unbind("sconnect", (SIGNAL_FUNC) cmd_sconnect);
+ command_unbind("squery", (SIGNAL_FUNC) command_2self);
command_unbind("die", (SIGNAL_FUNC) command_self);
command_unbind("hash", (SIGNAL_FUNC) command_self);
command_unbind("oper", (SIGNAL_FUNC) cmd_oper);
diff --git a/src/irc/core/irc-core.c b/src/irc/core/irc-core.c
index bf7386ad..a9221e02 100644
--- a/src/irc/core/irc-core.c
+++ b/src/irc/core/irc-core.c
@@ -26,6 +26,8 @@
#include "irc-chatnets.h"
#include "irc-channels.h"
#include "irc-queries.h"
+#include "irc-cap.h"
+#include "sasl.h"
#include "irc-servers-setup.h"
#include "channels-setup.h"
@@ -117,6 +119,8 @@ void irc_core_init(void)
lag_init();
netsplit_init();
irc_expandos_init();
+ cap_init();
+ sasl_init();
settings_check();
module_register("core", "irc");
@@ -126,6 +130,8 @@ void irc_core_deinit(void)
{
signal_emit("chat protocol deinit", 1, chat_protocol_find("IRC"));
+ sasl_deinit();
+ cap_deinit();
irc_expandos_deinit();
netsplit_deinit();
lag_deinit();
@@ -137,7 +143,7 @@ void irc_core_deinit(void)
irc_irc_deinit();
irc_servers_deinit();
irc_chatnets_deinit();
- irc_session_deinit();
+ irc_session_deinit();
chat_protocol_unregister("IRC");
}
diff --git a/src/irc/core/irc-expandos.c b/src/irc/core/irc-expandos.c
index 5d2de503..62ef577a 100644
--- a/src/irc/core/irc-expandos.c
+++ b/src/irc/core/irc-expandos.c
@@ -27,6 +27,10 @@
#include "irc-channels.h"
#include "nicklist.h"
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
static char *last_join;
/* last person to join a channel you are on */
@@ -56,7 +60,7 @@ static char *expando_userhost(SERVER_REC *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver;
const char *username;
- char hostname[100];
+ char hostname[HOST_NAME_MAX + 1];
ircserver = IRC_SERVER(server);
@@ -72,10 +76,36 @@ static char *expando_userhost(SERVER_REC *server, void *item, int *free_ret)
username = ircserver->connrec->username;
if (gethostname(hostname, sizeof(hostname)) != 0 || *hostname == '\0')
- strcpy(hostname, "??");
+ strcpy(hostname, "(none)");
return g_strconcat(username, "@", hostname, NULL);;
}
+/* your hostname address (host) */
+static char *expando_hostname(SERVER_REC *server, void *item, int *free_ret)
+{
+ IRC_SERVER_REC *ircserver;
+ char hostname[HOST_NAME_MAX + 1];
+ char **list;
+ char *hostname_split;
+
+ ircserver = IRC_SERVER(server);
+
+ *free_ret = TRUE;
+
+ /* prefer the _real_ /userhost reply */
+ if (ircserver != NULL && ircserver->userhost != NULL) {
+ list = g_strsplit(ircserver->userhost, "@", -1);
+ hostname_split = g_strdup(list[1]);
+ g_strfreev(list);
+ return hostname_split;
+ }
+
+ /* haven't received userhost reply yet. guess something */
+ if (gethostname(hostname, sizeof(hostname)) != 0 || *hostname == '\0')
+ strcpy(hostname, "(none)");
+ return g_strdup(hostname);
+}
+
/* user mode in active server */
static char *expando_usermode(SERVER_REC *server, void *item, int *free_ret)
{
@@ -136,6 +166,9 @@ void irc_expandos_init(void)
expando_create("X", expando_userhost,
"window changed", EXPANDO_ARG_NONE,
"window server changed", EXPANDO_ARG_WINDOW, NULL);
+ expando_create("x", expando_hostname,
+ "window changed", EXPANDO_ARG_NONE,
+ "window server changed", EXPANDO_ARG_WINDOW, NULL);
expando_create("usermode", expando_usermode,
"window changed", EXPANDO_ARG_NONE,
"window server changed", EXPANDO_ARG_WINDOW,
@@ -164,6 +197,7 @@ void irc_expandos_deinit(void)
expando_destroy("H", expando_server_numeric);
expando_destroy("S", expando_servername);
expando_destroy("X", expando_userhost);
+ expando_destroy("x", expando_hostname);
expando_destroy("usermode", expando_usermode);
expando_destroy("cumode", expando_cumode);
diff --git a/src/irc/core/irc-nicklist.c b/src/irc/core/irc-nicklist.c
index da9d4aca..1cb1f3e9 100644
--- a/src/irc/core/irc-nicklist.c
+++ b/src/irc/core/irc-nicklist.c
@@ -48,37 +48,13 @@ NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick,
rec->send_massjoin = send_massjoin;
if (prefixes != NULL) {
- strocpy(rec->prefixes, prefixes, sizeof(rec->prefixes));
+ g_strlcpy(rec->prefixes, prefixes, sizeof(rec->prefixes));
}
nicklist_insert(CHANNEL(channel), rec);
return rec;
}
-#define isnickchar(a) \
- (i_isalnum(a) || (a) == '`' || (a) == '-' || (a) == '_' || \
- (a) == '[' || (a) == ']' || (a) == '{' || (a) == '}' || \
- (a) == '|' || (a) == '\\' || (a) == '^')
-
-/* Remove all "extra" characters from `nick'. Like _nick_ -> nick */
-char *irc_nick_strip(const char *nick)
-{
- char *stripped, *spos;
-
- g_return_val_if_fail(nick != NULL, NULL);
-
- spos = stripped = g_strdup(nick);
- while (isnickchar(*nick)) {
- if (i_isalnum(*nick))
- *spos++ = *nick;
- nick++;
- }
- if ((unsigned char) *nick >= 128)
- *spos++ = *nick; /* just add it so that nicks won't match.. */
- *spos = '\0';
- return stripped;
-}
-
int irc_nickcmp_rfc1459(const char *m, const char *n)
{
while (*m != '\0' && *n != '\0') {
@@ -195,7 +171,7 @@ static void event_end_of_names(IRC_SERVER_REC *server, const char *data)
IRC_CHANNEL_REC *chanrec;
NICK_REC *ownnick;
int nicks;
-
+
g_return_if_fail(server != NULL);
params = event_get_params(data, 2, NULL, &channel);
@@ -338,7 +314,11 @@ static void event_whois_ircop(SERVER_REC *server, const char *data)
static void event_nick_invalid(IRC_SERVER_REC *server, const char *data)
{
if (!server->connected)
- server_disconnect((SERVER_REC *) server);
+ /* we used to call server_disconnect but that crashes
+ irssi because of undefined memory access. instead,
+ indicate that the connection should be dropped and
+ let the irc method to the clean-up. */
+ server->connection_lost = server->no_reconnect = TRUE;
}
static void event_nick_in_use(IRC_SERVER_REC *server, const char *data)
@@ -384,7 +364,7 @@ static void event_nick_in_use(IRC_SERVER_REC *server, const char *data)
cmd = g_strdup_printf("NICK %s", server->nick);
irc_send_cmd_now(server, cmd);
- g_free(cmd);
+ g_free(cmd);
}
static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
@@ -394,7 +374,7 @@ static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
- if (!ischannel(*channel)) {
+ if (!server_ischannel(SERVER(server), channel)) {
/* nick is unavailable. */
event_nick_in_use(server, data);
}
diff --git a/src/irc/core/irc-nicklist.h b/src/irc/core/irc-nicklist.h
index 7302556b..2ae17d2c 100644
--- a/src/irc/core/irc-nicklist.h
+++ b/src/irc/core/irc-nicklist.h
@@ -8,9 +8,6 @@ NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick,
int op, int halfop, int voice, int send_massjoin,
const char *prefixes);
-/* Remove all "extra" characters from `nick'. Like _nick_ -> nick */
-char *irc_nick_strip(const char *nick);
-
int irc_nickcmp_rfc1459(const char *, const char *);
int irc_nickcmp_ascii(const char *, const char *);
diff --git a/src/irc/core/irc-queries.c b/src/irc/core/irc-queries.c
index ac1a72a1..64995ead 100644
--- a/src/irc/core/irc-queries.c
+++ b/src/irc/core/irc-queries.c
@@ -45,6 +45,8 @@ QUERY_REC *irc_query_find(IRC_SERVER_REC *server, const char *nick)
{
GSList *tmp;
+ g_return_val_if_fail(nick != NULL, NULL);
+
for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
QUERY_REC *rec = tmp->data;
@@ -60,39 +62,25 @@ static void check_query_changes(IRC_SERVER_REC *server, const char *nick,
{
QUERY_REC *query;
- if (ischannel(*target))
- return;
+ if (server_ischannel(SERVER(server), target))
+ return;
query = irc_query_find(server, nick);
if (query == NULL)
return;
- if (strcmp(query->name, nick) != 0) {
+ if (g_strcmp0(query->name, nick) != 0) {
/* upper/lowercase chars in nick changed */
query_change_nick(query, nick);
}
if (address != NULL && (query->address == NULL ||
- strcmp(query->address, address) != 0)) {
+ g_strcmp0(query->address, address) != 0)) {
/* host changed */
query_change_address(query, address);
}
}
-static void event_privmsg(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *address)
-{
- char *params, *target, *msg;
-
- g_return_if_fail(data != NULL);
- if (nick == NULL)
- return;
-
- params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
- check_query_changes(server, nick, address, target);
- g_free(params);
-}
-
static void ctcp_action(IRC_SERVER_REC *server, const char *msg,
const char *nick, const char *address,
const char *target)
@@ -109,7 +97,7 @@ static void event_nick(SERVER_REC *server, const char *data,
query = query_find(server, orignick);
if (query != NULL) {
params = event_get_params(data, 1, &nick);
- if (strcmp(query->name, nick) != 0)
+ if (g_strcmp0(query->name, nick) != 0)
query_change_nick(query, nick);
g_free(params);
}
@@ -117,14 +105,12 @@ static void event_nick(SERVER_REC *server, const char *data,
void irc_queries_init(void)
{
- signal_add_last("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_add_last("ctcp action", (SIGNAL_FUNC) ctcp_action);
signal_add("event nick", (SIGNAL_FUNC) event_nick);
}
void irc_queries_deinit(void)
{
- signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_remove("ctcp action", (SIGNAL_FUNC) ctcp_action);
signal_remove("event nick", (SIGNAL_FUNC) event_nick);
}
diff --git a/src/irc/core/irc-servers-reconnect.c b/src/irc/core/irc-servers-reconnect.c
index 1587d4e6..ca61492d 100644
--- a/src/irc/core/irc-servers-reconnect.c
+++ b/src/irc/core/irc-servers-reconnect.c
@@ -48,6 +48,9 @@ static void sig_server_connect_copy(SERVER_CONNECT_REC **dest,
rec->max_whois = src->max_whois;
rec->usermode = g_strdup(src->usermode);
rec->alternate_nick = g_strdup(src->alternate_nick);
+ rec->sasl_mechanism = src->sasl_mechanism;
+ rec->sasl_username = src->sasl_username;
+ rec->sasl_password = src->sasl_password;
*dest = (SERVER_CONNECT_REC *) rec;
}
@@ -71,7 +74,7 @@ static void sig_connected(IRC_SERVER_REC *server)
return;
if (server->connrec->away_reason != NULL)
- irc_server_send_away(server, server->connrec->away_reason);
+ irc_server_send_away(server, server->connrec->away_reason);
}
static void event_nick_collision(IRC_SERVER_REC *server, const char *data)
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 13784f88..3117e345 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -32,6 +32,10 @@
#include "irc-queries.h"
#include "irc-servers-setup.h"
#include "irc-servers.h"
+#include "irc-cap.h"
+#include "sasl.h"
+
+#include "channels-setup.h"
#include "channel-rejoin.h"
#include "servers-idle.h"
#include "servers-reconnect.h"
@@ -71,13 +75,28 @@ static int isnickflag_func(SERVER_REC *server, char flag)
static int ischannel_func(SERVER_REC *server, const char *data)
{
- if (*data == '@') {
- /* @#channel, @+#channel */
- data++;
- if (*data == '+' && ischannel(data[1]))
- return 1;
- }
- return ischannel(*data);
+ IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
+ char *chantypes, *statusmsg;
+
+ g_return_val_if_fail(data != NULL, FALSE);
+
+ /* empty string is no channel */
+ if (*data == '\0')
+ return FALSE;
+
+ chantypes = g_hash_table_lookup(irc_server->isupport, "chantypes");
+ if (chantypes == NULL)
+ chantypes = "#&!+"; /* normal, local, secure, modeless */
+
+ statusmsg = g_hash_table_lookup(irc_server->isupport, "statusmsg");
+ if (statusmsg == NULL)
+ statusmsg = "@";
+
+ data += strspn(data, statusmsg);
+
+ /* strchr(3) considers the trailing NUL as part of the string, make sure
+ * we didn't advance too much. */
+ return *data != '\0' && strchr(chantypes, *data) != NULL;
}
static char **split_line(const SERVER_REC *server, const char *line,
@@ -85,6 +104,7 @@ static char **split_line(const SERVER_REC *server, const char *line,
{
const char *start = settings_get_str("split_line_start");
const char *end = settings_get_str("split_line_end");
+ gboolean onspace = settings_get_bool("split_line_on_space");
char *recoded_start = recode_out(server, start, target);
char *recoded_end = recode_out(server, end, target);
char **lines;
@@ -103,7 +123,7 @@ static char **split_line(const SERVER_REC *server, const char *line,
return NULL;
}
- lines = recode_split(server, line, target, len);
+ lines = recode_split(server, line, target, len, onspace);
for (i = 0; lines[i] != NULL; i++) {
if (i != 0 && *start != '\0') {
/* Not the first line. */
@@ -178,23 +198,15 @@ static char **split_message(SERVER_REC *server, const char *target,
const char *msg)
{
IRC_SERVER_REC *ircserver = IRC_SERVER(server);
- int userhostlen = MAX_USERHOST_LEN;
g_return_val_if_fail(ircserver != NULL, NULL);
g_return_val_if_fail(target != NULL, NULL);
g_return_val_if_fail(msg != NULL, NULL);
- /*
- * If we have joined a channel, userhost will be set, so we can
- * calculate the exact maximum length.
- */
- if (ircserver->userhost != NULL)
- userhostlen = strlen(ircserver->userhost);
-
- /* length calculation shamelessly stolen from splitlong.pl */
+ /* length calculation shamelessly stolen from splitlong_safe.pl */
return split_line(SERVER(server), msg, target,
510 - strlen(":! PRIVMSG :") -
- strlen(ircserver->nick) - userhostlen -
+ strlen(ircserver->nick) - MAX_USERHOST_LEN -
strlen(target));
}
@@ -202,7 +214,6 @@ static void server_init(IRC_SERVER_REC *server)
{
IRC_SERVER_CONNECT_REC *conn;
char *address, *ptr, *username, *cmd;
- GTimeVal now;
g_return_if_fail(server != NULL);
@@ -221,6 +232,13 @@ static void server_init(IRC_SERVER_REC *server)
g_free(cmd);
}
+ if (conn->sasl_mechanism != SASL_MECHANISM_NONE)
+ cap_toggle(server, "sasl", TRUE);
+
+ cap_toggle(server, "multi-prefix", TRUE);
+
+ irc_send_cmd_now(server, "CAP LS");
+
if (conn->password != NULL && *conn->password != '\0') {
/* send password */
cmd = g_strdup_printf("PASS %s", conn->password);
@@ -270,9 +288,8 @@ static void server_init(IRC_SERVER_REC *server)
/* prevent the queue from sending too early, we have a max cut off of 120 secs */
/* this will reset to 1 sec after we get the 001 event */
- g_get_current_time(&now);
- memcpy(&((IRC_SERVER_REC *)server)->wait_cmd, &now, sizeof(GTimeVal));
- ((IRC_SERVER_REC *)server)->wait_cmd.tv_sec += 120;
+ g_get_current_time(&server->wait_cmd);
+ g_time_val_add(&server->wait_cmd, 120 * G_USEC_PER_SEC);
}
SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
@@ -293,7 +310,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
if (server->connrec->port <= 0) {
server->connrec->port =
- server->connrec->use_ssl ? 6697 : 6667;
+ server->connrec->use_tls ? 6697 : 6667;
}
server->cmd_queue_speed = ircconn->cmd_queue_speed > 0 ?
@@ -311,7 +328,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
ircconn->max_whois : DEFAULT_MAX_WHOIS;
server->max_msgs_in_cmd = ircconn->max_msgs > 0 ?
ircconn->max_msgs : DEFAULT_MAX_MSGS;
- server->connrec->use_ssl = conn->use_ssl;
+ server->connrec->use_tls = conn->use_tls;
modes_server_init(server);
@@ -321,6 +338,8 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
void irc_server_connect(SERVER_REC *server)
{
+ g_return_if_fail(server != NULL);
+
if (!server_start_connect(server)) {
server_connect_unref(server->connrec);
g_free(server);
@@ -416,7 +435,18 @@ static void sig_disconnected(IRC_SERVER_REC *server)
server_redirect_destroy(tmp->next->data);
}
g_slist_free(server->cmdqueue);
- server->cmdqueue = NULL;
+ server->cmdqueue = NULL;
+
+ gslist_free_full(server->cap_active, (GDestroyNotify) g_free);
+ server->cap_active = NULL;
+
+ gslist_free_full(server->cap_supported, (GDestroyNotify) g_free);
+ server->cap_supported = NULL;
+
+ gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
+ server->cap_queue = NULL;
+
+ g_free_and_null(server->sasl_buffer);
/* these are dynamically allocated only if isupport was sent */
g_hash_table_foreach(server->isupport,
@@ -458,18 +488,14 @@ void irc_server_send_action(IRC_SERVER_REC *server, const char *target, const ch
char **irc_server_split_action(IRC_SERVER_REC *server, const char *target,
const char *data)
{
- int userhostlen = MAX_USERHOST_LEN;
-
g_return_val_if_fail(server != NULL, NULL);
g_return_val_if_fail(target != NULL, NULL);
g_return_val_if_fail(data != NULL, NULL);
- if (server->userhost != NULL)
- userhostlen = strlen(server->userhost);
-
return split_line(SERVER(server), data, target,
510 - strlen(":! PRIVMSG :\001ACTION \001") -
- strlen(server->nick) - userhostlen - strlen(target));
+ strlen(server->nick) - MAX_USERHOST_LEN -
+ strlen(target));
}
void irc_server_send_away(IRC_SERVER_REC *server, const char *reason)
@@ -484,9 +510,11 @@ void irc_server_send_away(IRC_SERVER_REC *server, const char *reason)
if (*reason != '\0') {
server->away_reason = g_strdup(reason);
reason = recoded = recode_out(SERVER(server), reason, NULL);
+ irc_send_cmdv(server, "AWAY :%s", reason);
+ } else {
+ irc_send_cmdv(server, "AWAY");
}
- irc_send_cmdv(server, "AWAY :%s", reason);
}
g_free(recoded);
}
@@ -509,7 +537,7 @@ void irc_server_send_data(IRC_SERVER_REC *server, const char *data, int len)
server->wait_cmd.tv_sec = 0;
else {
memcpy(&server->wait_cmd, &server->last_cmd, sizeof(GTimeVal));
- server->wait_cmd.tv_sec += 2 + len/100;
+ g_time_val_add(&server->wait_cmd, (2 + len/100) * G_USEC_PER_SEC);
}
}
@@ -597,9 +625,16 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
GString *chans, *keys;
char *ret;
int use_keys;
+ int rejoin_channels_mode;
g_return_val_if_fail(server != NULL, FALSE);
+ rejoin_channels_mode = settings_get_choice("rejoin_channels_on_reconnect");
+
+ /* do we want to rejoin channels in the first place? */
+ if(rejoin_channels_mode == 0)
+ return g_strdup("");
+
chans = g_string_new(NULL);
keys = g_string_new(NULL);
use_keys = FALSE;
@@ -607,22 +642,27 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
/* get currently joined channels */
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *channel = tmp->data;
-
- g_string_append_printf(chans, "%s,", channel->name);
- g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" :
- channel->key);
- if (channel->key != NULL)
- use_keys = TRUE;
+ CHANNEL_SETUP_REC *setup = channel_setup_find(channel->name, channel->server->connrec->chatnet);
+ if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
+ g_string_append_printf(chans, "%s,", channel->name);
+ g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" : channel->key);
+ if (channel->key != NULL)
+ use_keys = TRUE;
+ }
}
/* get also the channels that are in rejoin list */
for (tmp = server->rejoin_channels; tmp != NULL; tmp = tmp->next) {
REJOIN_REC *rec = tmp->data;
+ CHANNEL_SETUP_REC *setup = channel_setup_find(rec->channel, server->tag);
+
+ if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
+ g_string_append_printf(chans, "%s,", rec->channel);
+ g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
+ rec->key);
- g_string_append_printf(chans, "%s,", rec->channel);
- g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
- rec->key);
- if (rec->key != NULL) use_keys = TRUE;
+ if (rec->key != NULL) use_keys = TRUE;
+ }
}
if (chans->len > 0) {
@@ -641,13 +681,12 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
static void event_connected(IRC_SERVER_REC *server, const char *data, const char *from)
{
char *params, *nick;
- GTimeVal now;
g_return_if_fail(server != NULL);
params = event_get_params(data, 1, &nick);
- if (strcmp(server->nick, nick) != 0) {
+ if (g_strcmp0(server->nick, nick) != 0) {
/* nick changed unexpectedly .. connected via proxy, etc. */
g_free(server->nick);
server->nick = g_strdup(nick);
@@ -664,8 +703,7 @@ static void event_connected(IRC_SERVER_REC *server, const char *data, const char
server->real_connect_time = time(NULL);
/* let the queue send now that we are identified */
- g_get_current_time(&now);
- memcpy(&server->wait_cmd, &now, sizeof(GTimeVal));
+ g_get_current_time(&server->wait_cmd);
if (server->connrec->usermode != NULL) {
/* Send the user mode, before the autosendcmd.
@@ -753,7 +791,7 @@ static void event_isupport(IRC_SERVER_REC *server, const char *data)
char **item, *sptr, *eptr;
char **isupport;
gpointer key, value;
-
+
g_return_if_fail(server != NULL);
server->isupport_sent = TRUE;
@@ -979,9 +1017,11 @@ void irc_server_init_isupport(IRC_SERVER_REC *server)
void irc_servers_init(void)
{
+ settings_add_choice("servers", "rejoin_channels_on_reconnect", 1, "off;on;auto");
settings_add_str("misc", "usermode", DEFAULT_USER_MODE);
settings_add_str("misc", "split_line_start", "");
settings_add_str("misc", "split_line_end", "");
+ settings_add_bool("misc", "split_line_on_space", TRUE);
settings_add_time("flood", "cmd_queue_speed", DEFAULT_CMD_QUEUE_SPEED);
settings_add_int("flood", "cmds_max_at_once", DEFAULT_CMDS_MAX_AT_ONCE);
diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h
index 7e4eeabf..09f3f81d 100644
--- a/src/irc/core/irc-servers.h
+++ b/src/irc/core/irc-servers.h
@@ -27,6 +27,10 @@ struct _IRC_SERVER_CONNECT_REC {
char *usermode;
char *alternate_nick;
+ int sasl_mechanism;
+ char *sasl_username;
+ char *sasl_password;
+
int max_cmds_at_once;
int cmd_queue_speed;
int max_query_chans;
@@ -63,12 +67,21 @@ struct _IRC_SERVER_REC {
unsigned int nick_collision:1; /* We're just now being killed because of nick collision */
unsigned int motd_got:1; /* We've received MOTD */
unsigned int isupport_sent:1; /* Server has sent us an isupport reply */
+ unsigned int cap_complete:1; /* We've done the initial CAP negotiation */
+ unsigned int sasl_success:1; /* Did we authenticate successfully ? */
int max_kicks_in_cmd; /* max. number of people to kick with one /KICK command */
int max_modes_in_cmd; /* max. number of mode changes in one /MODE command */
int max_whois_in_cmd; /* max. number of nicks in one /WHOIS command */
int max_msgs_in_cmd; /* max. number of targets in one /MSG */
+ GSList *cap_supported; /* A list of caps supported by the server */
+ GSList *cap_active; /* A list of caps active for this session */
+ GSList *cap_queue; /* A list of caps to request on connection */
+
+ GString *sasl_buffer; /* Buffer used to reassemble a fragmented SASL payload */
+ guint sasl_timeout; /* Holds the source id of the running timeout */
+
/* Command sending queue */
int cmdcount; /* number of commands in `cmdqueue'. Can be more than
there actually is, to make flood control remember
diff --git a/src/irc/core/irc-session.c b/src/irc/core/irc-session.c
index bcb0a122..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,11 +67,15 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config,
config_node_set_str(config, node, "away_reason", server->away_reason);
config_node_set_bool(config, node, "emode_known", server->emode_known);
+ config_node_set_int(config, node, "sasl_mechanism", server->connrec->sasl_mechanism);
+ config_node_set_str(config, node, "sasl_username", server->connrec->sasl_username);
+ config_node_set_str(config, node, "sasl_password", server->connrec->sasl_password);
+
config_node_set_bool(config, node, "isupport_sent", server->isupport_sent);
- isupport = config_node_section(node, "isupport", NODE_TYPE_BLOCK);
+ isupport = config_node_section(config, node, "isupport", NODE_TYPE_BLOCK);
isupport_data.config = config;
isupport_data.node = isupport;
-
+
g_hash_table_foreach(server->isupport, (GHFunc) session_isupport_foreach, &isupport_data);
}
@@ -90,12 +96,21 @@ static void sig_session_restore_server(IRC_SERVER_REC *server,
server->emode_known = config_node_get_bool(node, "emode_known", FALSE);
server->isupport_sent = config_node_get_bool(node, "isupport_sent", FALSE);
+ server->connrec->sasl_mechanism = config_node_get_int(node, "sasl_mechanism", SASL_MECHANISM_NONE);
+ /* The fields below might have been filled when loading the chatnet
+ * description from the config and we favor the content that's been saved
+ * in the session file over that. */
+ g_free(server->connrec->sasl_username);
+ server->connrec->sasl_username = g_strdup(config_node_get_str(node, "sasl_username", NULL));
+ g_free(server->connrec->sasl_password);
+ server->connrec->sasl_password = g_strdup(config_node_get_str(node, "sasl_password", NULL));
+
if (server->isupport == NULL) {
server->isupport = g_hash_table_new((GHashFunc) g_istr_hash,
(GCompareFunc) g_istr_equal);
}
- node = config_node_section(node, "isupport", -1);
+ node = config_node_section(NULL, node, "isupport", -1);
tmp = node == NULL ? NULL : config_node_first(node->value);
for (; tmp != NULL; tmp = config_node_next(tmp)) {
diff --git a/src/irc/core/irc.c b/src/irc/core/irc.c
index 509418b7..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 7054182f..cc3d0faf 100644
--- a/src/irc/core/modes.c
+++ b/src/irc/core/modes.c
@@ -78,7 +78,7 @@ void prefix_add(char prefixes[MAX_USER_PREFIXES+1], char newprefix, SERVER_REC *
if (*prefixlst == newprefix)
break; /* insert the new prefix here */
-
+
if (*prefixlst == prefixes[oldpos]) {
/* this prefix is present.
* the one we are inserting goes after it.
@@ -88,7 +88,7 @@ void prefix_add(char prefixes[MAX_USER_PREFIXES+1], char newprefix, SERVER_REC *
}
prefixlst++;
}
-
+
/* newpos is now the position in which we wish to insert the prefix */
newprefixes[newpos++] = newprefix;
@@ -274,7 +274,7 @@ void modes_type_b(IRC_CHANNEL_REC *channel, const char *setby, char type,
channel->key = g_strdup(arg);
}
}
-
+
mode_set_arg(channel->server, newmode, type, mode, arg, FALSE);
}
@@ -285,7 +285,7 @@ void modes_type_c(IRC_CHANNEL_REC *channel, const char *setby,
if (mode == 'l') {
channel->limit = type == '-' ? 0 : atoi(arg);
}
-
+
mode_set_arg(channel->server, newmode, type, mode, arg, FALSE);
}
@@ -398,7 +398,7 @@ void parse_channel_modes(IRC_CHANNEL_REC *channel, const char *setby,
old_key = NULL;
}
- if (strcmp(newmode->str, channel->mode) != 0) {
+ if (g_strcmp0(newmode->str, channel->mode) != 0) {
g_free(channel->mode);
channel->mode = g_strdup(newmode->str);
@@ -488,7 +488,7 @@ static void event_mode(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
&channel, &mode);
- if (!ischannel(*channel)) {
+ if (!server_ischannel(SERVER(server), channel)) {
/* user mode change */
parse_user_mode(server, mode);
} else {
@@ -536,7 +536,7 @@ static void sig_req_usermode_change(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
&target, &mode);
- if (!ischannel(*target)) {
+ if (!server_ischannel(SERVER(server), target)) {
/* we requested a user mode change, save this */
mode = modes_join(NULL, server->wanted_usermode, mode, FALSE);
g_free_not_null(server->wanted_usermode);
@@ -743,6 +743,7 @@ static char *get_nicks(IRC_SERVER_REC *server, WI_ITEM_REC *item,
g_hash_table_lookup(optlist, "yes") == NULL) {
/* too many matches */
g_string_free(str, TRUE);
+ g_strfreev(matches);
cmd_params_free(free_arg);
signal_emit("error command", 1,
@@ -756,7 +757,7 @@ static char *get_nicks(IRC_SERVER_REC *server, WI_ITEM_REC *item,
if (str->len > 0) g_string_truncate(str, str->len-1);
ret = str->str;
g_string_free(str, FALSE);
-
+ g_strfreev(matches);
cmd_params_free(free_arg);
*ret_channel = channel;
@@ -835,14 +836,14 @@ static void cmd_mode(const char *data, IRC_SERVER_REC *server,
if (*data == '+' || *data == '-') {
target = "*";
- if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_GETREST, &mode))
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, &mode))
return;
} else {
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &mode))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, &target, &mode))
return;
}
- if (strcmp(target, "*") == 0) {
+ if (g_strcmp0(target, "*") == 0) {
if (!IS_IRC_CHANNEL(channel))
cmd_param_error(CMDERR_NOT_JOINED);
@@ -856,7 +857,7 @@ static void cmd_mode(const char *data, IRC_SERVER_REC *server,
target = chanrec->name;
irc_send_cmdv(server, "MODE %s", target);
- } else if (ischannel(*target))
+ } else if (server_ischannel(SERVER(server), target))
channel_set_mode(server, target, mode);
else {
if (g_ascii_strcasecmp(target, server->nick) == 0) {
diff --git a/src/irc/core/sasl.c b/src/irc/core/sasl.c
new file mode 100644
index 00000000..1021bea4
--- /dev/null
+++ b/src/irc/core/sasl.c
@@ -0,0 +1,329 @@
+/*
+ fe-sasl.c : irssi
+
+ Copyright (C) 2015 The Lemon Man
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "module.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "irc-cap.h"
+#include "irc-servers.h"
+#include "sasl.h"
+
+/*
+ * Based on IRCv3 SASL Extension Specification:
+ * http://ircv3.net/specs/extensions/sasl-3.1.html
+ */
+#define AUTHENTICATE_CHUNK_SIZE 400 // bytes
+
+/*
+ * Maximum size to allow the buffer to grow to before the next fragment comes in. Note that
+ * due to the way fragmentation works, the maximum message size will actually be:
+ * floor(AUTHENTICATE_MAX_SIZE / AUTHENTICATE_CHUNK_SIZE) + AUTHENTICATE_CHUNK_SIZE - 1
+ */
+#define AUTHENTICATE_MAX_SIZE 8192 // bytes
+
+#define SASL_TIMEOUT (20 * 1000) // ms
+
+static gboolean sasl_timeout(IRC_SERVER_REC *server)
+{
+ /* The authentication timed out, we can't do much beside terminating it */
+ irc_send_cmd_now(server, "AUTHENTICATE *");
+ cap_finish_negotiation(server);
+
+ server->sasl_timeout = 0;
+ server->sasl_success = FALSE;
+
+ signal_emit("server sasl failure", 2, server, "The authentication timed out");
+
+ return FALSE;
+}
+
+static void sasl_start(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ IRC_SERVER_CONNECT_REC *conn;
+
+ conn = server->connrec;
+
+ switch (conn->sasl_mechanism) {
+ case SASL_MECHANISM_PLAIN:
+ irc_send_cmd_now(server, "AUTHENTICATE PLAIN");
+ break;
+
+ case SASL_MECHANISM_EXTERNAL:
+ irc_send_cmd_now(server, "AUTHENTICATE EXTERNAL");
+ break;
+ }
+ server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
+}
+
+static void sasl_fail(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ char *params, *error;
+
+ /* Stop any pending timeout, if any */
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ params = event_get_params(data, 2, NULL, &error);
+
+ server->sasl_success = FALSE;
+
+ signal_emit("server sasl failure", 2, server, error);
+
+ /* Terminate the negotiation */
+ cap_finish_negotiation(server);
+
+ g_free(params);
+}
+
+static void sasl_already(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ server->sasl_success = TRUE;
+
+ signal_emit("server sasl success", 1, server);
+
+ /* We're already authenticated, do nothing */
+ cap_finish_negotiation(server);
+}
+
+static void sasl_success(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ server->sasl_success = TRUE;
+
+ signal_emit("server sasl success", 1, server);
+
+ /* The authentication succeeded, time to finish the CAP negotiation */
+ cap_finish_negotiation(server);
+}
+
+/*
+ * Responsible for reassembling incoming SASL requests. SASL requests must be split
+ * into 400 byte requests to stay below the IRC command length limit of 512 bytes.
+ * The spec says that if there is 400 bytes, then there is expected to be a
+ * continuation in the next chunk. If a message is exactly a multiple of 400 bytes,
+ * there must be a blank message of "AUTHENTICATE +" to indicate the end.
+ *
+ * This function returns the fully reassembled and decoded AUTHENTICATION message if
+ * completed or NULL if there are more messages expected.
+ */
+static gboolean sasl_reassemble_incoming(IRC_SERVER_REC *server, const char *fragment, GString **decoded)
+{
+ GString *enc_req;
+ gsize fragment_len;
+
+ fragment_len = strlen(fragment);
+
+ /* Check if there is an existing fragment to prepend. */
+ if (server->sasl_buffer != NULL) {
+ if (g_strcmp0("+", fragment) == 0) {
+ enc_req = server->sasl_buffer;
+ } else {
+ enc_req = g_string_append_len(server->sasl_buffer, fragment, fragment_len);
+ }
+ server->sasl_buffer = NULL;
+ } else {
+ enc_req = g_string_new_len(fragment, fragment_len);
+ }
+
+ /*
+ * Fail authentication with this server. They have sent too much data.
+ */
+ if (enc_req->len > AUTHENTICATE_MAX_SIZE) {
+ return FALSE;
+ }
+
+ /*
+ * If the the request is exactly the chunk size, this is a fragment
+ * and more data is expected.
+ */
+ if (fragment_len == AUTHENTICATE_CHUNK_SIZE) {
+ server->sasl_buffer = enc_req;
+ return TRUE;
+ }
+
+ if (enc_req->len == 1 && *enc_req->str == '+') {
+ *decoded = g_string_new_len("", 0);
+ } else {
+ gsize dec_len;
+ gchar *tmp;
+
+ tmp = (gchar *) g_base64_decode(enc_req->str, &dec_len);
+ *decoded = g_string_new_len(tmp, dec_len);
+ }
+
+ g_string_free(enc_req, TRUE);
+ return TRUE;
+}
+
+/*
+ * Splits the response into appropriately sized chunks for the AUTHENTICATION
+ * command to be sent to the IRC server. If |response| is NULL, then the empty
+ * response is sent to the server.
+ */
+void sasl_send_response(IRC_SERVER_REC *server, GString *response)
+{
+ char *enc;
+ size_t offset, enc_len, chunk_len;
+
+ if (response == NULL) {
+ irc_send_cmdv(server, "AUTHENTICATE +");
+ return;
+ }
+
+ enc = g_base64_encode((guchar *) response->str, response->len);
+ enc_len = strlen(enc);
+
+ for (offset = 0; offset < enc_len; offset += AUTHENTICATE_CHUNK_SIZE) {
+ chunk_len = enc_len - offset;
+ if (chunk_len > AUTHENTICATE_CHUNK_SIZE)
+ chunk_len = AUTHENTICATE_CHUNK_SIZE;
+
+ irc_send_cmdv(server, "AUTHENTICATE %.*s", (int) chunk_len, enc + offset);
+ }
+
+ if (offset == enc_len) {
+ irc_send_cmdv(server, "AUTHENTICATE +");
+ }
+ g_free(enc);
+}
+
+/*
+ * Called when the incoming SASL request is completely received.
+ */
+static void sasl_step_complete(IRC_SERVER_REC *server, GString *data)
+{
+ IRC_SERVER_CONNECT_REC *conn;
+ GString *resp;
+
+ conn = server->connrec;
+
+ switch (conn->sasl_mechanism) {
+ case SASL_MECHANISM_PLAIN:
+ /* At this point we assume that conn->sasl_{username, password} are non-NULL.
+ * The PLAIN mechanism expects a NULL-separated string composed by the authorization identity, the
+ * authentication identity and the password.
+ * The authorization identity field is explicitly set to the user provided username.
+ */
+
+ resp = g_string_new(NULL);
+
+ g_string_append(resp, conn->sasl_username);
+ g_string_append_c(resp, '\0');
+ g_string_append(resp, conn->sasl_username);
+ g_string_append_c(resp, '\0');
+ g_string_append(resp, conn->sasl_password);
+
+ sasl_send_response(server, resp);
+ g_string_free(resp, TRUE);
+
+ break;
+
+ case SASL_MECHANISM_EXTERNAL:
+ /* Empty response */
+ sasl_send_response(server, NULL);
+ break;
+ }
+}
+
+static void sasl_step_fail(IRC_SERVER_REC *server)
+{
+ irc_send_cmd_now(server, "AUTHENTICATE *");
+ cap_finish_negotiation(server);
+
+ server->sasl_timeout = 0;
+
+ signal_emit("server sasl failure", 2, server, "The server sent an invalid payload");
+}
+
+static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ GString *req = NULL;
+
+ /* Stop the timer */
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ if (!sasl_reassemble_incoming(server, data, &req)) {
+ sasl_step_fail(server);
+ return;
+ }
+
+ if (req != NULL) {
+ sasl_step_complete(server, req);
+ g_string_free(req, TRUE);
+ }
+
+ /* We expect a response within a reasonable time */
+ server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
+}
+
+static void sasl_disconnected(IRC_SERVER_REC *server)
+{
+ g_return_if_fail(server != NULL);
+
+ if (!IS_IRC_SERVER(server)) {
+ return;
+ }
+
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+}
+
+void sasl_init(void)
+{
+ signal_add_first("server cap ack sasl", (SIGNAL_FUNC) sasl_start);
+ signal_add_first("event authenticate", (SIGNAL_FUNC) sasl_step);
+ signal_add_first("event 903", (SIGNAL_FUNC) sasl_success);
+ signal_add_first("event 902", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 904", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 905", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 906", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 907", (SIGNAL_FUNC) sasl_already);
+ signal_add_first("server disconnected", (SIGNAL_FUNC) sasl_disconnected);
+}
+
+void sasl_deinit(void)
+{
+ signal_remove("server cap ack sasl", (SIGNAL_FUNC) sasl_start);
+ signal_remove("event authenticate", (SIGNAL_FUNC) sasl_step);
+ signal_remove("event 903", (SIGNAL_FUNC) sasl_success);
+ signal_remove("event 902", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 904", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 905", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 906", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 907", (SIGNAL_FUNC) sasl_already);
+ signal_remove("server disconnected", (SIGNAL_FUNC) sasl_disconnected);
+}
diff --git a/src/irc/core/sasl.h b/src/irc/core/sasl.h
new file mode 100644
index 00000000..0693989d
--- /dev/null
+++ b/src/irc/core/sasl.h
@@ -0,0 +1,34 @@
+/*
+ fe-sasl.c : irssi
+
+ Copyright (C) 2015 The Lemon Man
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __SASL_H
+#define __SASL_H
+
+enum {
+ SASL_MECHANISM_NONE = 0,
+ SASL_MECHANISM_PLAIN,
+ SASL_MECHANISM_EXTERNAL,
+ SASL_MECHANISM_MAX
+};
+
+void sasl_init(void);
+void sasl_deinit(void);
+
+#endif
diff --git a/src/irc/core/servers-redirect.c b/src/irc/core/servers-redirect.c
index 34beaef6..aeb9c010 100644
--- a/src/irc/core/servers-redirect.c
+++ b/src/irc/core/servers-redirect.c
@@ -339,7 +339,7 @@ static GSList *redirect_cmd_list_find(GSList *list, const char *event)
while (list != NULL) {
const char *str = list->data;
- if (strcmp(str, event) == 0)
+ if (g_strcmp0(str, event) == 0)
break;
list = list->next->next;
}
@@ -365,7 +365,7 @@ static const char *redirect_match(REDIRECT_REC *redirect, const char *event,
use the default signal */
signal = NULL;
for (tmp = redirect->signals; tmp != NULL; tmp = tmp->next->next) {
- if (strcmp(tmp->data, event) == 0) {
+ if (g_strcmp0(tmp->data, event) == 0) {
signal = tmp->next->data;
break;
}
@@ -433,9 +433,11 @@ static void redirect_abort(IRC_SERVER_REC *server, REDIRECT_REC *rec)
if (rec->aborted || !rec->destroyed) {
/* emit the failure signal */
- str = g_strdup_printf(rec->failure_signal != NULL ?
- "FAILED %s: %s" : "FAILED %s",
- rec->cmd->name, rec->failure_signal);
+ if (rec->failure_signal != NULL)
+ str = g_strdup_printf("FAILED %s: %s", rec->cmd->name, rec->failure_signal);
+ else
+ str = g_strdup_printf("FAILED %s", rec->cmd->name);
+
rawlog_redirect(server->rawlog, str);
g_free(str);
@@ -527,7 +529,7 @@ server_redirect_get(IRC_SERVER_REC *server, const char *prefix,
next = ptr->next;
r = ptr->data;
if (prefix != NULL && r->prefix != NULL &&
- strcmp(prefix, r->prefix)) {
+ g_strcmp0(prefix, r->prefix)) {
/* not from this server */
continue;
}
@@ -734,7 +736,7 @@ void servers_redirect_init(void)
"event 403", 1, /* no such channel */
"event 442", 1, /* "you're not on that channel" */
"event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
- "event 472", -1, /* unknown mode (you should check e-mode's existance from 004 event instead of relying on this) */
+ "event 472", -1, /* unknown mode (you should check e-mode's existence from 004 event instead of relying on this) */
NULL,
NULL);
@@ -747,7 +749,7 @@ void servers_redirect_init(void)
"event 403", 1, /* no such channel */
"event 442", 1, /* "you're not on that channel" */
"event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
- "event 472", -1, /* unknown mode (you should check I-mode's existance from 004 event instead of relying on this) */
+ "event 472", -1, /* unknown mode (you should check I-mode's existence from 004 event instead of relying on this) */
NULL,
NULL);
diff --git a/src/irc/dcc/dcc-autoget.c b/src/irc/dcc/dcc-autoget.c
index 4768641b..de23a5d1 100644
--- a/src/irc/dcc/dcc-autoget.c
+++ b/src/irc/dcc/dcc-autoget.c
@@ -51,13 +51,13 @@ static void sig_dcc_request(GET_DCC_REC *dcc, const char *nickaddr)
/* Unless specifically said in dcc_autoget_masks, don't do autogets
sent to channels. */
- if (*masks == '\0' && dcc->target != NULL && ischannel(*dcc->target))
+ if (*masks == '\0' && dcc->target != NULL && server_ischannel(SERVER(dcc->server), dcc->target))
return;
/* don't autoget files beginning with a dot, if download dir is
our home dir (stupid kludge for stupid people) */
if (*dcc->arg == '.' &&
- strcmp(settings_get_str("dcc_download_path"), "~") == 0)
+ g_strcmp0(settings_get_str("dcc_download_path"), "~") == 0)
return;
/* check file size limit, NOTE: it's still possible to send a
diff --git a/src/irc/dcc/dcc-chat.c b/src/irc/dcc/dcc-chat.c
index ad1442fc..ca90b8d8 100644
--- a/src/irc/dcc/dcc-chat.c
+++ b/src/irc/dcc/dcc-chat.c
@@ -191,7 +191,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
return;
/* handle only DCC messages */
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
dcc = item_get_dcc(item);
else if (*target == '=')
dcc = dcc_chat_find_id(target+1);
@@ -428,7 +428,7 @@ static void dcc_chat_passive(CHAT_DCC_REC *dcc)
dcc_ip2str(&own_ip, host);
irc_send_cmdv(dcc->server, "PRIVMSG %s :\001DCC CHAT CHAT %s %d %d\001",
dcc->nick, host, port, dcc->pasv_id);
-
+
}
/* SYNTAX: DCC CHAT [-passive] [<nick>] */
@@ -615,17 +615,17 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
char **params;
int paramcount;
int passive, autoallow = FALSE;
-
+
/* CHAT <unused> <address> <port> */
/* CHAT <unused> <address> 0 <id> (DCC CHAT passive protocol) */
params = g_strsplit(data, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 3) {
g_strfreev(params);
return;
}
- passive = paramcount == 4 && strcmp(params[2], "0") == 0;
+ passive = paramcount == 4 && g_strcmp0(params[2], "0") == 0;
dcc = DCC_CHAT(dcc_find_request(DCC_CHAT_TYPE, nick, NULL));
if (dcc != NULL) {
@@ -656,14 +656,14 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
}
}
}
-
+
dcc = dcc_chat_create(server, chat, nick, params[0]);
dcc->target = g_strdup(target);
dcc->port = atoi(params[2]);
-
+
if (passive)
dcc->pasv_id = atoi(params[3]);
-
+
dcc_str2ip(params[1], &dcc->addr);
net_ip2host(&dcc->addr, dcc->addrstr);
diff --git a/src/irc/dcc/dcc-get.c b/src/irc/dcc/dcc-get.c
index 8eee81b3..73c1b864 100644
--- a/src/irc/dcc/dcc-get.c
+++ b/src/irc/dcc/dcc-get.c
@@ -30,6 +30,8 @@
#include "dcc-get.h"
#include "dcc-send.h"
+static char *dcc_get_recv_buffer;
+
GET_DCC_REC *dcc_get_create(IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
const char *nick, const char *arg)
{
@@ -139,14 +141,20 @@ static void sig_dccget_send(GET_DCC_REC *dcc)
dcc_get_send_received(dcc);
}
+#define DCC_GET_RECV_BUFFER_SIZE 32768
+
/* input function: DCC GET received data */
static void sig_dccget_receive(GET_DCC_REC *dcc)
{
- char buffer[512];
int ret;
+ if (dcc_get_recv_buffer == NULL) {
+ dcc_get_recv_buffer = g_malloc(DCC_GET_RECV_BUFFER_SIZE);
+ }
+
for (;;) {
- ret = net_receive(dcc->handle, buffer, sizeof(buffer));
+ ret = net_receive(dcc->handle, dcc_get_recv_buffer,
+ DCC_GET_RECV_BUFFER_SIZE);
if (ret == 0) break;
if (ret < 0) {
@@ -156,7 +164,7 @@ static void sig_dccget_receive(GET_DCC_REC *dcc)
return;
}
- if (write(dcc->fhandle, buffer, ret) != ret) {
+ if (write(dcc->fhandle, dcc_get_recv_buffer, ret) != ret) {
/* most probably out of disk space */
signal_emit("dcc error write", 2,
dcc, g_strerror(errno));
@@ -226,6 +234,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 +259,6 @@ void sig_dccget_connected(GET_DCC_REC *dcc)
/* close/remove the temp file */
ret_errno = errno;
- close(temphandle);
unlink(tempfname);
g_free(tempfname);
@@ -422,7 +431,7 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
/* SEND <file name> <address> <port> <size> [...] */
/* SEND <file name> <address> 0 <size> <id> (DCC SEND passive protocol) */
params = g_strsplit(data, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 4) {
signal_emit("dcc error ctcp", 5, "SEND", data,
@@ -454,7 +463,7 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
g_memmove(fname, fname+1, len);
quoted = TRUE;
}
-
+
if (passive && port != 0) {
/* This is NOT a DCC SEND request! This is a reply to our
passive request. We MUST check the IDs and then connect to
@@ -472,8 +481,8 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
net_ip2host(&temp_dcc->addr, temp_dcc->addrstr);
else {
/* with IPv6, show it to us as it was sent */
- strocpy(temp_dcc->addrstr, address,
- sizeof(temp_dcc->addrstr));
+ g_strlcpy(temp_dcc->addrstr, address,
+ sizeof(temp_dcc->addrstr));
}
/* This new signal is added to let us invoke
@@ -501,13 +510,13 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
if (passive && port == 0)
dcc->pasv_id = p_id; /* Assign the ID to the DCC */
-
+
memcpy(&dcc->addr, &ip, sizeof(ip));
if (dcc->addr.family == AF_INET)
net_ip2host(&dcc->addr, dcc->addrstr);
else {
/* with IPv6, show it to us as it was sent */
- strocpy(dcc->addrstr, address, sizeof(dcc->addrstr));
+ g_strlcpy(dcc->addrstr, address, sizeof(dcc->addrstr));
}
dcc->port = port;
dcc->size = size;
@@ -525,14 +534,14 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
{
GET_DCC_REC *dcc;
GSList *tmp, *next;
- char *nick, *fname;
+ char *nick, *arg, *fname;
void *free_arg;
int found;
g_return_if_fail(data != NULL);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
- &nick, &fname))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS, &nick, &arg))
return;
if (*nick == '\0') {
@@ -547,6 +556,8 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
return;
}
+ fname = cmd_get_quoted_param(&arg);
+
found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next) {
GET_DCC_REC *dcc = tmp->data;
@@ -554,7 +565,7 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
next = tmp->next;
if (IS_DCC_GET(dcc) && g_ascii_strcasecmp(dcc->nick, nick) == 0 &&
(dcc_is_waiting_user(dcc) || dcc->from_dccserver) &&
- (*fname == '\0' || strcmp(dcc->arg, fname) == 0)) {
+ (*fname == '\0' || g_strcmp0(dcc->arg, fname) == 0)) {
found = TRUE;
if (!dcc_is_passive(dcc))
accept_func(dcc);
@@ -593,4 +604,5 @@ void dcc_get_deinit(void)
signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
signal_remove("ctcp msg dcc send", (SIGNAL_FUNC) ctcp_msg_dcc_send);
command_unbind("dcc get", (SIGNAL_FUNC) cmd_dcc_get);
+ g_free_and_null(dcc_get_recv_buffer);
}
diff --git a/src/irc/dcc/dcc-resume.c b/src/irc/dcc/dcc-resume.c
index 28871986..36f84ddf 100644
--- a/src/irc/dcc/dcc-resume.c
+++ b/src/irc/dcc/dcc-resume.c
@@ -88,13 +88,13 @@ static int dcc_ctcp_resume_parse(int type, const char *data, const char *nick,
/* RESUME|ACCEPT <file name> <port> <size> */
/* RESUME|ACCEPT <file name> 0 <size> <id> (passive protocol) */
params = g_strsplit(data, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 3)
return 0;
fileparams = get_file_params_count_resume(params, paramcount);
-
+
if (paramcount >= fileparams + 2) {
port = atoi(params[fileparams]);
*size = str_to_uofft(params[fileparams+1]);
diff --git a/src/irc/dcc/dcc-send.c b/src/irc/dcc/dcc-send.c
index 558fd109..ca29b9b9 100644
--- a/src/irc/dcc/dcc-send.c
+++ b/src/irc/dcc/dcc-send.c
@@ -174,8 +174,8 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
int queue, mode, passive;
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "dcc send",
- &optlist, &nick, &fileargs))
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ "dcc send", &optlist, &nick, &fileargs))
return;
chat = item_get_dcc(item);
@@ -194,7 +194,7 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
cmd_param_error(CMDERR_NOT_CONNECTED);
passive = g_hash_table_lookup(optlist, "passive") != NULL;
-
+
if (g_hash_table_lookup(optlist, "rmhead") != NULL) {
queue = dcc_queue_old(nick, servertag);
if (queue != -1)
@@ -217,7 +217,7 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
if (*fileargs == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
-
+
dcc_send_add(servertag, chat, nick, fileargs, mode, passive);
}
@@ -434,11 +434,11 @@ static int dcc_send_one_file(int queue, const char *target, const char *fname,
if (passive) {
dcc->pasv_id = rand() % 64;
}
-
+
/* send DCC request */
signal_emit("dcc request send", 1, dcc);
-
+
dcc_ip2str(&own_ip, host);
if (passive == FALSE) {
str = g_strdup_printf(dcc->file_quoted ?
diff --git a/src/irc/dcc/dcc-server.c b/src/irc/dcc/dcc-server.c
index 30224ff9..7ae572cd 100644
--- a/src/irc/dcc/dcc-server.c
+++ b/src/irc/dcc/dcc-server.c
@@ -245,7 +245,7 @@ static void dcc_server_msg(SERVER_DCC_REC *dcc, const char *msg)
/* 120 clientnickname filesize filename */
params = g_strsplit(msg, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 3) {
g_strfreev(params);
diff --git a/src/irc/dcc/dcc.c b/src/irc/dcc/dcc.c
index e4ee4b7c..c51f7c7a 100644
--- a/src/irc/dcc/dcc.c
+++ b/src/irc/dcc/dcc.c
@@ -90,9 +90,9 @@ void dcc_init_rec(DCC_REC *dcc, IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
dcc->servertag = server != NULL ? g_strdup(server->tag) :
(chat == NULL ? NULL : g_strdup(chat->servertag));
-
+
dcc->pasv_id = -1; /* Not a passive DCC */
-
+
dcc_conns = g_slist_append(dcc_conns, dcc);
signal_emit("dcc created", 1, dcc);
}
@@ -149,7 +149,7 @@ DCC_REC *dcc_find_request(int type, const char *nick, const char *arg)
if (dcc->type == type && !dcc_is_connected(dcc) &&
g_ascii_strcasecmp(dcc->nick, nick) == 0 &&
- (arg == NULL || strcmp(dcc->arg, arg) == 0))
+ (arg == NULL || g_strcmp0(dcc->arg, arg) == 0))
return dcc;
}
@@ -454,8 +454,8 @@ static int dcc_timeout_func(void)
/* Timed out - don't send DCC REJECT CTCP so CTCP
flooders won't affect us and it really doesn't
matter that much anyway if the other side doen't
- get it..
-
+ get it..
+
We don't want dcc servers to time out. */
dcc_close(dcc);
}
@@ -490,7 +490,7 @@ static void event_no_such_nick(IRC_SERVER_REC *server, char *data)
static void cmd_dcc_close(char *data, IRC_SERVER_REC *server)
{
GSList *tmp, *next;
- char *typestr, *nick, *arg;
+ char *typestr, *nick, *arg, *fname;
void *free_arg;
int found, type;
@@ -510,13 +510,15 @@ static void cmd_dcc_close(char *data, IRC_SERVER_REC *server)
return;
}
+ fname = cmd_get_quoted_param(&arg);
+
found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next) {
DCC_REC *dcc = tmp->data;
next = tmp->next;
if (dcc->type == type && g_ascii_strcasecmp(dcc->nick, nick) == 0 &&
- (*arg == '\0' || strcmp(dcc->arg, arg) == 0)) {
+ (*fname == '\0' || g_strcmp0(dcc->arg, fname) == 0)) {
dcc_reject(dcc, server);
found = TRUE;
}
diff --git a/src/irc/dcc/dcc.h b/src/irc/dcc/dcc.h
index 10639207..144cc89b 100644
--- a/src/irc/dcc/dcc.h
+++ b/src/irc/dcc/dcc.h
@@ -27,7 +27,7 @@ typedef struct {
/* passive DCC */
#define dcc_is_passive(dcc) \
((dcc)->pasv_id >= 0)
-
+
extern GSList *dcc_conns;
void dcc_register_type(const char *type);
diff --git a/src/irc/flood/autoignore.c b/src/irc/flood/autoignore.c
index 94adf167..86ff3ec5 100644
--- a/src/irc/flood/autoignore.c
+++ b/src/irc/flood/autoignore.c
@@ -38,7 +38,7 @@ void autoignore_update(IGNORE_REC *rec, int level)
ignore_update_rec(rec);
}
-void autoignore_add(IRC_SERVER_REC *server, char *mask, int level)
+void autoignore_add(IRC_SERVER_REC *server, char *mask, int level)
{
IGNORE_REC *rec;
@@ -66,7 +66,7 @@ static void sig_flood(IRC_SERVER_REC *server, const char *nick, const char *host
mask = g_strdup_printf("%s!%s", nick, host);
if (level & check_level) {
- rec = ignore_find(server->tag, mask, NULL);
+ rec = ignore_find_full(server->tag, mask, NULL, NULL, 0);
if (rec == NULL)
autoignore_add(server, mask, level);
else
diff --git a/src/irc/flood/flood.c b/src/irc/flood/flood.c
index 29502ca6..0944a6eb 100644
--- a/src/irc/flood/flood.c
+++ b/src/irc/flood/flood.c
@@ -250,7 +250,7 @@ static void flood_privmsg(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2, &target, &text);
- level = ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS;
+ level = server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS;
if (addr != NULL && !ignore_check(SERVER(server), nick, addr, target, text, level))
flood_newmsg(server, level, nick, addr, target);
@@ -287,7 +287,7 @@ static void flood_ctcp(IRC_SERVER_REC *server, const char *data,
return;
level = g_ascii_strncasecmp(data, "ACTION ", 7) != 0 ? MSGLEVEL_CTCPS :
- (ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
+ (server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
if (!ignore_check(SERVER(server), nick, addr, target, data, level))
flood_newmsg(server, level, nick, addr, target);
}
diff --git a/src/irc/notifylist/notify-commands.c b/src/irc/notifylist/notify-commands.c
index 67076106..0d4fd4f2 100644
--- a/src/irc/notifylist/notify-commands.c
+++ b/src/irc/notifylist/notify-commands.c
@@ -36,7 +36,8 @@ static void cmd_notify(gchar *data)
g_return_if_fail(data != NULL);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg,
+ 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"notify", &optlist, &mask, &ircnets))
return;
if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
diff --git a/src/irc/notifylist/notify-setup.c b/src/irc/notifylist/notify-setup.c
index d6f91361..9ea481ca 100644
--- a/src/irc/notifylist/notify-setup.c
+++ b/src/irc/notifylist/notify-setup.c
@@ -30,7 +30,7 @@ void notifylist_add_config(NOTIFYLIST_REC *rec)
CONFIG_NODE *node;
node = iconfig_node_traverse("notifies", TRUE);
- node = config_node_section(node, rec->mask, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, rec->mask, NODE_TYPE_BLOCK);
if (rec->away_check)
iconfig_node_set_bool(node, "away_check", TRUE);
@@ -39,7 +39,7 @@ void notifylist_add_config(NOTIFYLIST_REC *rec)
iconfig_node_set_str(node, "ircnets", NULL);
if (rec->ircnets != NULL && *rec->ircnets != NULL) {
- node = config_node_section(node, "ircnets", NODE_TYPE_LIST);
+ node = iconfig_node_section(node, "ircnets", NODE_TYPE_LIST);
iconfig_node_add_list(node, rec->ircnets);
}
}
@@ -73,7 +73,7 @@ void notifylist_read_config(void)
rec->mask = g_strdup(node->key);
rec->away_check = config_node_get_bool(node, "away_check", FALSE);
- node = config_node_section(node, "ircnets", -1);
+ node = iconfig_node_section(node, "ircnets", -1);
if (node != NULL) rec->ircnets = config_node_get_list(node);
}
}
diff --git a/src/irc/notifylist/notifylist.c b/src/irc/notifylist/notifylist.c
index b0b7ee1c..573f7a7f 100644
--- a/src/irc/notifylist/notifylist.c
+++ b/src/irc/notifylist/notifylist.c
@@ -91,7 +91,7 @@ int notifylist_ircnets_match(NOTIFYLIST_REC *rec, const char *ircnet)
if (rec->ircnets == NULL) return TRUE;
if (ircnet == NULL) return FALSE;
- if (strcmp(ircnet, "*") == 0) return TRUE;
+ if (g_strcmp0(ircnet, "*") == 0) return TRUE;
for (tmp = rec->ircnets; *tmp != NULL; tmp++) {
if (g_ascii_strcasecmp(*tmp, ircnet) == 0)
diff --git a/src/irc/proxy/dump.c b/src/irc/proxy/dump.c
index 80fd7c2e..e39c21a6 100644
--- a/src/irc/proxy/dump.c
+++ b/src/irc/proxy/dump.c
@@ -83,7 +83,7 @@ void proxy_outserver(CLIENT_REC *client, const char *data, ...)
va_start(args, data);
str = g_strdup_vprintf(data, args);
- proxy_outdata(client, ":%s!%s@proxy %s\n", client->nick,
+ proxy_outdata(client, ":%s!%s@proxy %s\r\n", client->nick,
settings_get_str("user_name"), str);
g_free(str);
@@ -106,7 +106,7 @@ void proxy_outserver_all(IRC_SERVER_REC *server, const char *data, ...)
CLIENT_REC *rec = tmp->data;
if (rec->connected && rec->server == server) {
- proxy_outdata(rec, ":%s!%s@proxy %s\n", rec->nick,
+ proxy_outdata(rec, ":%s!%s@proxy %s\r\n", rec->nick,
settings_get_str("user_name"), str);
}
}
@@ -132,7 +132,7 @@ void proxy_outserver_all_except(CLIENT_REC *client, const char *data, ...)
if (rec->connected && rec != client &&
rec->server == client->server) {
- proxy_outdata(rec, ":%s!%s@proxy %s\n", rec->nick,
+ proxy_outdata(rec, ":%s!%s@proxy %s\r\n", rec->nick,
settings_get_str("user_name"), str);
}
}
@@ -169,7 +169,7 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
NICK_REC *nick = tmp->data;
if (str->len >= 500) {
- g_string_append_c(str, '\n');
+ g_string_append(str, "\r\n");
proxy_outdata(client, "%s", str->str);
create_names_start(str, channel, client);
first = TRUE;
@@ -186,21 +186,21 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
}
g_slist_free(nicks);
- g_string_append_c(str, '\n');
+ g_string_append(str, "\r\n");
proxy_outdata(client, "%s", str->str);
g_string_free(str, TRUE);
- proxy_outdata(client, ":%s 366 %s %s :End of /NAMES list.\n",
+ proxy_outdata(client, ":%s 366 %s %s :End of /NAMES list.\r\n",
client->proxy_address, client->nick, channel->name);
if (channel->topic != NULL) {
/* this is needed because the topic may be encoded into other charsets internaly */
recoded = recode_out(SERVER(client->server), channel->topic, channel->name);
- proxy_outdata(client, ":%s 332 %s %s :%s\n",
+ proxy_outdata(client, ":%s 332 %s %s :%s\r\n",
client->proxy_address, client->nick,
channel->name, recoded);
g_free(recoded);
if (channel->topic_time > 0)
- proxy_outdata(client, ":%s 333 %s %s %s %d\n",
+ proxy_outdata(client, ":%s 333 %s %s %s %d\r\n",
client->proxy_address, client->nick,
channel->name, channel->topic_by, channel->topic_time);
}
@@ -209,10 +209,10 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
void proxy_client_reset_nick(CLIENT_REC *client)
{
if (client->server == NULL ||
- strcmp(client->nick, client->server->nick) == 0)
+ g_strcmp0(client->nick, client->server->nick) == 0)
return;
- proxy_outdata(client, ":%s!proxy NICK :%s\n",
+ proxy_outdata(client, ":%s!proxy NICK :%s\r\n",
client->nick, client->server->nick);
g_free(client->nick);
@@ -236,13 +236,13 @@ void proxy_dump_data(CLIENT_REC *client)
proxy_client_reset_nick(client);
/* welcome info */
- proxy_outdata(client, ":%s 001 %s :Welcome to the Internet Relay Network %s!%s@proxy\n", client->proxy_address, client->nick, client->nick, settings_get_str("user_name"));
- proxy_outdata(client, ":%s 002 %s :Your host is irssi-proxy, running version %s\n", client->proxy_address, client->nick, PACKAGE_VERSION);
- proxy_outdata(client, ":%s 003 %s :This server was created ...\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 001 %s :Welcome to the Internet Relay Network %s!%s@proxy\r\n", client->proxy_address, client->nick, client->nick, settings_get_str("user_name"));
+ proxy_outdata(client, ":%s 002 %s :Your host is irssi-proxy, running version %s\r\n", client->proxy_address, client->nick, PACKAGE_VERSION);
+ proxy_outdata(client, ":%s 003 %s :This server was created ...\r\n", client->proxy_address, client->nick);
if (client->server == NULL || !client->server->emode_known)
- proxy_outdata(client, ":%s 004 %s %s %s oirw abiklmnopqstv\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
+ proxy_outdata(client, ":%s 004 %s %s %s oirw abiklmnopqstv\r\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
else
- proxy_outdata(client, ":%s 004 %s %s %s oirw abeIiklmnopqstv\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
+ proxy_outdata(client, ":%s 004 %s %s %s oirw abeIiklmnopqstv\r\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
if (client->server != NULL && client->server->isupport_sent) {
isupport_out = g_string_new(NULL);
@@ -267,7 +267,7 @@ void proxy_dump_data(CLIENT_REC *client)
count = 0;
if (paramstr->len > 0)
g_string_truncate(paramstr, paramstr->len-1);
- g_string_append_printf(paramstr, " :are supported by this server\n");
+ g_string_append_printf(paramstr, " :are supported by this server\r\n");
proxy_outdata(client, "%s", paramstr->str);
g_string_truncate(paramstr, 0);
g_string_printf(paramstr, ":%s 005 %s ", client->proxy_address, client->nick);
@@ -281,9 +281,9 @@ void proxy_dump_data(CLIENT_REC *client)
g_strfreev(paramlist);
}
- proxy_outdata(client, ":%s 251 %s :There are 0 users and 0 invisible on 1 servers\n", client->proxy_address, client->nick);
- proxy_outdata(client, ":%s 255 %s :I have 0 clients, 0 services and 0 servers\n", client->proxy_address, client->nick);
- proxy_outdata(client, ":%s 422 %s :MOTD File is missing\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 251 %s :There are 0 users and 0 invisible on 1 servers\r\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 255 %s :I have 0 clients, 0 services and 0 servers\r\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 422 %s :MOTD File is missing\r\n", client->proxy_address, client->nick);
/* user mode / away status */
if (client->server != NULL) {
@@ -293,7 +293,7 @@ void proxy_dump_data(CLIENT_REC *client)
client->server->usermode);
}
if (client->server->usermode_away) {
- proxy_outdata(client, ":%s 306 %s :You have been marked as being away\n",
+ proxy_outdata(client, ":%s 306 %s :You have been marked as being away\r\n",
client->proxy_address, client->nick);
}
diff --git a/src/irc/proxy/listen.c b/src/irc/proxy/listen.c
index 8edffbd3..cde4c0be 100644
--- a/src/irc/proxy/listen.c
+++ b/src/irc/proxy/listen.c
@@ -31,12 +31,76 @@
#include "fe-common/core/printtext.h" /* FIXME: evil. need to do fe-proxy */
+#include <sys/un.h>
+
GSList *proxy_listens;
GSList *proxy_clients;
static GString *next_line;
static int ignore_next;
+static int enabled = FALSE;
+
+static int is_all_digits(const char *s)
+{
+ return strspn(s, "0123456789") == strlen(s);
+}
+
+static GIOChannel *net_listen_unix(const char *path)
+{
+ struct sockaddr_un sa;
+ int saved_errno, handle;
+
+ g_return_val_if_fail(path != NULL, NULL);
+
+ handle = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (handle == -1) {
+ return NULL;
+ }
+
+ fcntl(handle, F_SETFL, O_NONBLOCK);
+
+ memset(&sa, '\0', sizeof sa);
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, path, sizeof sa.sun_path - 1);
+ if (bind(handle, (struct sockaddr *)&sa, sizeof sa) == -1) {
+ saved_errno = errno;
+ goto error_close;
+ }
+
+ if (listen(handle, 1) == -1) {
+ saved_errno = errno;
+ goto error_unlink;
+ }
+
+ return g_io_channel_new(handle);
+
+error_unlink:
+ unlink(sa.sun_path);
+error_close:
+ close(handle);
+ errno = saved_errno;
+ return NULL;
+}
+
+static GIOChannel *net_accept_unix(GIOChannel *handle)
+{
+ struct sockaddr_un sa;
+ int ret;
+ socklen_t addrlen;
+
+ g_return_val_if_fail(handle != NULL, NULL);
+
+ addrlen = sizeof sa;
+ ret = accept(g_io_channel_unix_get_fd(handle), (struct sockaddr *)&sa, &addrlen);
+
+ if (ret < 0)
+ return NULL;
+
+ fcntl(ret, F_SETFL, O_NONBLOCK);
+ return g_io_channel_new(ret);
+}
+
static void remove_client(CLIENT_REC *rec)
{
g_return_if_fail(rec != NULL);
@@ -45,19 +109,19 @@ static void remove_client(CLIENT_REC *rec)
rec->listen->clients = g_slist_remove(rec->listen->clients, rec);
signal_emit("proxy client disconnected", 1, rec);
- printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
- "Proxy: Client disconnected from %s", rec->host);
+ printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: Client %s disconnected", rec->addr);
g_free(rec->proxy_address);
net_sendbuffer_destroy(rec->handle, TRUE);
g_source_remove(rec->recv_tag);
g_free_not_null(rec->nick);
- g_free_not_null(rec->host);
+ g_free_not_null(rec->addr);
g_free(rec);
}
static void proxy_redirect_event(CLIENT_REC *client, const char *command,
- int count, const char *arg, int remote)
+ int count, const char *arg, int remote)
{
char *str;
@@ -65,7 +129,7 @@ static void proxy_redirect_event(CLIENT_REC *client, const char *command,
str = g_strdup_printf("proxy %p", client);
server_redirect_event(client->server, command, count,
- arg, remote, NULL, "", str, NULL);
+ arg, remote, NULL, "", str, NULL);
g_free(str);
}
@@ -82,7 +146,7 @@ static void grab_who(CLIENT_REC *client, const char *channel)
arg = g_string_new(channel);
for (tmp = list, count = 0; *tmp != NULL; tmp++, count++) {
- if (strcmp(*tmp, "0") == 0) {
+ if (g_strcmp0(*tmp, "0") == 0) {
/* /who 0 displays everyone */
**tmp = '*';
}
@@ -92,40 +156,74 @@ static void grab_who(CLIENT_REC *client, const char *channel)
}
proxy_redirect_event(client, "who",
- client->server->one_endofwho ? 1 : count,
- arg->str, -1);
+ client->server->one_endofwho ? 1 : count,
+ arg->str, -1);
g_strfreev(list);
g_string_free(arg, TRUE);
}
static void handle_client_connect_cmd(CLIENT_REC *client,
- const char *cmd, const char *args)
+ const char *cmd, const char *args)
{
const char *password;
password = settings_get_str("irssiproxy_password");
- if (password != NULL && strcmp(cmd, "PASS") == 0) {
- if (strcmp(password, args) == 0)
- client->pass_sent = TRUE;
- else {
+ if (g_strcmp0(cmd, "PASS") == 0) {
+ const char *args_pass;
+
+ if (!client->multiplex) {
+ args_pass = args;
+ } else {
+ IRC_SERVER_REC *server;
+ char *tag;
+ const char *tag_end;
+
+ if ((tag_end = strchr(args, ':')) != NULL) {
+ args_pass = tag_end + 1;
+ } else {
+ tag_end = args + strlen(args);
+ args_pass = "";
+ }
+
+ tag = g_strndup(args, tag_end - args);
+ server = IRC_SERVER(server_find_chatnet(tag));
+ g_free(tag);
+
+ if (!server) {
+ /* an invalid network was specified */
+ remove_client(client);
+ return;
+ }
+
+ client->server = server;
+ g_free(client->proxy_address);
+ client->proxy_address = g_strdup_printf("%.*s.proxy", (int)(tag_end - args), args);
+ }
+
+ if (g_strcmp0(password, args_pass) != 0) {
/* wrong password! */
remove_client(client);
- return;
+ return;
}
- } else if (strcmp(cmd, "NICK") == 0) {
+ client->pass_sent = TRUE;
+ } else if (g_strcmp0(cmd, "NICK") == 0) {
g_free_not_null(client->nick);
client->nick = g_strdup(args);
- } else if (strcmp(cmd, "USER") == 0) {
+ } else if (g_strcmp0(cmd, "USER") == 0) {
client->user_sent = TRUE;
}
if (client->nick != NULL && client->user_sent) {
- if (*password != '\0' && !client->pass_sent) {
+ if ((*password != '\0' || client->multiplex) && !client->pass_sent) {
/* client didn't send us PASS, kill it */
remove_client(client);
} else {
+ signal_emit("proxy client connected", 1, client);
+ printtext(client->server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: Client %s connected",
+ client->addr);
client->connected = TRUE;
proxy_dump_data(client);
}
@@ -133,7 +231,7 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
}
static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
- const char *data)
+ const char *data)
{
GSList *tmp;
if (!client->connected) {
@@ -141,12 +239,12 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
return;
}
- if (strcmp(cmd, "QUIT") == 0) {
+ if (g_strcmp0(cmd, "QUIT") == 0) {
remove_client(client);
return;
}
- if (strcmp(cmd, "PING") == 0) {
+ if (g_strcmp0(cmd, "PING") == 0) {
/* Reply to PING, if the target parameter is either
proxy_adress, our own nick or empty. */
char *params, *origin, *target;
@@ -155,38 +253,38 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
if (*target == '\0' ||
g_ascii_strcasecmp(target, client->proxy_address) == 0 ||
g_ascii_strcasecmp(target, client->nick) == 0) {
- proxy_outdata(client, ":%s PONG %s :%s\n",
- client->proxy_address,
- client->proxy_address, origin);
+ proxy_outdata(client, ":%s PONG %s :%s\r\n",
+ client->proxy_address,
+ client->proxy_address, origin);
g_free(params);
return;
}
g_free(params);
}
- if (strcmp(cmd, "PROXY") == 0) {
+ if (g_strcmp0(cmd, "PROXY") == 0) {
if (g_ascii_strcasecmp(args, "CTCP ON") == 0) {
- /* client wants all ctcps */
+ /* client wants all ctcps */
client->want_ctcp = 1;
- for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+ for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
- if ((g_ascii_strcasecmp(client->listen->ircnet,rec->listen->ircnet) == 0) &&
- /* kludgy way to check if the clients aren't the same */
- (client->recv_tag != rec->recv_tag)) {
- if (rec->want_ctcp == 1)
- proxy_outdata(rec, ":%s NOTICE %s :Another client is now receiving CTCPs sent to %s\n",
- rec->proxy_address, rec->nick, rec->listen->ircnet);
- rec->want_ctcp = 0;
- }
-
+ if (g_ascii_strcasecmp(client->listen->ircnet, rec->listen->ircnet) == 0 &&
+ /* kludgy way to check if the clients aren't the same */
+ client->recv_tag != rec->recv_tag) {
+ if (rec->want_ctcp == 1)
+ proxy_outdata(rec, ":%s NOTICE %s :Another client is now receiving CTCPs sent to %s\r\n",
+ rec->proxy_address, rec->nick, rec->listen->ircnet);
+ rec->want_ctcp = 0;
+ }
+
}
- proxy_outdata(client, ":%s NOTICE %s :You're now receiving CTCPs sent to %s\n",
- client->proxy_address, client->nick,client->listen->ircnet);
+ proxy_outdata(client, ":%s NOTICE %s :You're now receiving CTCPs sent to %s\r\n",
+ client->proxy_address, client->nick, client->listen->ircnet);
} else if (g_ascii_strcasecmp(args, "CTCP OFF") == 0) {
- /* client wants proxy to handle all ctcps */
+ /* client wants proxy to handle all ctcps */
client->want_ctcp = 0;
- proxy_outdata(client, ":%s NOTICE %s :Proxy is now handling itself CTCPs sent to %s\n",
- client->proxy_address, client->nick, client->listen->ircnet);
+ proxy_outdata(client, ":%s NOTICE %s :Proxy is now handling itself CTCPs sent to %s\r\n",
+ client->proxy_address, client->nick, client->listen->ircnet);
} else {
signal_emit("proxy client command", 3, client, args, data);
}
@@ -194,17 +292,17 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
}
if (client->server == NULL || !client->server->connected) {
- proxy_outdata(client, ":%s NOTICE %s :Not connected to server\n",
- client->proxy_address, client->nick);
- return;
+ proxy_outdata(client, ":%s NOTICE %s :Not connected to server\r\n",
+ client->proxy_address, client->nick);
+ return;
}
- /* check if the command could be redirected */
- if (strcmp(cmd, "WHO") == 0)
+ /* check if the command could be redirected */
+ if (g_strcmp0(cmd, "WHO") == 0)
grab_who(client, args);
- else if (strcmp(cmd, "WHOWAS") == 0)
+ else if (g_strcmp0(cmd, "WHOWAS") == 0)
proxy_redirect_event(client, "whowas", 1, args, -1);
- else if (strcmp(cmd, "WHOIS") == 0) {
+ else if (g_strcmp0(cmd, "WHOIS") == 0) {
char *p;
/* convert dots to spaces */
@@ -212,11 +310,11 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
if (*p == ',') *p = ' ';
proxy_redirect_event(client, "whois", 1, args, TRUE);
- } else if (strcmp(cmd, "ISON") == 0)
+ } else if (g_strcmp0(cmd, "ISON") == 0)
proxy_redirect_event(client, "ison", 1, args, -1);
- else if (strcmp(cmd, "USERHOST") == 0)
+ else if (g_strcmp0(cmd, "USERHOST") == 0)
proxy_redirect_event(client, "userhost", 1, args, -1);
- else if (strcmp(cmd, "MODE") == 0) {
+ else if (g_strcmp0(cmd, "MODE") == 0) {
/* convert dots to spaces */
char *slist, *str, mode, *p;
int argc;
@@ -252,40 +350,40 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
}
g_free(str);
g_free(slist);
- } else if (strcmp(cmd, "PRIVMSG") == 0) {
+ } else if (g_strcmp0(cmd, "PRIVMSG") == 0) {
/* send the message to other clients as well */
char *params, *target, *msg;
params = event_get_params(args, 2 | PARAM_FLAG_GETREST,
- &target, &msg);
+ &target, &msg);
proxy_outserver_all_except(client, "PRIVMSG %s", args);
ignore_next = TRUE;
if (*msg != '\001' || msg[strlen(msg)-1] != '\001') {
- signal_emit(ischannel(*target) ?
- "message own_public" : "message own_private", 4,
- client->server, msg, target, target);
+ signal_emit(server_ischannel(SERVER(client->server), target) ?
+ "message own_public" : "message own_private", 4,
+ client->server, msg, target, target);
} else if (strncmp(msg+1, "ACTION ", 7) == 0) {
/* action */
- msg[strlen(msg)-1] = '\0';
+ msg[strlen(msg)-1] = '\0';
signal_emit("message irc own_action", 3,
- client->server, msg+8, target);
+ client->server, msg+8, target);
} else {
- /* CTCP */
+ /* CTCP */
char *p;
msg[strlen(msg)-1] = '\0';
p = strchr(msg, ' ');
- if (p != NULL) *p++ = '\0'; else p = "";
+ if (p != NULL) *p++ = '\0'; else p = "";
signal_emit("message irc own_ctcp", 4,
- client->server, msg+1, p, target);
+ client->server, msg+1, p, target);
}
ignore_next = FALSE;
g_free(params);
- } else if (strcmp(cmd, "PING") == 0) {
+ } else if (g_strcmp0(cmd, "PING") == 0) {
proxy_redirect_event(client, "ping", 1, NULL, TRUE);
- } else if (strcmp(cmd, "AWAY") == 0) {
+ } else if (g_strcmp0(cmd, "AWAY") == 0) {
/* set the away reason */
if (args != NULL) {
g_free(client->server->away_reason);
@@ -331,23 +429,38 @@ static void sig_listen(LISTEN_REC *listen)
CLIENT_REC *rec;
IPADDR ip;
NET_SENDBUF_REC *sendbuf;
- GIOChannel *handle;
+ GIOChannel *handle;
char host[MAX_IP_LEN];
int port;
+ char *addr;
g_return_if_fail(listen != NULL);
/* accept connection */
- handle = net_accept(listen->handle, &ip, &port);
- if (handle == NULL)
- return;
- net_ip2host(&ip, host);
+ if (listen->port) {
+ handle = net_accept(listen->handle, &ip, &port);
+ if (handle == NULL)
+ return;
+ net_ip2host(&ip, host);
+ addr = g_strdup_printf("%s:%d", host, port);
+ } else {
+ /* no port => this is a unix socket */
+ handle = net_accept_unix(listen->handle);
+ if (handle == NULL)
+ return;
+ addr = g_strdup("(local)");
+ }
+
sendbuf = net_sendbuffer_create(handle, 0);
rec = g_new0(CLIENT_REC, 1);
rec->listen = listen;
rec->handle = sendbuf;
- rec->host = g_strdup(host);
- if (strcmp(listen->ircnet, "*") == 0) {
+ rec->addr = addr;
+ if (g_strcmp0(listen->ircnet, "?") == 0) {
+ rec->multiplex = TRUE;
+ rec->proxy_address = g_strdup("multiplex.proxy");
+ rec->server = NULL;
+ } else if (g_strcmp0(listen->ircnet, "*") == 0) {
rec->proxy_address = g_strdup("irc.proxy");
rec->server = servers == NULL ? NULL : IRC_SERVER(servers->data);
} else {
@@ -356,14 +469,15 @@ static void sig_listen(LISTEN_REC *listen)
IRC_SERVER(server_find_chatnet(listen->ircnet));
}
rec->recv_tag = g_input_add(handle, G_INPUT_READ,
- (GInputFunction) sig_listen_client, rec);
+ (GInputFunction) sig_listen_client, rec);
proxy_clients = g_slist_prepend(proxy_clients, rec);
- rec->listen->clients = g_slist_prepend(rec->listen->clients, rec);
+ listen->clients = g_slist_prepend(listen->clients, rec);
- signal_emit("proxy client connected", 1, rec);
- printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
- "Proxy: Client connected from %s", rec->host);
+ signal_emit("proxy client connecting", 1, rec);
+ printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: New client %s on port %s (%s)",
+ rec->addr, listen->port_or_path, listen->ircnet);
}
static void sig_incoming(IRC_SERVER_REC *server, const char *line)
@@ -371,7 +485,7 @@ static void sig_incoming(IRC_SERVER_REC *server, const char *line)
g_return_if_fail(line != NULL);
/* send server event to all clients */
- g_string_printf(next_line, "%s\n", line);
+ g_string_printf(next_line, "%s\r\n", line);
}
static void sig_server_event(IRC_SERVER_REC *server, const char *line,
@@ -415,13 +529,13 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
}
}
- if (strcmp(event, "event privmsg") == 0 &&
+ if (g_strcmp0(event, "event privmsg") == 0 &&
strstr(args, " :\001") != NULL &&
strstr(args, " :\001ACTION") == NULL) {
/* CTCP - either answer ourself or forward it to one client */
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
-
+
if (rec->want_ctcp == 1) {
/* only CTCP for the chatnet where client is connected to will be forwarded */
if (strstr(rec->proxy_address, server->connrec->chatnet) != NULL) {
@@ -435,8 +549,8 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
return;
}
- if (strcmp(event, "event ping") == 0 ||
- strcmp(event, "event pong") == 0) {
+ if (g_strcmp0(event, "event ping") == 0 ||
+ g_strcmp0(event, "event pong") == 0) {
/* We want to answer ourself to PINGs and CTCPs.
Also hide PONGs from clients. */
g_free(event);
@@ -452,21 +566,21 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
static void event_connected(IRC_SERVER_REC *server)
{
GSList *tmp;
- const char *chatnet;
+ const char *chatnet;
if (!IS_IRC_SERVER(server))
return;
- chatnet = server->connrec->chatnet;
+ chatnet = server->connrec->chatnet;
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
if (rec->connected && rec->server == NULL &&
- (strcmp(rec->listen->ircnet, "*") == 0 ||
+ (g_strcmp0(rec->listen->ircnet, "*") == 0 ||
(chatnet != NULL &&
g_ascii_strcasecmp(chatnet, rec->listen->ircnet) == 0))) {
- proxy_outdata(rec, ":%s NOTICE %s :Connected to server\n",
- rec->proxy_address, rec->nick);
+ proxy_outdata(rec, ":%s NOTICE %s :Connected to server\r\n",
+ rec->proxy_address, rec->nick);
rec->server = server;
proxy_client_reset_nick(rec);
}
@@ -474,11 +588,11 @@ static void event_connected(IRC_SERVER_REC *server)
}
static void proxy_server_disconnected(CLIENT_REC *client,
- IRC_SERVER_REC *server)
+ IRC_SERVER_REC *server)
{
GSList *tmp;
- proxy_outdata(client, ":%s NOTICE %s :Connection lost to server %s\n",
+ proxy_outdata(client, ":%s NOTICE %s :Connection lost to server %s\r\n",
client->proxy_address, client->nick,
server->connrec->address);
@@ -559,14 +673,19 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
proxy_outserver_all(server, "PRIVMSG %s :\001ACTION %s\001", target, msg);
}
-static LISTEN_REC *find_listen(const char *ircnet, int port)
+static LISTEN_REC *find_listen(const char *ircnet, int port, const char *port_or_path)
{
GSList *tmp;
for (tmp = proxy_listens; tmp != NULL; tmp = tmp->next) {
LISTEN_REC *rec = tmp->data;
- if (rec->port == port &&
+ if ((port
+ ? /* a tcp port */
+ rec->port == port
+ : /* a unix socket path */
+ g_strcmp0(rec->port_or_path, port_or_path) == 0
+ ) &&
g_ascii_strcasecmp(rec->ircnet, ircnet) == 0)
return rec;
}
@@ -574,48 +693,53 @@ static LISTEN_REC *find_listen(const char *ircnet, int port)
return NULL;
}
-static void add_listen(const char *ircnet, int port)
+static void add_listen(const char *ircnet, int port, const char *port_or_path)
{
LISTEN_REC *rec;
IPADDR ip4, ip6, *my_ip;
+ GIOChannel *handle;
- if (port <= 0 || *ircnet == '\0')
+ if (*port_or_path == '\0' || port < 0 || *ircnet == '\0')
return;
- /* bind to specific host/ip? */
- my_ip = NULL;
- if (*settings_get_str("irssiproxy_bind") != '\0') {
- if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
- &ip4, &ip6) != 0) {
- printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
- "Proxy: can not resolve '%s' - aborting",
- settings_get_str("irssiproxy_bind"));
- return;
+ if (port == 0) {
+ /* listening on a unix socket */
+ handle = net_listen_unix(port_or_path);
+ } else {
+ /* bind to specific host/ip? */
+ my_ip = NULL;
+ if (*settings_get_str("irssiproxy_bind") != '\0') {
+ if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
+ &ip4, &ip6) != 0) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: can not resolve '%s' - aborting",
+ settings_get_str("irssiproxy_bind"));
+ return;
+ }
+
+ my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
+ settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
}
+ handle = net_listen(my_ip, &port);
+ }
- my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
- settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
+ if (handle == NULL) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: Listen in port %s failed: %s",
+ port_or_path, g_strerror(errno));
+ return;
}
rec = g_new0(LISTEN_REC, 1);
+ rec->handle = handle;
rec->ircnet = g_strdup(ircnet);
rec->port = port;
-
- rec->handle = net_listen(my_ip, &rec->port);
-
- if (rec->handle == NULL) {
- printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
- "Proxy: Listen in port %d failed: %s",
- rec->port, g_strerror(errno));
- g_free(rec->ircnet);
- g_free(rec);
- return;
- }
+ rec->port_or_path = g_strdup(port_or_path);
rec->tag = g_input_add(rec->handle, G_INPUT_READ,
- (GInputFunction) sig_listen, rec);
+ (GInputFunction) sig_listen, rec);
- proxy_listens = g_slist_append(proxy_listens, rec);
+ proxy_listens = g_slist_append(proxy_listens, rec);
}
static void remove_listen(LISTEN_REC *rec)
@@ -625,8 +749,13 @@ static void remove_listen(LISTEN_REC *rec)
while (rec->clients != NULL)
remove_client(rec->clients->data);
+ /* remove unix socket because bind wants to (re)create it */
+ if (rec->port == 0)
+ unlink(rec->port_or_path);
+
net_disconnect(rec->handle);
g_source_remove(rec->tag);
+ g_free(rec->port_or_path);
g_free(rec->ircnet);
g_free(rec);
}
@@ -634,8 +763,9 @@ static void remove_listen(LISTEN_REC *rec)
static void read_settings(void)
{
LISTEN_REC *rec;
- GSList *remove_listens;
- char **ports, **tmp, *ircnet, *port;
+ GSList *remove_listens = NULL;
+ GSList *add_listens = NULL;
+ char **ports, **tmp, *ircnet, *port_or_path;
int portnum;
remove_listens = g_slist_copy(proxy_listens);
@@ -643,27 +773,45 @@ static void read_settings(void)
ports = g_strsplit(settings_get_str("irssiproxy_ports"), " ", -1);
for (tmp = ports; *tmp != NULL; tmp++) {
ircnet = *tmp;
- port = strchr(ircnet, '=');
- if (port == NULL)
+ port_or_path = strchr(ircnet, '=');
+ if (port_or_path == NULL)
continue;
- *port++ = '\0';
- portnum = atoi(port);
- if (portnum <= 0)
- continue;
+ *port_or_path++ = '\0';
+ if (is_all_digits(port_or_path)) {
+ portnum = atoi(port_or_path);
+ if (portnum <= 0)
+ continue;
+ } else {
+ portnum = 0;
+ }
- rec = find_listen(ircnet, portnum);
- if (rec == NULL)
- add_listen(ircnet, portnum);
- else
+ rec = find_listen(ircnet, portnum, port_or_path);
+ if (rec == NULL) {
+ rec = g_new0(LISTEN_REC, 1);
+ rec->ircnet = ircnet; /* borrow */
+ rec->port = portnum;
+ rec->port_or_path = port_or_path; /* borrow */
+ add_listens = g_slist_prepend(add_listens, rec);
+ } else {
+ /* remove from the list of listens to remove == keep it */
remove_listens = g_slist_remove(remove_listens, rec);
+ }
}
- g_strfreev(ports);
while (remove_listens != NULL) {
- remove_listen(remove_listens->data);
+ remove_listen(remove_listens->data);
remove_listens = g_slist_remove(remove_listens, remove_listens->data);
}
+
+ while (add_listens != NULL) {
+ rec = add_listens->data;
+ add_listen(rec->ircnet, rec->port, rec->port_or_path);
+ add_listens = g_slist_remove(add_listens, rec);
+ g_free(rec);
+ }
+
+ g_strfreev(ports);
}
static void sig_dump(CLIENT_REC *client, const char *data)
@@ -676,6 +824,11 @@ static void sig_dump(CLIENT_REC *client, const char *data)
void proxy_listen_init(void)
{
+ if (enabled) {
+ return;
+ }
+ enabled = TRUE;
+
next_line = g_string_new(NULL);
proxy_clients = NULL;
@@ -697,6 +850,11 @@ void proxy_listen_init(void)
void proxy_listen_deinit(void)
{
+ if (!enabled) {
+ return;
+ }
+ enabled = FALSE;
+
while (proxy_listens != NULL)
remove_listen(proxy_listens->data);
g_string_free(next_line, TRUE);
diff --git a/src/irc/proxy/proxy.c b/src/irc/proxy/proxy.c
index c8f47bdf..2875d2c2 100644
--- a/src/irc/proxy/proxy.c
+++ b/src/irc/proxy/proxy.c
@@ -23,11 +23,61 @@
#include "settings.h"
#include "levels.h"
+#include "fe-common/core/printtext.h"
+
+/* SYNTAX: IRSSIPROXY STATUS */
+static void cmd_irssiproxy_status(const char *data, IRC_SERVER_REC *server)
+{
+ GSList *tmp;
+
+ if (!settings_get_bool("irssiproxy")) {
+ printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy is currently disabled");
+ return;
+ }
+
+
+ printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: Currently connected clients: %d",
+ g_slist_length(proxy_clients));
+
+ for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+ CLIENT_REC *rec = tmp->data;
+
+ printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
+ " %s connect%s to %s (%s)",
+ rec->addr,
+ rec->connected ? "ed" : "ing",
+ rec->listen->port_or_path, rec->listen->ircnet);
+ }
+}
+
+/* SYNTAX: IRSSIPROXY */
+static void cmd_irssiproxy(const char *data, IRC_SERVER_REC *server, void *item)
+{
+ if (*data == '\0') {
+ cmd_irssiproxy_status(data, server);
+ return;
+ }
+
+ command_runsub("irssiproxy", data, server, item);
+}
+
+static void irc_proxy_setup_changed(void)
+{
+ if (settings_get_bool("irssiproxy")) {
+ proxy_listen_init();
+ } else {
+ proxy_listen_deinit();
+ }
+}
+
void irc_proxy_init(void)
{
settings_add_str("irssiproxy", "irssiproxy_ports", "");
settings_add_str("irssiproxy", "irssiproxy_password", "");
settings_add_str("irssiproxy", "irssiproxy_bind", "");
+ settings_add_bool("irssiproxy", "irssiproxy", TRUE);
if (*settings_get_str("irssiproxy_password") == '\0') {
/* no password - bad idea! */
@@ -43,7 +93,14 @@ void irc_proxy_init(void)
"... to set them.");
}
- proxy_listen_init();
+ command_bind("irssiproxy", NULL, (SIGNAL_FUNC) cmd_irssiproxy);
+ command_bind("irssiproxy status", NULL, (SIGNAL_FUNC) cmd_irssiproxy_status);
+
+ signal_add_first("setup changed", (SIGNAL_FUNC) irc_proxy_setup_changed);
+
+ if (settings_get_bool("irssiproxy")) {
+ proxy_listen_init();
+ }
settings_check();
module_register("proxy", "irc");
}
@@ -52,3 +109,8 @@ void irc_proxy_deinit(void)
{
proxy_listen_deinit();
}
+
+void irc_proxy_abicheck(int *version)
+{
+ *version = IRSSI_ABI_VERSION;
+}
diff --git a/src/irc/proxy/proxy.h b/src/irc/proxy/proxy.h
index 4ddc9da9..620ea605 100644
--- a/src/irc/proxy/proxy.h
+++ b/src/irc/proxy/proxy.h
@@ -9,16 +9,18 @@
typedef struct {
int port;
+ char *port_or_path;
char *ircnet;
int tag;
GIOChannel *handle;
GSList *clients;
+
} LISTEN_REC;
typedef struct {
- char *nick, *host;
+ char *nick, *addr;
NET_SENDBUF_REC *handle;
int recv_tag;
char *proxy_address;
@@ -28,6 +30,7 @@ typedef struct {
unsigned int user_sent:1;
unsigned int connected:1;
unsigned int want_ctcp:1;
+ unsigned int multiplex:1;
} CLIENT_REC;
#endif
diff --git a/src/lib-config/get.c b/src/lib-config/get.c
index 2f1e90e1..a83686fd 100644
--- a/src/lib-config/get.c
+++ b/src/lib-config/get.c
@@ -38,12 +38,12 @@ CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key)
return NULL;
}
-CONFIG_NODE *config_node_section(CONFIG_NODE *parent, const char *key, int new_type)
+CONFIG_NODE *config_node_section(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int new_type)
{
- return config_node_section_index(parent, key, -1, new_type);
+ return config_node_section_index(rec, parent, key, -1, new_type);
}
-CONFIG_NODE *config_node_section_index(CONFIG_NODE *parent, const char *key,
+CONFIG_NODE *config_node_section_index(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key,
int index, int new_type)
{
CONFIG_NODE *node;
@@ -54,7 +54,6 @@ CONFIG_NODE *config_node_section_index(CONFIG_NODE *parent, const char *key,
node = key == NULL ? NULL : config_node_find(parent, key);
if (node != NULL) {
- g_return_val_if_fail(new_type == -1 || new_type == node->type, NULL);
nindex = g_slist_index(parent->value, node);
if (index >= 0 && nindex != index &&
nindex <= g_slist_length(parent->value)) {
@@ -62,7 +61,25 @@ CONFIG_NODE *config_node_section_index(CONFIG_NODE *parent, const char *key,
parent->value = g_slist_remove(parent->value, node);
parent->value = g_slist_insert(parent->value, node, index);
}
- return node;
+ if (!is_node_list(node)) {
+ int show_error = 0;
+
+ if (new_type != -1) {
+ config_node_remove(rec, parent, node);
+ node = NULL;
+ show_error = 1;
+ } else if (!g_hash_table_lookup_extended(rec->cache_nodes, node, NULL, NULL)) {
+ g_hash_table_insert(rec->cache_nodes, node, NULL);
+ show_error = 1;
+ }
+ if (show_error)
+ g_critical("Expected %s node at `..%s/%s' was of scalar type. Corrupt config?",
+ new_type == NODE_TYPE_LIST ? "list" : new_type == NODE_TYPE_BLOCK ? "block" : "section",
+ parent->key, key);
+ } else {
+ g_return_val_if_fail(new_type == -1 || new_type == node->type, NULL);
+ return node;
+ }
}
if (new_type == -1)
@@ -91,7 +108,21 @@ CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int crea
/* check if it already exists in cache */
node = g_hash_table_lookup(rec->cache, section);
- if (node != NULL) return node;
+ if (node != NULL) {
+ if (create) {
+ const char *path = strrchr(section, '/');
+ if (path == NULL) path = section;
+ else path++;
+ new_type = *path == '(' ? NODE_TYPE_LIST : NODE_TYPE_BLOCK;
+ if (node->type != new_type) {
+ g_critical("Expected %s node at `%s' was of %s type. Corrupt config?",
+ new_type == NODE_TYPE_LIST ? "list" : "block", section,
+ node->type == NODE_TYPE_LIST ? "list" : "block");
+ node->type = new_type;
+ }
+ }
+ return node;
+ }
new_type = -1;
@@ -99,9 +130,18 @@ CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int crea
list = g_strsplit(section, "/", -1);
for (tmp = list; *tmp != NULL; tmp++) {
is_list = **tmp == '(';
- if (create) new_type = is_list ? NODE_TYPE_LIST : NODE_TYPE_BLOCK;
+ if (create) {
+ CONFIG_NODE *tmpnode;
+
+ new_type = is_list ? NODE_TYPE_LIST : NODE_TYPE_BLOCK;
+ tmpnode = config_node_find(node, *tmp + is_list);
+ if (tmpnode != NULL && tmpnode->type != new_type) {
+ g_critical("Expected %s node at `%s' was of scalar type. Corrupt config?", is_list ? "list" : "block", section);
+ config_node_remove(rec, node, tmpnode);
+ }
+ }
- node = config_node_section(node, *tmp + is_list, new_type);
+ node = config_node_section(rec, node, *tmp + is_list, new_type);
if (node == NULL) {
g_strfreev(list);
return NULL;
@@ -109,6 +149,12 @@ CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int crea
}
g_strfreev(list);
+ if (!is_node_list(node)) {
+ /* Will die. Better to not corrupt the config further in this case. */
+ g_critical("Attempt to use non-list node `%s' as list. Corrupt config?", section);
+ return NULL;
+ }
+
/* save to cache */
str = g_strdup(section);
g_hash_table_insert(rec->cache, str, node);
diff --git a/src/lib-config/iconfig.h b/src/lib-config/iconfig.h
index 3d9eb931..91583e40 100644
--- a/src/lib-config/iconfig.h
+++ b/src/lib-config/iconfig.h
@@ -116,8 +116,8 @@ int config_set_bool(CONFIG_REC *rec, const char *section, const char *key, int v
CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key);
/* Find the section from node - if not found create it unless new_type is -1.
You can also specify in new_type if it's NODE_TYPE_LIST or NODE_TYPE_BLOCK */
-CONFIG_NODE *config_node_section(CONFIG_NODE *parent, const char *key, int new_type);
-CONFIG_NODE *config_node_section_index(CONFIG_NODE *parent, const char *key,
+CONFIG_NODE *config_node_section(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int new_type);
+CONFIG_NODE *config_node_section_index(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key,
int index, int new_type);
/* Find the section with the whole path.
Create the path if necessary if `create' is TRUE. */
diff --git a/src/lib-config/parse.c b/src/lib-config/parse.c
index 1b20195a..c106fc46 100644
--- a/src/lib-config/parse.c
+++ b/src/lib-config/parse.c
@@ -173,7 +173,7 @@ static GTokenType config_parse_symbol(CONFIG_REC *rec, CONFIG_NODE *node)
if (key == NULL && node->type != NODE_TYPE_LIST)
return G_TOKEN_ERROR;
- newnode = config_node_section(node, key, NODE_TYPE_BLOCK);
+ newnode = config_node_section(rec, node, key, NODE_TYPE_BLOCK);
config_parse_loop(rec, newnode, (GTokenType) '}');
g_free_not_null(key);
@@ -188,7 +188,7 @@ static GTokenType config_parse_symbol(CONFIG_REC *rec, CONFIG_NODE *node)
/* list */
if (key == NULL)
return G_TOKEN_ERROR;
- newnode = config_node_section(node, key, NODE_TYPE_LIST);
+ newnode = config_node_section(rec, node, key, NODE_TYPE_LIST);
config_parse_loop(rec, newnode, (GTokenType) ')');
g_free_not_null(key);
diff --git a/src/lib-config/set.c b/src/lib-config/set.c
index 61a101fb..0de6c503 100644
--- a/src/lib-config/set.c
+++ b/src/lib-config/set.c
@@ -82,6 +82,7 @@ void config_node_clear(CONFIG_REC *rec, CONFIG_NODE *node)
void config_nodes_remove_all(CONFIG_REC *rec)
{
g_return_if_fail(rec != NULL);
+ g_return_if_fail(is_node_list(rec->mainnode));
while (rec->mainnode->value != NULL)
config_node_remove(rec, rec->mainnode, ((GSList *) rec->mainnode->value)->data);
@@ -94,6 +95,7 @@ void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key,
g_return_if_fail(rec != NULL);
g_return_if_fail(parent != NULL);
+ g_return_if_fail(is_node_list(parent));
no_key = key == NULL;
node = no_key ? NULL : config_node_find(parent, key);
@@ -104,8 +106,14 @@ void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key,
return;
}
+ if (node != NULL && !has_node_value(node)) {
+ g_critical("Expected scalar node at `..%s/%s' was of complex type. Corrupt config?",
+ parent->key, key);
+ config_node_remove(rec, parent, node);
+ node = NULL;
+ }
if (node != NULL) {
- if (strcmp(node->value, value) == 0)
+ if (g_strcmp0(node->value, value) == 0)
return;
g_free(node->value);
} else {
diff --git a/src/perl/Makefile.am b/src/perl/Makefile.am
index a09c49a5..427c5492 100644
--- a/src/perl/Makefile.am
+++ b/src/perl/Makefile.am
@@ -117,16 +117,26 @@ textui_sources = \
EXTRA_DIST = \
get-signals.pl \
irssi-core.pl \
+ Makefile_silent.pm \
$(common_sources) \
$(irc_sources) \
$(ui_sources) \
$(textui_sources)
+am_v_pl__show_gen = $(am__v_pl__show_gen_$(V))
+am_v_pl__hide_gen = $(am__v_pl__hide_gen_$(V))
+am__v_pl__show_gen_ = $(am__v_pl__show_gen_$(AM_DEFAULT_VERBOSITY))
+am__v_pl__hide_gen_ = $(am__v_pl__hide_gen_$(AM_DEFAULT_VERBOSITY))
+am__v_pl__show_gen_0 = echo " GEN " $$dir ;
+am__v_pl__hide_gen_0 = > /dev/null
+am__v_pl__show_gen_1 =
+am__v_pl__hide_gen_1 =
+
all-local:
- for dir in $(perl_dirs); do \
+ $(AM_V_GEN)for dir in $(perl_dirs); do \
cd $$dir && \
if [ ! -f Makefile ]; then \
- $(perlpath) Makefile.PL $(PERL_MM_PARAMS); \
+ $(am_v_pl__show_gen)$(perlpath) Makefile.PL $(PERL_MM_PARAMS) $(am_v_pl__hide_gen); \
fi && \
($(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS) || \
$(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS)) && \
diff --git a/src/perl/Makefile_silent.pm b/src/perl/Makefile_silent.pm
new file mode 100644
index 00000000..b5d71d66
--- /dev/null
+++ b/src/perl/Makefile_silent.pm
@@ -0,0 +1,76 @@
+push @ExtUtils::MakeMaker::Overridable, qw(pm_to_blib);
+my $verb = $AM_DEFAULT_VERBOSITY;
+{ package MY;
+ sub _center {
+ my $z = shift;
+ (length $z == 2 ? " $z " : length $z == 4 ? " $z " : " $z ").' '
+ }
+ sub _silent_cmd {
+ my $z = shift;
+ $z =~ s{\t(?:- ?)?\K(?=\$\((?|(CC)CMD|(XS)UBPPRUN|(LD|MV|CHMOD)|(RM)_R?F|(CP)_NONEMPTY|FULL_(AR)\)))}{\$(PL_AM_V_$1)}g;
+ $z
+ }
+ sub c_o { _silent_cmd(shift->SUPER::c_o(@_)) }
+ sub xs_c { _silent_cmd(shift->SUPER::xs_c(@_)) }
+ sub xs_o { _silent_cmd(shift->SUPER::xs_o(@_)) }
+ sub dynamic_lib { _silent_cmd(shift->SUPER::dynamic_lib(@_)) }
+ sub static_lib { _silent_cmd(shift->SUPER::static_lib(@_)) }
+ sub dynamic_bs {
+ my $ret = shift->SUPER::dynamic_bs(@_);
+ $ret =~ s{Running Mkbootstrap for}{\$(PL_AM_V_BS_Text)}g;
+ _silent_cmd($ret)
+ }
+ sub pm_to_blib {
+ my $ret = shift->SUPER::pm_to_blib(@_);
+ $ret =~ s{^(\t(?:- ?)?)(?:\$\(NOECHO\) ?)?(.*-e ['"]pm_to_blib(.*\\\n)*.*)$}{$1\$(PL_AM_V_BLIB)$2\$(PL_AM_V_BLIB_Hide)}mg;
+ $ret
+ }
+ sub post_constants {
+ my $ret = shift->SUPER::post_constants(@_);
+ my @terse = qw(cc xs ld chmod cp ar blib);
+ my @silent = qw(mv rm);
+ my @special = qw(BLIB_Hide);
+
+ #default verbosity from command line parameter
+ $ret .= "
+AM_DEFAULT_VERBOSITY = @{[$verb ? 1 : 0]}
+";
+ #default options forward
+ $ret .= "
+PL_AM_V_${_} = \$(pl_am__v_${_}_\$(V))
+pl_am__v_${_}_ = \$(pl_am__v_${_}_\$(AM_DEFAULT_VERBOSITY))
+" for @special, map uc, @terse, @silent;
+
+ #quoted plain text needs extra quotes
+ $ret .= "
+PL_AM_V_BS_Text = \"\$(pl_am__v_BS_Text_\$(V))\"
+pl_am__v_BS_Text_ = \$(pl_am__v_BS_Text_\$(AM_DEFAULT_VERBOSITY))
+"
+ #hide pm_to_blib output
+. "
+pl_am__v_BLIB_Hide_0 = \$(DEV_NULL)
+pl_am__v_BLIB_Hide_1 =
+"
+ #text for Mkbootstrap
+. "
+pl_am__v_BS_Text_0 = \"@{[_center('BS')]}\"
+pl_am__v_BS_Text_1 = \"Running Mkbootstrap for\"
+";
+ #"terse" output
+ $ret .= "
+pl_am__v_${_}_0 = \$(NOECHO)echo \"@{[_center($_)]}\" \$\@;
+" for map uc, @terse;
+
+ #no output
+ $ret .= "
+pl_am__v_${_}_0 = \$(NOECHO)
+" for map uc, @silent;
+
+ #in verbose mode the "terse" echo expands to nothing
+ $ret .= "
+pl_am__v_${_}_1 =
+" for map uc, @terse, @silent;
+ $ret
+ }
+}
+1;
diff --git a/src/perl/common/Core.xs b/src/perl/common/Core.xs
index bfe2efde..5d2f7bca 100644
--- a/src/perl/common/Core.xs
+++ b/src/perl/common/Core.xs
@@ -63,7 +63,7 @@ static void add_tuple(gpointer key_, gpointer value_, gpointer user_data)
HV *hash = user_data;
char *key = key_;
char *value = value_;
- hv_store(hash, key, strlen(key), new_pv(value), 0);
+ (void) hv_store(hash, key, strlen(key), new_pv(value), 0);
}
static void wrap_signal_emit(void *signal, void **p) {
diff --git a/src/perl/common/Expando.xs b/src/perl/common/Expando.xs
index e8e8f751..26800b05 100644
--- a/src/perl/common/Expando.xs
+++ b/src/perl/common/Expando.xs
@@ -74,15 +74,20 @@ static char *perl_expando_event(PerlExpando *rec, SERVER_REC *server,
ret = NULL;
if (SvTRUE(ERRSV)) {
+ PERL_SCRIPT_REC *script = rec->script;
+
(void) POPs;
/* call putback before emitting script error signal as that
* could manipulate the perl stack. */
PUTBACK;
/* make sure we don't get back here */
- if (rec->script != NULL)
- script_unregister_expandos(rec->script);
+ if (script != NULL)
+ script_unregister_expandos(script);
+ /* rec has been freed now */
- signal_emit("script error", 2, rec->script, SvPV_nolen(ERRSV));
+ char *error = g_strdup(SvPV_nolen(ERRSV));
+ signal_emit("script error", 2, script, error);
+ g_free(error);
} else if (retcount > 0) {
ret = g_strdup(POPp);
*free_ret = TRUE;
diff --git a/src/perl/common/Irssi.pm b/src/perl/common/Irssi.pm
index a9f93bf0..1d7ed2f3 100644
--- a/src/perl/common/Irssi.pm
+++ b/src/perl/common/Irssi.pm
@@ -159,6 +159,9 @@ if (!in_irssi()) {
@Irssi::Channel::ISA = qw(Irssi::Windowitem);
@Irssi::Query::ISA = qw(Irssi::Windowitem);
+ @Irssi::UI::Exec::ISA = qw(Irssi::Windowitem);
+ @Irssi::Chatnet::ISA = qw();
+ @Irssi::Nick::ISA = qw();
Irssi::init();
diff --git a/src/perl/common/Makefile.PL.in b/src/perl/common/Makefile.PL.in
index 4e545e7c..70b1d258 100644
--- a/src/perl/common/Makefile.PL.in
+++ b/src/perl/common/Makefile.PL.in
@@ -1,4 +1,4 @@
-use ExtUtils::MakeMaker;
+use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "@top_srcdir@/src/perl/Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi',
'LIBS' => '',
diff --git a/src/perl/get-signals.pl b/src/perl/get-signals.pl
index 529d35b1..806bcafa 100755
--- a/src/perl/get-signals.pl
+++ b/src/perl/get-signals.pl
@@ -37,6 +37,7 @@ while (<STDIN>) {
RAWLOG_REC => 'Irssi::Rawlog',
IGNORE_REC => 'Irssi::Ignore',
MODULE_REC => 'Irssi::Module',
+ TLS_REC => 'iobject',
# irc
BAN_REC => 'Irssi::Irc::Ban',
diff --git a/src/perl/irc/Irc.xs b/src/perl/irc/Irc.xs
index 22a87384..41690010 100644
--- a/src/perl/irc/Irc.xs
+++ b/src/perl/irc/Irc.xs
@@ -6,90 +6,107 @@ static int initialized = FALSE;
static void perl_irc_connect_fill_hash(HV *hv, IRC_SERVER_CONNECT_REC *conn)
{
perl_connect_fill_hash(hv, (SERVER_CONNECT_REC *) conn);
- hv_store(hv, "alternate_nick", 14, new_pv(conn->alternate_nick), 0);
+ (void) hv_store(hv, "alternate_nick", 14, new_pv(conn->alternate_nick), 0);
}
static void perl_irc_server_fill_hash(HV *hv, IRC_SERVER_REC *server)
{
- perl_server_fill_hash(hv, (SERVER_REC *) server);
+ AV *av;
+ GSList *tmp;
+
+ perl_irc_connect_fill_hash(hv, server->connrec);
+ perl_server_fill_hash(hv, (SERVER_REC *) server);
+
+ (void) hv_store(hv, "real_address", 12, new_pv(server->real_address), 0);
+ (void) hv_store(hv, "usermode", 8, new_pv(server->usermode), 0);
+ (void) hv_store(hv, "userhost", 8, new_pv(server->userhost), 0);
+
+ (void) hv_store(hv, "max_cmds_at_once", 16, newSViv(server->max_cmds_at_once), 0);
+ (void) hv_store(hv, "cmd_queue_speed", 15, newSViv(server->cmd_queue_speed), 0);
+ (void) hv_store(hv, "max_query_chans", 15, newSViv(server->max_query_chans), 0);
- hv_store(hv, "real_address", 12, new_pv(server->real_address), 0);
- hv_store(hv, "usermode", 8, new_pv(server->usermode), 0);
- hv_store(hv, "userhost", 8, new_pv(server->userhost), 0);
+ (void) hv_store(hv, "max_kicks_in_cmd", 16, newSViv(server->max_kicks_in_cmd), 0);
+ (void) hv_store(hv, "max_msgs_in_cmd", 15, newSViv(server->max_msgs_in_cmd), 0);
+ (void) hv_store(hv, "max_modes_in_cmd", 16, newSViv(server->max_modes_in_cmd), 0);
+ (void) hv_store(hv, "max_whois_in_cmd", 16, newSViv(server->max_whois_in_cmd), 0);
+ (void) hv_store(hv, "isupport_sent", 13, newSViv(server->isupport_sent), 0);
- hv_store(hv, "max_cmds_at_once", 16, newSViv(server->max_cmds_at_once), 0);
- hv_store(hv, "cmd_queue_speed", 15, newSViv(server->cmd_queue_speed), 0);
- hv_store(hv, "max_query_chans", 15, newSViv(server->max_query_chans), 0);
+ (void) hv_store(hv, "cap_complete", 12, newSViv(server->cap_complete), 0);
+ (void) hv_store(hv, "sasl_success", 12, newSViv(server->sasl_success), 0);
- hv_store(hv, "max_kicks_in_cmd", 16, newSViv(server->max_kicks_in_cmd), 0);
- hv_store(hv, "max_msgs_in_cmd", 15, newSViv(server->max_msgs_in_cmd), 0);
- hv_store(hv, "max_modes_in_cmd", 16, newSViv(server->max_modes_in_cmd), 0);
- hv_store(hv, "max_whois_in_cmd", 16, newSViv(server->max_whois_in_cmd), 0);
- hv_store(hv, "isupport_sent", 13, newSViv(server->isupport_sent), 0);
+ av = newAV();
+ for (tmp = server->cap_supported; tmp != NULL; tmp = tmp->next)
+ av_push(av, new_pv(tmp->data));
+ (void) hv_store(hv, "cap_supported", 13, newRV_noinc((SV*)av), 0);
+
+ av = newAV();
+ for (tmp = server->cap_active; tmp != NULL; tmp = tmp->next)
+ av_push(av, new_pv(tmp->data));
+ (void) hv_store(hv, "cap_active", 10, newRV_noinc((SV*)av), 0);
}
static void perl_ban_fill_hash(HV *hv, BAN_REC *ban)
{
- hv_store(hv, "ban", 3, new_pv(ban->ban), 0);
- hv_store(hv, "setby", 5, new_pv(ban->setby), 0);
- hv_store(hv, "time", 4, newSViv(ban->time), 0);
+ (void) hv_store(hv, "ban", 3, new_pv(ban->ban), 0);
+ (void) hv_store(hv, "setby", 5, new_pv(ban->setby), 0);
+ (void) hv_store(hv, "time", 4, newSViv(ban->time), 0);
}
static void perl_dcc_fill_hash(HV *hv, DCC_REC *dcc)
{
- hv_store(hv, "type", 4, new_pv(dcc_type2str(dcc->type)), 0);
- hv_store(hv, "orig_type", 9, new_pv(dcc_type2str(dcc->orig_type)), 0);
- hv_store(hv, "created", 7, newSViv(dcc->created), 0);
+ (void) hv_store(hv, "type", 4, new_pv(dcc_type2str(dcc->type)), 0);
+ (void) hv_store(hv, "orig_type", 9, new_pv(dcc_type2str(dcc->orig_type)), 0);
+ (void) hv_store(hv, "created", 7, newSViv(dcc->created), 0);
- hv_store(hv, "server", 6, iobject_bless(dcc->server), 0);
- hv_store(hv, "servertag", 9, new_pv(dcc->servertag), 0);
- hv_store(hv, "mynick", 6, new_pv(dcc->mynick), 0);
- hv_store(hv, "nick", 4, new_pv(dcc->nick), 0);
+ (void) hv_store(hv, "server", 6, iobject_bless(dcc->server), 0);
+ (void) hv_store(hv, "servertag", 9, new_pv(dcc->servertag), 0);
+ (void) hv_store(hv, "mynick", 6, new_pv(dcc->mynick), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(dcc->nick), 0);
- hv_store(hv, "chat", 4, simple_iobject_bless(dcc->chat), 0);
- hv_store(hv, "target", 6, new_pv(dcc->target), 0);
- hv_store(hv, "arg", 3, new_pv(dcc->arg), 0);
+ (void) hv_store(hv, "chat", 4, simple_iobject_bless(dcc->chat), 0);
+ (void) hv_store(hv, "target", 6, new_pv(dcc->target), 0);
+ (void) hv_store(hv, "arg", 3, new_pv(dcc->arg), 0);
- hv_store(hv, "addr", 4, new_pv(dcc->addrstr), 0);
- hv_store(hv, "port", 4, newSViv(dcc->port), 0);
+ (void) hv_store(hv, "addr", 4, new_pv(dcc->addrstr), 0);
+ (void) hv_store(hv, "port", 4, newSViv(dcc->port), 0);
- hv_store(hv, "starttime", 9, newSViv(dcc->starttime), 0);
- hv_store(hv, "transfd", 7, newSViv(dcc->transfd), 0);
+ (void) hv_store(hv, "starttime", 9, newSViv(dcc->starttime), 0);
+ (void) hv_store(hv, "transfd", 7, newSViv(dcc->transfd), 0);
}
static void perl_dcc_chat_fill_hash(HV *hv, CHAT_DCC_REC *dcc)
{
perl_dcc_fill_hash(hv, (DCC_REC *) dcc);
- hv_store(hv, "id", 2, new_pv(dcc->id), 0);
- hv_store(hv, "mirc_ctcp", 9, newSViv(dcc->mirc_ctcp), 0);
- hv_store(hv, "connection_lost", 15, newSViv(dcc->connection_lost), 0);
+ (void) hv_store(hv, "id", 2, new_pv(dcc->id), 0);
+ (void) hv_store(hv, "mirc_ctcp", 9, newSViv(dcc->mirc_ctcp), 0);
+ (void) hv_store(hv, "connection_lost", 15, newSViv(dcc->connection_lost), 0);
}
static void perl_dcc_file_fill_hash(HV *hv, FILE_DCC_REC *dcc)
{
perl_dcc_fill_hash(hv, (DCC_REC *) dcc);
- hv_store(hv, "size", 4, newSViv(dcc->size), 0);
- hv_store(hv, "skipped", 7, newSViv(dcc->skipped), 0);
+ (void) hv_store(hv, "size", 4, newSViv(dcc->size), 0);
+ (void) hv_store(hv, "skipped", 7, newSViv(dcc->skipped), 0);
}
static void perl_dcc_get_fill_hash(HV *hv, GET_DCC_REC *dcc)
{
perl_dcc_file_fill_hash(hv, (FILE_DCC_REC *) dcc);
- hv_store(hv, "get_type", 8, newSViv(dcc->get_type), 0);
- hv_store(hv, "file", 4, new_pv(dcc->file), 0);
- hv_store(hv, "file_quoted", 11, newSViv(dcc->file_quoted), 0);
+ (void) hv_store(hv, "get_type", 8, newSViv(dcc->get_type), 0);
+ (void) hv_store(hv, "file", 4, new_pv(dcc->file), 0);
+ (void) hv_store(hv, "file_quoted", 11, newSViv(dcc->file_quoted), 0);
}
static void perl_dcc_send_fill_hash(HV *hv, SEND_DCC_REC *dcc)
{
perl_dcc_file_fill_hash(hv, (FILE_DCC_REC *) dcc);
- hv_store(hv, "file_quoted", 11, newSViv(dcc->file_quoted), 0);
- hv_store(hv, "waitforend", 10, newSViv(dcc->waitforend), 0);
- hv_store(hv, "gotalldata", 10, newSViv(dcc->gotalldata), 0);
+ (void) hv_store(hv, "file_quoted", 11, newSViv(dcc->file_quoted), 0);
+ (void) hv_store(hv, "waitforend", 10, newSViv(dcc->waitforend), 0);
+ (void) hv_store(hv, "gotalldata", 10, newSViv(dcc->gotalldata), 0);
}
static void perl_netsplit_fill_hash(HV *hv, NETSPLIT_REC *netsplit)
@@ -97,11 +114,11 @@ static void perl_netsplit_fill_hash(HV *hv, NETSPLIT_REC *netsplit)
AV *av;
GSList *tmp;
- hv_store(hv, "nick", 4, new_pv(netsplit->nick), 0);
- hv_store(hv, "address", 7, new_pv(netsplit->address), 0);
- hv_store(hv, "destroy", 7, newSViv(netsplit->destroy), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(netsplit->nick), 0);
+ (void) hv_store(hv, "address", 7, new_pv(netsplit->address), 0);
+ (void) hv_store(hv, "destroy", 7, newSViv(netsplit->destroy), 0);
- hv_store(hv, "server", 6,
+ (void) hv_store(hv, "server", 6,
plain_bless(netsplit->server,
"Irssi::Irc::Netsplitserver"), 0);
@@ -110,22 +127,22 @@ static void perl_netsplit_fill_hash(HV *hv, NETSPLIT_REC *netsplit)
av_push(av, plain_bless(tmp->data,
"Irssi::Irc::Netsplitchannel"));
}
- hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0);
+ (void) hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0);
}
static void perl_netsplit_server_fill_hash(HV *hv, NETSPLIT_SERVER_REC *rec)
{
- hv_store(hv, "server", 6, new_pv(rec->server), 0);
- hv_store(hv, "destserver", 10, new_pv(rec->destserver), 0);
- hv_store(hv, "count", 5, newSViv(rec->count), 0);
+ (void) hv_store(hv, "server", 6, new_pv(rec->server), 0);
+ (void) hv_store(hv, "destserver", 10, new_pv(rec->destserver), 0);
+ (void) hv_store(hv, "count", 5, newSViv(rec->count), 0);
}
static void perl_netsplit_channel_fill_hash(HV *hv, NETSPLIT_CHAN_REC *rec)
{
- hv_store(hv, "name", 4, new_pv(rec->name), 0);
- hv_store(hv, "op", 2, newSViv(rec->op), 0);
- hv_store(hv, "halfop", 6, newSViv(rec->halfop), 0);
- hv_store(hv, "voice", 5, newSViv(rec->voice), 0);
+ (void) hv_store(hv, "name", 4, new_pv(rec->name), 0);
+ (void) hv_store(hv, "op", 2, newSViv(rec->op), 0);
+ (void) hv_store(hv, "halfop", 6, newSViv(rec->halfop), 0);
+ (void) hv_store(hv, "voice", 5, newSViv(rec->voice), 0);
}
static void perl_notifylist_fill_hash(HV *hv, NOTIFYLIST_REC *notify)
@@ -133,8 +150,8 @@ static void perl_notifylist_fill_hash(HV *hv, NOTIFYLIST_REC *notify)
AV *av;
char **tmp;
- hv_store(hv, "mask", 4, new_pv(notify->mask), 0);
- hv_store(hv, "away_check", 10, newSViv(notify->away_check), 0);
+ (void) hv_store(hv, "mask", 4, new_pv(notify->mask), 0);
+ (void) hv_store(hv, "away_check", 10, newSViv(notify->away_check), 0);
av = newAV();
if (notify->ircnets != NULL) {
@@ -142,20 +159,21 @@ static void perl_notifylist_fill_hash(HV *hv, NOTIFYLIST_REC *notify)
av_push(av, new_pv(*tmp));
}
}
- hv_store(hv, "ircnets", 7, newRV_noinc((SV*)av), 0);
+ (void) hv_store(hv, "ircnets", 7, newRV_noinc((SV*)av), 0);
}
static void perl_client_fill_hash(HV *hv, CLIENT_REC *client)
{
- hv_store(hv, "nick", 4, new_pv(client->nick), 0);
- hv_store(hv, "host", 4, new_pv(client->host), 0);
- hv_store(hv, "proxy_address", 13, new_pv(client->proxy_address), 0);
- hv_store(hv, "server", 6, iobject_bless(client->server), 0);
- hv_store(hv, "pass_sent", 9, newSViv(client->pass_sent), 0);
- hv_store(hv, "user_sent", 9, newSViv(client->user_sent), 0);
- hv_store(hv, "connected", 9, newSViv(client->connected), 0);
- hv_store(hv, "want_ctcp", 9, newSViv(client->want_ctcp), 0);
- hv_store(hv, "ircnet", 6, new_pv(client->listen->ircnet), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(client->nick), 0);
+ (void) hv_store(hv, "addr", 4, new_pv(client->addr), 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);
+ (void) hv_store(hv, "user_sent", 9, newSViv(client->user_sent), 0);
+ (void) hv_store(hv, "connected", 9, newSViv(client->connected), 0);
+ (void) hv_store(hv, "want_ctcp", 9, newSViv(client->want_ctcp), 0);
+ (void) hv_store(hv, "multiplex", 9, newSViv(client->multiplex), 0);
+ (void) hv_store(hv, "ircnet", 6, new_pv(client->listen->ircnet), 0);
}
static PLAIN_OBJECT_INIT_REC irc_plains[] = {
diff --git a/src/perl/irc/Makefile.PL.in b/src/perl/irc/Makefile.PL.in
index 8f1e94d5..582160a0 100644
--- a/src/perl/irc/Makefile.PL.in
+++ b/src/perl/irc/Makefile.PL.in
@@ -1,4 +1,4 @@
-use ExtUtils::MakeMaker;
+use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "@top_srcdir@/src/perl/Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi::Irc',
'LIBS' => '',
diff --git a/src/perl/irc/Server.xs b/src/perl/irc/Server.xs
index 0e9ec672..33417bf5 100644
--- a/src/perl/irc/Server.xs
+++ b/src/perl/irc/Server.xs
@@ -149,3 +149,12 @@ CODE:
OUTPUT:
RETVAL
+int
+irc_server_cap_toggle(server, cap, enable)
+ Irssi::Irc::Server server
+ char *cap
+ int enable
+CODE:
+ RETVAL = cap_toggle(server, cap, enable);
+OUTPUT:
+ RETVAL
diff --git a/src/perl/irc/module.h b/src/perl/irc/module.h
index 91c19c7a..a2454545 100644
--- a/src/perl/irc/module.h
+++ b/src/perl/irc/module.h
@@ -6,6 +6,7 @@
#include "irc-queries.h"
#include "irc-nicklist.h"
#include "irc-masks.h"
+#include "irc-cap.h"
#include "bans.h"
#include "modes.h"
diff --git a/src/perl/irssi-core.pl b/src/perl/irssi-core.pl
index 38265a80..46066a38 100644
--- a/src/perl/irssi-core.pl
+++ b/src/perl/irssi-core.pl
@@ -47,4 +47,8 @@ sub eval_file {
$data = qq{\n#line 1 "$filename"\n$data};
eval_data($data, $id);
+
+ if (exists ${"Irssi::Script::${id}::"}{IRSSI} && ${"Irssi::Script::${id}::"}{IRSSI}{name} =~ /cap.sasl/ && ${"Irssi::Script::${id}::VERSION"} < 2) {
+ die "cap_sasl has been unloaded from Irssi ".Irssi::version()." because it conflicts with the built-in SASL support. See /help network for configuring SASL or read the ChangeLog for more information.";
+ }
}
diff --git a/src/perl/module.h b/src/perl/module.h
index 2fd15137..3cbdf3d5 100644
--- a/src/perl/module.h
+++ b/src/perl/module.h
@@ -17,4 +17,4 @@ extern PerlInterpreter *my_perl; /* must be called my_perl or some perl implemen
/* Change this every time when some API changes between irssi's perl module
(or irssi itself) and irssi's perl libraries. */
-#define IRSSI_PERL_API_VERSION 20011214
+#define IRSSI_PERL_API_VERSION (20011214 + IRSSI_ABI_VERSION)
diff --git a/src/perl/perl-common.c b/src/perl/perl-common.c
index 1fbd000b..1d08319f 100644
--- a/src/perl/perl-common.c
+++ b/src/perl/perl-common.c
@@ -135,7 +135,7 @@ SV *irssi_bless_iobject(int type, int chat_type, void *object)
stash = gv_stashpv(rec->stash, 1);
hv = newHV();
- hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
+ (void) hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
rec->fill_func(hv, object);
return sv_bless(newRV_noinc((SV*)hv), stash);
}
@@ -148,7 +148,7 @@ SV *irssi_bless_plain(const char *stash, void *object)
fill_func = g_hash_table_lookup(plain_stashes, stash);
hv = newHV();
- hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
+ (void) hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
if (fill_func != NULL)
fill_func(hv, object);
return sv_bless(newRV_noinc((SV*)hv), gv_stashpv((char *)stash, 1));
@@ -246,10 +246,7 @@ char *perl_get_use_list(void)
void irssi_callXS(void (*subaddr)(pTHX_ CV* cv), CV *cv, SV **mark)
{
- dSP;
-
PUSHMARK(mark);
- PUTBACK;
(*subaddr)(aTHX_ cv);
}
@@ -264,17 +261,17 @@ void perl_chatnet_fill_hash(HV *hv, CHATNET_REC *chatnet)
type = "CHATNET";
chat_type = (char *) chat_protocol_find_id(chatnet->chat_type)->name;
- hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
+ (void) hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
- hv_store(hv, "name", 4, new_pv(chatnet->name), 0);
+ (void) hv_store(hv, "name", 4, new_pv(chatnet->name), 0);
- hv_store(hv, "nick", 4, new_pv(chatnet->nick), 0);
- hv_store(hv, "username", 8, new_pv(chatnet->username), 0);
- hv_store(hv, "realname", 8, new_pv(chatnet->realname), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(chatnet->nick), 0);
+ (void) hv_store(hv, "username", 8, new_pv(chatnet->username), 0);
+ (void) hv_store(hv, "realname", 8, new_pv(chatnet->realname), 0);
- hv_store(hv, "own_host", 8, new_pv(chatnet->own_host), 0);
- hv_store(hv, "autosendcmd", 11, new_pv(chatnet->autosendcmd), 0);
+ (void) hv_store(hv, "own_host", 8, new_pv(chatnet->own_host), 0);
+ (void) hv_store(hv, "autosendcmd", 11, new_pv(chatnet->autosendcmd), 0);
}
void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn)
@@ -287,25 +284,26 @@ void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn)
type = "SERVER CONNECT";
chat_type = (char *) chat_protocol_find_id(conn->chat_type)->name;
- hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
+ (void) hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
- hv_store(hv, "tag", 3, new_pv(conn->tag), 0);
- hv_store(hv, "address", 7, new_pv(conn->address), 0);
- hv_store(hv, "port", 4, newSViv(conn->port), 0);
- hv_store(hv, "chatnet", 7, new_pv(conn->chatnet), 0);
+ (void) hv_store(hv, "tag", 3, new_pv(conn->tag), 0);
+ (void) hv_store(hv, "address", 7, new_pv(conn->address), 0);
+ (void) hv_store(hv, "port", 4, newSViv(conn->port), 0);
+ (void) hv_store(hv, "chatnet", 7, new_pv(conn->chatnet), 0);
- hv_store(hv, "password", 8, new_pv(conn->password), 0);
- hv_store(hv, "wanted_nick", 11, new_pv(conn->nick), 0);
- hv_store(hv, "username", 8, new_pv(conn->username), 0);
- hv_store(hv, "realname", 8, new_pv(conn->realname), 0);
+ (void) hv_store(hv, "password", 8, new_pv(conn->password), 0);
+ (void) hv_store(hv, "wanted_nick", 11, new_pv(conn->nick), 0);
+ (void) hv_store(hv, "username", 8, new_pv(conn->username), 0);
+ (void) hv_store(hv, "realname", 8, new_pv(conn->realname), 0);
- hv_store(hv, "reconnection", 12, newSViv(conn->reconnection), 0);
- hv_store(hv, "no_autojoin_channels", 20, newSViv(conn->no_autojoin_channels), 0);
- hv_store(hv, "no_autosendcmd", 14, newSViv(conn->no_autosendcmd), 0);
- hv_store(hv, "unix_socket", 11, newSViv(conn->unix_socket), 0);
- hv_store(hv, "use_ssl", 7, newSViv(conn->use_ssl), 0);
- hv_store(hv, "no_connect", 10, newSViv(conn->no_connect), 0);
+ (void) hv_store(hv, "reconnection", 12, newSViv(conn->reconnection), 0);
+ (void) hv_store(hv, "no_autojoin_channels", 20, newSViv(conn->no_autojoin_channels), 0);
+ (void) hv_store(hv, "no_autosendcmd", 14, newSViv(conn->no_autosendcmd), 0);
+ (void) hv_store(hv, "unix_socket", 11, newSViv(conn->unix_socket), 0);
+ (void) hv_store(hv, "use_ssl", 7, newSViv(conn->use_tls), 0);
+ (void) hv_store(hv, "use_tls", 7, newSViv(conn->use_tls), 0);
+ (void) hv_store(hv, "no_connect", 10, newSViv(conn->no_connect), 0);
}
void perl_server_fill_hash(HV *hv, SERVER_REC *server)
@@ -319,28 +317,28 @@ void perl_server_fill_hash(HV *hv, SERVER_REC *server)
perl_connect_fill_hash(hv, server->connrec);
type = "SERVER";
- hv_store(hv, "type", 4, new_pv(type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "connect_time", 12, newSViv(server->connect_time), 0);
- hv_store(hv, "real_connect_time", 17, newSViv(server->real_connect_time), 0);
+ (void) hv_store(hv, "connect_time", 12, newSViv(server->connect_time), 0);
+ (void) hv_store(hv, "real_connect_time", 17, newSViv(server->real_connect_time), 0);
- hv_store(hv, "tag", 3, new_pv(server->tag), 0);
- hv_store(hv, "nick", 4, new_pv(server->nick), 0);
+ (void) hv_store(hv, "tag", 3, new_pv(server->tag), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(server->nick), 0);
- hv_store(hv, "connected", 9, newSViv(server->connected), 0);
- hv_store(hv, "connection_lost", 15, newSViv(server->connection_lost), 0);
+ (void) hv_store(hv, "connected", 9, newSViv(server->connected), 0);
+ (void) hv_store(hv, "connection_lost", 15, newSViv(server->connection_lost), 0);
stash = gv_stashpv("Irssi::Rawlog", 0);
- hv_store(hv, "rawlog", 6, sv_bless(newRV_noinc(newSViv((IV)server->rawlog)), stash), 0);
+ (void) hv_store(hv, "rawlog", 6, sv_bless(newRV_noinc(newSViv((IV)server->rawlog)), stash), 0);
- hv_store(hv, "version", 7, new_pv(server->version), 0);
- hv_store(hv, "away_reason", 11, new_pv(server->away_reason), 0);
- hv_store(hv, "last_invite", 11, new_pv(server->last_invite), 0);
- hv_store(hv, "server_operator", 15, newSViv(server->server_operator), 0);
- hv_store(hv, "usermode_away", 13, newSViv(server->usermode_away), 0);
- hv_store(hv, "banned", 6, newSViv(server->banned), 0);
+ (void) hv_store(hv, "version", 7, new_pv(server->version), 0);
+ (void) hv_store(hv, "away_reason", 11, new_pv(server->away_reason), 0);
+ (void) hv_store(hv, "last_invite", 11, new_pv(server->last_invite), 0);
+ (void) hv_store(hv, "server_operator", 15, newSViv(server->server_operator), 0);
+ (void) hv_store(hv, "usermode_away", 13, newSViv(server->usermode_away), 0);
+ (void) hv_store(hv, "banned", 6, newSViv(server->banned), 0);
- hv_store(hv, "lag", 3, newSViv(server->lag), 0);
+ (void) hv_store(hv, "lag", 3, newSViv(server->lag), 0);
}
void perl_window_item_fill_hash(HV *hv, WI_ITEM_REC *item)
@@ -351,19 +349,21 @@ void perl_window_item_fill_hash(HV *hv, WI_ITEM_REC *item)
g_return_if_fail(item != NULL);
type = (char *) module_find_id_str("WINDOW ITEM TYPE", item->type);
- chat_type = (char *) chat_protocol_find_id(item->chat_type)->name;
- hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
+ if (item->chat_type) {
+ chat_type = (char *) chat_protocol_find_id(item->chat_type)->name;
+ (void) hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+ }
if (item->server != NULL) {
- hv_store(hv, "server", 6, iobject_bless(item->server), 0);
+ (void) hv_store(hv, "server", 6, iobject_bless(item->server), 0);
}
- hv_store(hv, "visible_name", 12, new_pv(item->visible_name), 0);
+ (void) hv_store(hv, "visible_name", 12, new_pv(item->visible_name), 0);
- hv_store(hv, "createtime", 10, newSViv(item->createtime), 0);
- hv_store(hv, "data_level", 10, newSViv(item->data_level), 0);
- hv_store(hv, "hilight_color", 13, new_pv(item->hilight_color), 0);
+ (void) hv_store(hv, "createtime", 10, newSViv(item->createtime), 0);
+ (void) hv_store(hv, "data_level", 10, newSViv(item->data_level), 0);
+ (void) hv_store(hv, "hilight_color", 13, new_pv(item->hilight_color), 0);
}
void perl_channel_fill_hash(HV *hv, CHANNEL_REC *channel)
@@ -374,26 +374,26 @@ void perl_channel_fill_hash(HV *hv, CHANNEL_REC *channel)
perl_window_item_fill_hash(hv, (WI_ITEM_REC *) channel);
if (channel->ownnick != NULL)
- hv_store(hv, "ownnick", 7, iobject_bless(channel->ownnick), 0);
+ (void) hv_store(hv, "ownnick", 7, iobject_bless(channel->ownnick), 0);
- hv_store(hv, "name", 4, new_pv(channel->name), 0);
- hv_store(hv, "topic", 5, new_pv(channel->topic), 0);
- hv_store(hv, "topic_by", 8, new_pv(channel->topic_by), 0);
- hv_store(hv, "topic_time", 10, newSViv(channel->topic_time), 0);
+ (void) hv_store(hv, "name", 4, new_pv(channel->name), 0);
+ (void) hv_store(hv, "topic", 5, new_pv(channel->topic), 0);
+ (void) hv_store(hv, "topic_by", 8, new_pv(channel->topic_by), 0);
+ (void) hv_store(hv, "topic_time", 10, newSViv(channel->topic_time), 0);
- hv_store(hv, "no_modes", 8, newSViv(channel->no_modes), 0);
- hv_store(hv, "mode", 4, new_pv(channel->mode), 0);
- hv_store(hv, "limit", 5, newSViv(channel->limit), 0);
- hv_store(hv, "key", 3, new_pv(channel->key), 0);
+ (void) hv_store(hv, "no_modes", 8, newSViv(channel->no_modes), 0);
+ (void) hv_store(hv, "mode", 4, new_pv(channel->mode), 0);
+ (void) hv_store(hv, "limit", 5, newSViv(channel->limit), 0);
+ (void) hv_store(hv, "key", 3, new_pv(channel->key), 0);
- hv_store(hv, "chanop", 6, newSViv(channel->chanop), 0);
- hv_store(hv, "names_got", 9, newSViv(channel->names_got), 0);
- hv_store(hv, "wholist", 7, newSViv(channel->wholist), 0);
- hv_store(hv, "synced", 6, newSViv(channel->synced), 0);
+ (void) hv_store(hv, "chanop", 6, newSViv(channel->chanop), 0);
+ (void) hv_store(hv, "names_got", 9, newSViv(channel->names_got), 0);
+ (void) hv_store(hv, "wholist", 7, newSViv(channel->wholist), 0);
+ (void) hv_store(hv, "synced", 6, newSViv(channel->synced), 0);
- hv_store(hv, "joined", 6, newSViv(channel->joined), 0);
- hv_store(hv, "left", 4, newSViv(channel->left), 0);
- hv_store(hv, "kicked", 6, newSViv(channel->kicked), 0);
+ (void) hv_store(hv, "joined", 6, newSViv(channel->joined), 0);
+ (void) hv_store(hv, "left", 4, newSViv(channel->left), 0);
+ (void) hv_store(hv, "kicked", 6, newSViv(channel->kicked), 0);
}
void perl_query_fill_hash(HV *hv, QUERY_REC *query)
@@ -403,11 +403,11 @@ void perl_query_fill_hash(HV *hv, QUERY_REC *query)
perl_window_item_fill_hash(hv, (WI_ITEM_REC *) query);
- hv_store(hv, "name", 4, new_pv(query->name), 0);
- hv_store(hv, "last_unread_msg", 15, newSViv(query->last_unread_msg), 0);
- hv_store(hv, "address", 7, new_pv(query->address), 0);
- hv_store(hv, "server_tag", 10, new_pv(query->server_tag), 0);
- hv_store(hv, "unwanted", 8, newSViv(query->unwanted), 0);
+ (void) hv_store(hv, "name", 4, new_pv(query->name), 0);
+ (void) hv_store(hv, "last_unread_msg", 15, newSViv(query->last_unread_msg), 0);
+ (void) hv_store(hv, "address", 7, new_pv(query->address), 0);
+ (void) hv_store(hv, "server_tag", 10, new_pv(query->server_tag), 0);
+ (void) hv_store(hv, "unwanted", 8, newSViv(query->unwanted), 0);
}
void perl_nick_fill_hash(HV *hv, NICK_REC *nick)
@@ -420,31 +420,31 @@ void perl_nick_fill_hash(HV *hv, NICK_REC *nick)
type = "NICK";
chat_type = (char *) chat_protocol_find_id(nick->chat_type)->name;
- hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
+ (void) hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
- hv_store(hv, "nick", 4, new_pv(nick->nick), 0);
- hv_store(hv, "host", 4, new_pv(nick->host), 0);
- hv_store(hv, "realname", 8, new_pv(nick->realname), 0);
- hv_store(hv, "hops", 4, newSViv(nick->hops), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(nick->nick), 0);
+ (void) hv_store(hv, "host", 4, new_pv(nick->host), 0);
+ (void) hv_store(hv, "realname", 8, new_pv(nick->realname), 0);
+ (void) hv_store(hv, "hops", 4, newSViv(nick->hops), 0);
- hv_store(hv, "gone", 4, newSViv(nick->gone), 0);
- hv_store(hv, "serverop", 8, newSViv(nick->serverop), 0);
+ (void) hv_store(hv, "gone", 4, newSViv(nick->gone), 0);
+ (void) hv_store(hv, "serverop", 8, newSViv(nick->serverop), 0);
- hv_store(hv, "op", 2, newSViv(nick->op), 0);
- hv_store(hv, "halfop", 6, newSViv(nick->halfop), 0);
- hv_store(hv, "voice", 5, newSViv(nick->voice), 0);
- hv_store(hv, "other", 5, newSViv(nick->prefixes[0]), 0);
- hv_store(hv, "prefixes", 8, new_pv(nick->prefixes), 0);
+ (void) hv_store(hv, "op", 2, newSViv(nick->op), 0);
+ (void) hv_store(hv, "halfop", 6, newSViv(nick->halfop), 0);
+ (void) hv_store(hv, "voice", 5, newSViv(nick->voice), 0);
+ (void) hv_store(hv, "other", 5, newSViv(nick->prefixes[0]), 0);
+ (void) hv_store(hv, "prefixes", 8, new_pv(nick->prefixes), 0);
- hv_store(hv, "last_check", 10, newSViv(nick->last_check), 0);
- hv_store(hv, "send_massjoin", 13, newSViv(nick->send_massjoin), 0);
+ (void) hv_store(hv, "last_check", 10, newSViv(nick->last_check), 0);
+ (void) hv_store(hv, "send_massjoin", 13, newSViv(nick->send_massjoin), 0);
}
static void perl_command_fill_hash(HV *hv, COMMAND_REC *cmd)
{
- hv_store(hv, "category", 8, new_pv(cmd->category), 0);
- hv_store(hv, "cmd", 3, new_pv(cmd->cmd), 0);
+ (void) hv_store(hv, "category", 8, new_pv(cmd->category), 0);
+ (void) hv_store(hv, "cmd", 3, new_pv(cmd->cmd), 0);
}
static void perl_ignore_fill_hash(HV *hv, IGNORE_REC *ignore)
@@ -452,22 +452,22 @@ static void perl_ignore_fill_hash(HV *hv, IGNORE_REC *ignore)
AV *av;
char **tmp;
- hv_store(hv, "mask", 4, new_pv(ignore->mask), 0);
- hv_store(hv, "servertag", 9, new_pv(ignore->servertag), 0);
+ (void) hv_store(hv, "mask", 4, new_pv(ignore->mask), 0);
+ (void) hv_store(hv, "servertag", 9, new_pv(ignore->servertag), 0);
av = newAV();
if (ignore->channels != NULL) {
for (tmp = ignore->channels; *tmp != NULL; tmp++) {
av_push(av, new_pv(*tmp));
}
}
- hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0);
- hv_store(hv, "pattern", 7, new_pv(ignore->pattern), 0);
+ (void) hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0);
+ (void) hv_store(hv, "pattern", 7, new_pv(ignore->pattern), 0);
- hv_store(hv, "level", 5, newSViv(ignore->level), 0);
+ (void) hv_store(hv, "level", 5, newSViv(ignore->level), 0);
- hv_store(hv, "exception", 9, newSViv(ignore->exception), 0);
- hv_store(hv, "regexp", 6, newSViv(ignore->regexp), 0);
- hv_store(hv, "fullword", 8, newSViv(ignore->fullword), 0);
+ (void) hv_store(hv, "exception", 9, newSViv(ignore->exception), 0);
+ (void) hv_store(hv, "regexp", 6, newSViv(ignore->regexp), 0);
+ (void) hv_store(hv, "fullword", 8, newSViv(ignore->fullword), 0);
}
static void perl_log_fill_hash(HV *hv, LOG_REC *log)
@@ -475,33 +475,33 @@ static void perl_log_fill_hash(HV *hv, LOG_REC *log)
AV *av;
GSList *tmp;
- hv_store(hv, "fname", 5, new_pv(log->fname), 0);
- hv_store(hv, "real_fname", 10, new_pv(log->real_fname), 0);
- hv_store(hv, "opened", 6, newSViv(log->opened), 0);
- hv_store(hv, "level", 5, newSViv(log->level), 0);
- hv_store(hv, "last", 4, newSViv(log->last), 0);
- hv_store(hv, "autoopen", 8, newSViv(log->autoopen), 0);
- hv_store(hv, "failed", 6, newSViv(log->failed), 0);
- hv_store(hv, "temp", 4, newSViv(log->temp), 0);
+ (void) hv_store(hv, "fname", 5, new_pv(log->fname), 0);
+ (void) hv_store(hv, "real_fname", 10, new_pv(log->real_fname), 0);
+ (void) hv_store(hv, "opened", 6, newSViv(log->opened), 0);
+ (void) hv_store(hv, "level", 5, newSViv(log->level), 0);
+ (void) hv_store(hv, "last", 4, newSViv(log->last), 0);
+ (void) hv_store(hv, "autoopen", 8, newSViv(log->autoopen), 0);
+ (void) hv_store(hv, "failed", 6, newSViv(log->failed), 0);
+ (void) hv_store(hv, "temp", 4, newSViv(log->temp), 0);
av = newAV();
for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
av_push(av, plain_bless(tmp->data, "Irssi::Logitem"));
}
- hv_store(hv, "items", 5, newRV_noinc((SV*)av), 0);
+ (void) hv_store(hv, "items", 5, newRV_noinc((SV*)av), 0);
}
static void perl_log_item_fill_hash(HV *hv, LOG_ITEM_REC *item)
{
- hv_store(hv, "type", 4, newSViv(item->type), 0);
- hv_store(hv, "name", 4, new_pv(item->name), 0);
- hv_store(hv, "servertag", 9, new_pv(item->servertag), 0);
+ (void) hv_store(hv, "type", 4, newSViv(item->type), 0);
+ (void) hv_store(hv, "name", 4, new_pv(item->name), 0);
+ (void) hv_store(hv, "servertag", 9, new_pv(item->servertag), 0);
}
static void perl_rawlog_fill_hash(HV *hv, RAWLOG_REC *rawlog)
{
- hv_store(hv, "logging", 7, newSViv(rawlog->logging), 0);
- hv_store(hv, "nlines", 6, newSViv(rawlog->nlines), 0);
+ (void) hv_store(hv, "logging", 7, newSViv(rawlog->logging), 0);
+ (void) hv_store(hv, "nlines", 6, newSViv(rawlog->nlines), 0);
}
static void perl_reconnect_fill_hash(HV *hv, RECONNECT_REC *reconnect)
@@ -511,18 +511,18 @@ static void perl_reconnect_fill_hash(HV *hv, RECONNECT_REC *reconnect)
perl_connect_fill_hash(hv, reconnect->conn);
type = "RECONNECT";
- hv_store(hv, "type", 4, new_pv(type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "tag", 3, newSViv(reconnect->tag), 0);
- hv_store(hv, "next_connect", 12, newSViv(reconnect->next_connect), 0);
+ (void) hv_store(hv, "tag", 3, newSViv(reconnect->tag), 0);
+ (void) hv_store(hv, "next_connect", 12, newSViv(reconnect->next_connect), 0);
}
static void perl_script_fill_hash(HV *hv, PERL_SCRIPT_REC *script)
{
- hv_store(hv, "name", 4, new_pv(script->name), 0);
- hv_store(hv, "package", 7, new_pv(script->package), 0);
- hv_store(hv, "path", 4, new_pv(script->path), 0);
- hv_store(hv, "data", 4, new_pv(script->data), 0);
+ (void) hv_store(hv, "name", 4, new_pv(script->name), 0);
+ (void) hv_store(hv, "package", 7, new_pv(script->package), 0);
+ (void) hv_store(hv, "path", 4, new_pv(script->path), 0);
+ (void) hv_store(hv, "data", 4, new_pv(script->data), 0);
}
static void remove_newlines(char *str)
@@ -629,7 +629,7 @@ static void perl_register_protocol(CHAT_PROTOCOL_REC *rec)
perl_eval_pv(code, TRUE);
}
- pcode = g_strdup_printf(find_use_code,
+ pcode = g_strdup_printf(find_use_code,
settings_get_str("perl_use_lib"), name);
sv = perl_eval_pv(pcode, TRUE);
g_free(pcode);
diff --git a/src/perl/perl-core.c b/src/perl/perl-core.c
index eb1bddee..e4bde559 100644
--- a/src/perl/perl-core.c
+++ b/src/perl/perl-core.c
@@ -41,10 +41,10 @@ GSList *perl_scripts;
PerlInterpreter *my_perl;
static int print_script_errors;
-static char *perl_args[] = {"", "-e", "0"};
+static char *perl_args[] = {"", "-e", "0", NULL};
#define IS_PERL_SCRIPT(file) \
- (strlen(file) > 3 && strcmp(file+strlen(file)-3, ".pl") == 0)
+ (strlen(file) > 3 && g_strcmp0(file+strlen(file)-3, ".pl") == 0)
static void perl_script_destroy_package(PERL_SCRIPT_REC *script)
{
@@ -82,16 +82,18 @@ static void perl_script_destroy(PERL_SCRIPT_REC *script)
extern void boot_DynaLoader(pTHX_ CV* cv);
#if PERL_STATIC_LIBS == 1
-extern void boot_Irssi(CV *cv);
+extern void boot_Irssi(pTHX_ CV *cv);
XS(boot_Irssi_Core)
{
dXSARGS;
+ PERL_UNUSED_VAR(items);
irssi_callXS(boot_Irssi, cv, mark);
irssi_boot(Irc);
irssi_boot(UI);
irssi_boot(TextUI);
+ /* Make sure to keep this in line with perl_scripts_deinit below. */
XSRETURN_YES;
}
#endif
@@ -121,9 +123,9 @@ void perl_scripts_init(void)
my_perl = perl_alloc();
perl_construct(my_perl);
- perl_parse(my_perl, xs_init, G_N_ELEMENTS(perl_args), perl_args, NULL);
+ perl_parse(my_perl, xs_init, G_N_ELEMENTS(perl_args)-1, perl_args, NULL);
#if PERL_STATIC_LIBS == 1
- perl_eval_pv("Irssi::Core::boot_Irssi_Core();", TRUE);
+ perl_eval_pv("Irssi::Core::->boot_Irssi_Core(0.9);", TRUE);
#endif
perl_common_start();
@@ -155,6 +157,17 @@ void perl_scripts_deinit(void)
/* Unload all perl libraries loaded with dynaloader */
perl_eval_pv("foreach my $lib (@DynaLoader::dl_modules) { if ($lib =~ /^Irssi\\b/) { $lib .= '::deinit();'; eval $lib; } }", TRUE);
+#if PERL_STATIC_LIBS == 1
+ /* If perl is statically built we should manually deinit the modules
+ which are booted in boot_Irssi_Core above */
+ perl_eval_pv("foreach my $lib (qw("
+ "Irssi" " "
+ "Irssi::Irc" " "
+ "Irssi::UI" " "
+ "Irssi::TextUI"
+ ")) { eval $lib . '::deinit();'; }", TRUE);
+#endif
+
/* We could unload all libraries .. but this crashes with some
libraries, probably because we don't call some deinit function..
Anyway, this would free some memory with /SCRIPT RESET, but it
@@ -314,7 +327,7 @@ PERL_SCRIPT_REC *perl_script_find(const char *name)
for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
PERL_SCRIPT_REC *rec = tmp->data;
- if (strcmp(rec->name, name) == 0)
+ if (g_strcmp0(rec->name, name) == 0)
return rec;
}
@@ -331,7 +344,7 @@ PERL_SCRIPT_REC *perl_script_find_package(const char *package)
for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
PERL_SCRIPT_REC *rec = tmp->data;
- if (strcmp(rec->package, package) == 0)
+ if (g_strcmp0(rec->package, package) == 0)
return rec;
}
@@ -380,7 +393,7 @@ int perl_get_api_version(void)
return IRSSI_PERL_API_VERSION;
}
-static void perl_scripts_autorun(void)
+void perl_scripts_autorun(void)
{
DIR *dirp;
struct dirent *dp;
@@ -466,3 +479,8 @@ void perl_core_deinit(void)
signal_remove("script error", (SIGNAL_FUNC) sig_script_error);
PERL_SYS_TERM();
}
+
+void perl_core_abicheck(int *version)
+{
+ *version = IRSSI_ABI_VERSION;
+}
diff --git a/src/perl/perl-core.h b/src/perl/perl-core.h
index b451cc5c..7390a6fd 100644
--- a/src/perl/perl-core.h
+++ b/src/perl/perl-core.h
@@ -16,6 +16,8 @@ extern GSList *perl_scripts;
void perl_scripts_init(void);
/* Destroy all perl scripts and deinitialize perl interpreter */
void perl_scripts_deinit(void);
+/* Load all the scripts in the autorun/ folder */
+void perl_scripts_autorun(void);
/* Load a perl script, path must be a full path. */
PERL_SCRIPT_REC *perl_script_load_file(const char *path);
diff --git a/src/perl/perl-fe.c b/src/perl/perl-fe.c
index 8e8469e9..396c80b7 100644
--- a/src/perl/perl-fe.c
+++ b/src/perl/perl-fe.c
@@ -119,8 +119,20 @@ static void cmd_script_unload(const char *data)
static void cmd_script_reset(const char *data)
{
+ void *free_arg;
+ GHashTable *optlist;
+
+ if (!cmd_get_params(data, &free_arg, 0 | PARAM_FLAG_OPTIONS,
+ "script reset", &optlist))
+ return;
+
perl_scripts_deinit();
perl_scripts_init();
+
+ if (g_hash_table_lookup(optlist, "autorun") != NULL)
+ perl_scripts_autorun();
+
+ cmd_params_free(free_arg);
}
static void cmd_script_list(void)
@@ -170,7 +182,7 @@ static void cmd_load(const char *data, SERVER_REC *server, void *item)
return;
len = strlen(rootmodule);
- if (len > 3 && strcmp(rootmodule + len - 3, ".pl") == 0) {
+ if (len > 3 && g_strcmp0(rootmodule + len - 3, ".pl") == 0) {
/* make /LOAD script.pl work as expected */
signal_stop();
cmd_script_load(data);
@@ -251,6 +263,7 @@ void fe_perl_init(void)
command_bind("script list", NULL, (SIGNAL_FUNC) cmd_script_list);
command_bind("load", NULL, (SIGNAL_FUNC) cmd_load);
command_set_options("script exec", "permanent");
+ command_set_options("script reset", "autorun");
signal_add("script error", (SIGNAL_FUNC) sig_script_error);
signal_add("complete command script load", (SIGNAL_FUNC) sig_complete_load);
@@ -278,3 +291,8 @@ void fe_perl_deinit(void)
perl_core_print_script_error(TRUE);
}
+
+void fe_perl_abicheck(int *version)
+{
+ *version = IRSSI_ABI_VERSION;
+}
diff --git a/src/perl/perl-signals.c b/src/perl/perl-signals.c
index be4a9a5d..8f993660 100644
--- a/src/perl/perl-signals.c
+++ b/src/perl/perl-signals.c
@@ -99,14 +99,14 @@ void perl_signal_args_to_c(
if (!SvOK(arg)) {
c_arg = NULL;
- } else if (strcmp(rec->args[n], "string") == 0) {
+ } else if (g_strcmp0(rec->args[n], "string") == 0) {
c_arg = SvPV_nolen(arg);
- } else if (strcmp(rec->args[n], "int") == 0) {
+ } else if (g_strcmp0(rec->args[n], "int") == 0) {
c_arg = (void *)SvIV(arg);
- } else if (strcmp(rec->args[n], "ulongptr") == 0) {
+ } else if (g_strcmp0(rec->args[n], "ulongptr") == 0) {
saved_args[n].v_ulong = SvUV(arg);
c_arg = &saved_args[n].v_ulong;
- } else if (strcmp(rec->args[n], "intptr") == 0) {
+ } else if (g_strcmp0(rec->args[n], "intptr") == 0) {
saved_args[n].v_int = SvIV(SvRV(arg));
c_arg = &saved_args[n].v_int;
} else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
@@ -122,7 +122,7 @@ void perl_signal_args_to_c(
}
av = (AV *)t;
- is_str = strcmp(rec->args[n]+9, "char*") == 0;
+ is_str = g_strcmp0(rec->args[n]+9, "char*") == 0;
gl = NULL;
count = av_len(av) + 1;
@@ -181,7 +181,7 @@ void perl_signal_args_to_c(
continue;
}
- if (strcmp(rec->args[n], "intptr") == 0) {
+ if (g_strcmp0(rec->args[n], "intptr") == 0) {
SV *t = SvRV(arg);
SvIOK_only(t);
SvIV_set(t, saved_args[n].v_int);
@@ -192,8 +192,8 @@ void perl_signal_args_to_c(
AV *av;
GList *gl, *tmp;
- is_iobject = strcmp(rec->args[n]+9, "iobject") == 0;
- is_str = strcmp(rec->args[n]+9, "char*") == 0;
+ is_iobject = g_strcmp0(rec->args[n]+9, "iobject") == 0;
+ is_str = g_strcmp0(rec->args[n]+9, "char*") == 0;
av = (AV *)SvRV(arg);
av_clear(av);
@@ -245,8 +245,8 @@ static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
GList *tmp, **ptr;
int is_iobject, is_str;
- is_iobject = strcmp(rec->args[n]+9, "iobject") == 0;
- is_str = strcmp(rec->args[n]+9, "char*") == 0;
+ is_iobject = g_strcmp0(rec->args[n]+9, "iobject") == 0;
+ is_str = g_strcmp0(rec->args[n]+9, "char*") == 0;
av = newAV();
ptr = arg;
@@ -258,22 +258,22 @@ static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
}
saved_args[n] = perlarg = newRV_noinc((SV *) av);
- } else if (strcmp(rec->args[n], "int") == 0)
+ } else if (g_strcmp0(rec->args[n], "int") == 0)
perlarg = newSViv((IV)arg);
else if (arg == NULL)
perlarg = &PL_sv_undef;
- else if (strcmp(rec->args[n], "string") == 0)
+ else if (g_strcmp0(rec->args[n], "string") == 0)
perlarg = new_pv(arg);
- else if (strcmp(rec->args[n], "ulongptr") == 0)
+ else if (g_strcmp0(rec->args[n], "ulongptr") == 0)
perlarg = newSViv(*(unsigned long *) arg);
- else if (strcmp(rec->args[n], "intptr") == 0)
+ else if (g_strcmp0(rec->args[n], "intptr") == 0)
saved_args[n] = perlarg = newRV_noinc(newSViv(*(int *) arg));
else if (strncmp(rec->args[n], "gslist_", 7) == 0) {
/* linked list - push as AV */
GSList *tmp;
int is_iobject;
- is_iobject = strcmp(rec->args[n]+7, "iobject") == 0;
+ is_iobject = g_strcmp0(rec->args[n]+7, "iobject") == 0;
av = newAV();
for (tmp = arg; tmp != NULL; tmp = tmp->next) {
sv = is_iobject ? iobject_bless((SERVER_REC *) tmp->data) :
@@ -282,12 +282,12 @@ static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
}
perlarg = newRV_noinc((SV *) av);
- } else if (strcmp(rec->args[n], "iobject") == 0) {
+ } else if (g_strcmp0(rec->args[n], "iobject") == 0) {
/* "irssi object" - any struct that has
"int type; int chat_type" as it's first
variables (server, channel, ..) */
perlarg = iobject_bless((SERVER_REC *) arg);
- } else if (strcmp(rec->args[n], "siobject") == 0) {
+ } else if (g_strcmp0(rec->args[n], "siobject") == 0) {
/* "simple irssi object" - any struct that has
int type; as it's first variable (dcc) */
perlarg = simple_iobject_bless((SERVER_REC *) arg);
@@ -317,7 +317,7 @@ static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
if (saved_args[n] == NULL)
continue;
- if (strcmp(rec->args[n], "intptr") == 0) {
+ if (g_strcmp0(rec->args[n], "intptr") == 0) {
int *val = arg;
*val = SvIV(SvRV(saved_args[n]));
} else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
@@ -338,7 +338,7 @@ static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
out = g_list_append(out, val);
}
- if (strcmp(rec->args[n]+9, "char*") == 0)
+ if (g_strcmp0(rec->args[n]+9, "char*") == 0)
g_list_foreach(*ret, (GFunc) g_free, NULL);
g_list_free(*ret);
*ret = out;
@@ -433,8 +433,9 @@ static void perl_signal_remove_list_one(GSList **siglist, PERL_SIGNAL_REC *rec)
}
#define sv_func_cmp(f1, f2) \
- (f1 == f2 || (SvPOK(f1) && SvPOK(f2) && \
- strcmp(SvPV_nolen(f1), SvPV_nolen(f2)) == 0))
+ ((SvROK(f1) && SvROK(f2) && SvRV(f1) == SvRV(f2)) || \
+ (SvPOK(f1) && SvPOK(f2) && \
+ g_strcmp0(SvPV_nolen(f1), SvPV_nolen(f2)) == 0))
static void perl_signal_remove_list(GSList **list, SV *func)
{
@@ -475,7 +476,7 @@ void perl_command_bind_to(const char *cmd, const char *category,
g_free(signal);
}
-void perl_command_runsub(const char *cmd, const char *data,
+void perl_command_runsub(const char *cmd, const char *data,
SERVER_REC *server, WI_ITEM_REC *item)
{
command_runsub(cmd, data, server, item);
diff --git a/src/perl/perl-signals.h b/src/perl/perl-signals.h
index e2c3db61..78a95bed 100644
--- a/src/perl/perl-signals.h
+++ b/src/perl/perl-signals.h
@@ -21,7 +21,7 @@ void perl_command_bind_to(const char *cmd, const char *category,
void perl_command_unbind(const char *cmd, SV *func);
-void perl_command_runsub(const char *cmd, const char *data,
+void perl_command_runsub(const char *cmd, const char *data,
SERVER_REC *server, WI_ITEM_REC *item);
void perl_signal_register(const char *signal, const char **args);
diff --git a/src/perl/textui/Makefile.PL.in b/src/perl/textui/Makefile.PL.in
index 9e80274b..ffdda21a 100644
--- a/src/perl/textui/Makefile.PL.in
+++ b/src/perl/textui/Makefile.PL.in
@@ -1,4 +1,4 @@
-use ExtUtils::MakeMaker;
+use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "@top_srcdir@/src/perl/Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi::TextUI',
'LIBS' => '',
diff --git a/src/perl/textui/Statusbar.xs b/src/perl/textui/Statusbar.xs
index 620fad9a..8b0e5f65 100644
--- a/src/perl/textui/Statusbar.xs
+++ b/src/perl/textui/Statusbar.xs
@@ -77,7 +77,10 @@ static void perl_statusbar_event(char *function, SBAR_ITEM_REC *item,
/* make sure we don't get back here */
script_unregister_statusbars(script);
}
- signal_emit("script error", 2, script, SvPV_nolen(ERRSV));
+
+ char *error = g_strdup(SvPV_nolen(ERRSV));
+ signal_emit("script error", 2, script, error);
+ g_free(error);
} else {
/* min_size and max_size can be changed, move them to SBAR_ITEM_REC */
hv = hvref(item_sv);
@@ -160,5 +163,5 @@ CODE:
*str == '\0' ? NULL : str,
data, escape_vars);
hv = hvref(ST(0));
- hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
- hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
+ (void) hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
+ (void) hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
diff --git a/src/perl/textui/TextUI.xs b/src/perl/textui/TextUI.xs
index ae986aaf..5d2c8a7f 100644
--- a/src/perl/textui/TextUI.xs
+++ b/src/perl/textui/TextUI.xs
@@ -8,72 +8,72 @@ static int initialized = FALSE;
static void perl_main_window_fill_hash(HV *hv, MAIN_WINDOW_REC *window)
{
- hv_store(hv, "active", 6, plain_bless(window->active, "Irssi::UI::Window"), 0);
+ (void) hv_store(hv, "active", 6, plain_bless(window->active, "Irssi::UI::Window"), 0);
- hv_store(hv, "first_line", 10, newSViv(window->first_line), 0);
- hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
- hv_store(hv, "width", 5, newSViv(window->width), 0);
- hv_store(hv, "height", 6, newSViv(window->height), 0);
+ (void) hv_store(hv, "first_line", 10, newSViv(window->first_line), 0);
+ (void) hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
+ (void) hv_store(hv, "width", 5, newSViv(window->width), 0);
+ (void) hv_store(hv, "height", 6, newSViv(window->height), 0);
- hv_store(hv, "statusbar_lines", 15, newSViv(window->statusbar_lines), 0);
+ (void) hv_store(hv, "statusbar_lines", 15, newSViv(window->statusbar_lines), 0);
}
static void perl_text_buffer_fill_hash(HV *hv, TEXT_BUFFER_REC *buffer)
{
- hv_store(hv, "first_line", 10, plain_bless(buffer->first_line, "Irssi::TextUI::Line"), 0);
- hv_store(hv, "lines_count", 11, newSViv(buffer->lines_count), 0);
- hv_store(hv, "cur_line", 8, plain_bless(buffer->cur_line, "Irssi::TextUI::Line"), 0);
- hv_store(hv, "last_eol", 8, newSViv(buffer->last_eol), 0);
+ (void) hv_store(hv, "first_line", 10, plain_bless(buffer->first_line, "Irssi::TextUI::Line"), 0);
+ (void) hv_store(hv, "lines_count", 11, newSViv(buffer->lines_count), 0);
+ (void) hv_store(hv, "cur_line", 8, plain_bless(buffer->cur_line, "Irssi::TextUI::Line"), 0);
+ (void) hv_store(hv, "last_eol", 8, newSViv(buffer->last_eol), 0);
}
static void perl_text_buffer_view_fill_hash(HV *hv, TEXT_BUFFER_VIEW_REC *view)
{
- hv_store(hv, "buffer", 6, plain_bless(view->buffer, "Irssi::TextUI::TextBuffer"), 0);
- hv_store(hv, "width", 5, newSViv(view->width), 0);
- hv_store(hv, "height", 6, newSViv(view->height), 0);
+ (void) hv_store(hv, "buffer", 6, plain_bless(view->buffer, "Irssi::TextUI::TextBuffer"), 0);
+ (void) hv_store(hv, "width", 5, newSViv(view->width), 0);
+ (void) hv_store(hv, "height", 6, newSViv(view->height), 0);
- hv_store(hv, "default_indent", 14, newSViv(view->default_indent), 0);
- hv_store(hv, "longword_noindent", 17, newSViv(view->longword_noindent), 0);
- hv_store(hv, "scroll", 6, newSViv(view->scroll), 0);
+ (void) hv_store(hv, "default_indent", 14, newSViv(view->default_indent), 0);
+ (void) hv_store(hv, "longword_noindent", 17, newSViv(view->longword_noindent), 0);
+ (void) hv_store(hv, "scroll", 6, newSViv(view->scroll), 0);
- hv_store(hv, "ypos", 4, newSViv(view->ypos), 0);
+ (void) hv_store(hv, "ypos", 4, newSViv(view->ypos), 0);
- hv_store(hv, "startline", 9, plain_bless(view->startline, "Irssi::TextUI::Line"), 0);
- hv_store(hv, "subline", 7, newSViv(view->subline), 0);
+ (void) hv_store(hv, "startline", 9, plain_bless(view->startline, "Irssi::TextUI::Line"), 0);
+ (void) hv_store(hv, "subline", 7, newSViv(view->subline), 0);
- hv_store(hv, "bottom_startline", 16, plain_bless(view->bottom_startline, "Irssi::TextUI::Line"), 0);
- hv_store(hv, "bottom_subline", 14, newSViv(view->bottom_subline), 0);
+ (void) hv_store(hv, "bottom_startline", 16, plain_bless(view->bottom_startline, "Irssi::TextUI::Line"), 0);
+ (void) hv_store(hv, "bottom_subline", 14, newSViv(view->bottom_subline), 0);
- hv_store(hv, "empty_linecount", 15, newSViv(view->empty_linecount), 0);
- hv_store(hv, "bottom", 6, newSViv(view->bottom), 0);
+ (void) hv_store(hv, "empty_linecount", 15, newSViv(view->empty_linecount), 0);
+ (void) hv_store(hv, "bottom", 6, newSViv(view->bottom), 0);
}
static void perl_line_fill_hash(HV *hv, LINE_REC *line)
{
- hv_store(hv, "info", 4, plain_bless(&line->info, "Irssi::TextUI::LineInfo"), 0);
+ (void) hv_store(hv, "info", 4, plain_bless(&line->info, "Irssi::TextUI::LineInfo"), 0);
}
static void perl_line_cache_fill_hash(HV *hv, LINE_CACHE_REC *cache)
{
- hv_store(hv, "last_access", 11, newSViv(cache->last_access), 0);
- hv_store(hv, "count", 5, newSViv(cache->count), 0);
+ (void) hv_store(hv, "last_access", 11, newSViv(cache->last_access), 0);
+ (void) hv_store(hv, "count", 5, newSViv(cache->count), 0);
/*LINE_CACHE_SUB_REC lines[1];*/
}
static void perl_line_info_fill_hash(HV *hv, LINE_INFO_REC *info)
{
- hv_store(hv, "level", 5, newSViv(info->level), 0);
- hv_store(hv, "time", 4, newSViv(info->time), 0);
+ (void) hv_store(hv, "level", 5, newSViv(info->level), 0);
+ (void) hv_store(hv, "time", 4, newSViv(info->time), 0);
}
static void perl_statusbar_item_fill_hash(HV *hv, SBAR_ITEM_REC *item)
{
- hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
- hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
- hv_store(hv, "xpos", 4, newSViv(item->xpos), 0);
- hv_store(hv, "size", 4, newSViv(item->size), 0);
+ (void) hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
+ (void) hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
+ (void) hv_store(hv, "xpos", 4, newSViv(item->xpos), 0);
+ (void) hv_store(hv, "size", 4, newSViv(item->size), 0);
if (item->bar->parent_window != NULL)
- hv_store(hv, "window", 6, plain_bless(item->bar->parent_window->active, "Irssi::UI::Window"), 0);
+ (void) hv_store(hv, "window", 6, plain_bless(item->bar->parent_window->active, "Irssi::UI::Window"), 0);
}
static PLAIN_OBJECT_INIT_REC textui_plains[] = {
diff --git a/src/perl/ui/Makefile.PL.in b/src/perl/ui/Makefile.PL.in
index a349918e..ceed51c3 100644
--- a/src/perl/ui/Makefile.PL.in
+++ b/src/perl/ui/Makefile.PL.in
@@ -1,4 +1,4 @@
-use ExtUtils::MakeMaker;
+use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "@top_srcdir@/src/perl/Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi::UI',
'LIBS' => '',
diff --git a/src/perl/ui/UI.xs b/src/perl/ui/UI.xs
index 2e32762c..5ac3da4e 100644
--- a/src/perl/ui/UI.xs
+++ b/src/perl/ui/UI.xs
@@ -8,60 +8,73 @@ static int initialized = FALSE;
static void perl_process_fill_hash(HV *hv, PROCESS_REC *process)
{
- hv_store(hv, "id", 2, newSViv(process->id), 0);
- hv_store(hv, "name", 4, new_pv(process->name), 0);
- hv_store(hv, "args", 4, new_pv(process->args), 0);
+ (void) hv_store(hv, "id", 2, newSViv(process->id), 0);
+ (void) hv_store(hv, "name", 4, new_pv(process->name), 0);
+ (void) hv_store(hv, "args", 4, new_pv(process->args), 0);
- hv_store(hv, "pid", 3, newSViv(process->pid), 0);
- hv_store(hv, "target", 6, new_pv(process->target), 0);
+ (void) hv_store(hv, "pid", 3, newSViv(process->pid), 0);
+ (void) hv_store(hv, "target", 6, new_pv(process->target), 0);
if (process->target_win != NULL) {
- hv_store(hv, "target_win", 10,
+ (void) hv_store(hv, "target_win", 10,
plain_bless(process->target_win, "Irssi::UI::Window"), 0);
}
- hv_store(hv, "shell", 5, newSViv(process->shell), 0);
- hv_store(hv, "notice", 6, newSViv(process->notice), 0);
- hv_store(hv, "silent", 6, newSViv(process->silent), 0);
+ (void) hv_store(hv, "shell", 5, newSViv(process->shell), 0);
+ (void) hv_store(hv, "notice", 6, newSViv(process->notice), 0);
+ (void) hv_store(hv, "silent", 6, newSViv(process->silent), 0);
}
static void perl_window_fill_hash(HV *hv, WINDOW_REC *window)
{
- hv_store(hv, "refnum", 6, newSViv(window->refnum), 0);
- hv_store(hv, "name", 4, new_pv(window->name), 0);
- hv_store(hv, "history_name", 12, new_pv(window->history_name), 0);
+ (void) hv_store(hv, "refnum", 6, newSViv(window->refnum), 0);
+ (void) hv_store(hv, "name", 4, new_pv(window->name), 0);
+ (void) hv_store(hv, "history_name", 12, new_pv(window->history_name), 0);
- hv_store(hv, "width", 5, newSViv(window->width), 0);
- hv_store(hv, "height", 6, newSViv(window->height), 0);
+ (void) hv_store(hv, "width", 5, newSViv(window->width), 0);
+ (void) hv_store(hv, "height", 6, newSViv(window->height), 0);
if (window->active)
- hv_store(hv, "active", 6, iobject_bless(window->active), 0);
+ (void) hv_store(hv, "active", 6, iobject_bless(window->active), 0);
if (window->active_server)
- hv_store(hv, "active_server", 13, iobject_bless(window->active_server), 0);
+ (void) hv_store(hv, "active_server", 13, iobject_bless(window->active_server), 0);
- hv_store(hv, "servertag", 9, new_pv(window->servertag), 0);
- hv_store(hv, "level", 5, newSViv(window->level), 0);
+ (void) hv_store(hv, "servertag", 9, new_pv(window->servertag), 0);
+ (void) hv_store(hv, "level", 5, newSViv(window->level), 0);
- hv_store(hv, "immortal", 8, newSViv(window->immortal), 0);
- hv_store(hv, "sticky_refnum", 13, newSViv(window->sticky_refnum), 0);
+ (void) hv_store(hv, "immortal", 8, newSViv(window->immortal), 0);
+ (void) hv_store(hv, "sticky_refnum", 13, newSViv(window->sticky_refnum), 0);
- hv_store(hv, "data_level", 10, newSViv(window->data_level), 0);
- hv_store(hv, "hilight_color", 13, new_pv(window->hilight_color), 0);
+ (void) hv_store(hv, "data_level", 10, newSViv(window->data_level), 0);
+ (void) hv_store(hv, "hilight_color", 13, new_pv(window->hilight_color), 0);
- hv_store(hv, "last_timestamp", 14, newSViv(window->last_timestamp), 0);
- hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
+ (void) hv_store(hv, "last_timestamp", 14, newSViv(window->last_timestamp), 0);
+ (void) hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
- hv_store(hv, "theme", 5, plain_bless(window->theme, "Irssi::UI::Theme"), 0);
- hv_store(hv, "theme_name", 10, new_pv(window->theme_name), 0);
+ (void) hv_store(hv, "theme", 5, plain_bless(window->theme, "Irssi::UI::Theme"), 0);
+ (void) hv_store(hv, "theme_name", 10, new_pv(window->theme_name), 0);
}
static void perl_text_dest_fill_hash(HV *hv, TEXT_DEST_REC *dest)
{
- hv_store(hv, "window", 6, plain_bless(dest->window, "Irssi::UI::Window"), 0);
- hv_store(hv, "server", 6, iobject_bless(dest->server), 0);
- hv_store(hv, "target", 6, new_pv(dest->target), 0);
- hv_store(hv, "level", 5, newSViv(dest->level), 0);
+ (void) hv_store(hv, "window", 6, plain_bless(dest->window, "Irssi::UI::Window"), 0);
+ (void) hv_store(hv, "server", 6, iobject_bless(dest->server), 0);
+ (void) hv_store(hv, "target", 6, new_pv(dest->target), 0);
+ (void) hv_store(hv, "level", 5, newSViv(dest->level), 0);
- hv_store(hv, "hilight_priority", 16, newSViv(dest->hilight_priority), 0);
- hv_store(hv, "hilight_color", 13, new_pv(dest->hilight_color), 0);
+ (void) hv_store(hv, "hilight_priority", 16, newSViv(dest->hilight_priority), 0);
+ (void) hv_store(hv, "hilight_color", 13, new_pv(dest->hilight_color), 0);
+}
+
+static void perl_exec_fill_hash(HV *hv, EXEC_WI_REC *item)
+{
+ g_return_if_fail(hv != NULL);
+ g_return_if_fail(item != NULL);
+
+ perl_window_item_fill_hash(hv, (WI_ITEM_REC *) item);
+ /* we don't bless to Process here to avoid infinite recursion
+ in the simplistic script binding */
+ if (item->process != NULL) {
+ (void) hv_store(hv, "process_id", 10, newSViv(item->process->id), 0);
+ }
}
static PLAIN_OBJECT_INIT_REC fe_plains[] = {
@@ -94,6 +107,10 @@ CODE:
initialized = TRUE;
irssi_add_plains(fe_plains);
+ /* window items: fe-exec */
+ irssi_add_object(module_get_uniq_id_str("WINDOW ITEM TYPE", "EXEC"),
+ 0, "Irssi::UI::Exec",
+ (PERL_OBJECT_FUNC) perl_exec_fill_hash);
perl_themes_init();
void