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