Merge branch 'master' into hide-lines

This commit is contained in:
ailin-nemui 2017-11-26 00:31:01 +01:00 committed by GitHub
commit 466d074200
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 2950 additions and 537 deletions

View file

@ -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 11
#define IRSSI_ABI_VERSION 13
#define DEFAULT_SERVER_ADD_PORT 6667
#define DEFAULT_SERVER_ADD_TLS_PORT 6697

View file

@ -56,6 +56,11 @@ libcore_a_SOURCES = \
tls.c \
write-buffer.c
if HAVE_CAPSICUM
libcore_a_SOURCES += \
capsicum.c
endif
structure_headers = \
channel-rec.h \
channel-setup-rec.h \
@ -69,6 +74,7 @@ structure_headers = \
pkginc_coredir=$(pkgincludedir)/src/core
pkginc_core_HEADERS = \
args.h \
capsicum.h \
channels.h \
channels-setup.h \
commands.h \
@ -89,6 +95,7 @@ pkginc_core_HEADERS = \
net-nonblock.h \
net-sendbuffer.h \
network.h \
network-openssl.h \
nick-rec.h \
nicklist.h \
nickmatch-cache.h \

508
src/core/capsicum.c Normal file
View file

@ -0,0 +1,508 @@
/*
capsicum.c : Capsicum sandboxing support
Copyright (C) 2017 Edward Tomasz Napierala <trasz@FreeBSD.org>
This software was developed by SRI International and the University of
Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
("CTSRD"), as part of the DARPA CRASH research programme.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "capsicum.h"
#include "commands.h"
#include "log.h"
#include "misc.h"
#include "network.h"
#include "network-openssl.h"
#include "settings.h"
#include "signals.h"
#include <sys/param.h>
#include <sys/capsicum.h>
#include <sys/filio.h>
#include <sys/nv.h>
#include <sys/procdesc.h>
#include <sys/socket.h>
#include <string.h>
#include <termios.h>
#define OPCODE_CONNECT 1
#define OPCODE_GETHOSTBYNAME 2
static char *irclogs_path;
static size_t irclogs_path_len;
static int irclogs_fd;
static int symbiontfds[2];
static int port_min;
static int port_max;
gboolean capsicum_enabled(void)
{
u_int mode;
int error;
error = cap_getmode(&mode);
if (error != 0)
return FALSE;
if (mode == 0)
return FALSE;
return TRUE;
}
int capsicum_net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
{
nvlist_t *nvl;
int error, saved_errno, sock;
/* Send request to the symbiont. */
nvl = nvlist_create(0);
nvlist_add_number(nvl, "opcode", OPCODE_CONNECT);
nvlist_add_binary(nvl, "ip", ip, sizeof(*ip));
nvlist_add_number(nvl, "port", port);
if (my_ip != NULL) {
/* nvlist_add_binary(3) can't handle NULL values. */
nvlist_add_binary(nvl, "my_ip", my_ip, sizeof(*my_ip));
}
error = nvlist_send(symbiontfds[1], nvl);
nvlist_destroy(nvl);
if (error != 0) {
g_warning("nvlist_send: %s", strerror(errno));
return -1;
}
/* Receive response. */
nvl = nvlist_recv(symbiontfds[1], 0);
if (nvl == NULL) {
g_warning("nvlist_recv: %s", strerror(errno));
return -1;
}
if (nvlist_exists_descriptor(nvl, "sock")) {
sock = nvlist_take_descriptor(nvl, "sock");
} else {
sock = -1;
}
saved_errno = nvlist_get_number(nvl, "errno");
nvlist_destroy(nvl);
if (sock == -1 && (port < port_min || port > port_max)) {
g_warning("Access restricted to ports between %d and %d "
"due to capability mode",
port_min, port_max);
}
errno = saved_errno;
return sock;
}
int capsicum_net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
{
nvlist_t *nvl;
const IPADDR *received_ip4, *received_ip6;
int error, ret, saved_errno;
/* Send request to the symbiont. */
nvl = nvlist_create(0);
nvlist_add_number(nvl, "opcode", OPCODE_GETHOSTBYNAME);
nvlist_add_string(nvl, "addr", addr);
error = nvlist_send(symbiontfds[1], nvl);
nvlist_destroy(nvl);
if (error != 0) {
g_warning("nvlist_send: %s", strerror(errno));
return -1;
}
/* Receive response. */
nvl = nvlist_recv(symbiontfds[1], 0);
if (nvl == NULL) {
g_warning("nvlist_recv: %s", strerror(errno));
return -1;
}
received_ip4 = nvlist_get_binary(nvl, "ip4", NULL);
received_ip6 = nvlist_get_binary(nvl, "ip6", NULL);
memcpy(ip4, received_ip4, sizeof(*ip4));
memcpy(ip6, received_ip6, sizeof(*ip6));
ret = nvlist_get_number(nvl, "ret");
saved_errno = nvlist_get_number(nvl, "errno");
nvlist_destroy(nvl);
errno = saved_errno;
return ret;
}
int capsicum_open(const char *path, int flags, int mode)
{
int fd;
/* +1 is for the slash separating irclogs_path and the rest. */
if (strlen(path) > irclogs_path_len + 1 &&
path[irclogs_path_len] == '/' &&
strncmp(path, irclogs_path, irclogs_path_len) == 0) {
fd = openat(irclogs_fd, path + irclogs_path_len + 1,
flags, mode);
} else {
fd = open(path, flags, mode);
}
if (fd < 0 && (errno == ENOTCAPABLE || errno == ECAPMODE))
g_warning("File system access restricted to %s "
"due to capability mode", irclogs_path);
return (fd);
}
int capsicum_open_wrapper(const char *path, int flags, int mode)
{
if (capsicum_enabled()) {
return capsicum_open(path, flags, mode);
}
return open(path, flags, mode);
}
void capsicum_mkdir_with_parents(const char *path, int mode)
{
char *component, *copy, *tofree;
int error, fd, newfd;
/* The directory already exists, nothing to do. */
if (strcmp(path, irclogs_path) == 0)
return;
/* +1 is for the slash separating irclogs_path and the rest. */
if (strlen(path) <= irclogs_path_len + 1 ||
path[irclogs_path_len] != '/' ||
strncmp(path, irclogs_path, irclogs_path_len) != 0) {
g_warning("Cannot create %s: file system access restricted "
"to %s due to capability mode", path, irclogs_path);
return;
}
copy = tofree = g_strdup(path + irclogs_path_len + 1);
fd = irclogs_fd;
for (;;) {
component = strsep(&copy, "/");
if (component == NULL)
break;
error = mkdirat(fd, component, mode);
if (error != 0 && errno != EEXIST) {
g_warning("cannot create %s: %s",
component, strerror(errno));
break;
}
newfd = openat(fd, component, O_DIRECTORY);
if (newfd < 0) {
g_warning("cannot open %s: %s",
component, strerror(errno));
break;
}
if (fd != irclogs_fd)
close(fd);
fd = newfd;
}
g_free(tofree);
if (fd != irclogs_fd)
close(fd);
}
void capsicum_mkdir_with_parents_wrapper(const char *path, int mode)
{
if (capsicum_enabled()) {
capsicum_mkdir_with_parents(path, mode);
return;
}
g_mkdir_with_parents(path, mode);
}
nvlist_t *symbiont_connect(const nvlist_t *request)
{
nvlist_t *response;
const IPADDR *ip, *my_ip;
int port, saved_errno, sock;
ip = nvlist_get_binary(request, "ip", NULL);
port = (int)nvlist_get_number(request, "port");
if (nvlist_exists(request, "my_ip"))
my_ip = nvlist_get_binary(request, "my_ip", NULL);
else
my_ip = NULL;
/*
* Check if the port is in allowed range. This is to minimize
* the chance of the attacker rooting another system in case of
* compromise.
*/
if (port < port_min || port > port_max) {
sock = -1;
saved_errno = EPERM;
} else {
/* Connect. */
sock = net_connect_ip_handle(ip, port, my_ip);
saved_errno = errno;
}
/* Send back the socket fd. */
response = nvlist_create(0);
if (sock != -1)
nvlist_move_descriptor(response, "sock", sock);
nvlist_add_number(response, "errno", saved_errno);
return (response);
}
nvlist_t *symbiont_gethostbyname(const nvlist_t *request)
{
nvlist_t *response;
IPADDR ip4, ip6;
const char *addr;
int ret, saved_errno;
addr = nvlist_get_string(request, "addr");
/* Connect. */
ret = net_gethostbyname(addr, &ip4, &ip6);
saved_errno = errno;
/* Send back the IPs. */
response = nvlist_create(0);
nvlist_add_number(response, "ret", ret);
nvlist_add_number(response, "errno", saved_errno);
nvlist_add_binary(response, "ip4", &ip4, sizeof(ip4));
nvlist_add_binary(response, "ip6", &ip6, sizeof(ip6));
return (response);
}
/*
* Child process, running outside the Capsicum sandbox.
*/
_Noreturn static void symbiont(void)
{
nvlist_t *request, *response;
int error, opcode;
setproctitle("capsicum symbiont");
close(symbiontfds[1]);
close(0);
close(1);
close(2);
for (;;) {
/* Receive parameters from the main irssi process. */
request = nvlist_recv(symbiontfds[0], 0);
if (request == NULL)
exit(1);
opcode = nvlist_get_number(request, "opcode");
switch (opcode) {
case OPCODE_CONNECT:
response = symbiont_connect(request);
break;
case OPCODE_GETHOSTBYNAME:
response = symbiont_gethostbyname(request);
break;
default:
exit(1);
}
/* Send back the response. */
error = nvlist_send(symbiontfds[0], response);
if (error != 0)
exit(1);
nvlist_destroy(request);
nvlist_destroy(response);
}
}
static int start_symbiont(void)
{
int childfd, error;
pid_t pid;
error = socketpair(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, symbiontfds);
if (error != 0) {
g_warning("socketpair: %s", strerror(errno));
return 1;
}
pid = pdfork(&childfd, PD_CLOEXEC);
if (pid < 0) {
g_warning("pdfork: %s", strerror(errno));
return 1;
}
if (pid > 0) {
close(symbiontfds[0]);
return 0;
}
symbiont();
/* NOTREACHED */
}
static void cmd_capsicum(const char *data, SERVER_REC *server, void *item)
{
command_runsub("capsicum", data, server, item);
}
/*
* The main difference between this and caph_limit_stdio(3) is that this
* one permits TIOCSETAW, which is requred for restoring the terminal state
* on exit.
*/
static int
limit_stdio_fd(int fd)
{
cap_rights_t rights;
unsigned long cmds[] = { TIOCGETA, TIOCGWINSZ, TIOCSETAW, FIODTYPE };
cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_FCNTL,
CAP_FSTAT, CAP_IOCTL, CAP_SEEK);
if (cap_rights_limit(fd, &rights) < 0) {
g_warning("cap_rights_limit(3) failed: %s", strerror(errno));
return (-1);
}
if (cap_ioctls_limit(fd, cmds, nitems(cmds)) < 0) {
g_warning("cap_ioctls_limit(3) failed: %s", strerror(errno));
return (-1);
}
if (cap_fcntls_limit(fd, CAP_FCNTL_GETFL) < 0) {
g_warning("cap_fcntls_limit(3) failed: %s", strerror(errno));
return (-1);
}
return (0);
}
static void cmd_capsicum_enter(void)
{
u_int mode;
gboolean inited;
int error;
error = cap_getmode(&mode);
if (error == 0 && mode != 0) {
g_warning("Already in capability mode");
return;
}
inited = irssi_ssl_init();
if (!inited) {
signal_emit("capability mode failed", 1, strerror(errno));
return;
}
port_min = settings_get_int("capsicum_port_min");
port_max = settings_get_int("capsicum_port_max");
irclogs_path = convert_home(settings_get_str("capsicum_irclogs_path"));
irclogs_path_len = strlen(irclogs_path);
/* Strip trailing slashes, if any. */
while (irclogs_path_len > 0 && irclogs_path[irclogs_path_len - 1] == '/') {
irclogs_path[irclogs_path_len - 1] = '\0';
irclogs_path_len--;
}
g_mkdir_with_parents(irclogs_path, log_dir_create_mode);
irclogs_fd = open(irclogs_path, O_DIRECTORY | O_CLOEXEC);
if (irclogs_fd < 0) {
g_warning("Unable to open %s: %s", irclogs_path, strerror(errno));
signal_emit("capability mode failed", 1, strerror(errno));
return;
}
error = start_symbiont();
if (error != 0) {
signal_emit("capability mode failed", 1, strerror(errno));
return;
}
/*
* XXX: We should use pdwait(2) to wait for children. Unfortunately
* it's not implemented yet. Thus the workaround, to get rid
* of the zombies at least.
*/
signal(SIGCHLD, SIG_IGN);
if (limit_stdio_fd(STDIN_FILENO) != 0 ||
limit_stdio_fd(STDOUT_FILENO) != 0 ||
limit_stdio_fd(STDERR_FILENO) != 0) {
signal_emit("capability mode failed", 1, strerror(errno));
return;
}
error = cap_enter();
if (error != 0) {
signal_emit("capability mode failed", 1, strerror(errno));
} else {
signal_emit("capability mode enabled", 0);
}
}
static void cmd_capsicum_status(void)
{
u_int mode;
int error;
error = cap_getmode(&mode);
if (error != 0) {
signal_emit("capability mode failed", 1, strerror(errno));
} else if (mode == 0) {
signal_emit("capability mode disabled", 0);
} else {
signal_emit("capability mode enabled", 0);
}
}
void sig_init_finished(void)
{
if (settings_get_bool("capsicum"))
cmd_capsicum_enter();
}
void capsicum_init(void)
{
settings_add_bool("misc", "capsicum", FALSE);
settings_add_str("misc", "capsicum_irclogs_path", "~/irclogs");
settings_add_int("misc", "capsicum_port_min", 6667);
settings_add_int("misc", "capsicum_port_max", 9999);
signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
command_bind("capsicum", NULL, (SIGNAL_FUNC) cmd_capsicum);
command_bind("capsicum enter", NULL, (SIGNAL_FUNC) cmd_capsicum_enter);
command_bind("capsicum status", NULL, (SIGNAL_FUNC) cmd_capsicum_status);
}
void capsicum_deinit(void)
{
signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
command_unbind("capsicum", (SIGNAL_FUNC) cmd_capsicum);
command_unbind("capsicum enter", (SIGNAL_FUNC) cmd_capsicum_enter);
command_unbind("capsicum status", (SIGNAL_FUNC) cmd_capsicum_status);
}

15
src/core/capsicum.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef __CAPSICUM_H
#define __CAPSICUM_H
gboolean capsicum_enabled(void);
int capsicum_net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip);
int capsicum_net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6);
int capsicum_open(const char *path, int flags, int mode);
int capsicum_open_wrapper(const char *path, int flags, int mode);
void capsicum_mkdir_with_parents(const char *path, int mode);
void capsicum_mkdir_with_parents_wrapper(const char *path, int mode);
void capsicum_init(void);
void capsicum_deinit(void);
#endif /* !__CAPSICUM_H */

View file

@ -29,6 +29,9 @@
#include "signals.h"
#include "settings.h"
#include "session.h"
#ifdef HAVE_CAPSICUM
#include "capsicum.h"
#endif
#include "chat-protocols.h"
#include "servers.h"
@ -235,6 +238,9 @@ void core_init(void)
commands_init();
nickmatch_cache_init();
session_init();
#ifdef HAVE_CAPSICUM
capsicum_init();
#endif
chat_protocols_init();
chatnets_init();
@ -292,6 +298,9 @@ void core_deinit(void)
chatnets_deinit();
chat_protocols_deinit();
#ifdef HAVE_CAPSICUM
capsicum_deinit();
#endif
session_deinit();
nickmatch_cache_deinit();
commands_deinit();

View file

@ -26,6 +26,9 @@
#include "servers.h"
#include "log.h"
#include "write-buffer.h"
#ifdef HAVE_CAPSICUM
#include "capsicum.h"
#endif
#include "lib-config/iconfig.h"
#include "settings.h"
@ -114,13 +117,23 @@ int log_start_logging(LOG_REC *log)
/* path may contain variables (%time, $vars),
make sure the directory is created */
dir = g_path_get_dirname(log->real_fname);
#ifdef HAVE_CAPSICUM
capsicum_mkdir_with_parents_wrapper(dir, log_dir_create_mode);
#else
g_mkdir_with_parents(dir, log_dir_create_mode);
#endif
g_free(dir);
}
#ifdef HAVE_CAPSICUM
log->handle = log->real_fname == NULL ? -1 :
capsicum_open_wrapper(log->real_fname, O_WRONLY | O_APPEND | O_CREAT,
log_file_create_mode);
#else
log->handle = log->real_fname == NULL ? -1 :
open(log->real_fname, O_WRONLY | O_APPEND | O_CREAT,
log_file_create_mode);
#endif
if (log->handle == -1) {
signal_emit("log create failed", 1, log);
log->failed = TRUE;

View file

@ -20,6 +20,7 @@
#include "module.h"
#include "network.h"
#include "network-openssl.h"
#include "net-sendbuffer.h"
#include "misc.h"
#include "servers.h"
@ -44,6 +45,19 @@
#define ASN1_STRING_data(x) ASN1_STRING_get0_data(x)
#endif
/* OpenSSL 1.1.0 also introduced some useful additions to the api */
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined (LIBRESSL_VERSION_NUMBER)
static int X509_STORE_up_ref(X509_STORE *vfy)
{
int n;
n = CRYPTO_add(&vfy->references, 1, CRYPTO_LOCK_X509_STORE);
g_assert(n > 1);
return (n > 1) ? 1 : 0;
}
#endif
/* ssl i/o channel object */
typedef struct
{
@ -58,6 +72,7 @@ typedef struct
} GIOSSLChannel;
static int ssl_inited = FALSE;
static X509_STORE *store = NULL;
static void irssi_ssl_free(GIOChannel *handle)
{
@ -362,8 +377,10 @@ static GIOFuncs irssi_ssl_channel_funcs = {
irssi_ssl_get_flags
};
static gboolean irssi_ssl_init(void)
gboolean irssi_ssl_init(void)
{
int success;
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
if (!OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, NULL)) {
g_error("Could not initialize OpenSSL");
@ -374,6 +391,20 @@ static gboolean irssi_ssl_init(void)
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
#endif
store = X509_STORE_new();
if (store == NULL) {
g_error("Could not initialize OpenSSL: X509_STORE_new() failed");
return FALSE;
}
success = X509_STORE_set_default_paths(store);
if (success == 0) {
g_warning("Could not load default certificates");
X509_STORE_free(store);
store = NULL;
/* Don't return an error; the user might have their own cafile/capath. */
}
ssl_inited = TRUE;
return TRUE;
@ -491,9 +522,12 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
g_free(scafile);
g_free(scapath);
verify = TRUE;
} else {
if (!SSL_CTX_set_default_verify_paths(ctx))
g_warning("Could not load default certificates");
} else if (store != NULL) {
/* Make sure to increment the refcount every time the store is
* used, that's essential not to get it free'd by OpenSSL when
* the SSL_CTX is destroyed. */
X509_STORE_up_ref(store);
SSL_CTX_set_cert_store(ctx, store);
}
if(!(ssl = SSL_new(ctx)))

View file

@ -0,0 +1,6 @@
#ifndef __NETWORK_OPENSSL_H
#define __NETWORK_OPENSSL_H
gboolean irssi_ssl_init(void);
#endif /* !__NETWORK_OPENSSL_H */

View file

@ -20,6 +20,9 @@
#include "module.h"
#include "network.h"
#ifdef HAVE_CAPSICUM
#include "capsicum.h"
#endif
#include <sys/un.h>
@ -45,9 +48,6 @@ GIOChannel *g_io_channel_new(int handle)
return chan;
}
/* Cygwin need this, don't know others.. */
/*#define BLOCKING_SOCKETS 1*/
IPADDR ip4_any = {
AF_INET,
#if defined(IN6ADDR_ANY_INIT)
@ -110,42 +110,7 @@ static int sin_get_port(union sockaddr_union *so)
so->sin.sin_port);
}
/* Connect to socket */
GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip)
{
IPADDR ip4, ip6, *ip;
g_return_val_if_fail(addr != NULL, NULL);
if (net_gethostbyname(addr, &ip4, &ip6) == -1)
return NULL;
if (my_ip == NULL) {
/* prefer IPv4 addresses */
ip = ip4.family != 0 ? &ip4 : &ip6;
} else if (IPADDR_IS_V6(my_ip)) {
/* my_ip is IPv6 address, use it if possible */
if (ip6.family != 0)
ip = &ip6;
else {
my_ip = NULL;
ip = &ip4;
}
} else {
/* my_ip is IPv4 address, use it if possible */
if (ip4.family != 0)
ip = &ip4;
else {
my_ip = NULL;
ip = &ip6;
}
}
return net_connect_ip(ip, port, my_ip);
}
/* Connect to socket with ip address */
GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
int net_connect_ip_handle(const IPADDR *ip, int port, const IPADDR *my_ip)
{
union sockaddr_union so;
int handle, ret, opt = 1;
@ -161,7 +126,7 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
handle = socket(ip->family, SOCK_STREAM, 0);
if (handle == -1)
return NULL;
return -1;
/* set socket options */
fcntl(handle, F_SETFL, O_NONBLOCK);
@ -176,7 +141,7 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
close(handle);
errno = old_errno;
return NULL;
return -1;
}
}
@ -190,9 +155,29 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
int old_errno = errno;
close(handle);
errno = old_errno;
return NULL;
return -1;
}
return handle;
}
/* Connect to socket with ip address */
GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
{
int handle = -1;
#ifdef HAVE_CAPSICUM
if (capsicum_enabled())
handle = capsicum_net_connect_ip(ip, port, my_ip);
else
handle = net_connect_ip_handle(ip, port, my_ip);
#else
handle = net_connect_ip_handle(ip, port, my_ip);
#endif
if (handle == -1)
return (NULL);
return g_io_channel_new(handle);
}
@ -383,6 +368,11 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
struct addrinfo hints, *ai, *ailist;
int ret, count_v4, count_v6, use_v4, use_v6;
#ifdef HAVE_CAPSICUM
if (capsicum_enabled())
return (capsicum_net_gethostbyname(addr, ip4, ip6));
#endif
g_return_val_if_fail(addr != NULL, -1);
memset(ip4, 0, sizeof(IPADDR));
@ -462,6 +452,7 @@ int net_gethostbyaddr(IPADDR *ip, char **name)
int net_ip2host(IPADDR *ip, char *host)
{
host[0] = '\0';
return inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN) ? 0 : -1;
}

View file

@ -33,11 +33,12 @@ extern IPADDR ip4_any;
GIOChannel *g_io_channel_new(int handle);
/* returns 1 if IPADDRs are the same */
int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
/* Returns 1 if IPADDRs are the same. */
/* Deprecated since it is unused. It will be deleted in a later release. */
int net_ip_compare(IPADDR *ip1, IPADDR *ip2) G_GNUC_DEPRECATED;
int net_connect_ip_handle(const IPADDR *ip, int port, const IPADDR *my_ip);
/* Connect to socket */
GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) G_GNUC_DEPRECATED;
/* Connect to socket with ip address and SSL*/
GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server);
/* Start TLS */

View file

@ -27,6 +27,9 @@
#include "misc.h"
#include "write-buffer.h"
#include "settings.h"
#ifdef HAVE_CAPSICUM
#include "capsicum.h"
#endif
#include "servers.h"
@ -126,8 +129,15 @@ void rawlog_open(RAWLOG_REC *rawlog, const char *fname)
return;
path = convert_home(fname);
#ifdef HAVE_CAPSICUM
rawlog->handle = capsicum_open_wrapper(path,
O_WRONLY | O_APPEND | O_CREAT,
log_file_create_mode);
#else
rawlog->handle = open(path, O_WRONLY | O_APPEND | O_CREAT,
log_file_create_mode);
#endif
g_free(path);
if (rawlog->handle == -1) {
@ -154,11 +164,20 @@ void rawlog_save(RAWLOG_REC *rawlog, const char *fname)
int f;
dir = g_path_get_dirname(fname);
#ifdef HAVE_CAPSICUM
capsicum_mkdir_with_parents_wrapper(dir, log_dir_create_mode);
#else
g_mkdir_with_parents(dir, log_dir_create_mode);
#endif
g_free(dir);
path = convert_home(fname);
#ifdef HAVE_CAPSICUM
f = capsicum_open_wrapper(path, O_WRONLY | O_APPEND | O_CREAT,
log_file_create_mode);
#else
f = open(path, O_WRONLY | O_APPEND | O_CREAT, log_file_create_mode);
#endif
g_free(path);
if (f < 0) {

View file

@ -198,7 +198,12 @@ char **recode_split(const SERVER_REC *server, const char *str,
int n = 0;
char **ret;
g_return_val_if_fail(str != NULL, NULL);
g_warn_if_fail(str != NULL);
if (str == NULL) {
ret = g_new(char *, 1);
ret[0] = NULL;
return ret;
}
if (settings_get_bool("recode")) {
to = find_conversion(server, target);

View file

@ -39,6 +39,7 @@ static GString *last_errors;
static GSList *last_invalid_modules;
static int fe_initialized;
static int config_changed; /* FIXME: remove after .98 (unless needed again) */
static unsigned int user_settings_changed;
static GHashTable *settings;
static int timeout_tag;
@ -464,6 +465,11 @@ SETTINGS_REC *settings_get_record(const char *key)
return g_hash_table_lookup(settings, key);
}
static void sig_init_userinfo_changed(gpointer changedp)
{
user_settings_changed |= GPOINTER_TO_UINT(changedp);
}
static void sig_init_finished(void)
{
fe_initialized = TRUE;
@ -479,6 +485,8 @@ static void sig_init_finished(void)
"updated, please /SAVE");
signal_emit("setup changed", 0);
}
signal_emit("settings userinfo changed", 1, GUINT_TO_POINTER(user_settings_changed));
}
static void settings_clean_invalid_module(const char *module)
@ -875,6 +883,7 @@ void settings_init(void)
timeout_tag = g_timeout_add(SETTINGS_AUTOSAVE_TIMEOUT,
(GSourceFunc) sig_autosave, NULL);
signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
signal_add("irssi init userinfo changed", (SIGNAL_FUNC) sig_init_userinfo_changed);
signal_add("gui exit", (SIGNAL_FUNC) sig_autosave);
}
@ -887,6 +896,7 @@ void settings_deinit(void)
{
g_source_remove(timeout_tag);
signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
signal_remove("irssi init userinfo changed", (SIGNAL_FUNC) sig_init_userinfo_changed);
signal_remove("gui exit", (SIGNAL_FUNC) sig_autosave);
g_slist_foreach(last_invalid_modules, (GFunc) g_free, NULL);

View file

@ -30,6 +30,13 @@ typedef struct {
char **choices;
} SETTINGS_REC;
enum {
USER_SETTINGS_REAL_NAME = 0x1,
USER_SETTINGS_USER_NAME = 0x2,
USER_SETTINGS_NICK = 0x4,
USER_SETTINGS_HOSTNAME = 0x8,
};
/* macros for handling the default Irssi configuration */
#define iconfig_get_str(a, b, c) config_get_str(mainconfig, a, b, c)
#define iconfig_get_int(a, b, c) config_get_int(mainconfig, a, b, c)

View file

@ -38,17 +38,24 @@ libfe_common_core_a_SOURCES = \
windows-layout.c \
fe-windows.c
if HAVE_CAPSICUM
libfe_common_core_a_SOURCES += \
fe-capsicum.c
endif
pkginc_fe_common_coredir=$(pkgincludedir)/src/fe-common/core
pkginc_fe_common_core_HEADERS = \
command-history.h \
chat-completion.h \
completion.h \
fe-capsicum.h \
fe-channels.h \
fe-common-core.h \
fe-core-commands.h \
fe-exec.h \
fe-messages.h \
fe-queries.h \
fe-settings.h \
fe-tls.h \
formats.h \
hilight-text.h \

View file

@ -30,10 +30,93 @@
#include "command-history.h"
/* command history */
static GList *history_entries;
static HISTORY_REC *global_history;
static int window_history;
static GSList *histories;
static HISTORY_ENTRY_REC *history_entry_new(HISTORY_REC *history, const char *text)
{
HISTORY_ENTRY_REC *entry;
entry = g_new0(HISTORY_ENTRY_REC, 1);
entry->text = g_strdup(text);
entry->history = history;
entry->time = time(NULL);
return entry;
}
static void history_entry_destroy(HISTORY_ENTRY_REC *entry)
{
g_free((char *)entry->text);
g_free(entry);
}
GList *command_history_list_last(HISTORY_REC *history)
{
GList *link;
link = g_list_last(history_entries);
while (link != NULL && history != NULL && ((HISTORY_ENTRY_REC *)link->data)->history != history) {
link = link->prev;
}
return link;
}
GList *command_history_list_first(HISTORY_REC *history)
{
GList *link;
link = history_entries;
while (link != NULL && history != NULL && ((HISTORY_ENTRY_REC *)link->data)->history != history) {
link = link->next;
}
return link;
}
GList *command_history_list_prev(HISTORY_REC *history, GList *pos)
{
GList *link;
link = pos != NULL ? pos->prev : NULL;
while (link != NULL && history != NULL && ((HISTORY_ENTRY_REC *)link->data)->history != history) {
link = link->prev;
}
return link;
}
GList *command_history_list_next(HISTORY_REC *history, GList *pos)
{
GList *link;
link = pos != NULL ? pos->next : NULL;
while (link != NULL && history != NULL && ((HISTORY_ENTRY_REC *)link->data)->history != history) {
link = link->next;
}
return link;
}
static void command_history_clear_pos_for_unlink_func(HISTORY_REC *history, GList* link)
{
if (history->pos == link) {
history->pos = command_history_list_next(history, link);
history->redo = 1;
}
}
static void history_list_delete_link_and_destroy(GList *link)
{
g_slist_foreach(histories,
(GFunc) command_history_clear_pos_for_unlink_func, link);
history_entry_destroy(link->data);
history_entries = g_list_delete_link(history_entries, link);
}
void command_history_add(HISTORY_REC *history, const char *text)
{
GList *link;
@ -41,21 +124,19 @@ void command_history_add(HISTORY_REC *history, const char *text)
g_return_if_fail(history != NULL);
g_return_if_fail(text != NULL);
link = g_list_last(history->list);
if (link != NULL && g_strcmp0(link->data, text) == 0)
return; /* same as previous entry */
link = command_history_list_last(history);
if (link != NULL && g_strcmp0(((HISTORY_ENTRY_REC *)link->data)->text, text) == 0)
return; /* same as previous entry */
if (settings_get_int("max_command_history") < 1 ||
history->lines < settings_get_int("max_command_history"))
history->lines++;
else {
link = history->list;
g_free(link->data);
history->list = g_list_remove_link(history->list, link);
g_list_free_1(link);
link = command_history_list_first(history);
history_list_delete_link_and_destroy(link);
}
history->list = g_list_append(history->list, g_strdup(text));
history_entries = g_list_append(history_entries, history_entry_new(history, text));
}
HISTORY_REC *command_history_find(HISTORY_REC *history)
@ -87,6 +168,61 @@ HISTORY_REC *command_history_find_name(const char *name)
return NULL;
}
static int history_entry_after_time_sort(const HISTORY_ENTRY_REC *a, const HISTORY_ENTRY_REC *b)
{
return a->time == b->time ? 1 : a->time - b->time;
}
void command_history_load_entry(time_t history_time, HISTORY_REC *history, const char *text)
{
HISTORY_ENTRY_REC *entry;
g_return_if_fail(history != NULL);
g_return_if_fail(text != NULL);
entry = g_new0(HISTORY_ENTRY_REC, 1);
entry->text = g_strdup(text);
entry->history = history;
entry->time = history_time;
history->lines++;
history_entries = g_list_insert_sorted(history_entries, entry, (GCompareFunc)history_entry_after_time_sort);
}
static int history_entry_find_func(const HISTORY_ENTRY_REC *data, const HISTORY_ENTRY_REC *user_data)
{
if ((user_data->time == -1 || (data->time == user_data->time)) &&
(user_data->history == NULL || (data->history == user_data->history)) &&
g_strcmp0(data->text, user_data->text) == 0) {
return 0;
} else {
return -1;
}
}
gboolean command_history_delete_entry(time_t history_time, HISTORY_REC *history, const char *text)
{
GList *link;
HISTORY_ENTRY_REC entry;
g_return_val_if_fail(history != NULL, FALSE);
g_return_val_if_fail(text != NULL, FALSE);
entry.text = text;
entry.history = history;
entry.time = history_time;
link = g_list_find_custom(history_entries, &entry, (GCompareFunc)history_entry_find_func);
if (link != NULL) {
((HISTORY_ENTRY_REC *)link->data)->history->lines--;
history_list_delete_link_and_destroy(link);
return TRUE;
} else {
return FALSE;
}
}
HISTORY_REC *command_history_current(WINDOW_REC *window)
{
HISTORY_REC *rec;
@ -104,7 +240,44 @@ HISTORY_REC *command_history_current(WINDOW_REC *window)
return global_history;
}
static const char *command_history_prev_int(WINDOW_REC *window, const char *text, gboolean global)
{
HISTORY_REC *history;
GList *pos;
history = command_history_current(window);
pos = history->pos;
history->redo = 0;
if (pos != NULL) {
/* don't go past the first entry (no wrap around) */
GList *prev = command_history_list_prev(global ? NULL : history, history->pos);
if (prev != NULL)
history->pos = prev;
} else {
history->pos = command_history_list_last(global ? NULL : history);
}
if (*text != '\0' &&
(pos == NULL || g_strcmp0(((HISTORY_ENTRY_REC *)pos->data)->text, text) != 0)) {
/* save the old entry to history */
command_history_add(history, text);
}
return history->pos == NULL ? text : ((HISTORY_ENTRY_REC *)history->pos->data)->text;
}
const char *command_history_prev(WINDOW_REC *window, const char *text)
{
return command_history_prev_int(window, text, FALSE);
}
const char *command_global_history_prev(WINDOW_REC *window, const char *text)
{
return command_history_prev_int(window, text, TRUE);
}
static const char *command_history_next_int(WINDOW_REC *window, const char *text, gboolean global)
{
HISTORY_REC *history;
GList *pos;
@ -112,24 +285,29 @@ const char *command_history_prev(WINDOW_REC *window, const char *text)
history = command_history_current(window);
pos = history->pos;
if (pos != NULL) {
/* don't go past the first entry (no wrap around) */
if (history->pos->prev != NULL)
history->pos = history->pos->prev;
} else {
history->pos = g_list_last(history->list);
}
if (!(history->redo) && pos != NULL)
history->pos = command_history_list_next(global ? NULL : history, history->pos);
history->redo = 0;
if (*text != '\0' &&
(pos == NULL || g_strcmp0(pos->data, text) != 0)) {
(pos == NULL || g_strcmp0(((HISTORY_ENTRY_REC *)pos->data)->text, text) != 0)) {
/* save the old entry to history */
command_history_add(history, text);
}
return history->pos == NULL ? text : history->pos->data;
return history->pos == NULL ? "" : ((HISTORY_ENTRY_REC *)history->pos->data)->text;
}
const char *command_history_next(WINDOW_REC *window, const char *text)
{
return command_history_next_int(window, text, FALSE);
}
const char *command_global_history_next(WINDOW_REC *window, const char *text)
{
return command_history_next_int(window, text, TRUE);
}
const char *command_history_delete_current(WINDOW_REC *window, const char *text)
{
HISTORY_REC *history;
GList *pos;
@ -137,15 +315,13 @@ const char *command_history_next(WINDOW_REC *window, const char *text)
history = command_history_current(window);
pos = history->pos;
if (pos != NULL)
history->pos = history->pos->next;
if (*text != '\0' &&
(pos == NULL || g_strcmp0(pos->data, text) != 0)) {
/* save the old entry to history */
command_history_add(history, text);
if (pos != NULL && g_strcmp0(((HISTORY_ENTRY_REC *)pos->data)->text, text) == 0) {
((HISTORY_ENTRY_REC *)pos->data)->history->lines--;
history_list_delete_link_and_destroy(pos);
}
return history->pos == NULL ? "" : history->pos->data;
history->redo = 0;
return history->pos == NULL ? "" : ((HISTORY_ENTRY_REC *)history->pos->data)->text;
}
void command_history_clear_pos_func(HISTORY_REC *history, gpointer user_data)
@ -175,12 +351,17 @@ HISTORY_REC *command_history_create(const char *name)
void command_history_clear(HISTORY_REC *history)
{
GList *link, *next;
g_return_if_fail(history != NULL);
command_history_clear_pos_func(history, NULL);
g_list_foreach(history->list, (GFunc) g_free, NULL);
g_list_free(history->list);
history->list = NULL;
link = command_history_list_first(history);
while (link != NULL) {
next = command_history_list_next(history, link);
history_list_delete_link_and_destroy(link);
link = next;
}
history->lines = 0;
}
@ -264,8 +445,8 @@ static char *special_history_func(const char *text, void *item, int *free_ret)
ret = NULL;
history = command_history_current(window);
for (tmp = history->list; tmp != NULL; tmp = tmp->next) {
const char *line = tmp->data;
for (tmp = command_history_list_first(history); tmp != NULL; tmp = command_history_list_next(history, tmp)) {
const char *line = ((HISTORY_ENTRY_REC *)tmp->data)->text;
if (match_wildcards(findtext, line)) {
*free_ret = TRUE;
@ -289,6 +470,8 @@ void command_history_init(void)
special_history_func_set(special_history_func);
history_entries = NULL;
global_history = command_history_create(NULL);
read_settings();
@ -308,4 +491,6 @@ void command_history_deinit(void)
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
command_history_destroy(global_history);
g_list_free_full(history_entries, (GDestroyNotify) history_entry_destroy);
}

View file

@ -6,12 +6,19 @@
typedef struct {
char *name;
GList *list, *pos;
GList *pos;
int lines;
int refcount;
int redo:1;
} HISTORY_REC;
typedef struct {
const char *text;
HISTORY_REC *history;
time_t time;
} HISTORY_ENTRY_REC;
HISTORY_REC *command_history_find(HISTORY_REC *history);
HISTORY_REC *command_history_find_name(const char *name);
@ -21,9 +28,19 @@ void command_history_init(void);
void command_history_deinit(void);
void command_history_add(HISTORY_REC *history, const char *text);
void command_history_load_entry(time_t time, HISTORY_REC *history, const char *text);
gboolean command_history_delete_entry(time_t history_time, HISTORY_REC *history, const char *text);
GList *command_history_list_last(HISTORY_REC *history);
GList *command_history_list_first(HISTORY_REC *history);
GList *command_history_list_prev(HISTORY_REC *history, GList *pos);
GList *command_history_list_next(HISTORY_REC *history, GList *pos);
const char *command_history_prev(WINDOW_REC *window, const char *text);
const char *command_history_next(WINDOW_REC *window, const char *text);
const char *command_global_history_prev(WINDOW_REC *window, const char *text);
const char *command_global_history_next(WINDOW_REC *window, const char *text);
const char *command_history_delete_current(WINDOW_REC *window, const char *text);
void command_history_clear_pos(WINDOW_REC *window);

View file

@ -0,0 +1,63 @@
/*
fe-capsicum.c : irssi
Copyright (C) 2017 Edward Tomasz Napierala <trasz@FreeBSD.org>
This software was developed by SRI International and the University of
Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
("CTSRD"), as part of the DARPA CRASH research programme.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "fe-capsicum.h"
#include "levels.h"
#include "module-formats.h"
#include "printtext.h"
#include "signals.h"
static void capability_mode_enabled(void)
{
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CAPSICUM_ENABLED);
}
static void capability_mode_disabled(void)
{
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CAPSICUM_DISABLED);
}
static void capability_mode_failed(gchar *msg)
{
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_CAPSICUM_FAILED, msg);
}
void fe_capsicum_init(void)
{
signal_add("capability mode enabled", (SIGNAL_FUNC) capability_mode_enabled);
signal_add("capability mode disabled", (SIGNAL_FUNC) capability_mode_disabled);
signal_add("capability mode failed", (SIGNAL_FUNC) capability_mode_failed);
}
void fe_capsicum_deinit(void)
{
signal_remove("capability mode enabled", (SIGNAL_FUNC) capability_mode_enabled);
signal_remove("capability mode disabled", (SIGNAL_FUNC) capability_mode_disabled);
signal_remove("capability mode failed", (SIGNAL_FUNC) capability_mode_failed);
}

View file

@ -0,0 +1,7 @@
#ifndef __FE_CAPSICUM_H
#define __FE_CAPSICUM_H
void fe_capsicum_init(void);
void fe_capsicum_deinit(void);
#endif

View file

@ -32,6 +32,9 @@
#include "special-vars.h"
#include "fe-core-commands.h"
#include "fe-queries.h"
#ifdef HAVE_CAPSICUM
#include "fe-capsicum.h"
#endif
#include "hilight-text.h"
#include "command-history.h"
#include "completion.h"
@ -179,6 +182,9 @@ void fe_common_core_init(void)
fe_server_init();
fe_settings_init();
fe_tls_init();
#ifdef HAVE_CAPSICUM
fe_capsicum_init();
#endif
windows_init();
window_activity_init();
window_commands_init();
@ -221,6 +227,9 @@ void fe_common_core_deinit(void)
fe_server_deinit();
fe_settings_deinit();
fe_tls_deinit();
#ifdef HAVE_CAPSICUM
fe_capsicum_deinit();
#endif
windows_deinit();
window_activity_deinit();
window_commands_deinit();

View file

@ -28,6 +28,9 @@
#include "settings.h"
#include "irssi-version.h"
#include "servers.h"
#ifdef HAVE_CAPSICUM
#include "capsicum.h"
#endif
#include "fe-windows.h"
#include "printtext.h"
@ -120,6 +123,9 @@ static void cmd_cat(const char *data)
GIOChannel *handle;
GString *buf;
gsize tpos;
#ifdef HAVE_CAPSICUM
int fd;
#endif
if (!cmd_get_params(data, &free_arg, 2, &fname, &fposstr))
return;
@ -128,7 +134,15 @@ static void cmd_cat(const char *data)
fpos = atoi(fposstr);
cmd_params_free(free_arg);
#ifdef HAVE_CAPSICUM
fd = capsicum_open_wrapper(fname, O_RDONLY, 0);
if (fd > 0)
handle = g_io_channel_unix_new(fd);
else
handle = NULL;
#else
handle = g_io_channel_new_file(fname, "r", NULL);
#endif
g_free(fname);
if (handle == NULL) {

View file

@ -30,6 +30,9 @@
#include "special-vars.h"
#include "settings.h"
#include "lib-config/iconfig.h"
#ifdef HAVE_CAPSICUM
#include "capsicum.h"
#endif
#include "fe-windows.h"
#include "window-items.h"
@ -451,7 +454,11 @@ static void autolog_open(SERVER_REC *server, const char *server_tag,
log_item_add(log, LOG_ITEM_TARGET, target, server_tag);
dir = g_path_get_dirname(log->real_fname);
#ifdef HAVE_CAPSICUM
capsicum_mkdir_with_parents_wrapper(dir, log_dir_create_mode);
#else
g_mkdir_with_parents(dir, log_dir_create_mode);
#endif
g_free(dir);
log->temp = TRUE;

View file

@ -26,7 +26,7 @@
#include "misc.h"
#include "lib-config/iconfig.h"
#include "settings.h"
#include "fe-settings.h"
#include "levels.h"
#include "printtext.h"
#include "keyboard.h"
@ -41,6 +41,11 @@ static void set_print(SETTINGS_REC *rec)
g_free(value);
}
void fe_settings_set_print(const char *key)
{
set_print(settings_get_record(key));
}
static void set_print_pattern(const char *pattern)
{
GSList *sets, *tmp;

View file

@ -0,0 +1,6 @@
#ifndef __FE_CHANNELS_H
#define __FE_CHANNELS_H
void fe_settings_set_print(const char *key);
#endif

View file

@ -106,6 +106,7 @@ static void hilight_destroy(HILIGHT_REC *rec)
if (rec->channels != NULL) g_strfreev(rec->channels);
g_free_not_null(rec->color);
g_free_not_null(rec->act_color);
g_free_not_null(rec->servertag);
g_free(rec->text);
g_free(rec);
}
@ -424,7 +425,7 @@ static void read_hilight_config(void)
CONFIG_NODE *node;
HILIGHT_REC *rec;
GSList *tmp;
char *text, *color;
char *text, *color, *servertag;
hilights_destroy_all();
@ -467,7 +468,9 @@ static void read_hilight_config(void)
rec->nickmask = config_node_get_bool(node, "mask", FALSE);
rec->fullword = config_node_get_bool(node, "fullword", FALSE);
rec->regexp = config_node_get_bool(node, "regexp", FALSE);
rec->servertag = config_node_get_str(node, "servertag", NULL);
servertag = config_node_get_str(node, "servertag", NULL);
rec->servertag = servertag == NULL || *servertag == '\0' ? NULL :
g_strdup(servertag);
hilight_init_rec(rec);
node = iconfig_node_section(node, "channels", -1);

View file

@ -290,6 +290,9 @@ FORMAT_REC fecommon_core_formats[] = {
{ "completion_header", "%#Key Value Auto", 0 },
{ "completion_line", "%#$[10]0 $[!40]1 $2", 3, { 0, 0, 0 } },
{ "completion_footer", "", 0 },
{ "capsicum_enabled", "Capability mode enabled", 0 },
{ "capsicum_disabled", "Capability mode not enabled", 0 },
{ "capsicum_failed", "Capability mode failed: $0", 1, { 0 } },
/* ---- */
{ NULL, "TLS", 0 },

View file

@ -255,6 +255,9 @@ enum {
TXT_COMPLETION_HEADER,
TXT_COMPLETION_LINE,
TXT_COMPLETION_FOOTER,
TXT_CAPSICUM_ENABLED,
TXT_CAPSICUM_DISABLED,
TXT_CAPSICUM_FAILED,
TLS_FILL_15,

View file

@ -587,7 +587,7 @@ static char *theme_format_compress_colors(THEME_REC *theme, const char *format)
/* a normal character */
g_string_append_c(str, *format);
format++;
} else {
} else if (format[1] != '\0') {
/* %format */
format++;
if (IS_OLD_FORMAT(*format, last_fg, last_bg)) {
@ -614,6 +614,11 @@ static char *theme_format_compress_colors(THEME_REC *theme, const char *format)
last_bg = '\0';
}
format++;
} else {
/* % at end of string */
format++;
g_string_append_c(str, '%');
g_string_append_c(str, '%');
}
}

View file

@ -48,6 +48,8 @@ static void cmd_network_list(void)
g_string_truncate(str, 0);
if (rec->nick != NULL)
g_string_append_printf(str, "nick: %s, ", rec->nick);
if (rec->alternate_nick != NULL)
g_string_append_printf(str, "alternate_nick: %s, ", rec->alternate_nick);
if (rec->username != NULL)
g_string_append_printf(str, "username: %s, ", rec->username);
if (rec->realname != NULL)
@ -114,6 +116,7 @@ static void cmd_network_add_modify(const char *data, gboolean add)
rec->name = g_strdup(name);
} else {
if (g_hash_table_lookup(optlist, "nick")) g_free_and_null(rec->nick);
if (g_hash_table_lookup(optlist, "alternate_nick")) g_free_and_null(rec->alternate_nick);
if (g_hash_table_lookup(optlist, "user")) g_free_and_null(rec->username);
if (g_hash_table_lookup(optlist, "realname")) g_free_and_null(rec->realname);
if (g_hash_table_lookup(optlist, "host")) {
@ -145,6 +148,8 @@ static void cmd_network_add_modify(const char *data, gboolean add)
value = g_hash_table_lookup(optlist, "nick");
if (value != NULL && *value != '\0') rec->nick = g_strdup(value);
value = g_hash_table_lookup(optlist, "alternate_nick");
if (value != NULL && *value != '\0') rec->alternate_nick = g_strdup(value);
value = g_hash_table_lookup(optlist, "user");
if (value != NULL && *value != '\0') rec->username = g_strdup(value);
value = g_hash_table_lookup(optlist, "realname");
@ -175,7 +180,7 @@ static void cmd_network_add_modify(const char *data, gboolean add)
cmd_params_free(free_arg);
}
/* SYNTAX: NETWORK ADD|MODIFY [-nick <nick>] [-user <user>] [-realname <name>]
/* SYNTAX: NETWORK ADD|MODIFY [-nick <nick>] [-alternate_nick <nick>] [-user <user>] [-realname <name>]
[-host <host>] [-usermode <mode>] [-autosendcmd <cmd>]
[-querychans <count>] [-whois <count>] [-msgs <count>]
[-kicks <count>] [-modes <count>] [-cmdspeed <ms>]
@ -228,9 +233,9 @@ void fe_ircnet_init(void)
command_bind("network remove", NULL, (SIGNAL_FUNC) cmd_network_remove);
command_set_options("network add", "-kicks -msgs -modes -whois -cmdspeed "
"-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
"-cmdmax -nick -alternate_nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
command_set_options("network modify", "-kicks -msgs -modes -whois -cmdspeed "
"-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
"-cmdmax -nick -alternate_nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
}
void fe_ircnet_deinit(void)

View file

@ -936,6 +936,26 @@ void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos)
gui_entry_draw(entry);
}
void gui_entry_set_text_and_pos_bytes(GUI_ENTRY_REC *entry, const char *str, int pos_bytes)
{
int pos;
const char *ptr;
g_return_if_fail(entry != NULL);
gui_entry_set_text(entry, str);
if (entry->utf8) {
g_utf8_validate(str, pos_bytes, &ptr);
pos = g_utf8_pointer_to_offset(str, ptr);
} else if (term_type == TERM_TYPE_BIG5)
pos = strlen_big5((const unsigned char *)str) - strlen_big5((const unsigned char *)(str + pos_bytes));
else
pos = pos_bytes;
gui_entry_set_pos(entry, pos);
}
void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos)
{
g_return_if_fail(entry != NULL);

View file

@ -50,6 +50,7 @@ void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8);
void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str);
char *gui_entry_get_text(GUI_ENTRY_REC *entry);
char *gui_entry_get_text_and_pos(GUI_ENTRY_REC *entry, int *pos);
void gui_entry_set_text_and_pos_bytes(GUI_ENTRY_REC *entry, const char *str, int pos_bytes);
void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str);
void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr);

View file

@ -530,6 +530,39 @@ static void key_forward_history(void)
g_free(line);
}
static void key_backward_global_history(void)
{
const char *text;
char *line;
line = gui_entry_get_text(active_entry);
text = command_global_history_prev(active_win, line);
gui_entry_set_text(active_entry, text);
g_free(line);
}
static void key_forward_global_history(void)
{
const char *text;
char *line;
line = gui_entry_get_text(active_entry);
text = command_global_history_next(active_win, line);
gui_entry_set_text(active_entry, text);
g_free(line);
}
static void key_erase_history_entry(void)
{
const char *text;
char *line;
line = gui_entry_get_text(active_entry);
text = command_history_delete_current(active_win, line);
gui_entry_set_text(active_entry, text);
g_free(line);
}
static void key_beginning_of_line(void)
{
gui_entry_set_pos(active_entry, 0);
@ -878,8 +911,7 @@ static void key_completion(int erase, int backward)
g_free(text);
if (line != NULL) {
gui_entry_set_text(active_entry, line);
gui_entry_set_pos(active_entry, pos);
gui_entry_set_text_and_pos_bytes(active_entry, line, pos);
g_free(line);
}
}
@ -909,8 +941,7 @@ static void key_check_replaces(void)
g_free(text);
if (line != NULL) {
gui_entry_set_text(active_entry, line);
gui_entry_set_pos(active_entry, pos);
gui_entry_set_text_and_pos_bytes(active_entry, line, pos);
g_free(line);
}
}
@ -1178,6 +1209,8 @@ void gui_readline_init(void)
key_bind("key", NULL, "meta2-5C", "cright", (SIGNAL_FUNC) key_combo);
key_bind("key", NULL, "meta2-1;5D", "cleft", (SIGNAL_FUNC) key_combo);
key_bind("key", NULL, "meta2-1;5C", "cright", (SIGNAL_FUNC) key_combo);
key_bind("key", NULL, "meta2-1;5A", "cup", (SIGNAL_FUNC) key_combo);
key_bind("key", NULL, "meta2-1;5B", "cdown", (SIGNAL_FUNC) key_combo);
key_bind("key", NULL, "meta2-1;3A", "mup", (SIGNAL_FUNC) key_combo);
key_bind("key", NULL, "meta2-1;3B", "mdown", (SIGNAL_FUNC) key_combo);
@ -1219,6 +1252,9 @@ void gui_readline_init(void)
/* history */
key_bind("backward_history", "Go back one line in the history", "up", NULL, (SIGNAL_FUNC) key_backward_history);
key_bind("forward_history", "Go forward one line in the history", "down", NULL, (SIGNAL_FUNC) key_forward_history);
key_bind("backward_global_history", "Go back one line in the global history", "cup", NULL, (SIGNAL_FUNC) key_backward_global_history);
key_bind("forward_global_history", "Go forward one line in the global history", "cdown", NULL, (SIGNAL_FUNC) key_forward_global_history);
key_bind("erase_history_entry", "Erase the currently active entry from the history", NULL, NULL, (SIGNAL_FUNC) key_erase_history_entry);
/* line editing */
key_bind("backspace", "Delete the previous character", "backspace", NULL, (SIGNAL_FUNC) key_backspace);
@ -1312,6 +1348,9 @@ void gui_readline_deinit(void)
key_unbind("backward_history", (SIGNAL_FUNC) key_backward_history);
key_unbind("forward_history", (SIGNAL_FUNC) key_forward_history);
key_unbind("backward_global_history", (SIGNAL_FUNC) key_backward_global_history);
key_unbind("forward_global_history", (SIGNAL_FUNC) key_forward_global_history);
key_unbind("erase_history_entry", (SIGNAL_FUNC) key_erase_history_entry);
key_unbind("backspace", (SIGNAL_FUNC) key_backspace);
key_unbind("delete_character", (SIGNAL_FUNC) key_delete_character);

View file

@ -31,6 +31,7 @@
#include "printtext.h"
#include "fe-common-core.h"
#include "fe-settings.h"
#include "themes.h"
#include "term.h"
@ -79,25 +80,8 @@ static int dirty, full_redraw;
static GMainLoop *main_loop;
int quitting;
static const char *banner_text =
" ___ _\n"
"|_ _|_ _ _____(_)\n"
" | || '_(_-<_-< |\n"
"|___|_| /__/__/_|\n"
"Irssi v" PACKAGE_VERSION " - http://www.irssi.org";
static const char *firsttimer_text =
"- - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"
"Hi there! If this is your first time using Irssi, you\n"
"might want to go to our website and read the startup\n"
"documentation to get you going.\n\n"
"Our community and staff are available to assist you or\n"
"to answer any questions you may have.\n\n"
"Use the /HELP command to get detailed information about\n"
"the available commands.\n"
"- - - - - - - - - - - - - - - - - - - - - - - - - - - -";
static int display_firsttimer = FALSE;
static unsigned int user_settings_changed = 0;
static void sig_exit(void)
@ -105,6 +89,11 @@ static void sig_exit(void)
quitting = TRUE;
}
static void sig_settings_userinfo_changed(gpointer changedp)
{
user_settings_changed = GPOINTER_TO_UINT(changedp);
}
/* redraw irssi's screen.. */
void irssi_redraw(void)
{
@ -161,6 +150,7 @@ static void textui_init(void)
fe_common_irc_init();
theme_register(gui_text_formats);
signal_add("settings userinfo changed", (SIGNAL_FUNC) sig_settings_userinfo_changed);
signal_add_last("gui exit", (SIGNAL_FUNC) sig_exit);
}
@ -199,14 +189,24 @@ static void textui_finish_init(void)
statusbar_redraw(NULL, TRUE);
if (servers == NULL && lookup_servers == NULL) {
printtext(NULL, NULL, MSGLEVEL_CRAP|MSGLEVEL_NO_ACT,
"%s", banner_text);
printformat(NULL, NULL, MSGLEVEL_CRAP|MSGLEVEL_NO_ACT, TXT_IRSSI_BANNER);
}
if (display_firsttimer) {
printtext(NULL, NULL, MSGLEVEL_CRAP|MSGLEVEL_NO_ACT,
"%s", firsttimer_text);
printformat(NULL, NULL, MSGLEVEL_CRAP|MSGLEVEL_NO_ACT, TXT_WELCOME_FIRSTTIME);
}
/* see irc-servers-setup.c:init_userinfo */
if (user_settings_changed)
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WELCOME_INIT_SETTINGS);
if (user_settings_changed & USER_SETTINGS_REAL_NAME)
fe_settings_set_print("real_name");
if (user_settings_changed & USER_SETTINGS_USER_NAME)
fe_settings_set_print("user_name");
if (user_settings_changed & USER_SETTINGS_NICK)
fe_settings_set_print("nick");
if (user_settings_changed & USER_SETTINGS_HOSTNAME)
fe_settings_set_print("hostname");
}
static void textui_deinit(void)
@ -222,7 +222,8 @@ static void textui_deinit(void)
fe_perl_deinit();
#endif
dirty_check(); /* one last time to print any quit messages */
dirty_check(); /* one last time to print any quit messages */
signal_remove("settings userinfo changed", (SIGNAL_FUNC) sig_settings_userinfo_changed);
signal_remove("gui exit", (SIGNAL_FUNC) sig_exit);
lastlog_deinit();
@ -259,12 +260,11 @@ static void check_files(void)
}
}
int main(int argc, char **argv)
{
static int version = 0;
static GOptionEntry options[] = {
{ "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Display irssi version", NULL },
{ "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Display Irssi version", NULL },
{ NULL }
};
int loglev;

View file

@ -79,5 +79,26 @@ FORMAT_REC gui_text_formats[] =
{ "paste_warning", "Pasting $0 lines to $1. Press Ctrl-K if you wish to do this or Ctrl-C to cancel.", 2, { 1, 0 } },
{ "paste_prompt", "Hit Ctrl-K to paste, Ctrl-C to abort?", 0 },
/* ---- */
{ NULL, "Welcome", 0 },
{ "irssi_banner",
" ___ _%:"
"|_ _|_ _ _____(_)%:"
" | || '_(_-<_-< |%:"
"|___|_| /__/__/_|%:"
"Irssi v$J - http://www.irssi.org", 0 },
{ "welcome_firsttime",
"- - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"
"Hi there! If this is your first time using Irssi, you%:"
"might want to go to our website and read the startup%:"
"documentation to get you going.%:%:"
"Our community and staff are available to assist you or%:"
"to answer any questions you may have.%:%:"
"Use the /HELP command to get detailed information about%:"
"the available commands.%:"
"- - - - - - - - - - - - - - - - - - - - - - - - - - - -", 0 },
{ "welcome_init_settings", "The following settings were initialized", 0 },
{ NULL, NULL, 0 }
};

View file

@ -53,6 +53,12 @@ enum {
TXT_PASTE_WARNING,
TXT_PASTE_PROMPT,
TXT_FILL_5, /* Welcome */
TXT_IRSSI_BANNER,
TXT_WELCOME_FIRSTTIME,
TXT_WELCOME_INIT_SETTINGS,
TXT_COUNT
};

View file

@ -37,7 +37,7 @@ static void check_join_failure(IRC_SERVER_REC *server, const char *channel)
channel++; /* server didn't understand !channels */
chanrec = channel_find(SERVER(server), channel);
if (chanrec == NULL && channel[0] == '!') {
if (chanrec == NULL && channel[0] == '!' && strlen(channel) > 6) {
/* it probably replied with the full !channel name,
find the channel with the short name.. */
chan2 = g_strdup_printf("!%s", channel+6);

View file

@ -119,21 +119,22 @@ static void query_remove_all(IRC_CHANNEL_REC *channel)
int n;
rec = channel->server->chanqueries;
if (rec == NULL) return;
/* remove channel from query lists */
for (n = 0; n < CHANNEL_QUERIES; n++)
rec->queries[n] = g_slist_remove(rec->queries[n], channel);
rec->current_queries = g_slist_remove(rec->current_queries, channel);
query_check(channel->server);
if (!channel->server->disconnected)
query_check(channel->server);
}
static void sig_channel_destroyed(IRC_CHANNEL_REC *channel)
{
g_return_if_fail(channel != NULL);
if (IS_IRC_CHANNEL(channel) && !channel->server->disconnected &&
!channel->synced)
if (IS_IRC_CHANNEL(channel))
query_remove_all(channel);
}

View file

@ -43,6 +43,9 @@ static void sig_chatnet_read(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
value = config_node_get_str(node, "usermode", NULL);
rec->usermode = (value != NULL && *value != '\0') ? g_strdup(value) : NULL;
value = config_node_get_str(node, "alternate_nick", NULL);
rec->alternate_nick = (value != NULL && *value != '\0') ? g_strdup(value) : NULL;
rec->max_cmds_at_once = config_node_get_int(node, "cmdmax", 0);
rec->cmd_queue_speed = config_node_get_int(node, "cmdspeed", 0);
rec->max_query_chans = config_node_get_int(node, "max_query_chans", 0);
@ -65,6 +68,9 @@ static void sig_chatnet_saved(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
if (rec->usermode != NULL)
iconfig_node_set_str(node, "usermode", rec->usermode);
if (rec->alternate_nick != NULL)
iconfig_node_set_str(node, "alternate_nick", rec->alternate_nick);
if (rec->max_cmds_at_once > 0)
iconfig_node_set_int(node, "cmdmax", rec->max_cmds_at_once);
if (rec->cmd_queue_speed > 0)
@ -93,6 +99,7 @@ static void sig_chatnet_destroyed(IRC_CHATNET_REC *rec)
{
if (IS_IRC_CHATNET(rec)) {
g_free(rec->usermode);
g_free(rec->alternate_nick);
g_free(rec->sasl_mechanism);
g_free(rec->sasl_username);
g_free(rec->sasl_password);

View file

@ -18,6 +18,7 @@ struct _IRC_CHATNET_REC {
#include "chatnet-rec.h"
char *usermode;
char *alternate_nick;
char *sasl_mechanism;
char *sasl_username;

View file

@ -69,7 +69,10 @@ static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn,
return;
g_return_if_fail(IS_IRCNET(ircnet));
if (ircnet->nick != NULL) g_free_and_null(conn->alternate_nick);
if (ircnet->alternate_nick != NULL) {
g_free_and_null(conn->alternate_nick);
conn->alternate_nick = g_strdup(ircnet->alternate_nick);
}
if (ircnet->usermode != NULL) {
g_free_and_null(conn->usermode);
conn->usermode = g_strdup(ircnet->usermode);
@ -113,14 +116,17 @@ static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn,
static void init_userinfo(void)
{
unsigned int changed;
const char *set, *nick, *user_name, *str;
changed = 0;
/* check if nick/username/realname wasn't read from setup.. */
set = settings_get_str("real_name");
if (set == NULL || *set == '\0') {
str = g_getenv("IRCNAME");
settings_set_str("real_name",
str != NULL ? str : g_get_real_name());
changed |= USER_SETTINGS_REAL_NAME;
}
/* username */
@ -131,6 +137,7 @@ static void init_userinfo(void)
str != NULL ? str : g_get_user_name());
user_name = settings_get_str("user_name");
changed |= USER_SETTINGS_USER_NAME;
}
/* nick */
@ -140,15 +147,20 @@ static void init_userinfo(void)
settings_set_str("nick", str != NULL ? str : user_name);
nick = settings_get_str("nick");
changed |= USER_SETTINGS_NICK;
}
/* host name */
set = settings_get_str("hostname");
if (set == NULL || *set == '\0') {
str = g_getenv("IRCHOST");
if (str != NULL)
if (str != NULL) {
settings_set_str("hostname", str);
changed |= USER_SETTINGS_HOSTNAME;
}
}
signal_emit("irssi init userinfo changed", 1, GUINT_TO_POINTER(changed));
}
static void sig_server_setup_read(IRC_SERVER_SETUP_REC *rec, CONFIG_NODE *node)

View file

@ -116,11 +116,14 @@ static char **split_line(const SERVER_REC *server, const char *line,
* the code much simpler. It's worth it.
*/
len -= strlen(recoded_start) + strlen(recoded_end);
g_warn_if_fail(len > 0);
if (len <= 0) {
/* There is no room for anything. */
g_free(recoded_start);
g_free(recoded_end);
return NULL;
lines = g_new(char *, 1);
lines[0] = NULL;
return lines;
}
lines = recode_split(server, line, target, len, onspace);

View file

@ -66,6 +66,13 @@ CHAT_DCC_REC *dcc_chat_create(IRC_SERVER_REC *server,
dcc->id = dcc_chat_get_new_id(nick);
dcc_init_rec(DCC(dcc), server, chat, nick, arg);
if (dcc->module_data == NULL) {
/* failed to successfully init; TODO: change init_rec API */
g_free(dcc->id);
g_free(dcc);
return NULL;
}
return dcc;
}
@ -471,6 +478,7 @@ static void cmd_dcc_chat(const char *data, IRC_SERVER_REC *server)
/* We are accepting a passive DCC CHAT. */
dcc_chat_passive(dcc);
}
cmd_params_free(free_arg);
return;
}
@ -485,6 +493,11 @@ static void cmd_dcc_chat(const char *data, IRC_SERVER_REC *server)
cmd_param_error(CMDERR_NOT_CONNECTED);
dcc = dcc_chat_create(server, NULL, nick, "chat");
if (dcc == NULL) {
cmd_params_free(free_arg);
g_warn_if_reached();
return;
}
if (g_hash_table_lookup(optlist, "passive") == NULL) {
/* Standard DCC CHAT... let's listen for incoming connections */
@ -627,6 +640,9 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
}
passive = paramcount == 4 && g_strcmp0(params[2], "0") == 0;
if (nick == NULL)
nick = "";
dcc = DCC_CHAT(dcc_find_request(DCC_CHAT_TYPE, nick, NULL));
if (dcc != NULL) {
if (dcc_is_listening(dcc)) {
@ -658,6 +674,11 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
}
dcc = dcc_chat_create(server, chat, nick, params[0]);
if (dcc == NULL) {
g_strfreev(params);
g_warn_if_reached();
return;
}
dcc->target = g_strdup(target);
dcc->port = atoi(params[2]);

View file

@ -43,6 +43,12 @@ GET_DCC_REC *dcc_get_create(IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
dcc->fhandle = -1;
dcc_init_rec(DCC(dcc), server, chat, nick, arg);
if (dcc->module_data == NULL) {
/* failed to successfully init; TODO: change API */
g_free(dcc);
return NULL;
}
return dcc;
}
@ -430,9 +436,10 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
int p_id = -1;
int passive = FALSE;
if (addr == NULL) {
if (addr == NULL)
addr = "";
}
if (nick == NULL)
nick = "";
/* SEND <file name> <address> <port> <size> [...] */
/* SEND <file name> <address> 0 <size> <id> (DCC SEND passive protocol) */
@ -512,6 +519,12 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
dcc_destroy(DCC(dcc)); /* remove the old DCC */
dcc = dcc_get_create(server, chat, nick, fname);
if (dcc == NULL) {
g_free(address);
g_free(fname);
g_warn_if_reached();
return;
}
dcc->target = g_strdup(target);
if (passive && port == 0)

View file

@ -237,6 +237,12 @@ static SEND_DCC_REC *dcc_send_create(IRC_SERVER_REC *server,
dcc->queue = -1;
dcc_init_rec(DCC(dcc), server, chat, nick, arg);
if (dcc->module_data == NULL) {
/* failed to successfully init; TODO: change API */
g_free(dcc);
return NULL;
}
return dcc;
}
@ -417,6 +423,10 @@ static int dcc_send_one_file(int queue, const char *target, const char *fname,
dcc = dcc_send_create(server, chat, target, str);
g_free(str);
if (dcc == NULL) {
g_warn_if_reached();
return FALSE;
}
dcc->handle = handle;
dcc->port = port;

View file

@ -59,7 +59,7 @@ perl-signals-list.h: $(top_srcdir)/docs/signals.txt $(srcdir)/get-signals.pl
cat $(top_srcdir)/docs/signals.txt | $(perlpath) $(srcdir)/get-signals.pl > perl-signals-list.h
irssi-core.pl.h: irssi-core.pl
$(top_srcdir)/file2header.sh $(srcdir)/irssi-core.pl irssi_core_code > irssi-core.pl.h
$(top_srcdir)/utils/file2header.sh $(srcdir)/irssi-core.pl irssi_core_code > irssi-core.pl.h
common_sources = \
common/Irssi.xs \

View file

@ -252,8 +252,148 @@ PREINIT:
GList *tmp;
PPCODE:
rec = command_history_current(window);
for (tmp = rec->list; tmp != NULL; tmp = tmp->next)
XPUSHs(sv_2mortal(new_pv(tmp->data)));
for (tmp = command_history_list_first(rec); tmp != NULL; tmp = command_history_list_next(rec, tmp))
XPUSHs(sv_2mortal(new_pv(((HISTORY_ENTRY_REC *)tmp->data)->text)));
void
window_get_history_entries(window)
Irssi::UI::Window window
PREINIT:
HISTORY_REC *rec;
HISTORY_ENTRY_REC *ent;
WINDOW_REC *win;
GList *tmp;
GSList *stmp;
HV *hv;
PPCODE:
rec = window == NULL ? NULL : command_history_current(window);
for (tmp = command_history_list_first(rec); tmp != NULL; tmp = command_history_list_next(rec, tmp)) {
hv = (HV*)sv_2mortal((SV*)newHV());
ent = tmp->data;
hv_store(hv, "text", 4, newSVpv(ent->text, 0), 0);
hv_store(hv, "time", 4, newSViv(ent->time), 0);
if (ent->history == command_history_current(NULL)) {
hv_store(hv, "history", 7, newSV(0), 0);
hv_store(hv, "window", 6, newSV(0), 0);
} else {
if (ent->history->name == NULL) {
hv_store(hv, "history", 7, newSV(0), 0);
for (stmp = windows; stmp != NULL; stmp = stmp->next) {
win = stmp->data;
if (win->history == ent->history) {
hv_store(hv, "window", 6, newSViv(win->refnum), 0);
break;
}
}
} else {
hv_store(hv, "history", 7, new_pv(ent->history->name), 0);
hv_store(hv, "window", 6, newSV(0), 0);
}
}
XPUSHs(sv_2mortal(newRV_inc((SV*)hv)));
}
void
window_load_history_entries(window, ...)
Irssi::UI::Window window
PREINIT:
HV *hv;
SV **sv;
HISTORY_REC *history;
WINDOW_REC *tmp;
const char *text;
long hist_time;
int i;
PPCODE:
for (i = 1; i < items; i++) {
if (!is_hvref(ST(i))) {
croak("Usage: Irssi::UI::Window::load_history_entries(window, hash...)");
}
hv = hvref(ST(i));
if (hv != NULL) {
tmp = NULL;
text = NULL;
hist_time = time(NULL);
history = command_history_current(NULL);
sv = hv_fetch(hv, "text", 4, 0);
if (sv != NULL) text = SvPV_nolen(*sv);
sv = hv_fetch(hv, "time", 4, 0);
if (sv != NULL && SvOK(*sv)) hist_time = SvIV(*sv);
if (window != NULL) {
history = command_history_current(window);
} else {
sv = hv_fetch(hv, "history", 7, 0);
if (sv != NULL && SvOK(*sv)) {
history = command_history_find_name(SvPV_nolen(*sv));
}
sv = hv_fetch(hv, "window", 6, 0);
if (sv != NULL && SvOK(*sv)) {
tmp = window_find_refnum(SvIV(*sv));
if (tmp != NULL) {
history = tmp->history;
}
}
}
if (text != NULL && history != NULL) {
command_history_load_entry(hist_time, history, text);
}
}
}
void
window_delete_history_entries(window, ...)
Irssi::UI::Window window
PREINIT:
HV *hv;
SV **sv;
HISTORY_REC *history;
WINDOW_REC *tmp;
const char *text;
long hist_time;
int i;
PPCODE:
for (i = 1; i < items; i++) {
if (!is_hvref(ST(i))) {
croak("Usage: Irssi::UI::Window::delete_history_entries(window, hash...)");
}
hv = hvref(ST(i));
if (hv != NULL) {
tmp = NULL;
text = NULL;
hist_time = -1;
history = command_history_current(NULL);
sv = hv_fetch(hv, "text", 4, 0);
if (sv != NULL) text = SvPV_nolen(*sv);
sv = hv_fetch(hv, "time", 4, 0);
if (sv != NULL && SvOK(*sv)) hist_time = SvIV(*sv);
if (window != NULL) {
history = command_history_current(window);
} else {
sv = hv_fetch(hv, "history", 7, 0);
if (sv != NULL && SvOK(*sv)) {
history = command_history_find_name(SvPV_nolen(*sv));
}
sv = hv_fetch(hv, "window", 6, 0);
if (sv != NULL && SvOK(*sv)) {
tmp = window_find_refnum(SvIV(*sv));
if (tmp != NULL) {
history = tmp->history;
}
}
}
if (text != NULL && history != NULL) {
XPUSHs(boolSV(command_history_delete_entry(hist_time, history, text)));
}
}
}
#*******************************
MODULE = Irssi::UI::Window PACKAGE = Irssi::Windowitem PREFIX = window_item_