summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes3
-rw-r--r--INSTALL7
-rw-r--r--NEWS5
-rw-r--r--acconfig.h1
-rw-r--r--configure.ac35
-rw-r--r--docs/proxy.txt12
-rw-r--r--docs/startup-HOWTO.html17
-rw-r--r--src/common.h2
-rw-r--r--src/core/channels.c31
-rw-r--r--src/core/channels.h1
-rw-r--r--src/core/chat-commands.c11
-rw-r--r--src/core/commands.h3
-rw-r--r--src/core/misc.c4
-rw-r--r--src/core/network.c124
-rw-r--r--src/core/network.h10
-rw-r--r--src/core/servers-setup.c4
-rw-r--r--src/fe-common/core/completion.c5
-rw-r--r--src/fe-common/core/fe-common-core.c11
-rw-r--r--src/fe-common/core/fe-core-commands.c3
-rw-r--r--src/fe-common/core/module-formats.c1
-rw-r--r--src/fe-common/core/module-formats.h1
-rw-r--r--src/fe-text/gui-entry.c64
-rw-r--r--src/fe-text/gui-entry.h14
-rw-r--r--src/fe-text/gui-readline.c45
-rw-r--r--src/fe-text/term-terminfo.c2
-rw-r--r--src/irc/core/irc-channels-setup.c6
-rw-r--r--src/irc/proxy/listen.c316
-rw-r--r--src/irc/proxy/proxy.c6
-rw-r--r--src/irc/proxy/proxy.h6
-rw-r--r--src/perl/irc/Irc.xs4
30 files changed, 446 insertions, 308 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..e1ae7d9d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+# On Windows, some Git clients may normalize all text files' line-endings to
+# CRLF, which causes obscure errors when running shell scripts.
+*.sh eol=lf
diff --git a/INSTALL b/INSTALL
index a83a6c76..4b20c1ba 100644
--- a/INSTALL
+++ b/INSTALL
@@ -11,7 +11,8 @@ To compile irssi you need:
For most people, this should work just fine:
- ./configure
+ ./autogen.sh (for people who just cloned the repository)
+ ./configure (if this script already exists, skip ./autogen.sh)
make
su
make install (not _really_ required except for perl support)
@@ -28,10 +29,6 @@ configure options
Build the irssi proxy (see startup-HOWTO).
- --disable-ipv6
-
- Disable IPv6 support.
-
--disable-ssl
Disable SSL support.
diff --git a/NEWS b/NEWS
index 65aab5ed..0d450b02 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+v0.8.20-head 2016-xx-xx The Irssi team <staff@irssi.org>
+ * Removed --disable-ipv6
+ + irssiproxy can now forward all tags through a single port.
+ + send channel -botcmds immediately when no mask is specified (#175).
+
v0.8.19 2016-03-23 The Irssi team <staff@irssi.org>
- Fixed regression when joining and parting channels on IRCnet (#435)
- Fixed SASL EXTERNAL (#432)
diff --git a/acconfig.h b/acconfig.h
index 9ace70d8..53e268d7 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -1,5 +1,4 @@
/* misc.. */
-#undef HAVE_IPV6
#undef HAVE_SOCKS_H
#undef HAVE_STATIC_PERL
#undef HAVE_GMODULE
diff --git a/configure.ac b/configure.ac
index b25e7286..2c47bd2b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT(irssi, 0.8.19)
+AC_INIT(irssi, 0.8.20-head)
AC_CONFIG_SRCDIR([src])
AC_CONFIG_AUX_DIR(build-aux)
AC_PREREQ(2.50)
@@ -140,15 +140,6 @@ AC_ARG_WITH(perl,
fi,
want_perl=static)
-AC_ARG_ENABLE(ipv6,
-[ --disable-ipv6 Disable IPv6 support],
- if test x$enableval = xno; then
- want_ipv6=no
- else
- want_ipv6=yes
- fi,
- want_ipv6=yes)
-
AC_ARG_ENABLE(dane,
[ --enable-dane Enable DANE support],
if test x$enableval = xno ; then
@@ -577,29 +568,6 @@ COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_LIBS"
AC_SUBST(COMMON_NOUI_LIBS)
AC_SUBST(COMMON_LIBS)
-dnl **
-dnl ** IPv6 support
-dnl **
-
-have_ipv6=no
-if test "x$want_ipv6" = "xyes"; then
- AC_MSG_CHECKING([for IPv6])
- AC_CACHE_VAL(irssi_cv_type_in6_addr,
- [AC_TRY_COMPILE([
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <arpa/inet.h>],
- [struct in6_addr i = in6addr_any; return &i == &i;],
- have_ipv6=yes,
- )])
- if test $have_ipv6 = yes; then
- AC_DEFINE(HAVE_IPV6)
- fi
- AC_MSG_RESULT($have_ipv6)
-fi
-
have_dane=no
if test "x$want_dane" = "xyes"; then
AC_MSG_CHECKING([for DANE])
@@ -742,7 +710,6 @@ echo "Install prefix ................... : $prefix"
echo
-echo "Building with IPv6 support ....... : $have_ipv6"
echo "Building with SSL support ........ : $have_openssl"
if test "x$have_openssl" = "xno" -a "x$enable_ssl" = "xyes"; then
if test -f /etc/debian_version; then
diff --git a/docs/proxy.txt b/docs/proxy.txt
index e6360a82..224d24e3 100644
--- a/docs/proxy.txt
+++ b/docs/proxy.txt
@@ -30,6 +30,18 @@ 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.
+The special network name "?" allows the client to select the network
+dynamically on connect:
+
+ /SET irssiproxy_ports ?=2777
+
+Now the client can send <network>:<password> as the server password, e.g.
+
+ /CONNECT ... 2777 efnet:secret
+
+to connect to efnet. If there is no irssiproxy_password set, you can
+omit the ":" and just send the network name as the password.
+
By default, the proxy binds to all available interfaces. To make it
only listen on (for example) the loopback address:
diff --git a/docs/startup-HOWTO.html b/docs/startup-HOWTO.html
index 12128c59..07a9f47c 100644
--- a/docs/startup-HOWTO.html
+++ b/docs/startup-HOWTO.html
@@ -497,6 +497,13 @@
<pre><code> /SET irssiproxy_ports *=2777
</code></pre>
+<p>The special network name <code>?</code> allows the client to select the
+network dynamically on connect (see below):</p>
+
+<pre>
+/SET irssiproxy_ports ?=2777
+</pre>
+
<p>Usage in client side:</p>
<p>Just connect to the irssi proxy like it is a normal server with password specified in <code>/SET irssiproxy_password</code>. For example:</p>
@@ -505,6 +512,16 @@
/SERVER ADD -network efnet my.irssi-proxy.org 2778 secret
</code></pre>
+<p>Or, if you used <code>?</code> in <code>irssiproxy_ports</code>:</p>
+
+<pre>
+/SERVER ADD -network IRCnet my.irssi-proxy.org 2777 IRCnet:secret
+/SERVER ADD -network efnet my.irssi-proxy.org 2777 efnet:secret
+</pre>
+
+<p>I.e. the network to connect to is specified as part of the password,
+separated by <code>:</code> from the actual proxy password.</p>
+
<p>Irssi proxy works fine with other IRC clients as well.</p>
<p><strong>SOCKS</strong></p>
diff --git a/src/common.h b/src/common.h
index 729049ab..33dbd481 100644
--- a/src/common.h
+++ b/src/common.h
@@ -6,7 +6,7 @@
#define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */
#define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */
-#define IRSSI_ABI_VERSION 2
+#define IRSSI_ABI_VERSION 3
#define DEFAULT_SERVER_ADD_PORT 6667
diff --git a/src/core/channels.c b/src/core/channels.c
index 9af8b844..9c3b92ba 100644
--- a/src/core/channels.c
+++ b/src/core/channels.c
@@ -233,23 +233,46 @@ static int match_nick_flags(SERVER_REC *server, NICK_REC *nick, char flag)
void channel_send_autocommands(CHANNEL_REC *channel)
{
CHANNEL_SETUP_REC *rec;
- NICK_REC *nick;
- char **bots, **bot;
g_return_if_fail(IS_CHANNEL(channel));
if (channel->session_rejoin)
- return;
+ return;
rec = channel_setup_find(channel->name, channel->server->connrec->chatnet);
if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
return;
+ /* if the autosendcmd alone (with no -bots parameter) has been
+ * specified then send it right after joining the channel, when
+ * the WHO list hasn't been yet retrieved.
+ * Depending on the value of the 'channel_max_who_sync' option
+ * the WHO list might not be retrieved after the join event. */
+
if (rec->botmasks == NULL || !*rec->botmasks) {
/* just send the command. */
eval_special_string(rec->autosendcmd, "", channel->server, channel);
- return;
}
+}
+
+void channel_send_botcommands(CHANNEL_REC *channel)
+{
+ CHANNEL_SETUP_REC *rec;
+ NICK_REC *nick;
+ char **bots, **bot;
+
+ g_return_if_fail(IS_CHANNEL(channel));
+
+ if (channel->session_rejoin)
+ return;
+
+ rec = channel_setup_find(channel->name, channel->server->connrec->chatnet);
+ if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
+ return;
+
+ /* this case has already been handled by channel_send_autocommands */
+ if (rec->botmasks == NULL || !*rec->botmasks)
+ return;
/* find first available bot.. */
bots = g_strsplit(rec->botmasks, " ", -1);
diff --git a/src/core/channels.h b/src/core/channels.h
index 0839d69b..bd136fe2 100644
--- a/src/core/channels.h
+++ b/src/core/channels.h
@@ -31,6 +31,7 @@ void channel_change_visible_name(CHANNEL_REC *channel, const char *name);
/* Send the auto send command to channel */
void channel_send_autocommands(CHANNEL_REC *channel);
+void channel_send_botcommands(CHANNEL_REC *channel);
void channels_init(void);
void channels_deinit(void);
diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c
index 8e881679..a9404fa3 100644
--- a/src/core/chat-commands.c
+++ b/src/core/chat-commands.c
@@ -73,6 +73,13 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
conn = server_create_conn(proto != NULL ? proto->id : -1, addr,
atoi(portstr), chatnet, password, nick);
+ if (conn == NULL) {
+ signal_emit("error command", 1,
+ GINT_TO_POINTER(CMDERR_NO_SERVER_DEFINED));
+ cmd_params_free(free_arg);
+ return NULL;
+ }
+
if (proto == NULL)
proto = chat_protocol_find_id(conn->chat_type);
@@ -117,8 +124,8 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
if (g_hash_table_lookup(optlist, "!") != NULL)
conn->no_autojoin_channels = TRUE;
- if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
- conn->no_autosendcmd = TRUE;
+ if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
+ conn->no_autosendcmd = TRUE;
if (g_hash_table_lookup(optlist, "noproxy") != NULL)
g_free_and_null(conn->proxy);
diff --git a/src/core/commands.h b/src/core/commands.h
index 72105ad3..93d6276b 100644
--- a/src/core/commands.h
+++ b/src/core/commands.h
@@ -41,7 +41,8 @@ enum {
CMDERR_INVALID_TIME, /* invalid time specification */
CMDERR_INVALID_CHARSET, /* invalid charset specification */
CMDERR_EVAL_MAX_RECURSE, /* eval hit recursion limit */
- CMDERR_PROGRAM_NOT_FOUND /* program not found */
+ CMDERR_PROGRAM_NOT_FOUND, /* program not found */
+ CMDERR_NO_SERVER_DEFINED, /* no server has been defined for a given chatnet */
};
/* Return the full command for `alias' */
diff --git a/src/core/misc.c b/src/core/misc.c
index aaa470f5..c26610ec 100644
--- a/src/core/misc.c
+++ b/src/core/misc.c
@@ -169,8 +169,8 @@ int strarray_find(char **array, const char *item)
char **tmp;
int index;
- g_return_val_if_fail(array != NULL, 0);
- g_return_val_if_fail(item != NULL, 0);
+ g_return_val_if_fail(array != NULL, -1);
+ g_return_val_if_fail(item != NULL, -1);
index = 0;
for (tmp = array; *tmp != NULL; tmp++, index++) {
diff --git a/src/core/network.c b/src/core/network.c
index 0751aa95..76659f3e 100644
--- a/src/core/network.c
+++ b/src/core/network.c
@@ -30,17 +30,11 @@
union sockaddr_union {
struct sockaddr sa;
struct sockaddr_in sin;
-#ifdef HAVE_IPV6
struct sockaddr_in6 sin6;
-#endif
};
-#ifdef HAVE_IPV6
-# define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
+#define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
sizeof(so.sin6) : sizeof(so.sin))
-#else
-# define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
-#endif
GIOChannel *g_io_channel_new(int handle)
{
@@ -56,7 +50,7 @@ GIOChannel *g_io_channel_new(int handle)
IPADDR ip4_any = {
AF_INET,
-#if defined(HAVE_IPV6) && defined(IN6ADDR_ANY_INIT)
+#if defined(IN6ADDR_ANY_INIT)
IN6ADDR_ANY_INIT
#else
{ INADDR_ANY }
@@ -68,10 +62,8 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
if (ip1->family != ip2->family)
return 0;
-#ifdef HAVE_IPV6
if (ip1->family == AF_INET6)
return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0;
-#endif
return memcmp(&ip1->ip, &ip2->ip, 4) == 0;
}
@@ -80,22 +72,16 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
static void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
{
if (ip == NULL) {
-#ifdef HAVE_IPV6
so->sin6.sin6_family = AF_INET6;
so->sin6.sin6_addr = in6addr_any;
-#else
- so->sin.sin_family = AF_INET;
- so->sin.sin_addr.s_addr = INADDR_ANY;
-#endif
return;
}
so->sin.sin_family = ip->family;
-#ifdef HAVE_IPV6
+
if (ip->family == AF_INET6)
memcpy(&so->sin6.sin6_addr, &ip->ip, sizeof(ip->ip));
else
-#endif
memcpy(&so->sin.sin_addr, &ip->ip, 4);
}
@@ -103,31 +89,25 @@ void sin_get_ip(const union sockaddr_union *so, IPADDR *ip)
{
ip->family = so->sin.sin_family;
-#ifdef HAVE_IPV6
if (ip->family == AF_INET6)
memcpy(&ip->ip, &so->sin6.sin6_addr, sizeof(ip->ip));
else
-#endif
memcpy(&ip->ip, &so->sin.sin_addr, 4);
}
static void sin_set_port(union sockaddr_union *so, int port)
{
-#ifdef HAVE_IPV6
if (so->sin.sin_family == AF_INET6)
so->sin6.sin6_port = htons((unsigned short)port);
else
-#endif
so->sin.sin_port = htons((unsigned short)port);
}
static int sin_get_port(union sockaddr_union *so)
{
-#ifdef HAVE_IPV6
- if (so->sin.sin_family == AF_INET6)
- return ntohs(so->sin6.sin6_port);
-#endif
- return ntohs(so->sin.sin_port);
+ return ntohs((so->sin.sin_family == AF_INET6) ?
+ so->sin6.sin6_port :
+ so->sin.sin_port);
}
/* Connect to socket */
@@ -272,7 +252,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port)
/* create the socket */
handle = socket(so.sin.sin_family, SOCK_STREAM, 0);
-#ifdef HAVE_IPV6
+
if (handle == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) {
/* IPv6 is not supported by OS */
so.sin.sin_family = AF_INET;
@@ -280,7 +260,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port)
handle = socket(AF_INET, SOCK_STREAM, 0);
}
-#endif
+
if (handle == -1)
return NULL;
@@ -399,23 +379,18 @@ int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port)
Returns 0 = ok, others = error code for net_gethosterror() */
int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
{
-#ifdef HAVE_IPV6
union sockaddr_union *so;
struct addrinfo hints, *ai, *ailist;
int ret, count_v4, count_v6, use_v4, use_v6;
-#else
- struct hostent *hp;
- int count;
-#endif
g_return_val_if_fail(addr != NULL, -1);
memset(ip4, 0, sizeof(IPADDR));
memset(ip6, 0, sizeof(IPADDR));
-#ifdef HAVE_IPV6
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG;
/* save error to host_error for later use */
ret = getaddrinfo(addr, NULL, &hints, &ailist);
@@ -454,85 +429,40 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
}
freeaddrinfo(ailist);
return 0;
-#else
- hp = gethostbyname(addr);
- if (hp == NULL)
- return h_errno;
-
- /* count IPs */
- count = 0;
- while (hp->h_addr_list[count] != NULL)
- count++;
-
- if (count == 0)
- return HOST_NOT_FOUND; /* shouldn't happen? */
-
- /* if there are multiple addresses, return random one */
- ip4->family = AF_INET;
- memcpy(&ip4->ip, hp->h_addr_list[rand() % count], 4);
-
- return 0;
-#endif
}
/* Get name for host, *name should be g_free()'d unless it's NULL.
Return values are the same as with net_gethostbyname() */
int net_gethostbyaddr(IPADDR *ip, char **name)
{
-#ifdef HAVE_IPV6
union sockaddr_union so;
int host_error;
char hostname[NI_MAXHOST];
-#else
- struct hostent *hp;
-#endif
g_return_val_if_fail(ip != NULL, -1);
g_return_val_if_fail(name != NULL, -1);
*name = NULL;
-#ifdef HAVE_IPV6
+
memset(&so, 0, sizeof(so));
sin_set_ip(&so, ip);
/* save error to host_error for later use */
- host_error = getnameinfo((struct sockaddr *) &so, sizeof(so),
- hostname, sizeof(hostname), NULL, 0, 0);
+ host_error = getnameinfo((struct sockaddr *)&so, sizeof(so),
+ hostname, sizeof(hostname),
+ NULL, 0,
+ NI_NAMEREQD);
if (host_error != 0)
return host_error;
*name = g_strdup(hostname);
-#else
- if (ip->family != AF_INET) return -1;
- hp = gethostbyaddr((const char *) &ip->ip, 4, AF_INET);
- if (hp == NULL) return -1;
-
- *name = g_strdup(hp->h_name);
-#endif
return 0;
}
int net_ip2host(IPADDR *ip, char *host)
{
-#ifdef HAVE_IPV6
- if (!inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN))
- return -1;
-#else
- unsigned long ip4;
-
- if (ip->family != AF_INET) {
- strcpy(host, "0.0.0.0");
- } else {
- ip4 = ntohl(ip->ip.s_addr);
- g_snprintf(host, MAX_IP_LEN, "%lu.%lu.%lu.%lu",
- (ip4 & 0xff000000UL) >> 24,
- (ip4 & 0x00ff0000) >> 16,
- (ip4 & 0x0000ff00) >> 8,
- (ip4 & 0x000000ff));
- }
-#endif
- return 0;
+ return inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN) ? 0 : -1;
}
int net_host2ip(const char *host, IPADDR *ip)
@@ -542,12 +472,8 @@ int net_host2ip(const char *host, IPADDR *ip)
if (strchr(host, ':') != NULL) {
/* IPv6 */
ip->family = AF_INET6;
-#ifdef HAVE_IPV6
if (inet_pton(AF_INET6, host, &ip->ip) == 0)
return -1;
-#else
- ip->ip.s_addr = 0;
-#endif
} else {
/* IPv4 */
ip->family = AF_INET;
@@ -582,40 +508,20 @@ int net_geterror(GIOChannel *handle)
/* get error of net_gethostname() */
const char *net_gethosterror(int error)
{
-#ifdef HAVE_IPV6
g_return_val_if_fail(error != 0, NULL);
return gai_strerror(error);
-#else
- switch (error) {
- case HOST_NOT_FOUND:
- return "Host not found";
- case NO_ADDRESS:
- return "No IP address found for name";
- case NO_RECOVERY:
- return "A non-recovable name server error occurred";
- case TRY_AGAIN:
- return "A temporary error on an authoritative name server";
- }
-
- /* unknown error */
- return NULL;
-#endif
}
/* return TRUE if host lookup failed because it didn't exist (ie. not
some error with name server) */
int net_hosterror_notfound(int error)
{
-#ifdef HAVE_IPV6
#ifdef EAI_NODATA /* NODATA is deprecated */
return error != 1 && (error == EAI_NONAME || error == EAI_NODATA);
#else
return error != 1 && (error == EAI_NONAME);
#endif
-#else
- return error == HOST_NOT_FOUND || error == NO_ADDRESS;
-#endif
}
/* Get name of TCP service */
diff --git a/src/core/network.h b/src/core/network.h
index 73ac8dee..fb627b4d 100644
--- a/src/core/network.h
+++ b/src/core/network.h
@@ -21,19 +21,11 @@
struct _IPADDR {
unsigned short family;
-#ifdef HAVE_IPV6
struct in6_addr ip;
-#else
- struct in_addr ip;
-#endif
};
/* maxmimum string length of IP address */
-#ifdef HAVE_IPV6
-# define MAX_IP_LEN INET6_ADDRSTRLEN
-#else
-# define MAX_IP_LEN 20
-#endif
+#define MAX_IP_LEN INET6_ADDRSTRLEN
#define IPADDR_IS_V6(ip) ((ip)->family != AF_INET)
diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c
index 4a048282..0cecfece 100644
--- a/src/core/servers-setup.c
+++ b/src/core/servers-setup.c
@@ -321,8 +321,8 @@ server_create_conn(int chat_type, const char *dest, int port,
chatrec = chatnet_find(dest);
if (chatrec != NULL) {
rec = create_chatnet_conn(chatrec->name, port, password, nick);
- if (rec != NULL)
- return rec;
+ /* If rec is NULL the chatnet has no url to connect to */
+ return rec;
}
chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
diff --git a/src/fe-common/core/completion.c b/src/fe-common/core/completion.c
index 4461de92..861054b5 100644
--- a/src/fe-common/core/completion.c
+++ b/src/fe-common/core/completion.c
@@ -217,6 +217,11 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
want_space = TRUE;
signal_emit("complete word", 5, &complist, window, word, linestart, &want_space);
last_want_space = want_space;
+
+ if (complist != NULL) {
+ /* Remove all nulls (from the signal) before doing further processing */
+ complist = g_list_remove_all(g_list_first(complist), NULL);
+ }
}
g_free(linestart);
diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c
index ee7f9424..1b2ab1e2 100644
--- a/src/fe-common/core/fe-common-core.c
+++ b/src/fe-common/core/fe-common-core.c
@@ -459,15 +459,24 @@ gboolean strarray_find_dest(char **array, const TEXT_DEST_REC *dest)
{
g_return_val_if_fail(array != NULL, FALSE);
+ if (strarray_find(array, "*") != -1)
+ return TRUE;
+
if (strarray_find(array, dest->target) != -1)
return TRUE;
if (dest->server_tag != NULL) {
- char *tagtarget = g_strdup_printf("%s/%s", dest->server_tag, dest->target);
+ char *tagtarget = g_strdup_printf("%s/%s", dest->server_tag, "*");
int ret = strarray_find(array, tagtarget);
g_free(tagtarget);
if (ret != -1)
return TRUE;
+
+ tagtarget = g_strdup_printf("%s/%s", dest->server_tag, dest->target);
+ ret = strarray_find(array, tagtarget);
+ g_free(tagtarget);
+ if (ret != -1)
+ return TRUE;
}
return FALSE;
}
diff --git a/src/fe-common/core/fe-core-commands.c b/src/fe-common/core/fe-core-commands.c
index 7b1d7b94..97a246ec 100644
--- a/src/fe-common/core/fe-core-commands.c
+++ b/src/fe-common/core/fe-core-commands.c
@@ -51,7 +51,8 @@ static int ret_texts[] = {
TXT_INVALID_TIME,
TXT_INVALID_CHARSET,
TXT_EVAL_MAX_RECURSE,
- TXT_PROGRAM_NOT_FOUND
+ TXT_PROGRAM_NOT_FOUND,
+ TXT_NO_SERVER_DEFINED,
};
int command_hide_output;
diff --git a/src/fe-common/core/module-formats.c b/src/fe-common/core/module-formats.c
index e6d32b6d..b897b0c6 100644
--- a/src/fe-common/core/module-formats.c
+++ b/src/fe-common/core/module-formats.c
@@ -223,6 +223,7 @@ FORMAT_REC fecommon_core_formats[] = {
{ "invalid_charset", "Invalid charset: $0", 1, { 0 } },
{ "eval_max_recurse", "/eval hit maximum recursion limit", 0 },
{ "program_not_found", "Could not find file or file is not executable", 0 },
+ { "no_server_defined", "No servers defined for this network, see /help server for how to add one", 0 },
/* ---- */
{ NULL, "Themes", 0 },
diff --git a/src/fe-common/core/module-formats.h b/src/fe-common/core/module-formats.h
index 3f06bb97..de1e13f2 100644
--- a/src/fe-common/core/module-formats.h
+++ b/src/fe-common/core/module-formats.h
@@ -192,6 +192,7 @@ enum {
TXT_INVALID_CHARSET,
TXT_EVAL_MAX_RECURSE,
TXT_PROGRAM_NOT_FOUND,
+ TXT_NO_SERVER_DEFINED,
TXT_FILL_11,
diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c
index 306141ec..f275c235 100644
--- a/src/fe-text/gui-entry.c
+++ b/src/fe-text/gui-entry.c
@@ -562,7 +562,7 @@ char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry)
return buf;
}
-void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
+void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, CUTBUFFER_UPDATE_OP update_cutbuffer)
{
int newpos, size = 0;
@@ -573,7 +573,7 @@ void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
gui_entry_erase(entry, size, update_cutbuffer);
}
-void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer)
{
size_t w = 0;
@@ -582,17 +582,51 @@ void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
if (size == 0 || entry->pos < size)
return;
- if (update_cutbuffer) {
- /* put erased text to cutbuffer */
- if (entry->cutbuffer_len < size) {
- g_free(entry->cutbuffer);
- entry->cutbuffer = g_new(unichar, size+1);
- }
+ if (update_cutbuffer != CUTBUFFER_UPDATE_NOOP
+ && entry->cutbuffer_len == 0) {
+ update_cutbuffer = CUTBUFFER_UPDATE_REPLACE;
+ }
+ int cutbuffer_new_size = entry->cutbuffer_len + size;
+ unichar *tmpcutbuffer = entry->cutbuffer;
+ switch (update_cutbuffer) {
+ case CUTBUFFER_UPDATE_APPEND:
+ entry->cutbuffer = g_new(unichar, cutbuffer_new_size+1);
+ memcpy(entry->cutbuffer, tmpcutbuffer,
+ entry->cutbuffer_len * sizeof(unichar));
+ memcpy(entry->cutbuffer + entry->cutbuffer_len * sizeof(unichar),
+ entry->text + entry->pos - size, size * sizeof(unichar));
+
+ entry->cutbuffer_len = cutbuffer_new_size;
+ entry->cutbuffer[cutbuffer_new_size] = '\0';
+ g_free(tmpcutbuffer);
+ break;
+
+ case CUTBUFFER_UPDATE_PREPEND:
+ entry->cutbuffer = g_new(unichar, cutbuffer_new_size+1);
+ memcpy(entry->cutbuffer, entry->text + entry->pos - size,
+ size * sizeof(unichar));
+ memcpy(entry->cutbuffer + size, tmpcutbuffer,
+ entry->cutbuffer_len * sizeof(unichar));
+
+ entry->cutbuffer_len = cutbuffer_new_size;
+ entry->cutbuffer[cutbuffer_new_size] = '\0';
+ g_free(tmpcutbuffer);
+ break;
- entry->cutbuffer_len = size;
- entry->cutbuffer[size] = '\0';
- memcpy(entry->cutbuffer, entry->text + entry->pos - size,
- size * sizeof(unichar));
+ case CUTBUFFER_UPDATE_REPLACE:
+ /* put erased text to cutbuffer */
+ if (entry->cutbuffer_len < size) {
+ g_free(entry->cutbuffer);
+ entry->cutbuffer = g_new(unichar, size+1);
+ }
+
+ entry->cutbuffer_len = size;
+ entry->cutbuffer[size] = '\0';
+ memcpy(entry->cutbuffer, entry->text + entry->pos - size, size * sizeof(unichar));
+ break;
+
+ case CUTBUFFER_UPDATE_NOOP:
+ break;
}
if (entry->utf8)
@@ -629,7 +663,7 @@ void gui_entry_erase_cell(GUI_ENTRY_REC *entry)
gui_entry_draw(entry);
}
-void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
+void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op)
{
int to;
@@ -652,7 +686,7 @@ void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
}
if (to > 0) to++;
- gui_entry_erase(entry, entry->pos-to, TRUE);
+ gui_entry_erase(entry, entry->pos-to, CUTBUFFER_UPDATE_REPLACE);
}
void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
@@ -678,7 +712,7 @@ void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
size = to-entry->pos;
entry->pos = to;
- gui_entry_erase(entry, size, TRUE);
+ gui_entry_erase(entry, size, CUTBUFFER_UPDATE_REPLACE);
}
void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)
diff --git a/src/fe-text/gui-entry.h b/src/fe-text/gui-entry.h
index 29d8dea2..d22a7fb8 100644
--- a/src/fe-text/gui-entry.h
+++ b/src/fe-text/gui-entry.h
@@ -20,6 +20,13 @@ typedef struct {
unsigned int utf8:1;
} GUI_ENTRY_REC;
+typedef enum {
+ CUTBUFFER_UPDATE_NOOP,
+ CUTBUFFER_UPDATE_REPLACE,
+ CUTBUFFER_UPDATE_APPEND,
+ CUTBUFFER_UPDATE_PREPEND
+} CUTBUFFER_UPDATE_OP;
+
extern GUI_ENTRY_REC *active_entry;
GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8);
@@ -40,10 +47,10 @@ void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str);
void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr);
char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry);
-void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer);
-void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer);
+void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, CUTBUFFER_UPDATE_OP update_cutbuffer);
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer);
void gui_entry_erase_cell(GUI_ENTRY_REC *entry);
-void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space);
+void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op);
void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space);
void gui_entry_transpose_chars(GUI_ENTRY_REC *entry);
@@ -60,4 +67,5 @@ void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space);
void gui_entry_redraw(GUI_ENTRY_REC *entry);
+
#endif
diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c
index af560894..b93e5f0f 100644
--- a/src/fe-text/gui-readline.c
+++ b/src/fe-text/gui-readline.c
@@ -40,6 +40,9 @@
#include <string.h>
#include <signal.h>
+/* After LINE_SPLIT_LIMIT characters, the message will be split into multiple lines */
+#define LINE_SPLIT_LIMIT 400
+
typedef void (*ENTRY_REDIRECT_KEY_FUNC) (int key, void *data, SERVER_REC *server, WI_ITEM_REC *item);
typedef void (*ENTRY_REDIRECT_ENTRY_FUNC) (const char *line, void *data, SERVER_REC *server, WI_ITEM_REC *item);
@@ -227,7 +230,7 @@ static void paste_buffer_join_lines(GArray *buf)
}
/* all looks fine - now remove the whitespace, but don't let lines
- get longer than 400 chars */
+ get longer than LINE_SPLIT_LIMIT chars */
dest = arr; last_lf = TRUE; last_lf_pos = NULL; line_len = 0;
for (i = 0; i < buf->len; i++) {
if (last_lf && isblank(arr[i])) {
@@ -245,7 +248,7 @@ static void paste_buffer_join_lines(GArray *buf)
last_lf = TRUE;
} else {
last_lf = FALSE;
- if (++line_len >= 400 && last_lf_pos != NULL) {
+ if (++line_len >= LINE_SPLIT_LIMIT && last_lf_pos != NULL) {
memmove(last_lf_pos+1, last_lf_pos,
(dest - last_lf_pos) * sizeof(unichar));
*last_lf_pos = '\n'; last_lf_pos = NULL;
@@ -357,11 +360,24 @@ static void insert_paste_prompt(void)
{
char *str;
+ /* The actual number of lines that will show up post-line-split */
+ int actual_line_count = paste_line_count;
+ int split_lines = paste_buffer->len / LINE_SPLIT_LIMIT;
+
+ /* in case this prompt is happening due to line-splitting, calculate the
+ number of lines obtained from this. The number isn't entirely accurate;
+ we just choose the greater of the two since the exact value isn't
+ important */
+ if (split_lines > paste_verify_line_count &&
+ split_lines > paste_line_count) {
+ actual_line_count = split_lines;
+ }
+
paste_prompt = TRUE;
paste_old_prompt = g_strdup(active_entry->prompt);
printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
TXT_PASTE_WARNING,
- paste_line_count,
+ actual_line_count,
active_win->active == NULL ? "window" :
active_win->active->visible_name);
@@ -547,7 +563,7 @@ static void key_forward_to_space(void)
static void key_erase_line(void)
{
gui_entry_set_pos(active_entry, active_entry->text_len);
- gui_entry_erase(active_entry, active_entry->text_len, TRUE);
+ gui_entry_erase(active_entry, active_entry->text_len, CUTBUFFER_UPDATE_REPLACE);
}
static void key_erase_to_beg_of_line(void)
@@ -555,7 +571,7 @@ static void key_erase_to_beg_of_line(void)
int pos;
pos = gui_entry_get_pos(active_entry);
- gui_entry_erase(active_entry, pos, TRUE);
+ gui_entry_erase(active_entry, pos, CUTBUFFER_UPDATE_REPLACE);
}
static void key_erase_to_end_of_line(void)
@@ -564,7 +580,7 @@ static void key_erase_to_end_of_line(void)
pos = gui_entry_get_pos(active_entry);
gui_entry_set_pos(active_entry, active_entry->text_len);
- gui_entry_erase(active_entry, active_entry->text_len - pos, TRUE);
+ gui_entry_erase(active_entry, active_entry->text_len - pos, CUTBUFFER_UPDATE_REPLACE);
}
static void key_yank_from_cutbuffer(void)
@@ -611,12 +627,12 @@ static void key_delete_character(void)
static void key_backspace(void)
{
- gui_entry_erase(active_entry, 1, FALSE);
+ gui_entry_erase(active_entry, 1, CUTBUFFER_UPDATE_NOOP);
}
static void key_delete_previous_word(void)
{
- gui_entry_erase_word(active_entry, FALSE);
+ gui_entry_erase_word(active_entry, FALSE, CUTBUFFER_UPDATE_REPLACE);
}
static void key_delete_next_word(void)
@@ -626,7 +642,7 @@ static void key_delete_next_word(void)
static void key_delete_to_previous_space(void)
{
- gui_entry_erase_word(active_entry, TRUE);
+ gui_entry_erase_word(active_entry, TRUE, CUTBUFFER_UPDATE_REPLACE);
}
static void key_delete_to_next_space(void)
@@ -638,7 +654,11 @@ static gboolean paste_timeout(gpointer data)
{
paste_was_bracketed_mode = paste_bracketed_mode;
- if (paste_line_count == 0) {
+ /* number of lines after splitting extra-long messages */
+ int split_lines = paste_buffer->len / LINE_SPLIT_LIMIT;
+
+ /* Take into account the fact that a line may be split every LINE_SPLIT_LIMIT characters */
+ if (paste_line_count == 0 && split_lines <= paste_verify_line_count) {
int i;
for (i = 0; i < paste_buffer->len; i++) {
@@ -647,8 +667,9 @@ static gboolean paste_timeout(gpointer data)
}
g_array_set_size(paste_buffer, 0);
} else if (paste_verify_line_count > 0 &&
- paste_line_count >= paste_verify_line_count &&
- active_win->active != NULL)
+ (paste_line_count >= paste_verify_line_count ||
+ split_lines > paste_verify_line_count) &&
+ active_win->active != NULL)
insert_paste_prompt();
else
paste_flush(TRUE);
diff --git a/src/fe-text/term-terminfo.c b/src/fe-text/term-terminfo.c
index 27be904e..b2478c62 100644
--- a/src/fe-text/term-terminfo.c
+++ b/src/fe-text/term-terminfo.c
@@ -611,8 +611,6 @@ void term_stop(void)
{
terminfo_stop(current_term);
kill(getpid(), SIGTSTP);
- terminfo_cont(current_term);
- irssi_redraw();
}
static int input_utf8(const unsigned char *buffer, int size, unichar *result)
diff --git a/src/irc/core/irc-channels-setup.c b/src/irc/core/irc-channels-setup.c
index 2320352d..bbbc095c 100644
--- a/src/irc/core/irc-channels-setup.c
+++ b/src/irc/core/irc-channels-setup.c
@@ -24,10 +24,12 @@
void irc_channels_setup_init(void)
{
- signal_add("channel wholist", (SIGNAL_FUNC) channel_send_autocommands);
+ signal_add("channel wholist", (SIGNAL_FUNC) channel_send_botcommands);
+ signal_add("channel joined", (SIGNAL_FUNC) channel_send_autocommands);
}
void irc_channels_setup_deinit(void)
{
- signal_remove("channel wholist", (SIGNAL_FUNC) channel_send_autocommands);
+ signal_remove("channel wholist", (SIGNAL_FUNC) channel_send_botcommands);
+ signal_remove("channel joined", (SIGNAL_FUNC) channel_send_autocommands);
}
diff --git a/src/irc/proxy/listen.c b/src/irc/proxy/listen.c
index 5dc9a704..cde4c0be 100644
--- a/src/irc/proxy/listen.c
+++ b/src/irc/proxy/listen.c
@@ -31,6 +31,8 @@
#include "fe-common/core/printtext.h" /* FIXME: evil. need to do fe-proxy */
+#include <sys/un.h>
+
GSList *proxy_listens;
GSList *proxy_clients;
@@ -39,6 +41,66 @@ static int ignore_next;
static int enabled = FALSE;
+static int is_all_digits(const char *s)
+{
+ return strspn(s, "0123456789") == strlen(s);
+}
+
+static GIOChannel *net_listen_unix(const char *path)
+{
+ struct sockaddr_un sa;
+ int saved_errno, handle;
+
+ g_return_val_if_fail(path != NULL, NULL);
+
+ handle = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (handle == -1) {
+ return NULL;
+ }
+
+ fcntl(handle, F_SETFL, O_NONBLOCK);
+
+ memset(&sa, '\0', sizeof sa);
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, path, sizeof sa.sun_path - 1);
+ if (bind(handle, (struct sockaddr *)&sa, sizeof sa) == -1) {
+ saved_errno = errno;
+ goto error_close;
+ }
+
+ if (listen(handle, 1) == -1) {
+ saved_errno = errno;
+ goto error_unlink;
+ }
+
+ return g_io_channel_new(handle);
+
+error_unlink:
+ unlink(sa.sun_path);
+error_close:
+ close(handle);
+ errno = saved_errno;
+ return NULL;
+}
+
+static GIOChannel *net_accept_unix(GIOChannel *handle)
+{
+ struct sockaddr_un sa;
+ int ret;
+ socklen_t addrlen;
+
+ g_return_val_if_fail(handle != NULL, NULL);
+
+ addrlen = sizeof sa;
+ ret = accept(g_io_channel_unix_get_fd(handle), (struct sockaddr *)&sa, &addrlen);
+
+ if (ret < 0)
+ return NULL;
+
+ fcntl(ret, F_SETFL, O_NONBLOCK);
+ return g_io_channel_new(ret);
+}
+
static void remove_client(CLIENT_REC *rec)
{
g_return_if_fail(rec != NULL);
@@ -48,18 +110,18 @@ static void remove_client(CLIENT_REC *rec)
signal_emit("proxy client disconnected", 1, rec);
printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
- "Proxy: Client %s:%d disconnected", rec->host, rec->port);
+ "Proxy: Client %s disconnected", rec->addr);
g_free(rec->proxy_address);
net_sendbuffer_destroy(rec->handle, TRUE);
g_source_remove(rec->recv_tag);
g_free_not_null(rec->nick);
- g_free_not_null(rec->host);
+ g_free_not_null(rec->addr);
g_free(rec);
}
static void proxy_redirect_event(CLIENT_REC *client, const char *command,
- int count, const char *arg, int remote)
+ int count, const char *arg, int remote)
{
char *str;
@@ -67,7 +129,7 @@ static void proxy_redirect_event(CLIENT_REC *client, const char *command,
str = g_strdup_printf("proxy %p", client);
server_redirect_event(client->server, command, count,
- arg, remote, NULL, "", str, NULL);
+ arg, remote, NULL, "", str, NULL);
g_free(str);
}
@@ -94,28 +156,58 @@ static void grab_who(CLIENT_REC *client, const char *channel)
}
proxy_redirect_event(client, "who",
- client->server->one_endofwho ? 1 : count,
- arg->str, -1);
+ client->server->one_endofwho ? 1 : count,
+ arg->str, -1);
g_strfreev(list);
g_string_free(arg, TRUE);
}
static void handle_client_connect_cmd(CLIENT_REC *client,
- const char *cmd, const char *args)
+ const char *cmd, const char *args)
{
const char *password;
password = settings_get_str("irssiproxy_password");
- if (password != NULL && g_strcmp0(cmd, "PASS") == 0) {
- if (g_strcmp0(password, args) == 0)
- client->pass_sent = TRUE;
- else {
+ if (g_strcmp0(cmd, "PASS") == 0) {
+ const char *args_pass;
+
+ if (!client->multiplex) {
+ args_pass = args;
+ } else {
+ IRC_SERVER_REC *server;
+ char *tag;
+ const char *tag_end;
+
+ if ((tag_end = strchr(args, ':')) != NULL) {
+ args_pass = tag_end + 1;
+ } else {
+ tag_end = args + strlen(args);
+ args_pass = "";
+ }
+
+ tag = g_strndup(args, tag_end - args);
+ server = IRC_SERVER(server_find_chatnet(tag));
+ g_free(tag);
+
+ if (!server) {
+ /* an invalid network was specified */
+ remove_client(client);
+ return;
+ }
+
+ client->server = server;
+ g_free(client->proxy_address);
+ client->proxy_address = g_strdup_printf("%.*s.proxy", (int)(tag_end - args), args);
+ }
+
+ if (g_strcmp0(password, args_pass) != 0) {
/* wrong password! */
remove_client(client);
- return;
+ return;
}
+ client->pass_sent = TRUE;
} else if (g_strcmp0(cmd, "NICK") == 0) {
g_free_not_null(client->nick);
client->nick = g_strdup(args);
@@ -124,14 +216,14 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
}
if (client->nick != NULL && client->user_sent) {
- if (*password != '\0' && !client->pass_sent) {
+ if ((*password != '\0' || client->multiplex) && !client->pass_sent) {
/* client didn't send us PASS, kill it */
remove_client(client);
} else {
signal_emit("proxy client connected", 1, client);
printtext(client->server, NULL, MSGLEVEL_CLIENTNOTICE,
- "Proxy: Client %s:%d connected",
- client->host, client->port);
+ "Proxy: Client %s connected",
+ client->addr);
client->connected = TRUE;
proxy_dump_data(client);
}
@@ -139,7 +231,7 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
}
static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
- const char *data)
+ const char *data)
{
GSList *tmp;
if (!client->connected) {
@@ -162,8 +254,8 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
g_ascii_strcasecmp(target, client->proxy_address) == 0 ||
g_ascii_strcasecmp(target, client->nick) == 0) {
proxy_outdata(client, ":%s PONG %s :%s\r\n",
- client->proxy_address,
- client->proxy_address, origin);
+ client->proxy_address,
+ client->proxy_address, origin);
g_free(params);
return;
}
@@ -172,27 +264,27 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
if (g_strcmp0(cmd, "PROXY") == 0) {
if (g_ascii_strcasecmp(args, "CTCP ON") == 0) {
- /* client wants all ctcps */
+ /* client wants all ctcps */
client->want_ctcp = 1;
- for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+ for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
- if ((g_ascii_strcasecmp(client->listen->ircnet,rec->listen->ircnet) == 0) &&
- /* kludgy way to check if the clients aren't the same */
- (client->recv_tag != rec->recv_tag)) {
- if (rec->want_ctcp == 1)
- proxy_outdata(rec, ":%s NOTICE %s :Another client is now receiving CTCPs sent to %s\r\n",
- rec->proxy_address, rec->nick, rec->listen->ircnet);
- rec->want_ctcp = 0;
- }
+ if (g_ascii_strcasecmp(client->listen->ircnet, rec->listen->ircnet) == 0 &&
+ /* kludgy way to check if the clients aren't the same */
+ client->recv_tag != rec->recv_tag) {
+ if (rec->want_ctcp == 1)
+ proxy_outdata(rec, ":%s NOTICE %s :Another client is now receiving CTCPs sent to %s\r\n",
+ rec->proxy_address, rec->nick, rec->listen->ircnet);
+ rec->want_ctcp = 0;
+ }
}
proxy_outdata(client, ":%s NOTICE %s :You're now receiving CTCPs sent to %s\r\n",
- client->proxy_address, client->nick,client->listen->ircnet);
+ client->proxy_address, client->nick, client->listen->ircnet);
} else if (g_ascii_strcasecmp(args, "CTCP OFF") == 0) {
- /* client wants proxy to handle all ctcps */
+ /* client wants proxy to handle all ctcps */
client->want_ctcp = 0;
proxy_outdata(client, ":%s NOTICE %s :Proxy is now handling itself CTCPs sent to %s\r\n",
- client->proxy_address, client->nick, client->listen->ircnet);
+ client->proxy_address, client->nick, client->listen->ircnet);
} else {
signal_emit("proxy client command", 3, client, args, data);
}
@@ -201,11 +293,11 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
if (client->server == NULL || !client->server->connected) {
proxy_outdata(client, ":%s NOTICE %s :Not connected to server\r\n",
- client->proxy_address, client->nick);
- return;
+ client->proxy_address, client->nick);
+ return;
}
- /* check if the command could be redirected */
+ /* check if the command could be redirected */
if (g_strcmp0(cmd, "WHO") == 0)
grab_who(client, args);
else if (g_strcmp0(cmd, "WHOWAS") == 0)
@@ -263,29 +355,29 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
char *params, *target, *msg;
params = event_get_params(args, 2 | PARAM_FLAG_GETREST,
- &target, &msg);
+ &target, &msg);
proxy_outserver_all_except(client, "PRIVMSG %s", args);
ignore_next = TRUE;
if (*msg != '\001' || msg[strlen(msg)-1] != '\001') {
signal_emit(server_ischannel(SERVER(client->server), target) ?
- "message own_public" : "message own_private", 4,
- client->server, msg, target, target);
+ "message own_public" : "message own_private", 4,
+ client->server, msg, target, target);
} else if (strncmp(msg+1, "ACTION ", 7) == 0) {
/* action */
- msg[strlen(msg)-1] = '\0';
+ msg[strlen(msg)-1] = '\0';
signal_emit("message irc own_action", 3,
- client->server, msg+8, target);
+ client->server, msg+8, target);
} else {
- /* CTCP */
+ /* CTCP */
char *p;
msg[strlen(msg)-1] = '\0';
p = strchr(msg, ' ');
- if (p != NULL) *p++ = '\0'; else p = "";
+ if (p != NULL) *p++ = '\0'; else p = "";
signal_emit("message irc own_ctcp", 4,
- client->server, msg+1, p, target);
+ client->server, msg+1, p, target);
}
ignore_next = FALSE;
g_free(params);
@@ -337,24 +429,38 @@ static void sig_listen(LISTEN_REC *listen)
CLIENT_REC *rec;
IPADDR ip;
NET_SENDBUF_REC *sendbuf;
- GIOChannel *handle;
+ GIOChannel *handle;
char host[MAX_IP_LEN];
int port;
+ char *addr;
g_return_if_fail(listen != NULL);
/* accept connection */
- handle = net_accept(listen->handle, &ip, &port);
- if (handle == NULL)
- return;
- net_ip2host(&ip, host);
+ if (listen->port) {
+ handle = net_accept(listen->handle, &ip, &port);
+ if (handle == NULL)
+ return;
+ net_ip2host(&ip, host);
+ addr = g_strdup_printf("%s:%d", host, port);
+ } else {
+ /* no port => this is a unix socket */
+ handle = net_accept_unix(listen->handle);
+ if (handle == NULL)
+ return;
+ addr = g_strdup("(local)");
+ }
+
sendbuf = net_sendbuffer_create(handle, 0);
rec = g_new0(CLIENT_REC, 1);
rec->listen = listen;
rec->handle = sendbuf;
- rec->host = g_strdup(host);
- rec->port = port;
- if (g_strcmp0(listen->ircnet, "*") == 0) {
+ rec->addr = addr;
+ if (g_strcmp0(listen->ircnet, "?") == 0) {
+ rec->multiplex = TRUE;
+ rec->proxy_address = g_strdup("multiplex.proxy");
+ rec->server = NULL;
+ } else if (g_strcmp0(listen->ircnet, "*") == 0) {
rec->proxy_address = g_strdup("irc.proxy");
rec->server = servers == NULL ? NULL : IRC_SERVER(servers->data);
} else {
@@ -363,15 +469,15 @@ static void sig_listen(LISTEN_REC *listen)
IRC_SERVER(server_find_chatnet(listen->ircnet));
}
rec->recv_tag = g_input_add(handle, G_INPUT_READ,
- (GInputFunction) sig_listen_client, rec);
+ (GInputFunction) sig_listen_client, rec);
proxy_clients = g_slist_prepend(proxy_clients, rec);
- rec->listen->clients = g_slist_prepend(rec->listen->clients, rec);
+ listen->clients = g_slist_prepend(listen->clients, rec);
signal_emit("proxy client 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);
+ "Proxy: New client %s on port %s (%s)",
+ rec->addr, listen->port_or_path, listen->ircnet);
}
static void sig_incoming(IRC_SERVER_REC *server, const char *line)
@@ -460,12 +566,12 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
static void event_connected(IRC_SERVER_REC *server)
{
GSList *tmp;
- const char *chatnet;
+ const char *chatnet;
if (!IS_IRC_SERVER(server))
return;
- chatnet = server->connrec->chatnet;
+ chatnet = server->connrec->chatnet;
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
@@ -474,7 +580,7 @@ static void event_connected(IRC_SERVER_REC *server)
(chatnet != NULL &&
g_ascii_strcasecmp(chatnet, rec->listen->ircnet) == 0))) {
proxy_outdata(rec, ":%s NOTICE %s :Connected to server\r\n",
- rec->proxy_address, rec->nick);
+ rec->proxy_address, rec->nick);
rec->server = server;
proxy_client_reset_nick(rec);
}
@@ -482,7 +588,7 @@ static void event_connected(IRC_SERVER_REC *server)
}
static void proxy_server_disconnected(CLIENT_REC *client,
- IRC_SERVER_REC *server)
+ IRC_SERVER_REC *server)
{
GSList *tmp;
@@ -567,14 +673,19 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
proxy_outserver_all(server, "PRIVMSG %s :\001ACTION %s\001", target, msg);
}
-static LISTEN_REC *find_listen(const char *ircnet, int port)
+static LISTEN_REC *find_listen(const char *ircnet, int port, const char *port_or_path)
{
GSList *tmp;
for (tmp = proxy_listens; tmp != NULL; tmp = tmp->next) {
LISTEN_REC *rec = tmp->data;
- if (rec->port == port &&
+ if ((port
+ ? /* a tcp port */
+ rec->port == port
+ : /* a unix socket path */
+ g_strcmp0(rec->port_or_path, port_or_path) == 0
+ ) &&
g_ascii_strcasecmp(rec->ircnet, ircnet) == 0)
return rec;
}
@@ -582,48 +693,53 @@ static LISTEN_REC *find_listen(const char *ircnet, int port)
return NULL;
}
-static void add_listen(const char *ircnet, int port)
+static void add_listen(const char *ircnet, int port, const char *port_or_path)
{
LISTEN_REC *rec;
IPADDR ip4, ip6, *my_ip;
+ GIOChannel *handle;
- if (port <= 0 || *ircnet == '\0')
+ if (*port_or_path == '\0' || port < 0 || *ircnet == '\0')
return;
- /* bind to specific host/ip? */
- my_ip = NULL;
- if (*settings_get_str("irssiproxy_bind") != '\0') {
- if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
- &ip4, &ip6) != 0) {
- printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
- "Proxy: can not resolve '%s' - aborting",
- settings_get_str("irssiproxy_bind"));
- return;
+ if (port == 0) {
+ /* listening on a unix socket */
+ handle = net_listen_unix(port_or_path);
+ } else {
+ /* bind to specific host/ip? */
+ my_ip = NULL;
+ if (*settings_get_str("irssiproxy_bind") != '\0') {
+ if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
+ &ip4, &ip6) != 0) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: can not resolve '%s' - aborting",
+ settings_get_str("irssiproxy_bind"));
+ return;
+ }
+
+ my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
+ settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
}
+ handle = net_listen(my_ip, &port);
+ }
- my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
- settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
+ if (handle == NULL) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: Listen in port %s failed: %s",
+ port_or_path, g_strerror(errno));
+ return;
}
rec = g_new0(LISTEN_REC, 1);
+ rec->handle = handle;
rec->ircnet = g_strdup(ircnet);
rec->port = port;
-
- rec->handle = net_listen(my_ip, &rec->port);
-
- if (rec->handle == NULL) {
- printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
- "Proxy: Listen in port %d failed: %s",
- rec->port, g_strerror(errno));
- g_free(rec->ircnet);
- g_free(rec);
- return;
- }
+ rec->port_or_path = g_strdup(port_or_path);
rec->tag = g_input_add(rec->handle, G_INPUT_READ,
- (GInputFunction) sig_listen, rec);
+ (GInputFunction) sig_listen, rec);
- proxy_listens = g_slist_append(proxy_listens, rec);
+ proxy_listens = g_slist_append(proxy_listens, rec);
}
static void remove_listen(LISTEN_REC *rec)
@@ -633,8 +749,13 @@ static void remove_listen(LISTEN_REC *rec)
while (rec->clients != NULL)
remove_client(rec->clients->data);
+ /* remove unix socket because bind wants to (re)create it */
+ if (rec->port == 0)
+ unlink(rec->port_or_path);
+
net_disconnect(rec->handle);
g_source_remove(rec->tag);
+ g_free(rec->port_or_path);
g_free(rec->ircnet);
g_free(rec);
}
@@ -644,7 +765,7 @@ static void read_settings(void)
LISTEN_REC *rec;
GSList *remove_listens = NULL;
GSList *add_listens = NULL;
- char **ports, **tmp, *ircnet, *port;
+ char **ports, **tmp, *ircnet, *port_or_path;
int portnum;
remove_listens = g_slist_copy(proxy_listens);
@@ -652,20 +773,25 @@ static void read_settings(void)
ports = g_strsplit(settings_get_str("irssiproxy_ports"), " ", -1);
for (tmp = ports; *tmp != NULL; tmp++) {
ircnet = *tmp;
- port = strchr(ircnet, '=');
- if (port == NULL)
+ port_or_path = strchr(ircnet, '=');
+ if (port_or_path == NULL)
continue;
- *port++ = '\0';
- portnum = atoi(port);
- if (portnum <= 0)
- continue;
+ *port_or_path++ = '\0';
+ if (is_all_digits(port_or_path)) {
+ portnum = atoi(port_or_path);
+ if (portnum <= 0)
+ continue;
+ } else {
+ portnum = 0;
+ }
- rec = find_listen(ircnet, portnum);
+ rec = find_listen(ircnet, portnum, port_or_path);
if (rec == NULL) {
rec = g_new0(LISTEN_REC, 1);
rec->ircnet = ircnet; /* borrow */
rec->port = portnum;
+ rec->port_or_path = port_or_path; /* borrow */
add_listens = g_slist_prepend(add_listens, rec);
} else {
/* remove from the list of listens to remove == keep it */
@@ -680,7 +806,7 @@ static void read_settings(void)
while (add_listens != NULL) {
rec = add_listens->data;
- add_listen(rec->ircnet, rec->port);
+ add_listen(rec->ircnet, rec->port, rec->port_or_path);
add_listens = g_slist_remove(add_listens, rec);
g_free(rec);
}
diff --git a/src/irc/proxy/proxy.c b/src/irc/proxy/proxy.c
index 50a41b21..1a714045 100644
--- a/src/irc/proxy/proxy.c
+++ b/src/irc/proxy/proxy.c
@@ -44,10 +44,10 @@ static void cmd_irssiproxy_status(const char *data, IRC_SERVER_REC *server)
CLIENT_REC *rec = tmp->data;
printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
- " %s:%d connect%s to %d (%s)",
- rec->host, rec->port,
+ " %s connect%s to %s (%s)",
+ rec->addr,
rec->connected ? "ed" : "ing",
- rec->listen->port, rec->listen->ircnet);
+ rec->listen->port_or_path, rec->listen->ircnet);
}
}
diff --git a/src/irc/proxy/proxy.h b/src/irc/proxy/proxy.h
index 158b0675..620ea605 100644
--- a/src/irc/proxy/proxy.h
+++ b/src/irc/proxy/proxy.h
@@ -9,17 +9,18 @@
typedef struct {
int port;
+ char *port_or_path;
char *ircnet;
int tag;
GIOChannel *handle;
GSList *clients;
+
} LISTEN_REC;
typedef struct {
- char *nick, *host;
- int port;
+ char *nick, *addr;
NET_SENDBUF_REC *handle;
int recv_tag;
char *proxy_address;
@@ -29,6 +30,7 @@ typedef struct {
unsigned int user_sent:1;
unsigned int connected:1;
unsigned int want_ctcp:1;
+ unsigned int multiplex:1;
} CLIENT_REC;
#endif
diff --git a/src/perl/irc/Irc.xs b/src/perl/irc/Irc.xs
index db5c5f79..3f8ccc2e 100644
--- a/src/perl/irc/Irc.xs
+++ b/src/perl/irc/Irc.xs
@@ -149,14 +149,14 @@ static void perl_notifylist_fill_hash(HV *hv, NOTIFYLIST_REC *notify)
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, "addr", 4, new_pv(client->addr), 0);
(void) hv_store(hv, "proxy_address", 13, new_pv(client->proxy_address), 0);
(void) hv_store(hv, "server", 6, iobject_bless(client->server), 0);
(void) hv_store(hv, "pass_sent", 9, newSViv(client->pass_sent), 0);
(void) hv_store(hv, "user_sent", 9, newSViv(client->user_sent), 0);
(void) hv_store(hv, "connected", 9, newSViv(client->connected), 0);
(void) hv_store(hv, "want_ctcp", 9, newSViv(client->want_ctcp), 0);
+ (void) hv_store(hv, "multiplex", 9, newSViv(client->multiplex), 0);
(void) hv_store(hv, "ircnet", 6, new_pv(client->listen->ircnet), 0);
}