Merge pull request #2 from irssi/master

Merge with head fork.
This commit is contained in:
Samuel Hoffman 2015-10-21 21:25:52 -04:00
commit 4e74fa7284
83 changed files with 1080 additions and 394 deletions

View file

@ -1,3 +1,4 @@
sudo: false
language: perl
perl:
- "5.20-shrplib"
@ -7,28 +8,19 @@ env:
- CC=clang
- CC=gcc
addons:
apt:
packages:
- libperl-dev
- elinks
before_install:
- sudo apt-get update -qq
- perl -V
- sudo apt-get build-dep -qq irssi
- sudo apt-get install -qq lynx
install: true
script:
- ./autogen.sh --with-proxy --with-bot --with-perl=module
- ./autogen.sh --with-proxy --with-bot --with-perl=module --prefix=$HOME/irssi-build
- cat config.log
- make
- sudo make install
notifications:
irc:
channels:
- "irc.freenode.net#irssi"
template:
- "%{repository} (%{commit}: %{author}): %{message}"
- "Build details : %{build_url}"
on_success: always
on_failure: always
use_notice: true
skip_join: true
- make install

View file

@ -94,6 +94,10 @@ things that can go wrong:
- If configure complains that it doesn't find some perl stuff, you're
probably missing libperl.so or libperl.a. In debian, you'll need to do
apt-get install libperl-dev
- For unprivileged home directory installations, using the local::lib CPAN
module is recommended. Read its docs, bootstrap it if needed, ensure that
the environment variables are set before running the configure script, and
append "--with-perl-lib=site" to the configure parameters to use it.
You can verify that the perl module is loaded and working with "/LOAD"
command. It should print something like:

2
NEWS
View file

@ -1,4 +1,6 @@
v0.8.18-head 2014-XX-YY The Irssi team <staff@irssi.org>
+ Garbage Collection support has been removed. This will hardly have any
effect for anyone given that it has been unsupported for several years.
+ Disable SSLv3 due to the POODLE vulnerability.
+ Try to split long lines on spaces to avoid words being splitted. Adds
a new option: 'split_line_on_space' which defaults to on.

View file

@ -3,9 +3,6 @@
#undef HAVE_SOCKS_H
#undef HAVE_STATIC_PERL
#undef HAVE_GMODULE
#undef HAVE_GC_H
#undef HAVE_GC_GC_H
#undef USE_GC
/* macros/curses checks */
#undef HAS_CURSES

View file

@ -7,6 +7,8 @@ AC_CONFIG_HEADERS([irssi-config.h])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([1.9 no-define foreign])
AM_SILENT_RULES([yes])
AM_MAINTAINER_MODE
AC_PROG_CC
@ -83,15 +85,6 @@ if test "x$prefix" != "xNONE"; then
perl_prefix_note=yes
fi
AC_ARG_WITH(gc,
[ --with-gc Use garbage collector],
if test x$withval = xno; then
want_gc=no
else
want_gc=yes
fi,
want_gc=no)
AC_ARG_WITH(perl-staticlib,
[ --with-perl-staticlib Specify that we want to link perl libraries
statically in irssi, default is no],
@ -270,7 +263,7 @@ for try in 1 2; do
echo "*** trying without -lgmodule"
glib_modules=
fi
AM_PATH_GLIB_2_0(2.6.0,,, $glib_modules)
AM_PATH_GLIB_2_0(2.16.0,,, $glib_modules)
if test "$GLIB_LIBS"; then
if test $glib_modules = gmodule; then
AC_DEFINE(HAVE_GMODULE)
@ -322,28 +315,6 @@ if test "$enable_ssl" = "yes"; then
fi
fi
dnl **
dnl ** Garbage Collector
dnl **
have_gc=no
if test "x$want_gc" = xyes; then
AC_CHECK_LIB(gc, GC_malloc, [
AC_CHECK_HEADER(gc/gc.h, [
AC_DEFINE(HAVE_GC_GC_H)
AC_DEFINE(USE_GC)
LIBS="$LIBS -lgc"
have_gc=yes
], [
AC_CHECK_HEADER(gc.h, [
AC_DEFINE(HAVE_GC_H)
AC_DEFINE(USE_GC)
LIBS="$LIBS -lgc"
have_gc=yes
])
])
])
fi
dnl **
dnl ** curses checks
dnl **
@ -782,7 +753,6 @@ if test "x$have_openssl" = "xno" -a "x$enable_ssl" = "xyes"; then
fi
fi
echo "Building with 64bit DCC support .. : $offt_64bit"
echo "Building with garbage collector .. : $have_gc"
echo "Building with DANE support ....... : $have_dane"
echo "Building with true color support.. : $want_truecolor"

View file

@ -1,5 +1,3 @@
docdir = $(datadir)/doc/irssi
man_MANS = \
irssi.1

View file

@ -0,0 +1,14 @@
@SYNTAX:irssiproxy@
%9Description:%9
Displays the list of clients connected to irssiproxy.
%9Examples:%9
/IRSSIPROXY
/IRSSIPROXY STATUS
%9See also:%9 LOAD PROXY, SET irssiproxy

View file

@ -32,6 +32,12 @@
additional commands to the server.
-cmdmax: Specifies the maximum number of commands to perform before
starting the internal flood protection.
-sasl_mechanism Specifies the mechanism to use for the SASL authentication.
At the moment irssi only supports the 'plain' and the
'external' mechanisms.
-sasl_username Specifies the username to use during the SASL authentication.
-sasl_password Specifies the password to use during the SASL authentication.
The name of the network to add, edit or remove; if no parameter is given,
the list of networks will be displayed.

View file

@ -5,49 +5,49 @@
%9Parameters:%9
LOG: Modifies the logging status.
LOGFILE: Modifies the location to the log file.
NEW: Creates a new window.
CLOSE: Closes a window.
REFNUM: Go to the window with the given number.
GOTO: Go to the window with the given nickname, channel or number.
NEXT: Go to the next window.
LAST: Go to the last window.
PREVIOUS: Go to the previous window.
LEVEL: Modifies the text levels to display in the window.
IMMORTAL: Modifies the window mortality status.
SERVER: Set the active server of the window.
ITEM PREV: Go to the previous item in the window.
ITEM NEXT: Go to the next item in the window.
ITEM GOTO: Go to the specified nickname, channel or window item number.
ITEM MOVE: Move the active window item to another window.
NUMBER: Move the active window to another position.
NAME: Give the window a name.
HISTORY: Clears the window history buffer.
MOVE PREV: Move the window down.
MOVE NEXT: Move the window up.
MOVE FIRST: Move the window to the first position.
MOVE LAST: Move the window to the last position.
MOVE: Move the window.
LIST: List all the windows.
THEME: Applies a theme to the windows.
GROW: Increase the window size when using split windows.
SHRINK: Decrease the window size when using split windows.
SIZE: Modify the window size when using split windows.
BALANCE: Balance the window locations when using split windows.
HIDE: Hide the window when using split windows.
SHOW: Show the window when using split windows.
UP: Go to the window above when using split windows.
DOWN: Go to the window below when using split windows.
LEFT: Go to the previous window.
RIGHT: Go to the next window.
STICK: Make the window sticky.
MOVE LEFT: Move the window to the previous location.
MOVE RIGHT: Move the window to the next location.
MOVE UP: Move the window up when using split windows.
MOVE DOWN: Move the window down when using split windows.
LOG: %|Turn on or off logging of the active window, optionally specifying the log file to use.
LOGFILE: %|Sets the location of the log file to use for window logging without starting to log.
NEW: %|Creates a new hidden or split window.
CLOSE: %|Closes the current window, the specified one or all windows in the given range.
REFNUM: %|Go to the window with the given number.
GOTO: %|Go to the window with activity, with the given nickname, channel or with the specified number.
NEXT: %|Go to the next window numerically.
LAST: %|Go to the previously active window.
PREVIOUS: %|Go to the previous window numerically.
LEVEL: %|Changes the text levels to display in the window, or query the current level.
IMMORTAL: %|Modifies or queries the window mortality status. Immortal windows have an extra protection against WINDOW CLOSE.
SERVER: %|Change the active server of the window or the server stickyness. If the server is sticky, it cannot be cycled with next_window_item/previous_window_item
ITEM PREV: %|Make the previous item in this window active.
ITEM NEXT: %|Make the next item in this window active.
ITEM GOTO: %|Change to the query with the specified nickname, channel with the given name or window item number.
ITEM MOVE: %|Move the active window item to another window, or move the channel or query item specified by their name to the current window.
NUMBER: %|Change the active window number to the specified number, swapping the window already in that place if required. With -sticky, protect the window number from renumbering done by windows_auto_renumber. (To re-set the sticky attribute, use WINDOW NUMBER again without -sticky.)
NAME: %|Change or clear the window name. Window names must be unique.
HISTORY: %|Set or clear a specific named history to use for this window. All windows with the same named history will share a history.
MOVE PREV: %|Move the window to the place of the numerically previous window. At the first position, move the window to the end and renumber the consecutive block that it was part of.
MOVE NEXT: %|Move the window to the place of the numerically next window. At the last position, move the window to the first position and renumber the consecutive block at first position (if any)
MOVE FIRST: %|Move the window to the first position. Any windows inbetween are moved to their numerically next positions.
MOVE LAST: %|Move the window to the last position. Any windows inbetween are moved to their numerically previous positions.
MOVE: %|Move the window to the specified number or the first number that is in use when moving the window in the direction of the specified position. Any windows inbetween are shifted towards the old position of the window (unused positions remain empty)
LIST: %|List all the windows.
THEME: %|Applies or removes a per-window theme.
GROW: %|Increase the size of the active split window by the specified number of lines.
SHRINK: %|Decrease the size of the active split window by the specified number of lines.
SIZE: %|Set the current split window size to the specified numer of lines.
BALANCE: %|Balance the heights of all split windows.
HIDE: %|Hides the current split window, or the split window specified by number or item name.
SHOW: %|Show the window specified by number or item name as a new split windows. It is made sticky when autostick_split_windows is turned on.
UP: %|Set the split window above the current one active. At the top, wraps to the bottom.
DOWN: %|Set the split window below the current one active. At the bottom, wraps to the top.
LEFT: %|Go to the previous window numerically that is part of the current sticky group (or not part of any sticky group).
RIGHT: %|Go to the next window numerically that is part of the current sticky group (or not part of any sticky group).
STICK: %|Make the currently active window sticky, or stick the window specified by number to the currently visible split window. Or turn off stickyness of the currently active window or the window specified by number.
MOVE LEFT: %|Move the window to the numerically previous location inside the current sticky group.
MOVE RIGHT: %|Move the window to the numerically next location inside the current sticky group.
MOVE UP: %|Move the current window to the sticky group of the split window above. If no sticky group remains, the split window collapses.
MOVE DOWN: %|Move the current window to the sticky group of the split window below. If no sticky group remains, the split window collapses.
Add the required arguments for the given command.
%|Add the required arguments for the given command. Without arguments, the details (size, immortality, levels, server, name and sticky group) of the currently active window are displayed. If used with a number as argument, same as WINDOW REFNUM.
%9Description:%9

View file

@ -1185,3 +1185,11 @@ Client->{}
connected - whether the client is connected and ready
want_ctcp - whether the client wants to receive CTCPs
ircnet - network tag of the network we proxy
Bugs and Limitations
--------------------
* Calling die in 'script error' handler causes segfault (#101)
* Storing and later using any Irssi object may result in use-after-free related crash

View file

@ -12,6 +12,11 @@ In irssi, say:
/LOAD proxy
If you want the proxy to be loaded automatically at startup, add the
load command to ~/.irssi/startup:
echo "load proxy" >> ~/.irssi/startup
You really should set some password for the proxy with:
/SET irssiproxy_password secret
@ -24,3 +29,20 @@ something like:
There we have 3 different irc networks answering in 3 ports. Note that
you'll have to make the correct /IRCNET ADD and /SERVER ADD commands to
make it work properly.
By default, the proxy binds to all available interfaces. To make it
only listen on (for example) the loopback address:
/SET irssiproxy_bind 127.0.0.1
Note that bind address changes won't take effect until the proxy is
disabled and then reenabled.
Once everything is set up, you can enable / disable the proxy:
/TOGGLE irssiproxy
When the proxy is configured and running, the following command will
show all the currently connected clients:
/IRSSIPROXY status

View file

@ -136,6 +136,10 @@ irc-cap.c
"server cap nak "<cmd>, SERVER_REC
"server cap end", SERVER_REC
sasl.c
"server sasl failure", SERVER_REC, char *reason
"server sasl success", SERVER_REC
irc.c:
"server event", SERVER_REC, char *data, char *sender_nick, char *sender_address
@ -225,6 +229,7 @@ notifylist.c:
proxy/listen.c:
"proxy client connecting", CLIENT_REC
"proxy client connected", CLIENT_REC
"proxy client disconnected", CLIENT_REC
"proxy client command", CLIENT_REC, char *args, char *data

View file

@ -391,7 +391,7 @@ messages window.</p>
the connection into some window. IRSSI DOES NOT. There is no required
relationship between window and server. You can connect to 10 servers
and manage them all in just one window, or join channel in each one of
them to one sigle window if you really want to. That being said, here's
them to one single window if you really want to. That being said, here's
how you do connect to new server without closing the old connection:</p>
<pre>

View file

@ -22,6 +22,7 @@ if echo "${VERSION}" | grep -q -- -head; then
# Because the git tag won't yet include the next release we modify the git
# describe output using the version defined from configure.ac.
version="${new_version}-$(echo "${git_version}" | sed 's/^.*-[0-9]\+-//')"
echo "#undef PACKAGE_VERSION"
echo "#define PACKAGE_VERSION \"${version}\""
fi
fi

View file

@ -43,23 +43,6 @@
# 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
#if !GLIB_CHECK_VERSION(2,16,0)
#define g_strcmp0(a, b) (!a ? -(a != b) : (!b ? (a != b) : strcmp(a, b)))
#endif
#ifdef USE_GC
# define g_free(x) G_STMT_START { (x) = NULL; } G_STMT_END
#endif
#if defined (UOFF_T_INT)
typedef unsigned int uoff_t;
#elif defined (UOFF_T_LONG)

View file

@ -156,11 +156,17 @@ static void sig_init_finished(void)
static char *fix_path(const char *str)
{
char *new_str = convert_home(str);
if (!g_path_is_absolute(new_str)) {
char *tmp_str = new_str;
new_str = g_strdup_printf("%s/%s", g_get_current_dir(), tmp_str);
char *current_dir = g_get_current_dir();
new_str = g_build_path(G_DIR_SEPARATOR_S, current_dir, tmp_str, NULL);
g_free(current_dir);
g_free(tmp_str);
}
return new_str;
}

View file

@ -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);

View file

@ -215,7 +215,7 @@ void gslist_free_full (GSList *list, GDestroyNotify free_func)
{
GSList *tmp;
if (!list)
if (list == NULL)
return;
for (tmp = list; tmp != NULL; tmp = tmp->next)

View file

@ -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);

View file

@ -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);

View file

@ -32,6 +32,7 @@
static int rawlog_lines;
static int signal_rawlog;
static int log_file_create_mode;
static int log_dir_create_mode;
RAWLOG_REC *rawlog_create(void)
{
@ -145,9 +146,13 @@ void rawlog_close(RAWLOG_REC *rawlog)
void rawlog_save(RAWLOG_REC *rawlog, const char *fname)
{
char *path;
char *path, *dir;
int f;
dir = g_path_get_dirname(fname);
mkpath(dir, log_dir_create_mode);
g_free(dir);
path = convert_home(fname);
f = open(path, O_WRONLY | O_APPEND | O_CREAT, log_file_create_mode);
g_free(path);
@ -165,6 +170,11 @@ static void read_settings(void)
{
rawlog_set_size(settings_get_int("rawlog_lines"));
log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
log_dir_create_mode = log_file_create_mode;
if (log_file_create_mode & 0400) log_dir_create_mode |= 0100;
if (log_file_create_mode & 0040) log_dir_create_mode |= 0010;
if (log_file_create_mode & 0004) log_dir_create_mode |= 0001;
}
static void cmd_rawlog(const char *data, SERVER_REC *server, void *item)

View file

@ -8,6 +8,9 @@ char *address;
int port;
char *password;
int sasl_mechanism;
char *sasl_password;
char *ssl_cert;
char *ssl_pkey;
char *ssl_pass;

View file

@ -420,8 +420,8 @@ static void cmd_reconnect(const char *data, SERVER_REC *server)
cmd_param_error(CMDERR_NOT_CONNECTED);
rec = reconnects->data;
} else {
if (g_ascii_strncasecmp(data, "RECON-", 6) == 0)
data += 6;
if (g_ascii_strncasecmp(tag, "RECON-", 6) == 0)
tag += 6;
tagnum = atoi(tag);
rec = tagnum <= 0 ? NULL : reconnect_find_tag(tagnum);

View file

@ -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 */

View file

@ -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);

View file

@ -183,7 +183,9 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
nickrec = nicklist_find(chanrec, nick);
for_me = !settings_get_bool("hilight_nick_matches") ? FALSE :
nick_match_msg(chanrec, msg, server->nick);
!settings_get_bool("hilight_nick_matches_everywhere") ?
nick_match_msg(chanrec, msg, server->nick) :
nick_match_msg_everywhere(chanrec, msg, server->nick);
hilight = for_me ? NULL :
hilight_match_nick(server, target, nick, address, MSGLEVEL_PUBLIC, msg);
color = (hilight == NULL) ? NULL : hilight_get_color(hilight);
@ -694,6 +696,7 @@ void fe_messages_init(void)
(GCompareFunc) g_direct_equal);
settings_add_bool("lookandfeel", "hilight_nick_matches", TRUE);
settings_add_bool("lookandfeel", "hilight_nick_matches_everywhere", FALSE);
settings_add_bool("lookandfeel", "emphasis", TRUE);
settings_add_bool("lookandfeel", "emphasis_replace", FALSE);
settings_add_bool("lookandfeel", "emphasis_multiword", FALSE);

View file

@ -31,6 +31,8 @@
#include "fe-windows.h"
#include "printtext.h"
#define MAX_EXPAND_RECURSION 100
GSList *keyinfos;
static GHashTable *keys, *default_keys;
@ -171,7 +173,7 @@ KEYINFO_REC *key_info_find(const char *id)
return NULL;
}
static int expand_key(const char *key, GSList **out);
static int expand_key(const char *key, GSList **out, int *limit);
#define expand_out_char(out, c) \
{ \
@ -188,13 +190,17 @@ static int expand_key(const char *key, GSList **out);
g_slist_free(out); out = NULL; \
}
static int expand_combo(const char *start, const char *end, GSList **out)
static int expand_combo(const char *start, const char *end, GSList **out, int *limit)
{
KEY_REC *rec;
KEYINFO_REC *info;
GSList *tmp, *tmp2, *list, *copy, *newout;
char *str, *p;
if ((*limit)-- < 0) {
return FALSE;
}
if (start == end) {
/* single key */
expand_out_char(*out, *start);
@ -229,7 +235,7 @@ static int expand_combo(const char *start, const char *end, GSList **out)
/* only one way to generate the combo, good */
rec = list->data;
g_slist_free(list);
return expand_key(rec->key, out);
return expand_key(rec->key, out, limit);
}
/* multiple ways to generate the combo -
@ -244,7 +250,11 @@ static int expand_combo(const char *start, const char *end, GSList **out)
copy = g_slist_append(copy, g_string_new(str->str));
}
if (!expand_key(rec->key, &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;
@ -332,7 +350,7 @@ static int expand_key(const char *key, GSList **out)
}
if (start != NULL)
return expand_combo(start, key-1, out);
return expand_combo(start, key-1, out, limit);
for (tmp = *out; tmp != NULL; tmp = tmp->next) {
GString *str = tmp->data;
@ -346,12 +364,13 @@ static int expand_key(const char *key, GSList **out)
static void key_states_scan_key(const char *key, KEY_REC *rec)
{
GSList *tmp, *out;
int limit = MAX_EXPAND_RECURSION;
if (g_strcmp0(rec->info->id, "key") == 0)
return;
out = g_slist_append(NULL, g_string_new(NULL));
if (expand_key(key, &out)) {
if (expand_key(key, &out, &limit)) {
for (tmp = out; tmp != NULL; tmp = tmp->next) {
GString *str = tmp->data;

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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();

View file

@ -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);
}

View file

@ -400,7 +400,7 @@ static void event_target_unavailable(IRC_SERVER_REC *server, const char *data,
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &target);
if (!ischannel(*target)) {
if (!server_ischannel(SERVER(server), target)) {
/* nick unavailable */
printformat(server, NULL, MSGLEVEL_CRAP,
IRCTXT_NICK_UNAVAILABLE, target);
@ -583,7 +583,7 @@ static void print_event_received(IRC_SERVER_REC *server, const char *data,
return;
ptr++;
if (ischannel(*data)) /* directed at channel */
if (server_ischannel(SERVER(server), data)) /* directed at channel */
target = g_strndup(data, (int)(ptr - data - 1));
else if (!target_param || *ptr == ':' || (ptr2 = strchr(ptr, ' ')) == NULL)
target = NULL;

View file

@ -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));

View file

@ -31,6 +31,51 @@
#include "fe-windows.h"
#include "window-items.h"
int fe_channel_is_opchannel(IRC_SERVER_REC *server, const char *target)
{
const char *statusmsg;
/* Quick check */
if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0)
return FALSE;
statusmsg = g_hash_table_lookup(server->isupport, "statusmsg");
if (statusmsg == NULL)
statusmsg = "@+";
return strchr(statusmsg, *target) != NULL;
}
const char *fe_channel_skip_prefix(IRC_SERVER_REC *server, const char *target)
{
const char *statusmsg;
/* Quick check */
if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0)
return target;
/* Exit early if target doesn't name a channel */
if (server_ischannel(SERVER(server), target) == FALSE)
return target;
statusmsg = g_hash_table_lookup(server->isupport, "statusmsg");
/* Hack: for bahamut 1.4 which sends neither STATUSMSG nor
* WALLCHOPS in 005, accept @#chan and @+#chan (but not +#chan) */
if (statusmsg == NULL && *target != '@')
return target;
if (statusmsg == NULL)
statusmsg = "@+";
/* Strip the leading statusmsg prefixes */
while (strchr(statusmsg, *target) != NULL) {
target++;
}
return target;
}
static void sig_channel_rejoin(SERVER_REC *server, REJOIN_REC *rec)
{
g_return_if_fail(rec != NULL);
@ -46,7 +91,7 @@ static void sig_event_forward(SERVER_REC *server, const char *data,
char *params, *from, *to;
params = event_get_params(data, 3, NULL, &from, &to);
if (from != NULL && to != NULL && ischannel(*from) && ischannel(*to)) {
if (from != NULL && to != NULL && server_ischannel(server, from) && server_ischannel(server, to)) {
channel = irc_channel_find(server, from);
if (channel != NULL && irc_channel_find(server, to) == NULL) {
window_bind_add(window_item_window(channel),

View file

@ -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

View file

@ -36,32 +36,8 @@
#include "fe-queries.h"
#include "window-items.h"
static const char *skip_target(IRC_SERVER_REC *server, const char *target)
{
int i = 0;
const char *val, *chars;
/* Quick check */
if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0)
return target;
/* Hack: for bahamut 1.4 which sends neither STATUSMSG nor
* WALLCHOPS in 005, accept @#chan and @+#chan (but not +#chan) */
val = g_hash_table_lookup(server->isupport, "STATUSMSG");
if (val == NULL && *target != '@')
return target;
chars = val ? val : "@+";
for(i = 0; target[i] != '\0'; i++) {
if (strchr(chars, target[i]) == NULL)
break;
};
if(ischannel(target[i]))
target += i;
return target;
}
#include "fe-irc-channels.h"
#include "fe-irc-server.h"
static void sig_message_own_public(SERVER_REC *server, const char *msg,
const char *target, const char *origtarget)
@ -72,7 +48,7 @@ static void sig_message_own_public(SERVER_REC *server, const char *msg,
if (!IS_IRC_SERVER(server))
return;
oldtarget = target;
target = skip_target(IRC_SERVER(server), target);
target = fe_channel_skip_prefix(IRC_SERVER(server), target);
if (target != oldtarget) {
/* Hybrid 6 / Bahamut feature, send msg to all
ops / ops+voices in channel */
@ -95,18 +71,28 @@ static void sig_message_irc_op_public(SERVER_REC *server, const char *msg,
const char *nick, const char *address,
const char *target)
{
char *nickmode, *optarget;
char *nickmode, *optarget, *prefix;
const char *cleantarget;
nickmode = channel_get_nickmode(channel_find(server, target),
/* only skip here so the difference can be stored in prefix */
cleantarget = fe_channel_skip_prefix(IRC_SERVER(server), target);
prefix = g_strndup(target, cleantarget - target);
/* and clean the rest here */
cleantarget = get_visible_target(IRC_SERVER(server), cleantarget);
nickmode = channel_get_nickmode(channel_find(server, cleantarget),
nick);
optarget = g_strconcat("@", target, NULL);
printformat_module("fe-common/core", server, target,
optarget = g_strconcat(prefix, cleantarget, NULL);
printformat_module("fe-common/core", server, cleantarget,
MSGLEVEL_PUBLIC,
TXT_PUBMSG_CHANNEL,
nick, optarget, msg, nickmode);
g_free(nickmode);
g_free(optarget);
g_free(optarget);
g_free(prefix);
}
static void sig_message_own_wall(SERVER_REC *server, const char *msg,
@ -117,7 +103,8 @@ static void sig_message_own_wall(SERVER_REC *server, const char *msg,
nickmode = channel_get_nickmode(channel_find(server, target),
server->nick);
optarget = g_strconcat("@", target, NULL);
/* this is always @, skip_prefix is not needed here */
optarget = g_strconcat("@", target, NULL);
printformat_module("fe-common/core", server, target,
MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT |
MSGLEVEL_NO_ACT,
@ -135,8 +122,8 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
char *freemsg = NULL;
oldtarget = target;
target = skip_target(IRC_SERVER(server), target);
if (ischannel(*target))
target = fe_channel_skip_prefix(IRC_SERVER(server), target);
if (server_ischannel(SERVER(server), target))
item = irc_channel_find(server, target);
else
item = irc_query_find(server, target);
@ -146,7 +133,7 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
printformat(server, target,
MSGLEVEL_ACTIONS | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT |
(ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS),
(server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS),
item != NULL && oldtarget == target ? IRCTXT_OWN_ACTION : IRCTXT_OWN_ACTION_TARGET,
server->nick, msg, oldtarget);
g_free_not_null(freemsg);
@ -163,10 +150,10 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
int own = FALSE;
oldtarget = target;
target = skip_target(IRC_SERVER(server), target);
target = fe_channel_skip_prefix(IRC_SERVER(server), target);
level = MSGLEVEL_ACTIONS |
(ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
(server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
if (ignore_check(SERVER(server), nick, address, target, msg, level))
return;
@ -175,7 +162,7 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
level | MSGLEVEL_NO_ACT))
level |= MSGLEVEL_NO_ACT;
if (ischannel(*target)) {
if (server_ischannel(SERVER(server), target)) {
item = irc_channel_find(server, target);
} else {
own = (!g_strcmp0(nick, server->nick));
@ -185,7 +172,7 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
if (settings_get_bool("emphasis"))
msg = freemsg = expand_emphasis(item, msg);
if (ischannel(*target)) {
if (server_ischannel(SERVER(server), target)) {
/* channel action */
if (window_item_is_active(item) && target == oldtarget) {
/* message to active channel in window */
@ -219,7 +206,7 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
static void sig_message_own_notice(IRC_SERVER_REC *server, const char *msg,
const char *target)
{
printformat(server, skip_target(server, target), MSGLEVEL_NOTICES |
printformat(server, fe_channel_skip_prefix(server, target), MSGLEVEL_NOTICES |
MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
IRCTXT_OWN_NOTICE, target, msg);
}
@ -232,7 +219,7 @@ static void sig_message_irc_notice(SERVER_REC *server, const char *msg,
int level = MSGLEVEL_NOTICES;
oldtarget = target;
target = skip_target(IRC_SERVER(server), target);
target = fe_channel_skip_prefix(IRC_SERVER(server), target);
if (address == NULL || *address == '\0') {
/* notice from server */
@ -245,16 +232,16 @@ static void sig_message_irc_notice(SERVER_REC *server, const char *msg,
}
if (ignore_check(server, nick, address,
ischannel(*target) ? target : NULL,
server_ischannel(SERVER(server), target) ? target : NULL,
msg, level))
return;
if (ignore_check(server, nick, address,
ischannel(*target) ? target : NULL,
server_ischannel(SERVER(server), target) ? target : NULL,
msg, level | MSGLEVEL_NO_ACT))
level |= MSGLEVEL_NO_ACT;
if (ischannel(*target)) {
if (server_ischannel(SERVER(server), target)) {
/* notice in some channel */
printformat(server, target, level,
IRCTXT_NOTICE_PUBLIC, nick, oldtarget, msg);
@ -270,7 +257,7 @@ static void sig_message_irc_notice(SERVER_REC *server, const char *msg,
static void sig_message_own_ctcp(IRC_SERVER_REC *server, const char *cmd,
const char *data, const char *target)
{
printformat(server, skip_target(server, target), MSGLEVEL_CTCPS |
printformat(server, fe_channel_skip_prefix(server, target), MSGLEVEL_CTCPS |
MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
IRCTXT_OWN_CTCP, target, cmd, data);
}
@ -282,8 +269,8 @@ static void sig_message_irc_ctcp(IRC_SERVER_REC *server, const char *cmd,
const char *oldtarget;
oldtarget = target;
target = skip_target(server, target);
printformat(server, ischannel(*target) ? target : nick, MSGLEVEL_CTCPS,
target = fe_channel_skip_prefix(server, target);
printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS,
IRCTXT_CTCP_REQUESTED, nick, addr, cmd, data, oldtarget);
}

View file

@ -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;

View file

@ -56,7 +56,12 @@ static void cmd_network_list(void)
g_string_append_printf(str, "autosendcmd: %s, ", rec->autosendcmd);
if (rec->usermode != NULL)
g_string_append_printf(str, "usermode: %s, ", rec->usermode);
if (rec->sasl_mechanism != NULL)
g_string_append_printf(str, "sasl_mechanism: %s, ", rec->sasl_mechanism);
if (rec->sasl_username != NULL)
g_string_append_printf(str, "sasl_username: %s, ", rec->sasl_username);
if (rec->sasl_password != NULL)
g_string_append_printf(str, "sasl_password: (pass), ");
if (rec->cmd_queue_speed > 0)
g_string_append_printf(str, "cmdspeed: %d, ", rec->cmd_queue_speed);
if (rec->max_cmds_at_once > 0)
@ -82,10 +87,12 @@ static void cmd_network_list(void)
}
/* SYNTAX: NETWORK ADD [-nick <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> */
[-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)
{
GHashTable *optlist;
@ -112,6 +119,9 @@ static void cmd_network_add(const char *data)
}
if (g_hash_table_lookup(optlist, "usermode")) g_free_and_null(rec->usermode);
if (g_hash_table_lookup(optlist, "autosendcmd")) g_free_and_null(rec->autosendcmd);
if (g_hash_table_lookup(optlist, "sasl_mechanism")) g_free_and_null(rec->sasl_mechanism);
if (g_hash_table_lookup(optlist, "sasl_username")) g_free_and_null(rec->sasl_username);
if (g_hash_table_lookup(optlist, "sasl_password")) g_free_and_null(rec->sasl_password);
}
value = g_hash_table_lookup(optlist, "kicks");
@ -148,6 +158,14 @@ static void cmd_network_add(const char *data)
value = g_hash_table_lookup(optlist, "autosendcmd");
if (value != NULL && *value != '\0') rec->autosendcmd = g_strdup(value);
/* the validity of the parameters is checked in sig_server_setup_fill_chatnet */
value = g_hash_table_lookup(optlist, "sasl_mechanism");
if (value != NULL && *value != '\0') rec->sasl_mechanism = g_strdup(value);
value = g_hash_table_lookup(optlist, "sasl_username");
if (value != NULL && *value != '\0') rec->sasl_username = g_strdup(value);
value = g_hash_table_lookup(optlist, "sasl_password");
if (value != NULL && *value != '\0') rec->sasl_password = g_strdup(value);
ircnet_create(rec);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NETWORK_ADDED, name);
@ -186,7 +204,8 @@ void fe_ircnet_init(void)
command_bind("network add", NULL, (SIGNAL_FUNC) cmd_network_add);
command_bind("network remove", NULL, (SIGNAL_FUNC) cmd_network_remove);
command_set_options("network add", "-kicks -msgs -modes -whois -cmdspeed -cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode");
command_set_options("network add", "-kicks -msgs -modes -whois -cmdspeed "
"-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
}
void fe_ircnet_deinit(void)

View file

@ -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);

View file

@ -400,7 +400,7 @@ static void msg_mode(IRC_SERVER_REC *server, const char *channel,
int show;
g_return_if_fail(data != NULL);
if (!ischannel(*channel) || addr != NULL)
if (!server_ischannel(SERVER(server), channel) || addr != NULL)
return;
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,

View file

@ -0,0 +1,48 @@
/*
fe-sasl.c : irssi
Copyright (C) 2015 The Lemon Man
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "module-formats.h"
#include "signals.h"
#include "levels.h"
#include "printtext.h"
static void sig_sasl_success(IRC_SERVER_REC *server)
{
printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_SASL_SUCCESS);
}
static void sig_sasl_failure(IRC_SERVER_REC *server, const char *reason)
{
printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_SASL_ERROR, reason);
}
void fe_sasl_init(void)
{
signal_add("server sasl success", (SIGNAL_FUNC) sig_sasl_success);
signal_add("server sasl failure", (SIGNAL_FUNC) sig_sasl_failure);
}
void fe_sasl_deinit(void)
{
signal_remove("server sasl success", (SIGNAL_FUNC) sig_sasl_success);
signal_remove("server sasl failure", (SIGNAL_FUNC) sig_sasl_failure);
}

View file

@ -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 },

View file

@ -22,6 +22,8 @@ enum {
IRCTXT_SETUPSERVER_HEADER,
IRCTXT_SETUPSERVER_LINE,
IRCTXT_SETUPSERVER_FOOTER,
IRCTXT_SASL_SUCCESS,
IRCTXT_SASL_ERROR,
IRCTXT_FILL_2,

View file

@ -220,16 +220,15 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
get_colors(flags, &fg, &bg, &attr);
if (window == NULL) {
g_return_if_fail(next_xpos != -1);
g_return_if_fail(next_xpos != -1);
term_set_color2(root_window, attr, fg, bg);
term_move(root_window, next_xpos, next_ypos);
if (flags & GUI_PRINT_FLAG_CLRTOEOL)
term_clrtoeol(root_window);
term_addstr(root_window, str);
next_xpos += strlen(str); /* FIXME utf8 or big5 */
return;
next_xpos += term_addstr(root_window, str);
return;
}
lineinfo.level = dest == NULL ? 0 : dest->level;

View file

@ -237,7 +237,7 @@ static void paste_buffer_join_lines(GArray *buf)
last_lf = FALSE;
if (++line_len >= 400 && last_lf_pos != NULL) {
memmove(last_lf_pos+1, last_lf_pos,
dest - last_lf_pos);
(dest - last_lf_pos) * sizeof(unichar));
*last_lf_pos = '\n'; last_lf_pos = NULL;
line_len = 0;
dest++;

View file

@ -286,22 +286,6 @@ static void winsock_init(void)
}
#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;
@ -312,10 +296,6 @@ int main(int argc, char **argv)
};
int loglev;
#ifdef USE_GC
g_mem_set_vtable(&gc_mem_table);
#endif
core_register_options();
fe_common_core_register_options();
args_register(options);
@ -369,9 +349,6 @@ int main(int argc, char **argv)
/* Does the same as g_main_run(main_loop), except we
can call our dirty-checker after each iteration */
while (!quitting) {
#ifdef USE_GC
GC_collect_a_little();
#endif
if (!dummy) term_refresh_freeze();
g_main_iteration(TRUE);
if (!dummy) term_refresh_thaw();

View file

@ -347,10 +347,14 @@ static void item_lag(SBAR_ITEM_REC *item, int get_size_only)
last_lag_unknown = lag_unknown;
if (lag_unknown) {
g_snprintf(str, sizeof(str), "%d (?""?)", lag/100);
// "??)" in C becomes ']'
// See: https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#C
g_snprintf(str, sizeof(str), "%d (?""?)", lag / 100);
} else {
g_snprintf(str, sizeof(str),
lag%100 == 0 ? "%d" : "%d.%02d", lag/100, lag%100);
if (lag % 100 == 0)
g_snprintf(str, sizeof(str), "%d", lag / 100);
else
g_snprintf(str, sizeof(str), "%d.%02d", lag / 100, lag % 100);
}
statusbar_item_default_handler(item, get_size_only,

View file

@ -522,15 +522,36 @@ void term_add_unichar(TERM_WINDOW *window, unichar chr)
}
}
void term_addstr(TERM_WINDOW *window, const char *str)
int term_addstr(TERM_WINDOW *window, const char *str)
{
int len;
int len, raw_len;
unichar tmp;
const char *ptr;
if (vcmove) term_move_real();
len = strlen(str); /* FIXME utf8 or big5 */
len = 0;
raw_len = strlen(str);
/* The string length depends on the terminal encoding */
ptr = str;
if (term_type == TERM_TYPE_UTF8) {
while (*ptr != '\0') {
tmp = g_utf8_get_char(ptr);
len += unichar_isprint(tmp) ? mk_wcwidth(tmp) : 1;
ptr = g_utf8_next_char(ptr);
}
} else
len = raw_len;
term_printed_text(len);
fwrite(str, 1, len, window->term->out);
/* Use strlen() here since we need the number of raw bytes */
fwrite(str, 1, raw_len, window->term->out);
return len;
}
void term_clrtoeol(TERM_WINDOW *window)

View file

@ -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);

View file

@ -50,62 +50,66 @@ TERM_REC *current_term;
/* Define only what we might need */
static TERMINFO_REC tcaps[] = {
/* Terminal size */
{ "cols", "co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, width) },
{ "lines", "li", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, height) },
/* Terminal size */
{ "cols", "co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, width) },
{ "lines", "li", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, height) },
/* Cursor movement */
{ "smcup", "ti", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smcup) },
{ "rmcup", "te", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmcup) },
{ "cup", "cm", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cup) },
{ "hpa", "ch", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_hpa) },
{ "vpa", "vh", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_vpa) },
{ "cub1", "le", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cub1) },
{ "cuf1", "nd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cuf1) },
{ "civis", "vi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_civis) },
{ "cnorm", "ve", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cnorm) },
/* Cursor movement */
{ "smcup", "ti", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smcup) },
{ "rmcup", "te", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmcup) },
{ "cup", "cm", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cup) },
{ "hpa", "ch", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_hpa) },
{ "vpa", "vh", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_vpa) },
{ "cub1", "le", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cub1) },
{ "cuf1", "nd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cuf1) },
{ "civis", "vi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_civis) },
{ "cnorm", "ve", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cnorm) },
/* Scrolling */
{ "csr", "cs", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_csr) },
{ "wind", "wi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_wind) },
{ "ri", "sr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ri) },
{ "rin", "SR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rin) },
{ "ind", "sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ind) },
{ "indn", "SF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_indn) },
{ "il", "AL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il) },
{ "il1", "al", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il1) },
{ "dl", "DL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl) },
{ "dl1", "dl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl1) },
/* Scrolling */
{ "csr", "cs", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_csr) },
{ "wind", "wi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_wind) },
{ "ri", "sr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ri) },
{ "rin", "SR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rin) },
{ "ind", "sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ind) },
{ "indn", "SF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_indn) },
{ "il", "AL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il) },
{ "il1", "al", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il1) },
{ "dl", "DL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl) },
{ "dl1", "dl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl1) },
/* Clearing screen */
{ "clear", "cl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_clear) },
{ "ed", "cd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ed) },
{ "clear", "cl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_clear) },
{ "ed", "cd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ed) },
/* Clearing to end of line */
{ "el", "ce", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_el) },
/* Clearing to end of line */
{ "el", "ce", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_el) },
/* Repeating character */
{ "rep", "rp", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rep) },
/* Repeating character */
{ "rep", "rp", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rep) },
/* Colors */
{ "colors", "Co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, TI_colors) },
{ "sgr0", "me", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sgr0) },
{ "smul", "us", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smul) },
{ "rmul", "ue", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmul) },
{ "smso", "so", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smso) },
{ "rmso", "se", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmso) },
{ "sitm", "ZH", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sitm) },
{ "ritm", "ZR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ritm) },
{ "bold", "md", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bold) },
{ "blink", "mb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_blink) },
{ "rev", "mr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rev) },
{ "setaf", "AF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setaf) },
{ "setab", "AB", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setab) },
{ "setf", "Sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setf) },
{ "setb", "Sb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setb) },
{ "colors", "Co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, TI_colors) },
{ "sgr0", "me", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sgr0) },
{ "smul", "us", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smul) },
{ "rmul", "ue", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmul) },
{ "smso", "so", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smso) },
{ "rmso", "se", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmso) },
{ "sitm", "ZH", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sitm) },
{ "ritm", "ZR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ritm) },
{ "bold", "md", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bold) },
{ "blink", "mb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_blink) },
{ "rev", "mr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rev) },
{ "setaf", "AF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setaf) },
{ "setab", "AB", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setab) },
{ "setf", "Sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setf) },
{ "setb", "Sb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setb) },
/* Beep */
{ "bel", "bl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bel) },
/* Beep */
{ "bel", "bl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bel) },
/* Keyboard-transmit mode */
{ "smkx", "ks", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smkx) },
{ "rmkx", "ke", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmkx) },
};
/* Move cursor (cursor_address / cup) */
@ -498,6 +502,9 @@ static void terminfo_input_init(TERM_REC *term)
memcpy(&term->tio, &term->old_tio, sizeof(term->tio));
term->tio.c_lflag &= ~(ICANON | ECHO); /* CBREAK, no ECHO */
/* Disable the ICRNL flag to disambiguate ^J and Enter, also disable the
* software flow control to leave ^Q and ^S ready to be bound */
term->tio.c_iflag &= ~(ICRNL | IXON | IXOFF);
term->tio.c_cc[VMIN] = 1; /* read() is satisfied after 1 char */
term->tio.c_cc[VTIME] = 0; /* No timer */
@ -523,7 +530,11 @@ static void terminfo_input_deinit(TERM_REC *term)
void terminfo_cont(TERM_REC *term)
{
if (term->TI_smcup)
tput(tparm(term->TI_smcup));
tput(tparm(term->TI_smcup));
if (term->TI_smkx)
tput(tparm(term->TI_smkx));
terminfo_input_init(term);
}
@ -538,6 +549,9 @@ void terminfo_stop(TERM_REC *term)
if (term->TI_rmcup)
tput(tparm(term->TI_rmcup));
if (term->TI_rmkx)
tput(tparm(term->TI_rmkx));
/* reset input settings */
terminfo_input_deinit(term);
fflush(term->out);

View file

@ -88,6 +88,10 @@ struct _TERM_REC {
/* Beep */
char *TI_bel;
/* Keyboard-transmit mode */
const char *TI_smkx;
const char *TI_rmkx;
};
extern TERM_REC *current_term;

View file

@ -27,6 +27,7 @@ libirc_core_a_SOURCES = \
irc-servers-setup.c \
irc-session.c \
irc-cap.c \
sasl.c \
lag.c \
massjoin.c \
modes.c \
@ -50,6 +51,7 @@ pkginc_irc_core_HEADERS = \
irc-servers.h \
irc-servers-setup.h \
irc-cap.h \
sasl.h \
modes.h \
mode-lists.h \
module.h \

View file

@ -186,7 +186,7 @@ static void command_set_ban(const char *data, IRC_SERVER_REC *server,
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
item, &channel, &nicks)) return;
if (!ischannel(*channel)) cmd_param_error(CMDERR_NOT_JOINED);
if (!server_ischannel(SERVER(server), channel)) cmd_param_error(CMDERR_NOT_JOINED);
if (*nicks == '\0') {
if (g_strcmp0(data, "*") != 0)
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);

View file

@ -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

View file

@ -1,3 +1,22 @@
/* irc-cap.c : irssi
Copyright (C) 2015 The Lemon Man
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "signals.h"
#include "misc.h"

View file

@ -99,7 +99,7 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
tmp = chanlist;
for (;; tmp++) {
if (*tmp != NULL) {
channel = ischannel(**tmp) ? g_strdup(*tmp) :
channel = server_ischannel(SERVER(server), *tmp) ? g_strdup(*tmp) :
g_strdup_printf("#%s", *tmp);
chanrec = irc_channel_find(server, channel);
@ -134,7 +134,7 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
if (use_keys)
cmdlen += outkeys->len;
if (*tmpstr != NULL)
cmdlen += ischannel(**tmpstr) ? strlen(*tmpstr) :
cmdlen += server_ischannel(SERVER(server), *tmpstr) ? strlen(*tmpstr) :
strlen(*tmpstr)+1;
if (*tmpkey != NULL)
cmdlen += strlen(*tmpkey);
@ -146,11 +146,13 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
continue;
}
if (outchans->len > 0) {
g_string_truncate(outchans, outchans->len-1);
g_string_truncate(outkeys, outkeys->len-1);
irc_send_cmdv(IRC_SERVER(server),
use_keys ? "JOIN %s %s" : "JOIN %s",
outchans->str, outkeys->str);
g_string_truncate(outchans, outchans->len - 1);
g_string_truncate(outkeys, outkeys->len - 1);
if (use_keys)
irc_send_cmdv(IRC_SERVER(server), "JOIN %s %s", outchans->str, outkeys->str);
else
irc_send_cmdv(IRC_SERVER(server), "JOIN %s", outchans->str);
}
cmdlen = 0;
g_string_truncate(outchans,0);

View file

@ -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);
}
}

View file

@ -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;

View file

@ -170,8 +170,11 @@ static void cmd_part(const char *data, IRC_SERVER_REC *server,
if (*msg != '\0')
recoded = recode_out(SERVER(server), msg, channame);
irc_send_cmdv(server, ! recoded ? "PART %s" : "PART %s :%s",
channame, recoded);
if (recoded == NULL)
irc_send_cmdv(server, "PART %s", channame);
else
irc_send_cmdv(server, "PART %s :%s", channame, recoded);
g_free(recoded);
cmd_params_free(free_arg);
@ -191,7 +194,7 @@ static void cmd_kick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
return;
if (*channame == '\0' || *nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (!ischannel(*channame)) cmd_param_error(CMDERR_NOT_JOINED);
if (!server_ischannel(SERVER(server), channame)) cmd_param_error(CMDERR_NOT_JOINED);
recoded = recode_out(SERVER(server), reason, channame);
g_string_printf(tmpstr, "KICK %s %s :%s", channame, nicks, recoded);
@ -219,8 +222,12 @@ static void cmd_topic(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *ite
if (*topic != '\0' || g_hash_table_lookup(optlist, "delete") != NULL)
recoded = recode_out(SERVER(server), topic, channame);
irc_send_cmdv(server, recoded == NULL ? "TOPIC %s" : "TOPIC %s :%s",
channame, recoded);
if (recoded == NULL)
irc_send_cmdv(server, "TOPIC %s", channame);
else
irc_send_cmdv(server, "TOPIC %s :%s", channame, recoded);
g_free(recoded);
cmd_params_free(free_arg);
@ -279,14 +286,14 @@ static void cmd_who(const char *data, IRC_SERVER_REC *server,
char *channel, *rest;
void *free_arg;
CMD_IRC_SERVER(server);
CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &channel, &rest))
return;
if (g_strcmp0(channel, "*") == 0 || *channel == '\0') {
if (!IS_IRC_CHANNEL(item))
cmd_param_error(CMDERR_NOT_JOINED);
cmd_param_error(CMDERR_NOT_JOINED);
channel = IRC_CHANNEL(item)->name;
}
@ -295,8 +302,11 @@ static void cmd_who(const char *data, IRC_SERVER_REC *server,
*channel = '\0';
}
irc_send_cmdv(server, *rest == '\0' ? "WHO %s" : "WHO %s %s",
channel, rest);
if (rest[0] == '\0')
irc_send_cmdv(server, "WHO %s", channel);
else
irc_send_cmdv(server, "WHO %s %s", channel, rest);
cmd_params_free(free_arg);
}
@ -502,8 +512,11 @@ static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
if (free_nick) g_free(nicks_redir);
server->whowas_found = FALSE;
irc_send_cmdv(server, *rest == '\0' ? "WHOWAS %s" :
"WHOWAS %s %s", nicks, rest);
if (rest[0] == '\0')
irc_send_cmdv(server, "WHOWAS %s", nicks);
else
irc_send_cmdv(server, "WHOWAS %s %s", nicks, rest);
cmd_params_free(free_arg);
}
@ -913,9 +926,12 @@ static void cmd_unsilence(const char *data, IRC_SERVER_REC *server)
static void command_self(const char *data, IRC_SERVER_REC *server)
{
CMD_IRC_SERVER(server);
CMD_IRC_SERVER(server);
irc_send_cmdv(server, *data == '\0' ? "%s" : "%s %s", current_command, data);
if (data[0] == '\0')
irc_send_cmdv(server, "%s", current_command);
else
irc_send_cmdv(server, "%s %s", current_command, data);
}
static void command_1self(const char *data, IRC_SERVER_REC *server)

View file

@ -27,6 +27,7 @@
#include "irc-channels.h"
#include "irc-queries.h"
#include "irc-cap.h"
#include "sasl.h"
#include "irc-servers-setup.h"
#include "channels-setup.h"
@ -119,6 +120,7 @@ void irc_core_init(void)
netsplit_init();
irc_expandos_init();
cap_init();
sasl_init();
settings_check();
module_register("core", "irc");
@ -128,6 +130,7 @@ void irc_core_deinit(void)
{
signal_emit("chat protocol deinit", 1, chat_protocol_find("IRC"));
sasl_deinit();
cap_deinit();
irc_expandos_deinit();
netsplit_deinit();
@ -140,7 +143,7 @@ void irc_core_deinit(void)
irc_irc_deinit();
irc_servers_deinit();
irc_chatnets_deinit();
irc_session_deinit();
irc_session_deinit();
chat_protocol_unregister("IRC");
}

View file

@ -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);

View file

@ -394,7 +394,7 @@ static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
if (!ischannel(*channel)) {
if (!server_ischannel(SERVER(server), channel)) {
/* nick is unavailable. */
event_nick_in_use(server, data);
}

View file

@ -60,8 +60,8 @@ static void check_query_changes(IRC_SERVER_REC *server, const char *nick,
{
QUERY_REC *query;
if (ischannel(*target))
return;
if (server_ischannel(SERVER(server), target))
return;
query = irc_query_find(server, nick);
if (query == NULL)

View file

@ -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)

View file

@ -33,6 +33,9 @@
#include "irc-servers-setup.h"
#include "irc-servers.h"
#include "irc-cap.h"
#include "sasl.h"
#include "channels-setup.h"
#include "channel-rejoin.h"
#include "servers-idle.h"
#include "servers-reconnect.h"
@ -72,13 +75,20 @@ static int isnickflag_func(SERVER_REC *server, char flag)
static int ischannel_func(SERVER_REC *server, const char *data)
{
if (*data == '@') {
/* @#channel, @+#channel */
IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
char *chantypes, *statusmsg;
chantypes = g_hash_table_lookup(irc_server->isupport, "chantypes");
if (chantypes == NULL)
chantypes = "#&!+"; /* normal, local, secure, modeless */
statusmsg = g_hash_table_lookup(irc_server->isupport, "statusmsg");
if (statusmsg == NULL)
statusmsg = "@+";
while (strchr(statusmsg, *data) != NULL)
data++;
if (*data == '+' && ischannel(data[1]))
return 1;
}
return ischannel(*data);
return strchr(chantypes, *data) != NULL;
}
static char **split_line(const SERVER_REC *server, const char *line,
@ -215,6 +225,11 @@ static void server_init(IRC_SERVER_REC *server)
g_free(cmd);
}
if (conn->sasl_mechanism != SASL_MECHANISM_NONE)
cap_toggle(server, "sasl", TRUE);
cap_toggle(server, "multi-prefix", TRUE);
irc_send_cmd_now(server, "CAP LS");
if (conn->password != NULL && *conn->password != '\0') {
@ -602,32 +617,59 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
GString *chans, *keys;
char *ret;
int use_keys;
char *rejoin_channels_mode;
g_return_val_if_fail(server != NULL, FALSE);
rejoin_channels_mode = g_strdup(settings_get_str("rejoin_channels_on_reconnect"));
if (rejoin_channels_mode == NULL ||
(g_ascii_strcasecmp(rejoin_channels_mode, "on") != 0 &&
g_ascii_strcasecmp(rejoin_channels_mode, "off") != 0 &&
g_ascii_strcasecmp(rejoin_channels_mode, "auto") != 0)) {
g_warning("Invalid value for 'rejoin_channels_on_reconnect', valid values are 'on', 'off', 'auto', using 'on' as default value.");
g_free(rejoin_channels_mode);
rejoin_channels_mode = g_strdup("on");
}
chans = g_string_new(NULL);
keys = g_string_new(NULL);
use_keys = FALSE;
/* do we want to rejoin channels in the first place? */
if(g_ascii_strcasecmp(rejoin_channels_mode, "off") == 0) {
g_string_free(chans, TRUE);
g_string_free(keys, TRUE);
g_free(rejoin_channels_mode);
return g_strdup("");
}
/* get currently joined channels */
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *channel = tmp->data;
g_string_append_printf(chans, "%s,", channel->name);
g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" :
channel->key);
if (channel->key != NULL)
use_keys = TRUE;
CHANNEL_SETUP_REC *setup = channel_setup_find(channel->name, channel->server->connrec->chatnet);
if ((setup != NULL && setup->autojoin && g_ascii_strcasecmp(rejoin_channels_mode, "auto") == 0) ||
g_ascii_strcasecmp(rejoin_channels_mode, "on") == 0) {
g_string_append_printf(chans, "%s,", channel->name);
g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" : channel->key);
if (channel->key != NULL)
use_keys = TRUE;
}
}
/* get also the channels that are in rejoin list */
for (tmp = server->rejoin_channels; tmp != NULL; tmp = tmp->next) {
REJOIN_REC *rec = tmp->data;
CHANNEL_SETUP_REC *setup = channel_setup_find(rec->channel, server->tag);
g_string_append_printf(chans, "%s,", rec->channel);
g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
rec->key);
if (rec->key != NULL) use_keys = TRUE;
if ((setup != NULL && setup->autojoin && g_ascii_strcasecmp(rejoin_channels_mode, "auto") == 0) ||
g_ascii_strcasecmp(rejoin_channels_mode, "on") == 0) {
g_string_append_printf(chans, "%s,", rec->channel);
g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
rec->key);
if (rec->key != NULL) use_keys = TRUE;
}
}
if (chans->len > 0) {
@ -639,6 +681,7 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
ret = chans->str;
g_string_free(chans, FALSE);
g_string_free(keys, TRUE);
g_free(rejoin_channels_mode);
return ret;
}
@ -984,6 +1027,7 @@ void irc_server_init_isupport(IRC_SERVER_REC *server)
void irc_servers_init(void)
{
settings_add_str("servers", "rejoin_channels_on_reconnect", "on");
settings_add_str("misc", "usermode", DEFAULT_USER_MODE);
settings_add_str("misc", "split_line_start", "");
settings_add_str("misc", "split_line_end", "");

View file

@ -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;

View file

@ -28,6 +28,8 @@
#include "irc-channels.h"
#include "irc-nicklist.h"
#include "sasl.h"
struct _isupport_data { CONFIG_REC *config; CONFIG_NODE *node; };
static void session_isupport_foreach(char *key, char *value, struct _isupport_data *data)
@ -65,6 +67,10 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config,
config_node_set_str(config, node, "away_reason", server->away_reason);
config_node_set_bool(config, node, "emode_known", server->emode_known);
config_node_set_int(config, node, "sasl_mechanism", server->connrec->sasl_mechanism);
config_node_set_str(config, node, "sasl_username", server->connrec->sasl_username);
config_node_set_str(config, node, "sasl_password", server->connrec->sasl_password);
config_node_set_bool(config, node, "isupport_sent", server->isupport_sent);
isupport = config_node_section(config, node, "isupport", NODE_TYPE_BLOCK);
isupport_data.config = config;
@ -90,6 +96,15 @@ static void sig_session_restore_server(IRC_SERVER_REC *server,
server->emode_known = config_node_get_bool(node, "emode_known", FALSE);
server->isupport_sent = config_node_get_bool(node, "isupport_sent", FALSE);
server->connrec->sasl_mechanism = config_node_get_int(node, "sasl_mechanism", SASL_MECHANISM_NONE);
/* The fields below might have been filled when loading the chatnet
* description from the config and we favor the content that's been saved
* in the session file over that. */
g_free(server->connrec->sasl_username);
server->connrec->sasl_username = g_strdup(config_node_get_str(node, "sasl_username", NULL));
g_free(server->connrec->sasl_password);
server->connrec->sasl_password = g_strdup(config_node_get_str(node, "sasl_password", NULL));
if (server->isupport == NULL) {
server->isupport = g_hash_table_new((GHashFunc) g_istr_hash,
(GCompareFunc) g_istr_equal);

View file

@ -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)

View file

@ -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"))

View file

@ -488,7 +488,7 @@ static void event_mode(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
&channel, &mode);
if (!ischannel(*channel)) {
if (!server_ischannel(SERVER(server), channel)) {
/* user mode change */
parse_user_mode(server, mode);
} else {
@ -536,7 +536,7 @@ static void sig_req_usermode_change(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
&target, &mode);
if (!ischannel(*target)) {
if (!server_ischannel(SERVER(server), target)) {
/* we requested a user mode change, save this */
mode = modes_join(NULL, server->wanted_usermode, mode, FALSE);
g_free_not_null(server->wanted_usermode);
@ -856,7 +856,7 @@ static void cmd_mode(const char *data, IRC_SERVER_REC *server,
target = chanrec->name;
irc_send_cmdv(server, "MODE %s", target);
} else if (ischannel(*target))
} else if (server_ischannel(SERVER(server), target))
channel_set_mode(server, target, mode);
else {
if (g_ascii_strcasecmp(target, server->nick) == 0) {

178
src/irc/core/sasl.c Normal file
View file

@ -0,0 +1,178 @@
/*
fe-sasl.c : irssi
Copyright (C) 2015 The Lemon Man
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "misc.h"
#include "settings.h"
#include "irc-cap.h"
#include "irc-servers.h"
#include "sasl.h"
#define SASL_TIMEOUT (20 * 1000) // ms
static gboolean sasl_timeout(IRC_SERVER_REC *server)
{
/* The authentication timed out, we can't do much beside terminating it */
irc_send_cmd_now(server, "AUTHENTICATE *");
cap_finish_negotiation(server);
server->sasl_timeout = -1;
signal_emit("server sasl failure", 2, server, "The authentication timed out");
return FALSE;
}
static void sasl_start(IRC_SERVER_REC *server, const char *data, const char *from)
{
IRC_SERVER_CONNECT_REC *conn;
conn = server->connrec;
switch (conn->sasl_mechanism) {
case SASL_MECHANISM_PLAIN:
irc_send_cmd_now(server, "AUTHENTICATE PLAIN");
break;
case SASL_MECHANISM_EXTERNAL:
irc_send_cmd_now(server, "AUTHENTICATE EXTERNAL");
break;
}
server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
}
static void sasl_fail(IRC_SERVER_REC *server, const char *data, const char *from)
{
char *params, *error;
/* Stop any pending timeout, if any */
if (server->sasl_timeout != -1) {
g_source_remove(server->sasl_timeout);
server->sasl_timeout = -1;
}
params = event_get_params(data, 2, NULL, &error);
signal_emit("server sasl fail", 2, server, error);
/* Terminate the negotiation */
cap_finish_negotiation(server);
g_free(params);
}
static void sasl_already(IRC_SERVER_REC *server, const char *data, const char *from)
{
if (server->sasl_timeout != -1) {
g_source_remove(server->sasl_timeout);
server->sasl_timeout = -1;
}
signal_emit("server sasl success", 1, server);
/* We're already authenticated, do nothing */
cap_finish_negotiation(server);
}
static void sasl_success(IRC_SERVER_REC *server, const char *data, const char *from)
{
if (server->sasl_timeout != -1) {
g_source_remove(server->sasl_timeout);
server->sasl_timeout = -1;
}
signal_emit("server sasl success", 1, server);
/* The authentication succeeded, time to finish the CAP negotiation */
cap_finish_negotiation(server);
}
static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from)
{
IRC_SERVER_CONNECT_REC *conn;
GString *req;
char *enc_req;
conn = server->connrec;
/* Stop the timer */
if (server->sasl_timeout != -1) {
g_source_remove(server->sasl_timeout);
server->sasl_timeout = -1;
}
switch (conn->sasl_mechanism) {
case SASL_MECHANISM_PLAIN:
/* At this point we assume that conn->sasl_{username, password} are non-NULL.
* The PLAIN mechanism expects a NULL-separated string composed by the authorization identity, the
* authentication identity and the password.
* The authorization identity field is explicitly set to the user provided username.
* The whole request is then encoded in base64. */
req = g_string_new(NULL);
g_string_append(req, conn->sasl_username);
g_string_append_c(req, '\0');
g_string_append(req, conn->sasl_username);
g_string_append_c(req, '\0');
g_string_append(req, conn->sasl_password);
enc_req = g_base64_encode((const guchar *)req->str, req->len);
irc_send_cmdv(server, "AUTHENTICATE %s", enc_req);
g_free(enc_req);
g_string_free(req, TRUE);
break;
case SASL_MECHANISM_EXTERNAL:
/* Empty response */
irc_send_cmdv(server, "+");
break;
}
/* We expect a response within a reasonable time */
server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
}
void sasl_init(void)
{
signal_add_first("server cap ack sasl", (SIGNAL_FUNC) sasl_start);
signal_add_first("event authenticate", (SIGNAL_FUNC) sasl_step);
signal_add_first("event 903", (SIGNAL_FUNC) sasl_success);
signal_add_first("event 902", (SIGNAL_FUNC) sasl_fail);
signal_add_first("event 904", (SIGNAL_FUNC) sasl_fail);
signal_add_first("event 905", (SIGNAL_FUNC) sasl_fail);
signal_add_first("event 906", (SIGNAL_FUNC) sasl_fail);
signal_add_first("event 907", (SIGNAL_FUNC) sasl_already);
}
void sasl_deinit(void)
{
signal_remove("server cap ack sasl", (SIGNAL_FUNC) sasl_start);
signal_remove("event authenticate", (SIGNAL_FUNC) sasl_step);
signal_remove("event 903", (SIGNAL_FUNC) sasl_success);
signal_remove("event 902", (SIGNAL_FUNC) sasl_fail);
signal_remove("event 904", (SIGNAL_FUNC) sasl_fail);
signal_remove("event 905", (SIGNAL_FUNC) sasl_fail);
signal_remove("event 906", (SIGNAL_FUNC) sasl_fail);
signal_remove("event 907", (SIGNAL_FUNC) sasl_already);
}

34
src/irc/core/sasl.h Normal file
View file

@ -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

View file

@ -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);

View file

@ -51,7 +51,7 @@ static void sig_dcc_request(GET_DCC_REC *dcc, const char *nickaddr)
/* Unless specifically said in dcc_autoget_masks, don't do autogets
sent to channels. */
if (*masks == '\0' && dcc->target != NULL && ischannel(*dcc->target))
if (*masks == '\0' && dcc->target != NULL && server_ischannel(SERVER(dcc->server), dcc->target))
return;
/* don't autoget files beginning with a dot, if download dir is

View file

@ -226,6 +226,8 @@ void sig_dccget_connected(GET_DCC_REC *dcc)
else
ret = fchmod(temphandle, dcc_file_create_mode);
close(temphandle);
if (ret != -1) {
ret = link(tempfname, dcc->file);
@ -249,7 +251,6 @@ void sig_dccget_connected(GET_DCC_REC *dcc)
/* close/remove the temp file */
ret_errno = errno;
close(temphandle);
unlink(tempfname);
g_free(tempfname);

View file

@ -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);
}

View file

@ -169,7 +169,7 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
NICK_REC *nick = tmp->data;
if (str->len >= 500) {
g_string_append_c(str, '\r\n');
g_string_append(str, "\r\n");
proxy_outdata(client, "%s", str->str);
create_names_start(str, channel, client);
first = TRUE;
@ -186,7 +186,7 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
}
g_slist_free(nicks);
g_string_append_c(str, '\r\n');
g_string_append(str, "\r\n");
proxy_outdata(client, "%s", str->str);
g_string_free(str, TRUE);

View file

@ -37,6 +37,8 @@ GSList *proxy_clients;
static GString *next_line;
static int ignore_next;
static int enabled = FALSE;
static void remove_client(CLIENT_REC *rec)
{
g_return_if_fail(rec != NULL);
@ -45,8 +47,8 @@ static void remove_client(CLIENT_REC *rec)
rec->listen->clients = g_slist_remove(rec->listen->clients, rec);
signal_emit("proxy client disconnected", 1, rec);
printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Client disconnected from %s", rec->host);
printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Client %s:%d disconnected", rec->host, rec->port);
g_free(rec->proxy_address);
net_sendbuffer_destroy(rec->handle, TRUE);
@ -126,6 +128,10 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
/* client didn't send us PASS, kill it */
remove_client(client);
} else {
signal_emit("proxy client connected", 1, client);
printtext(client->server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Client %s:%d connected",
client->host, client->port);
client->connected = TRUE;
proxy_dump_data(client);
}
@ -262,7 +268,7 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
ignore_next = TRUE;
if (*msg != '\001' || msg[strlen(msg)-1] != '\001') {
signal_emit(ischannel(*target) ?
signal_emit(server_ischannel(SERVER(client->server), target) ?
"message own_public" : "message own_private", 4,
client->server, msg, target, target);
} else if (strncmp(msg+1, "ACTION ", 7) == 0) {
@ -347,6 +353,7 @@ static void sig_listen(LISTEN_REC *listen)
rec->listen = listen;
rec->handle = sendbuf;
rec->host = g_strdup(host);
rec->port = port;
if (g_strcmp0(listen->ircnet, "*") == 0) {
rec->proxy_address = g_strdup("irc.proxy");
rec->server = servers == NULL ? NULL : IRC_SERVER(servers->data);
@ -361,9 +368,10 @@ static void sig_listen(LISTEN_REC *listen)
proxy_clients = g_slist_prepend(proxy_clients, rec);
rec->listen->clients = g_slist_prepend(rec->listen->clients, rec);
signal_emit("proxy client connected", 1, rec);
printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Client connected from %s", rec->host);
signal_emit("proxy client connecting", 1, rec);
printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: New client %s:%d on port %d (%s)",
rec->host, rec->port, listen->port, listen->ircnet);
}
static void sig_incoming(IRC_SERVER_REC *server, const char *line)
@ -634,7 +642,8 @@ static void remove_listen(LISTEN_REC *rec)
static void read_settings(void)
{
LISTEN_REC *rec;
GSList *remove_listens;
GSList *remove_listens = NULL;
GSList *add_listens = NULL;
char **ports, **tmp, *ircnet, *port;
int portnum;
@ -653,17 +662,30 @@ static void read_settings(void)
continue;
rec = find_listen(ircnet, portnum);
if (rec == NULL)
add_listen(ircnet, portnum);
else
if (rec == NULL) {
rec = g_new0(LISTEN_REC, 1);
rec->ircnet = ircnet; /* borrow */
rec->port = portnum;
add_listens = g_slist_prepend(add_listens, rec);
} else {
/* remove from the list of listens to remove == keep it */
remove_listens = g_slist_remove(remove_listens, rec);
}
}
g_strfreev(ports);
while (remove_listens != NULL) {
remove_listen(remove_listens->data);
remove_listen(remove_listens->data);
remove_listens = g_slist_remove(remove_listens, remove_listens->data);
}
while (add_listens != NULL) {
rec = add_listens->data;
add_listen(rec->ircnet, rec->port);
g_free(rec);
add_listens = g_slist_remove(add_listens, add_listens->data);
}
g_strfreev(ports);
}
static void sig_dump(CLIENT_REC *client, const char *data)
@ -676,6 +698,11 @@ static void sig_dump(CLIENT_REC *client, const char *data)
void proxy_listen_init(void)
{
if (enabled) {
return;
}
enabled = TRUE;
next_line = g_string_new(NULL);
proxy_clients = NULL;
@ -697,6 +724,11 @@ void proxy_listen_init(void)
void proxy_listen_deinit(void)
{
if (!enabled) {
return;
}
enabled = FALSE;
while (proxy_listens != NULL)
remove_listen(proxy_listens->data);
g_string_free(next_line, TRUE);

View file

@ -23,11 +23,60 @@
#include "settings.h"
#include "levels.h"
#include "fe-common/core/printtext.h"
/* SYNTAX: IRSSIPROXY STATUS */
static void cmd_irssiproxy_status(const char *data, IRC_SERVER_REC *server)
{
if (!settings_get_bool("irssiproxy")) {
printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy is currently disabled");
return;
}
GSList *tmp;
printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Currently connected clients: %d",
g_slist_length(proxy_clients));
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
" %s:%d connect%s to %d (%s)",
rec->host, rec->port,
rec->connected ? "ed" : "ing",
rec->listen->port, rec->listen->ircnet);
}
}
/* SYNTAX: IRSSIPROXY */
static void cmd_irssiproxy(const char *data, IRC_SERVER_REC *server, void *item)
{
if (*data == '\0') {
cmd_irssiproxy_status(data, server);
return;
}
command_runsub("irssiproxy", data, server, item);
}
static void irc_proxy_setup_changed(void)
{
if (settings_get_bool("irssiproxy")) {
proxy_listen_init();
} else {
proxy_listen_deinit();
}
}
void irc_proxy_init(void)
{
settings_add_str("irssiproxy", "irssiproxy_ports", "");
settings_add_str("irssiproxy", "irssiproxy_password", "");
settings_add_str("irssiproxy", "irssiproxy_bind", "");
settings_add_bool("irssiproxy", "irssiproxy", TRUE);
if (*settings_get_str("irssiproxy_password") == '\0') {
/* no password - bad idea! */
@ -43,7 +92,14 @@ void irc_proxy_init(void)
"... to set them.");
}
proxy_listen_init();
command_bind("irssiproxy", NULL, (SIGNAL_FUNC) cmd_irssiproxy);
command_bind("irssiproxy status", NULL, (SIGNAL_FUNC) cmd_irssiproxy_status);
signal_add_first("setup changed", (SIGNAL_FUNC) irc_proxy_setup_changed);
if (settings_get_bool("irssiproxy")) {
proxy_listen_init();
}
settings_check();
module_register("proxy", "irc");
}

View file

@ -19,6 +19,7 @@ typedef struct {
typedef struct {
char *nick, *host;
int port;
NET_SENDBUF_REC *handle;
int recv_tag;
char *proxy_address;

View file

@ -149,6 +149,7 @@ static void perl_client_fill_hash(HV *hv, CLIENT_REC *client)
{
(void) hv_store(hv, "nick", 4, new_pv(client->nick), 0);
(void) hv_store(hv, "host", 4, new_pv(client->host), 0);
(void) hv_store(hv, "port", 4, newSViv(client->port), 0);
(void) hv_store(hv, "proxy_address", 13, new_pv(client->proxy_address), 0);
(void) hv_store(hv, "server", 6, iobject_bless(client->server), 0);
(void) hv_store(hv, "pass_sent", 9, newSViv(client->pass_sent), 0);

View file

@ -10,7 +10,7 @@
# Remember to include the asterisk ('*').
$SRC_PATH='src';
@files = `find src -name '*.c'`;
@files = sort `find src -name '*.c'`;
foreach $file (@files) {
open (FILE, "$file");