summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorTimo Sirainen <cras@irssi.org>2000-08-26 15:39:44 +0000
committercras <cras@dbcabf3a-b0e7-0310-adc4-f8d773084564>2000-08-26 15:39:44 +0000
commite395e87dedd9aa85f05e5c74330a76f1ef700371 (patch)
tree1184487b13038499f1771e4c553222f85b8524d2 /src/core
parent3d124da13b8da5c0b535abfe6265fc471d9d2ebd (diff)
downloadirssi-e395e87dedd9aa85f05e5c74330a76f1ef700371.zip
Lots of moving stuff around - hopefully I didn't break too much :)
git-svn-id: http://svn.irssi.org/repos/irssi/trunk@632 dbcabf3a-b0e7-0310-adc4-f8d773084564
Diffstat (limited to 'src/core')
-rw-r--r--src/core/Makefile.am41
-rw-r--r--src/core/channel-rec.h21
-rw-r--r--src/core/channels-setup.c170
-rw-r--r--src/core/channels-setup.h33
-rw-r--r--src/core/channels.c116
-rw-r--r--src/core/channels.h31
-rw-r--r--src/core/chatnet-rec.h12
-rw-r--r--src/core/chatnets.c148
-rw-r--r--src/core/chatnets.h37
-rw-r--r--src/core/commands.c9
-rw-r--r--src/core/core.c20
-rw-r--r--src/core/masks.c137
-rw-r--r--src/core/masks.h13
-rw-r--r--src/core/memdebug.c53
-rw-r--r--src/core/misc.c4
-rw-r--r--src/core/misc.h2
-rw-r--r--src/core/modules.c21
-rw-r--r--src/core/nicklist.c268
-rw-r--r--src/core/nicklist.h50
-rw-r--r--src/core/queries.c124
-rw-r--r--src/core/queries.h32
-rw-r--r--src/core/query-rec.h9
-rw-r--r--src/core/server-connect-rec.h26
-rw-r--r--src/core/server-rec.h50
-rw-r--r--src/core/server-setup-rec.h19
-rw-r--r--src/core/server.h83
-rw-r--r--src/core/servers-reconnect.c356
-rw-r--r--src/core/servers-reconnect.h20
-rw-r--r--src/core/servers-redirect.c (renamed from src/core/server-redirect.c)21
-rw-r--r--src/core/servers-redirect.h (renamed from src/core/server-redirect.h)6
-rw-r--r--src/core/servers-setup.c456
-rw-r--r--src/core/servers-setup.h46
-rw-r--r--src/core/servers.c (renamed from src/core/server.c)133
-rw-r--r--src/core/servers.h52
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/signals.c3
-rw-r--r--src/core/special-vars.c125
-rw-r--r--src/core/special-vars.h17
-rw-r--r--src/core/window-item-def.h9
-rw-r--r--src/core/window-item-rec.h14
40 files changed, 2614 insertions, 174 deletions
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 2361419f..d7534ee5 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -1,4 +1,4 @@
-noinst_LTLIBRARIES = libcore.la
+noinst_LIBRARIES = libcore.a
INCLUDES = \
$(GLIB_CFLAGS) \
@@ -13,13 +13,17 @@ else
memdebug_src=
endif
-libcore_la_SOURCES = \
+libcore_a_SOURCES = \
args.c \
+ channels.c \
+ channels-setup.c \
commands.c \
+ chatnets.c \
core.c \
levels.c \
line-split.c \
log.c \
+ masks.c \
$(memdebug_src) \
misc.c \
modules.c \
@@ -27,21 +31,38 @@ libcore_la_SOURCES = \
net-nonblock.c \
net-sendbuffer.c \
network.c \
+ nicklist.c \
pidwait.c \
+ queries.c \
rawlog.c \
- server.c \
- server-redirect.c \
+ servers.c \
+ servers-reconnect.c \
+ servers-redirect.c \
+ servers-setup.c \
settings.c \
signals.c \
special-vars.c
+structure_headers = \
+ chatnet-rec.h \
+ channel-rec.h \
+ query-rec.h \
+ server-rec.h \
+ server-setup-rec.h \
+ server-connect-rec.h \
+ window-item-rec.h
+
noinst_HEADERS = \
args.h \
+ channels.h \
+ channels-setup.h \
commands.h \
+ chatnets.h \
core.h \
levels.h \
line-split.h \
log.h \
+ masks.h \
memdebug.h \
misc.h \
module.h \
@@ -51,13 +72,19 @@ noinst_HEADERS = \
net-nonblock.h \
net-sendbuffer.h \
network.h \
+ nicklist.h \
pidwait.h \
+ queries.h \
rawlog.h \
- server.h \
- server-redirect.h \
+ servers.h \
+ servers-reconnect.h \
+ servers-redirect.h \
+ servers-setup.h \
settings.h \
signals.h \
- special-vars.h
+ special-vars.h \
+ window-item-def.h \
+ $(structure_headers)
EXTRA_DIST = \
memdebug.c
diff --git a/src/core/channel-rec.h b/src/core/channel-rec.h
new file mode 100644
index 00000000..d3b3cd0d
--- /dev/null
+++ b/src/core/channel-rec.h
@@ -0,0 +1,21 @@
+/* CHANNEL_REC definition, used for inheritance */
+
+#include "window-item-rec.h"
+
+char *topic;
+GHashTable *nicks; /* list of nicks */
+
+int no_modes:1; /* channel doesn't support modes */
+char *mode;
+int limit; /* user limit */
+char *key; /* password key */
+
+int chanop:1; /* You're a channel operator */
+int names_got:1; /* Received /NAMES list */
+int wholist:1; /* WHO list got */
+int synced:1; /* Channel synced - all queries done */
+
+int joined:1; /* Have we even received JOIN event for this channel? */
+int left:1; /* You just left the channel */
+int kicked:1; /* You just got kicked */
+int destroying:1;
diff --git a/src/core/channels-setup.c b/src/core/channels-setup.c
new file mode 100644
index 00000000..6867568d
--- /dev/null
+++ b/src/core/channels-setup.c
@@ -0,0 +1,170 @@
+/*
+ channels-setup.c : irssi
+
+ Copyright (C) 1999-2000 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "channels.h"
+#include "channels-setup.h"
+#include "servers-setup.h"
+
+GSList *setupchannels;
+
+static CHANNEL_SETUP_REC *channel_setup_read(CONFIG_NODE *node)
+{
+ CHANNEL_SETUP_REC *rec;
+ char *channel, *password, *botmasks, *autosendcmd;
+
+ g_return_val_if_fail(node != NULL, NULL);
+
+ channel = config_node_get_str(node, "name", NULL);
+ if (channel == NULL) {
+ /* missing information.. */
+ return NULL;
+ }
+
+ password = config_node_get_str(node, "password", NULL);
+ botmasks = config_node_get_str(node, "botmasks", NULL);
+ autosendcmd = config_node_get_str(node, "autosendcmd", NULL);
+
+ rec = g_new(CHANNEL_SETUP_REC, 1);
+ rec->autojoin = config_node_get_bool(node, "autojoin", FALSE);
+ rec->name = g_strdup(channel);
+ rec->chatnet = g_strdup(config_node_get_str(node, "chatnet", NULL));
+ rec->password = (password == NULL || *password == '\0') ? NULL : g_strdup(password);
+ rec->botmasks = (botmasks == NULL || *botmasks == '\0') ? NULL : g_strdup(botmasks);
+ rec->autosendcmd = (autosendcmd == NULL || *autosendcmd == '\0') ? NULL : g_strdup(autosendcmd);
+
+ setupchannels = g_slist_append(setupchannels, rec);
+ signal_emit("channel setup created", 2, rec, node);
+ return rec;
+}
+
+static void channel_setup_save(CHANNEL_SETUP_REC *channel)
+{
+ CONFIG_NODE *parentnode, *node;
+ int index;
+
+ index = g_slist_index(setupchannels, channel);
+
+ parentnode = iconfig_node_traverse("(channels", TRUE);
+ node = config_node_index(parentnode, index);
+ if (node == NULL)
+ node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
+
+ iconfig_node_clear(node);
+ iconfig_node_set_str(node, "name", channel->name);
+ iconfig_node_set_str(node, "chatnet", channel->chatnet);
+ if (channel->autojoin)
+ config_node_set_bool(node, "autojoin", TRUE);
+ iconfig_node_set_str(node, "password", channel->password);
+ iconfig_node_set_str(node, "botmasks", channel->botmasks);
+ iconfig_node_set_str(node, "autosendcmd", channel->autosendcmd);
+}
+
+static void channel_config_remove(CHANNEL_SETUP_REC *channel)
+{
+ CONFIG_NODE *node;
+
+ node = iconfig_node_traverse("channels", FALSE);
+ if (node != NULL) iconfig_node_list_remove(node, g_slist_index(setupchannels, channel));
+}
+
+void channels_setup_create(CHANNEL_SETUP_REC *channel)
+{
+ if (g_slist_find(setupchannels, channel) == NULL)
+ setupchannels = g_slist_append(setupchannels, channel);
+ channel_setup_save(channel);
+
+ signal_emit("channel setup created", 1, channel);
+}
+
+static void channels_setup_destroy_rec(CHANNEL_SETUP_REC *channel)
+{
+ g_return_if_fail(channel != NULL);
+
+ setupchannels = g_slist_remove(setupchannels, channel);
+ signal_emit("channel setup destroyed", 1, channel);
+
+ g_free(channel->name);
+ g_free(channel->chatnet);
+ g_free_not_null(channel->password);
+ g_free_not_null(channel->botmasks);
+ g_free_not_null(channel->autosendcmd);
+ g_free(channel);
+}
+
+void channels_setup_destroy(CHANNEL_SETUP_REC *channel)
+{
+ channel_config_remove(channel);
+ channels_setup_destroy_rec(channel);
+}
+
+CHANNEL_SETUP_REC *channels_setup_find(const char *channel, const char *chatnet)
+{
+ GSList *tmp;
+
+ g_return_val_if_fail(channel != NULL, NULL);
+
+ for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
+ CHANNEL_SETUP_REC *rec = tmp->data;
+
+ if (g_strcasecmp(rec->name, channel) == 0 &&
+ channel_chatnet_match(rec->chatnet, chatnet))
+ return rec;
+ }
+
+ return NULL;
+}
+
+static void channels_read_config(void)
+{
+ CONFIG_NODE *node;
+ GSList *tmp;
+
+ while (setupchannels != NULL)
+ channels_setup_destroy_rec(setupchannels->data);
+
+ /* Read channels */
+ node = iconfig_node_traverse("channels", FALSE);
+ if (node != NULL) {
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next)
+ channel_setup_read(tmp->data);
+ }
+}
+
+void channels_setup_init(void)
+{
+ source_host_ok = FALSE;
+
+ signal_add("setup reread", (SIGNAL_FUNC) channels_read_config);
+ signal_add("irssi init read settings", (SIGNAL_FUNC) channels_read_config);
+}
+
+void channels_setup_deinit(void)
+{
+ while (setupchannels != NULL)
+ channels_setup_destroy(setupchannels->data);
+
+ signal_remove("setup reread", (SIGNAL_FUNC) channels_read_config);
+ signal_remove("irssi init read settings", (SIGNAL_FUNC) channels_read_config);
+}
diff --git a/src/core/channels-setup.h b/src/core/channels-setup.h
new file mode 100644
index 00000000..e30893fe
--- /dev/null
+++ b/src/core/channels-setup.h
@@ -0,0 +1,33 @@
+#ifndef __CHANNELS_SETUP_H
+#define __CHANNELS_SETUP_H
+
+#include "modules.h"
+
+typedef struct {
+ char *name;
+ char *chatnet;
+ char *password;
+
+ char *botmasks;
+ char *autosendcmd;
+
+ int autojoin:1;
+ GHashTable *module_data;
+} CHANNEL_SETUP_REC;
+
+extern GSList *setupchannels;
+
+void channels_setup_init(void);
+void channels_setup_deinit(void);
+
+void channels_setup_create(CHANNEL_SETUP_REC *channel);
+void channels_setup_destroy(CHANNEL_SETUP_REC *channel);
+
+CHANNEL_SETUP_REC *channels_setup_find(const char *channel,
+ const char *chatnet);
+
+#define channel_chatnet_match(rec, chatnet) \
+ ((rec) == NULL || (rec)[0] == '\0' || \
+ ((chatnet) != NULL && g_strcasecmp(rec, chatnet) == 0))
+
+#endif
diff --git a/src/core/channels.c b/src/core/channels.c
new file mode 100644
index 00000000..bc3d8c0b
--- /dev/null
+++ b/src/core/channels.c
@@ -0,0 +1,116 @@
+/*
+ channel.c : irssi
+
+ Copyright (C) 1999-2000 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+
+#include "channels.h"
+
+typedef CHANNEL_REC *(*CHANNEL_FIND_FUNC)(SERVER_REC *, const char *);
+
+GSList *channels; /* List of all channels */
+
+void channel_init(CHANNEL_REC *channel, int automatic)
+{
+ g_return_if_fail(channel != NULL);
+ g_return_if_fail(channel->name != NULL);
+
+ channels = g_slist_append(channels, channel);
+ if (channel->server != NULL) {
+ channel->server->channels =
+ g_slist_append(channel->server->channels, channel);
+ }
+
+ MODULE_DATA_INIT(channel);
+ channel->type = module_get_uniq_id("CHANNEL", 0);
+ channel->mode = g_strdup("");
+ channel->createtime = time(NULL);
+
+ signal_emit("channel created", 2, channel, GINT_TO_POINTER(automatic));
+}
+
+void channel_destroy(CHANNEL_REC *channel)
+{
+ g_return_if_fail(IS_CHANNEL(channel));
+
+ if (channel->destroying) return;
+ channel->destroying = TRUE;
+
+ channels = g_slist_remove(channels, channel);
+ if (channel->server != NULL)
+ channel->server->channels = g_slist_remove(channel->server->channels, channel);
+ signal_emit("channel destroyed", 1, channel);
+
+ MODULE_DATA_DEINIT(channel);
+ g_free_not_null(channel->topic);
+ g_free_not_null(channel->key);
+ g_free(channel->name);
+ g_free(channel);
+}
+
+static CHANNEL_REC *channel_find_server(SERVER_REC *server,
+ const char *name)
+{
+ GSList *tmp;
+
+ g_return_val_if_fail(IS_SERVER(server), NULL);
+
+ if (server->channel_find_func != NULL) {
+ /* use the server specific channel find function */
+ CHANNEL_FIND_FUNC channel_find_func;
+ channel_find_func =
+ (CHANNEL_FIND_FUNC) server->channel_find_func;
+ return channel_find_func(server, name);
+ }
+
+ for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+ CHANNEL_REC *rec = tmp->data;
+
+ if (rec->chat_type == server->channel_type &&
+ g_strcasecmp(name, rec->name) == 0)
+ return rec;
+ }
+
+ return NULL;
+}
+
+CHANNEL_REC *channel_find(SERVER_REC *server, const char *name)
+{
+ g_return_val_if_fail(server == NULL || IS_SERVER(server), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ if (server != NULL)
+ return channel_find_server(server, name);
+
+ /* find from any server */
+ return gslist_foreach_find(servers,
+ (FOREACH_FIND_FUNC) channel_find_server,
+ (void *) name);
+}
+
+void channels_init(void)
+{
+}
+
+void channels_deinit(void)
+{
+ module_uniq_destroy("CHANNEL");
+}
diff --git a/src/core/channels.h b/src/core/channels.h
new file mode 100644
index 00000000..f413e658
--- /dev/null
+++ b/src/core/channels.h
@@ -0,0 +1,31 @@
+#ifndef __CHANNELS_H
+#define __CHANNELS_H
+
+#include "servers.h"
+
+#define IS_CHANNEL(channel) \
+ ((channel) != NULL && \
+ module_find_id("CHANNEL", ((CHANNEL_REC *) (channel))->type) != -1)
+
+/* Returns CHANNEL_REC if it's channel, NULL if it isn't. */
+#define CHANNEL(channel) \
+ (IS_CHANNEL(channel) ? (CHANNEL_REC *) (channel) : NULL)
+
+#define STRUCT_SERVER_REC SERVER_REC
+typedef struct {
+#include "channel-rec.h"
+} CHANNEL_REC;
+
+extern GSList *channels;
+
+void channels_init(void);
+void channels_deinit(void);
+
+/* Create new channel record */
+void channel_init(CHANNEL_REC *channel, int automatic);
+void channel_destroy(CHANNEL_REC *channel);
+
+/* find channel by name, if `server' is NULL, search from all servers */
+CHANNEL_REC *channel_find(SERVER_REC *server, const char *name);
+
+#endif
diff --git a/src/core/chatnet-rec.h b/src/core/chatnet-rec.h
new file mode 100644
index 00000000..e166627a
--- /dev/null
+++ b/src/core/chatnet-rec.h
@@ -0,0 +1,12 @@
+int type; /* should always be "CHATNET" */
+int chat_type;
+
+char *name;
+
+char *nick;
+char *username;
+char *realname;
+
+char *own_host; /* address to use when connecting this server */
+char *autosendcmd; /* command to send after connecting to this ircnet */
+IPADDR *own_ip; /* resolved own_address if not NULL */
diff --git a/src/core/chatnets.c b/src/core/chatnets.c
new file mode 100644
index 00000000..5b1b811d
--- /dev/null
+++ b/src/core/chatnets.c
@@ -0,0 +1,148 @@
+/*
+ chatnets.c : irssi
+
+ Copyright (C) 1999-2000 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "network.h"
+#include "signals.h"
+#include "special-vars.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "chatnets.h"
+#include "servers.h"
+
+GSList *chatnets; /* list of available chat networks */
+
+void chatnet_read(CHATNET_REC *chatnet, void *nodep)
+{
+ CONFIG_NODE *node = nodep;
+
+ g_return_if_fail(chatnet != NULL);
+ g_return_if_fail(node != NULL);
+ g_return_if_fail(node->key != NULL);
+
+ chatnet->type = module_get_uniq_id("CHATNET", 0);
+ chatnet->name = g_strdup(node->key);
+ chatnet->nick = g_strdup(config_node_get_str(node, "nick", NULL));
+ chatnet->username = g_strdup(config_node_get_str(node, "username", NULL));
+ chatnet->realname = g_strdup(config_node_get_str(node, "realname", NULL));
+ chatnet->own_host = g_strdup(config_node_get_str(node, "host", NULL));
+ chatnet->autosendcmd = g_strdup(config_node_get_str(node, "autosendcmd", NULL));
+
+ chatnets = g_slist_append(chatnets, chatnet);
+}
+
+void *chatnet_save(CHATNET_REC *chatnet, void *parentnode)
+{
+ CONFIG_NODE *node = parentnode;
+
+ g_return_val_if_fail(parentnode != NULL, NULL);
+ g_return_val_if_fail(IS_CHATNET(chatnet), NULL);
+
+ node = config_node_section(node, chatnet->name, NODE_TYPE_BLOCK);
+ iconfig_node_clear(node);
+
+ iconfig_node_set_str(node, "nick", chatnet->nick);
+ iconfig_node_set_str(node, "username", chatnet->username);
+ iconfig_node_set_str(node, "realname", chatnet->realname);
+ iconfig_node_set_str(node, "host", chatnet->own_host);
+ iconfig_node_set_str(node, "autosendcmd", chatnet->autosendcmd);
+ return node;
+}
+
+void chatnet_create(CHATNET_REC *chatnet)
+{
+ g_return_if_fail(chatnet != NULL);
+
+ chatnet->type = module_get_uniq_id("CHATNET", 0);
+ if (g_slist_find(chatnets, chatnet) == NULL)
+ chatnets = g_slist_append(chatnets, chatnet);
+
+ signal_emit("chatnet created", 1, chatnet);
+}
+
+void chatnet_remove(CHATNET_REC *chatnet)
+{
+ g_return_if_fail(IS_CHATNET(chatnet));
+
+ signal_emit("chatnet removed", 1, chatnet);
+ chatnet_destroy(chatnet);
+}
+
+void chatnet_destroy(CHATNET_REC *chatnet)
+{
+ g_return_if_fail(IS_CHATNET(chatnet));
+
+ chatnets = g_slist_remove(chatnets, chatnet);
+ signal_emit("chatnet destroyed", 1, chatnet);
+
+ g_free(chatnet->name);
+ g_free_not_null(chatnet->nick);
+ g_free_not_null(chatnet->username);
+ g_free_not_null(chatnet->realname);
+ g_free_not_null(chatnet->own_host);
+ g_free_not_null(chatnet->autosendcmd);
+ g_free(chatnet);
+}
+
+/* Find the irc network by name */
+CHATNET_REC *chatnet_find(const char *name)
+{
+ GSList *tmp;
+
+ g_return_val_if_fail(name != NULL, NULL);
+
+ for (tmp = chatnets; tmp != NULL; tmp = tmp->next) {
+ CHATNET_REC *rec = tmp->data;
+
+ if (g_strcasecmp(rec->name, name) == 0)
+ return rec;
+ }
+
+ return NULL;
+}
+
+static void sig_connected(SERVER_REC *server)
+{
+ CHATNET_REC *rec;
+
+ g_return_if_fail(IS_SERVER(server));
+
+ if (server->connrec->chatnet == NULL)
+ return;
+
+ rec = chatnet_find(server->connrec->chatnet);
+ if (rec != NULL && rec->autosendcmd)
+ eval_special_string(rec->autosendcmd, "", server, NULL);
+}
+
+void chatnets_init(void)
+{
+ signal_add("event connected", (SIGNAL_FUNC) sig_connected);
+}
+
+void chatnets_deinit(void)
+{
+ while (chatnets != NULL)
+ chatnet_destroy(chatnets->data);
+
+ signal_remove("event connected", (SIGNAL_FUNC) sig_connected);
+ module_uniq_destroy("CHATNET");
+}
diff --git a/src/core/chatnets.h b/src/core/chatnets.h
new file mode 100644
index 00000000..724421cf
--- /dev/null
+++ b/src/core/chatnets.h
@@ -0,0 +1,37 @@
+#ifndef __CHATNETS_H
+#define __CHATNETS_H
+
+#include "modules.h"
+
+#define IS_CHATNET(chatnet) \
+ ((chatnet) != NULL && \
+ module_find_id("CHATNET", (chatnet)->type) != -1)
+
+/* Returns CHATNET_REC if it's chatnet, NULL if it isn't. */
+#define CHATNET(chatnet) \
+ (IS_CHATNET(chatnet) ? (CHATNET_REC *) (chatnet) : NULL)
+
+typedef struct {
+#include "chatnet-rec.h"
+} CHATNET_REC;
+
+extern GSList *chatnets; /* list of available chat networks */
+
+/* read/save to configuration file */
+void chatnet_read(CHATNET_REC *chatnet, void *node);
+void *chatnet_save(CHATNET_REC *chatnet, void *parentnode);
+
+/* add the chatnet to chat networks list */
+void chatnet_create(CHATNET_REC *chatnet);
+/* remove the chatnet from chat networks list */
+void chatnet_remove(CHATNET_REC *chatnet);
+/* destroy the chatnet structure. doesn't remove from config file */
+void chatnet_destroy(CHATNET_REC *chatnet);
+
+/* Find the irc network by name */
+CHATNET_REC *chatnet_find(const char *name);
+
+void chatnets_init(void);
+void chatnets_deinit(void);
+
+#endif
diff --git a/src/core/commands.c b/src/core/commands.c
index 174824a3..ff35a0b6 100644
--- a/src/core/commands.c
+++ b/src/core/commands.c
@@ -19,12 +19,11 @@
*/
#include "module.h"
-#include "modules.h"
#include "signals.h"
#include "commands.h"
#include "misc.h"
-#include "server.h"
-#include "server-redirect.h"
+#include "servers.h"
+#include "servers-redirect.h"
#include "special-vars.h"
#include "lib-config/iconfig.h"
@@ -617,6 +616,8 @@ static void parse_command(const char *command, int expand_aliases,
const char *alias, *newcmd;
char *cmd, *orig, *args, *oldcmd;
+ g_return_if_fail(command != NULL);
+
cmd = orig = g_strconcat("command ", command, NULL);
args = strchr(cmd+8, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
@@ -643,7 +644,7 @@ static void parse_command(const char *command, int expand_aliases,
cmd = g_strconcat("command ", newcmd, NULL);
if (server != NULL)
- server_redirect_default((SERVER_REC *) server, cmd);
+ server_redirect_default(SERVER(server), cmd);
g_strdown(cmd);
oldcmd = current_command;
diff --git a/src/core/core.c b/src/core/core.c
index 642aa47b..48f4af8f 100644
--- a/src/core/core.c
+++ b/src/core/core.c
@@ -1,7 +1,7 @@
/*
core.c : irssi
- Copyright (C) 1999 Timo Sirainen
+ Copyright (C) 1999-2000 Timo Sirainen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@
#include "module.h"
-#include "modules.h"
#include "pidwait.h"
#include "net-disconnect.h"
@@ -28,12 +27,17 @@
#include "signals.h"
#include "settings.h"
-#include "server.h"
+#include "servers.h"
+#include "chatnets.h"
#include "commands.h"
#include "log.h"
#include "rawlog.h"
#include "special-vars.h"
+#include "channels.h"
+#include "queries.h"
+#include "nicklist.h"
+
int irssi_gui;
void core_init(void)
@@ -47,18 +51,28 @@ void core_init(void)
settings_init();
commands_init();
+ chatnets_init();
servers_init();
log_init();
rawlog_init();
special_vars_init();
+
+ channels_init();
+ queries_init();
+ nicklist_init();
}
void core_deinit(void)
{
+ nicklist_deinit();
+ queries_deinit();
+ channels_deinit();
+
special_vars_deinit();
rawlog_deinit();
log_deinit();
servers_deinit();
+ chatnets_deinit();
commands_deinit();
settings_deinit();
diff --git a/src/core/masks.c b/src/core/masks.c
new file mode 100644
index 00000000..38fbdf45
--- /dev/null
+++ b/src/core/masks.c
@@ -0,0 +1,137 @@
+/*
+ masks.c : irssi
+
+ Copyright (C) 1999-2000 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "network.h"
+#include "misc.h"
+
+#include "servers.h"
+
+typedef int (*MASK_MATCH_FUNC) (const char *, const char *);
+
+/* Returns TRUE if mask contains '!' ie. address should be checked too.
+ Also checks if mask contained any wildcards. */
+static int check_address(const char *mask, int *wildcards)
+{
+ int ret;
+
+ ret = FALSE;
+ while (*mask != '\0') {
+ if (*mask == '!') {
+ if (*wildcards) return TRUE;
+ ret = TRUE;
+ }
+
+ if (*mask == '?' || *mask == '*') {
+ *wildcards = TRUE;
+ if (ret) return TRUE;
+ }
+ mask++;
+ }
+
+ return ret;
+}
+
+static int check_mask(SERVER_REC *server, const char *mask,
+ const char *str, int wildcards)
+{
+ MASK_MATCH_FUNC mask_match_func;
+
+ if (server != NULL && server->mask_match_func != NULL) {
+ /* use server specified mask match function */
+ mask_match_func = (MASK_MATCH_FUNC)server->mask_match_func;
+ return mask_match_func(mask, str);
+ }
+
+ return wildcards ? match_wildcards(mask, str) :
+ g_strcasecmp(mask, str) == 0;
+}
+
+int mask_match(SERVER_REC *server, const char *mask,
+ const char *nick, const char *user, const char *host)
+{
+ char *str;
+ int ret, wildcards;
+
+ g_return_val_if_fail(server == NULL || IS_SERVER(server), FALSE);
+ g_return_val_if_fail(mask != NULL && nick != NULL &&
+ nick != NULL && host != NULL, FALSE);
+
+ str = !check_address(mask, &wildcards) ? (char *) nick :
+ g_strdup_printf("%s!%s@%s", nick, user, host);
+ ret = check_mask(server, mask, str, wildcards);
+ if (str != nick) g_free(str);
+
+ return ret;
+}
+
+int mask_match_address(SERVER_REC *server, const char *mask,
+ const char *nick, const char *address)
+{
+ char *str;
+ int ret, wildcards;
+
+ g_return_val_if_fail(server == NULL || IS_SERVER(server), FALSE);
+ g_return_val_if_fail(mask != NULL && nick != NULL, FALSE);
+ if (address == NULL) address = "";
+
+ str = !check_address(mask, &wildcards) ? (char *) nick :
+ g_strdup_printf("%s!%s", nick, address);
+ ret = check_mask(server, mask, str, wildcards);
+ if (str != nick) g_free(str);
+
+ return ret;
+}
+
+int masks_match(SERVER_REC *server, const char *masks,
+ const char *nick, const char *address)
+{
+ MASK_MATCH_FUNC mask_match_func;
+ char **list, **tmp, *mask;
+ int found;
+
+ g_return_val_if_fail(server == NULL || IS_SERVER(server), FALSE);
+ g_return_val_if_fail(masks != NULL &&
+ nick != NULL && address != NULL, FALSE);
+
+ mask_match_func = server != NULL && server->mask_match_func != NULL ?
+ (MASK_MATCH_FUNC) server->mask_match_func :
+ (MASK_MATCH_FUNC) match_wildcards;
+
+ found = FALSE;
+ mask = g_strdup_printf("%s!%s", nick, address);
+ list = g_strsplit(masks, " ", -1);
+ for (tmp = list; *tmp != NULL; tmp++) {
+ if (strchr(*tmp, '!') == NULL &&
+ g_strcasecmp(*tmp, nick) == 0) {
+ found = TRUE;
+ break;
+ }
+
+ if (mask_match_func(*tmp, mask)) {
+ found = TRUE;
+ break;
+ }
+ }
+ g_strfreev(list);
+ g_free(mask);
+
+ return found;
+}
diff --git a/src/core/masks.h b/src/core/masks.h
new file mode 100644
index 00000000..437c8cb4
--- /dev/null
+++ b/src/core/masks.h
@@ -0,0 +1,13 @@
+#ifndef __MASKS_H
+#define __MASKS_H
+
+#include "servers.h"
+
+int mask_match(SERVER_REC *server, const char *mask,
+ const char *nick, const char *user, const char *host);
+int mask_match_address(SERVER_REC *server, const char *mask,
+ const char *nick, const char *address);
+int masks_match(SERVER_REC *server, const char *masks,
+ const char *nick, const char *address);
+
+#endif
diff --git a/src/core/memdebug.c b/src/core/memdebug.c
index 807d1c56..3dbed391 100644
--- a/src/core/memdebug.c
+++ b/src/core/memdebug.c
@@ -38,7 +38,7 @@ typedef struct {
static GHashTable *data = NULL, *preallocs = NULL;
static const char *comment = "";
-static void add_flow_checks(guchar *p, unsigned long size)
+static void add_flow_checks(char *p, unsigned long size)
{
#ifdef ENABLE_BUFFER_CHECKS
int n;
@@ -75,7 +75,7 @@ static void mem_check(void)
#endif
}
-static void data_add(void *p, int size, const char *file, int line)
+static void data_add(char *p, int size, const char *file, int line)
{
MEM_REC *rec;
@@ -106,7 +106,19 @@ static void data_add(void *p, int size, const char *file, int line)
mem_check();
}
-static void *data_remove(void *p, const char *file, int line)
+static void data_clear(char *p)
+{
+ MEM_REC *rec;
+
+ if (g_hash_table_lookup(preallocs, p) != NULL)
+ p += BUFFER_CHECK_SIZE;
+
+ rec = g_hash_table_lookup(data, p);
+ if (rec != NULL && rec->size > 0)
+ memset(p, 'F', rec->size);
+}
+
+static void *data_remove(char *p, const char *file, int line)
{
MEM_REC *rec;
@@ -132,34 +144,34 @@ static void *data_remove(void *p, const char *file, int line)
void *ig_malloc(int size, const char *file, int line)
{
- void *p;
+ char *p;
size += BUFFER_CHECK_SIZE*2;
p = g_malloc(size);
data_add(p, size, file, line);
- return p+BUFFER_CHECK_SIZE;
+ return (void *) (p+BUFFER_CHECK_SIZE);
}
void *ig_malloc0(int size, const char *file, int line)
{
- void *p;
+ char *p;
size += BUFFER_CHECK_SIZE*2;
p = g_malloc0(size);
data_add(p, size, file, line);
- return p+BUFFER_CHECK_SIZE;
+ return (void *) (p+BUFFER_CHECK_SIZE);
}
void *ig_realloc(void *mem, unsigned long size, const char *file, int line)
{
- void *p;
+ char *p, *oldmem = mem;
size += BUFFER_CHECK_SIZE*2;
- mem -= BUFFER_CHECK_SIZE;
- data_remove(mem, file, line);
- p = g_realloc(mem, size);
+ oldmem -= BUFFER_CHECK_SIZE;
+ data_remove(oldmem, file, line);
+ p = g_realloc(oldmem, size);
data_add(p, size, file, line);
- return p+BUFFER_CHECK_SIZE;
+ return (void *) (p+BUFFER_CHECK_SIZE);
}
char *ig_strdup(const char *str, const char *file, int line)
@@ -252,11 +264,14 @@ char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list
void ig_free(void *p)
{
- if (p == NULL) g_error("ig_free() : trying to free NULL");
+ char *cp = p;
+
+ if (cp == NULL) g_error("ig_free() : trying to free NULL");
- p -= BUFFER_CHECK_SIZE;
- p = data_remove(p, "??", 0);
- if (p != NULL) g_free(p);
+ cp -= BUFFER_CHECK_SIZE;
+ data_clear(cp);
+ cp = data_remove(cp, "??", 0);
+ if (cp != NULL) g_free(cp);
}
GString *ig_string_new(const char *file, int line, const char *str)
@@ -264,13 +279,13 @@ GString *ig_string_new(const char *file, int line, const char *str)
GString *ret;
ret = g_string_new(str);
- data_add(ret, INT_MIN, file, line);
+ data_add((void *) ret, INT_MIN, file, line);
return ret;
}
void ig_string_free(const char *file, int line, GString *str, gboolean freeit)
{
- data_remove(str, file, line);
+ data_remove((void *) str, file, line);
if (!freeit)
data_add(str->str, INT_MIN, file, line);
@@ -304,7 +319,7 @@ void ig_profile_line(void *key, MEM_REC *rec)
strcmp(rec->file, "ig_strdup_vprintf") == 0 ||
strcmp(rec->file, "ig_strconcat") == 0 ||
strcmp(rec->file, "ig_string_free (free = FALSE)") == 0))
- data = rec->p + BUFFER_CHECK_SIZE;
+ data = (char *) rec->p + BUFFER_CHECK_SIZE;
else
data = rec->comment;
fprintf(stderr, "%s:%d %d bytes (%s)\n", rec->file, rec->line, rec->size, data);
diff --git a/src/core/misc.c b/src/core/misc.c
index c9abeb07..2f7d8049 100644
--- a/src/core/misc.c
+++ b/src/core/misc.c
@@ -196,12 +196,12 @@ GSList *gslist_find_icase_string(GSList *list, const char *key)
return NULL;
}
-void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, void *data)
+void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, const void *data)
{
void *ret;
while (list != NULL) {
- ret = func(list->data, data);
+ ret = func(list->data, (void *) data);
if (ret != NULL) return ret;
list = list->next;
diff --git a/src/core/misc.h b/src/core/misc.h
index 3dc50dc1..2bcdb67c 100644
--- a/src/core/misc.h
+++ b/src/core/misc.h
@@ -23,7 +23,7 @@ GSList *gslist_find_icase_string(GSList *list, const char *key);
GList *glist_find_string(GList *list, const char *key);
GList *glist_find_icase_string(GList *list, const char *key);
-void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, void *data);
+void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, const void *data);
/* `list' contains pointer to structure with a char* to string. */
char *gslistptr_to_string(GSList *list, int offset, const char *delimiter);
diff --git a/src/core/modules.c b/src/core/modules.c
index 1749c6fb..fc10487d 100644
--- a/src/core/modules.c
+++ b/src/core/modules.c
@@ -324,6 +324,11 @@ void module_unload(MODULE_REC *module)
g_free(module);
}
+static void uniq_get_modules(char *key, void *value, GSList **list)
+{
+ *list = g_slist_append(*list, key);
+}
+
void modules_init(void)
{
modules = NULL;
@@ -342,11 +347,19 @@ void modules_init(void)
void modules_deinit(void)
{
- g_hash_table_foreach(idlookup, (GHFunc) module_uniq_destroy, NULL);
- g_hash_table_destroy(idlookup);
- g_hash_table_destroy(uniqids);
+ GSList *list;
- g_hash_table_foreach(stridlookup, (GHFunc) module_uniq_destroy, NULL);
+ list = NULL;
+ g_hash_table_foreach(idlookup, (GHFunc) uniq_get_modules, &list);
+ g_hash_table_foreach(stridlookup, (GHFunc) uniq_get_modules, &list);
+
+ while (list != NULL) {
+ module_uniq_destroy(list->data);
+ list = g_slist_remove(list, list->data);
+ }
+
+ g_hash_table_destroy(idlookup);
g_hash_table_destroy(stridlookup);
+ g_hash_table_destroy(uniqids);
g_hash_table_destroy(uniqstrids);
}
diff --git a/src/core/nicklist.c b/src/core/nicklist.c
new file mode 100644
index 00000000..1ba78f94
--- /dev/null
+++ b/src/core/nicklist.c
@@ -0,0 +1,268 @@
+/*
+ nicklist.c : irssi
+
+ Copyright (C) 1999-2000 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+
+#include "nicklist.h"
+#include "masks.h"
+
+/* Add new nick to list */
+NICK_REC *nicklist_insert(CHANNEL_REC *channel, const char *nick,
+ int op, int voice, int send_massjoin)
+{
+ NICK_REC *rec;
+
+ g_return_val_if_fail(IS_CHANNEL(channel), NULL);
+ g_return_val_if_fail(nick != NULL, NULL);
+
+ rec = g_new0(NICK_REC, 1);
+
+ if (op) rec->op = TRUE;
+ if (voice) rec->voice = TRUE;
+
+ rec->send_massjoin = send_massjoin;
+ rec->nick = g_strdup(nick);
+ rec->host = NULL;
+
+ g_hash_table_insert(channel->nicks, rec->nick, rec);
+ signal_emit("nicklist new", 2, channel, rec);
+ return rec;
+}
+
+static void nicklist_destroy(CHANNEL_REC *channel, NICK_REC *nick)
+{
+ signal_emit("nicklist remove", 2, channel, nick);
+
+ g_free(nick->nick);
+ g_free_not_null(nick->realname);
+ g_free_not_null(nick->host);
+ g_free(nick);
+}
+
+/* remove nick from list */
+void nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick)
+{
+ g_return_if_fail(IS_CHANNEL(channel));
+ g_return_if_fail(nick != NULL);
+
+ g_hash_table_remove(channel->nicks, nick->nick);
+ nicklist_destroy(channel, nick);
+}
+
+static NICK_REC *nicklist_find_wildcards(CHANNEL_REC *channel,
+ const char *mask)
+{
+ GSList *nicks, *tmp;
+ NICK_REC *nick;
+
+ nicks = nicklist_getnicks(channel);
+ nick = NULL;
+ for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
+ nick = tmp->data;
+
+ if (mask_match_address(channel->server, mask,
+ nick->nick, nick->host))
+ break;
+ }
+ g_slist_free(nicks);
+ return tmp == NULL ? NULL : nick;
+}
+
+GSList *nicklist_find_multiple(CHANNEL_REC *channel, const char *mask)
+{
+ GSList *nicks, *tmp, *next;
+
+ g_return_val_if_fail(IS_CHANNEL(channel), NULL);
+ g_return_val_if_fail(mask != NULL, NULL);
+
+ nicks = nicklist_getnicks(channel);
+ for (tmp = nicks; tmp != NULL; tmp = next) {
+ NICK_REC *nick = tmp->data;
+
+ next = tmp->next;
+ if (!mask_match_address(channel->server, mask,
+ nick->nick, nick->host))
+ nicks = g_slist_remove(nicks, tmp->data);
+ }
+
+ return nicks;
+}
+
+/* Find nick record from list */
+NICK_REC *nicklist_find(CHANNEL_REC *channel, const char *mask)
+{
+ NICK_REC *nickrec;
+ char *nick, *host;
+
+ g_return_val_if_fail(IS_CHANNEL(channel), NULL);
+ g_return_val_if_fail(mask != NULL, NULL);
+
+ nick = g_strdup(mask);
+ host = strchr(nick, '!');
+ if (host != NULL) *host++ = '\0';
+
+ if (strchr(nick, '*') || strchr(nick, '?')) {
+ g_free(nick);
+ return nicklist_find_wildcards(channel, mask);
+ }
+
+ nickrec = g_hash_table_lookup(channel->nicks, nick);
+
+ if (nickrec != NULL && host != NULL &&
+ (nickrec->host == NULL || !match_wildcards(host, nickrec->host))) {
+ /* hosts didn't match */
+ nickrec = NULL;
+ }
+ g_free(nick);
+ return nickrec;
+}
+
+static void get_nicks_hash(gpointer key, NICK_REC *rec, GSList **list)
+{
+ *list = g_slist_append(*list, rec);
+}
+
+/* Get list of nicks */
+GSList *nicklist_getnicks(CHANNEL_REC *channel)
+{
+ GSList *list;
+
+ g_return_val_if_fail(IS_CHANNEL(channel), NULL);
+
+ list = NULL;
+ g_hash_table_foreach(channel->nicks, (GHFunc) get_nicks_hash, &list);
+ return list;
+}
+
+typedef struct {
+ CHANNEL_REC *channel;
+ const char *nick;
+ GSList *list;
+} NICKLIST_GET_SAME_REC;
+
+static void get_nicks_same_hash(gpointer key, NICK_REC *nick,
+ NICKLIST_GET_SAME_REC *rec)
+{
+ if (g_strcasecmp(nick->nick, rec->nick) == 0) {
+ rec->list = g_slist_append(rec->list, rec->channel);
+ rec->list = g_slist_append(rec->list, nick);
+ }
+}
+
+GSList *nicklist_get_same(SERVER_REC *server, const char *nick)
+{
+ NICKLIST_GET_SAME_REC rec;
+ GSList *tmp;
+
+ g_return_val_if_fail(IS_SERVER(server), NULL);
+
+ rec.nick = nick;
+ rec.list = NULL;
+ for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+ rec.channel = tmp->data;
+ g_hash_table_foreach(rec.channel->nicks,
+ (GHFunc) get_nicks_same_hash, &rec);
+ }
+ return rec.list;
+}
+
+/* nick record comparision for sort functions */
+int nicklist_compare(NICK_REC *p1, NICK_REC *p2)
+{
+ if (p1 == NULL) return -1;
+ if (p2 == NULL) return 1;
+
+ if (p1->op && !p2->op) return -1;
+ if (!p1->op && p2->op) return 1;
+
+ if (!p1->op) {
+ if (p1->voice && !p2->voice) return -1;
+ if (!p1->voice && p2->voice) return 1;
+ }
+
+ return g_strcasecmp(p1->nick, p2->nick);
+}
+
+void nicklist_update_flags(SERVER_REC *server, const char *nick,
+ int gone, int serverop)
+{
+ GSList *nicks, *tmp;
+ CHANNEL_REC *channel;
+ NICK_REC *rec;
+
+ g_return_if_fail(IS_SERVER(server));
+ g_return_if_fail(nick != NULL);
+
+ nicks = nicklist_get_same(server, nick);
+ for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
+ channel = tmp->data;
+ rec = tmp->next->data;
+
+ rec->last_check = time(NULL);
+
+ if (gone != -1 && rec->gone != gone) {
+ rec->gone = gone;
+ signal_emit("nick gone changed", 2, channel, rec);
+ }
+
+ if (serverop != -1 && rec->serverop != serverop) {
+ rec->serverop = serverop;
+ signal_emit("nick serverop changed", 2, channel, rec);
+ }
+ }
+ g_slist_free(nicks);
+}
+
+static void sig_channel_created(CHANNEL_REC *channel)
+{
+ g_return_if_fail(IS_CHANNEL(channel));
+
+ channel->nicks = g_hash_table_new((GHashFunc) g_istr_hash,
+ (GCompareFunc) g_istr_equal);
+}
+
+static void nicklist_remove_hash(gpointer key, NICK_REC *nick,
+ CHANNEL_REC *channel)
+{
+ nicklist_destroy(channel, nick);
+}
+
+static void sig_channel_destroyed(CHANNEL_REC *channel)
+{
+ g_return_if_fail(IS_CHANNEL(channel));
+
+ g_hash_table_foreach(channel->nicks,
+ (GHFunc) nicklist_remove_hash, channel);
+ g_hash_table_destroy(channel->nicks);
+}
+
+void nicklist_init(void)
+{
+ signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
+ signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+}
+
+void nicklist_deinit(void)
+{
+ signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created);
+ signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+}
diff --git a/src/core/nicklist.h b/src/core/nicklist.h
new file mode 100644
index 00000000..cd9276c3
--- /dev/null
+++ b/src/core/nicklist.h
@@ -0,0 +1,50 @@
+#ifndef __NICKLIST_H
+#define __NICKLIST_H
+
+#include "servers.h"
+#include "channels.h"
+
+typedef struct {
+ time_t last_check; /* last time gone was checked */
+
+ char *nick;
+ char *host;
+ char *realname;
+ int hops;
+
+ /* status in server */
+ int gone:1;
+ int serverop:1;
+
+ /* status in channel */
+ int send_massjoin:1; /* Waiting to be sent in massjoin signal */
+ int op:1;
+ int halfop:1;
+ int voice:1;
+} NICK_REC;
+
+/* Add new nick to list */
+NICK_REC *nicklist_insert(CHANNEL_REC *channel, const char *nick,
+ int op, int voice, int send_massjoin);
+/* remove nick from list */
+void nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick);
+/* Find nick record from list */
+NICK_REC *nicklist_find(CHANNEL_REC *channel, const char *mask);
+/* Get list of nicks that match the mask */
+GSList *nicklist_find_multiple(CHANNEL_REC *channel, const char *mask);
+/* Get list of nicks */
+GSList *nicklist_getnicks(CHANNEL_REC *channel);
+/* Get all the nick records of `nick'. Returns channel, nick, channel, ... */
+GSList *nicklist_get_same(SERVER_REC *server, const char *nick);
+
+/* Update specified nick's status in server. */
+void nicklist_update_flags(SERVER_REC *server, const char *nick,
+ int gone, int ircop);
+
+/* Nick record comparision for sort functions */
+int nicklist_compare(NICK_REC *p1, NICK_REC *p2);
+
+void nicklist_init(void);
+void nicklist_deinit(void);
+
+#endif
diff --git a/src/core/queries.c b/src/core/queries.c
new file mode 100644
index 00000000..17a371f9
--- /dev/null
+++ b/src/core/queries.c
@@ -0,0 +1,124 @@
+/*
+ queries.c : irssi
+
+ Copyright (C) 1999-2000 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+
+#include "queries.h"
+
+GSList *queries;
+
+typedef QUERY_REC *(*QUERY_FIND_FUNC)(SERVER_REC *, const char *);
+
+void query_init(QUERY_REC *query, int automatic)
+{
+ g_return_if_fail(query != NULL);
+ g_return_if_fail(query->name != NULL);
+
+ queries = g_slist_append(queries, query);
+ if (query->server != NULL) {
+ query->server->queries =
+ g_slist_append(query->server->queries, query);
+ }
+
+ MODULE_DATA_INIT(query);
+ query->type = module_get_uniq_id("QUERY", 0);
+ if (query->server != NULL)
+ query->server_tag = g_strdup(query->server->tag);
+
+ signal_emit("query created", 2, query, GINT_TO_POINTER(automatic));
+}
+
+void query_destroy(QUERY_REC *query)
+{
+ g_return_if_fail(IS_QUERY(query));
+
+ if (query->destroying) return;
+ query->destroying = TRUE;
+
+ queries = g_slist_remove(queries, query);
+ if (query->server != NULL) {
+ query->server->queries =
+ g_slist_remove(query->server->queries, query);
+ }
+ signal_emit("query destroyed", 1, query);
+
+ MODULE_DATA_DEINIT(query);
+ g_free_not_null(query->address);
+ g_free(query->name);
+ g_free(query->server_tag);
+ g_free(query);
+}
+
+static QUERY_REC *query_find_server(SERVER_REC *server, const char *nick)
+{
+ GSList *tmp;
+
+ g_return_val_if_fail(IS_SERVER(server), NULL);
+
+ if (server->query_find_func != NULL) {
+ /* use the server specific query find function */
+ QUERY_FIND_FUNC query_find_func;
+ query_find_func = (QUERY_FIND_FUNC) server->query_find_func;
+ return query_find_func(server, nick);
+ }
+
+ for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
+ QUERY_REC *rec = tmp->data;
+
+ if (rec->chat_type == server->query_type &&
+ g_strcasecmp(nick, rec->name) == 0)
+ return rec;
+ }
+
+ return NULL;
+}
+
+QUERY_REC *query_find(SERVER_REC *server, const char *nick)
+{
+ g_return_val_if_fail(server == NULL || IS_SERVER(server), NULL);
+ g_return_val_if_fail(nick != NULL, NULL);
+
+ if (server != NULL)
+ return query_find_server(server, nick);
+
+ /* find from any server */
+ return gslist_foreach_find(servers,
+ (FOREACH_FIND_FUNC) query_find_server,
+ (void *) nick);
+}
+
+void query_change_server(QUERY_REC *query, SERVER_REC *server)
+{
+ g_return_if_fail(IS_QUERY(query));
+
+ query->server = server;
+ signal_emit("query server changed", 2, query, server);
+}
+
+void queries_init(void)
+{
+}
+
+void queries_deinit(void)
+{
+ module_uniq_destroy("QUERY");
+}
diff --git a/src/core/queries.h b/src/core/queries.h
new file mode 100644
index 00000000..106b4ca7
--- /dev/null
+++ b/src/core/queries.h
@@ -0,0 +1,32 @@
+#ifndef __QUERIES_H
+#define __QUERIES_H
+
+#include "servers.h"
+
+#define IS_QUERY(query) \
+ ((query) != NULL && \
+ module_find_id("QUERY", ((QUERY_REC *) (query))->type) != -1)
+
+/* Returns QUERY_REC if it's query, NULL if it isn't. */
+#define QUERY(query) \
+ (IS_QUERY(query) ? (QUERY_REC *) (query) : NULL)
+
+#define STRUCT_SERVER_REC SERVER_REC
+typedef struct {
+#include "query-rec.h"
+} QUERY_REC;
+
+extern GSList *queries;
+
+void query_init(QUERY_REC *query, int automatic);
+void query_destroy(QUERY_REC *query);
+
+/* Find query by name, if `server' is NULL, search from all servers */
+QUERY_REC *query_find(SERVER_REC *server, const char *nick);
+
+void query_change_server(QUERY_REC *query, SERVER_REC *server);
+
+void queries_init(void);
+void queries_deinit(void);
+
+#endif
diff --git a/src/core/query-rec.h b/src/core/query-rec.h
new file mode 100644
index 00000000..8b587f5e
--- /dev/null
+++ b/src/core/query-rec.h
@@ -0,0 +1,9 @@
+/* QUERY_REC definition, used for inheritance */
+
+#include "window-item-rec.h"
+
+char *address;
+char *server_tag;
+int unwanted:1; /* TRUE if the other side closed or
+ some error occured (DCC chats!) */
+int destroying:1;
diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h
new file mode 100644
index 00000000..863235de
--- /dev/null
+++ b/src/core/server-connect-rec.h
@@ -0,0 +1,26 @@
+/* SERVER_CONNECT_REC definition, used for inheritance */
+
+int type;
+int chat_type;
+
+/* if we're connecting via proxy, or just NULLs */
+char *proxy;
+int proxy_port;
+char *proxy_string;
+
+char *address;
+int port;
+char *chatnet;
+
+IPADDR *own_ip;
+
+char *password;
+char *nick;
+char *username;
+char *realname;
+
+/* when reconnecting, the old server status */
+int reconnection:1; /* we're trying to reconnect */
+char *channels;
+char *away_reason;
+char *usermode;
diff --git a/src/core/server-rec.h b/src/core/server-rec.h
new file mode 100644
index 00000000..8aaabed2
--- /dev/null
+++ b/src/core/server-rec.h
@@ -0,0 +1,50 @@
+/* SERVER_REC definition, used for inheritance */
+
+int type; /* should always be "SERVER" */
+int chat_type;
+
+STRUCT_SERVER_CONNECT_REC *connrec;
+time_t connect_time; /* connection time */
+time_t real_connect_time; /* time when server replied that we really are connected */
+
+char *tag; /* tag name for addressing server */
+char *nick; /* current nick */
+
+int connected:1; /* connected to server */
+int connection_lost:1; /* Connection lost unintentionally */
+
+void *handle; /* NET_SENDBUF_REC socket */
+int readtag; /* input tag */
+
+/* for net_connect_nonblock() */
+int connect_pipe[2];
+int connect_tag;
+int connect_pid;
+
+/* For deciding if event should be handled internally */
+GHashTable *eventtable; /* "event xxx" : GSList* of REDIRECT_RECs */
+GHashTable *eventgrouptable; /* event group : GSList* of REDIRECT_RECs */
+GHashTable *cmdtable; /* "command xxx" : REDIRECT_CMD_REC* */
+
+void *rawlog;
+void *buffer; /* receive buffer */
+GHashTable *module_data;
+
+char *version; /* server version */
+char *away_reason;
+int server_operator:1;
+int usermode_away:1;
+int banned:1; /* not allowed to connect to this server */
+
+GSList *channels;
+GSList *queries;
+
+/* support for multiple server types */
+void *channel_find_func;
+void *query_find_func;
+int channel_type;
+int query_type;
+
+void *mask_match_func;
+
+#undef STRUCT_SERVER_CONNECT_REC
diff --git a/src/core/server-setup-rec.h b/src/core/server-setup-rec.h
new file mode 100644
index 00000000..5158780d
--- /dev/null
+++ b/src/core/server-setup-rec.h
@@ -0,0 +1,19 @@
+int type;
+int chat_type;
+
+char *chatnet;
+
+char *address;
+int port;
+char *password;
+
+char *own_host; /* address to use when connecting this server */
+IPADDR *own_ip; /* resolved own_address if not NULL */
+
+time_t last_connect; /* to avoid reconnecting too fast.. */
+
+int autoconnect:1;
+int last_failed:1; /* if last connection attempt failed */
+int banned:1; /* if we're banned from this server */
+
+GHashTable *module_data;
diff --git a/src/core/server.h b/src/core/server.h
deleted file mode 100644
index 25073379..00000000
--- a/src/core/server.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#ifndef __SERVER_H
-#define __SERVER_H
-
-#ifndef __NETWORK_H
-typedef struct _ipaddr IPADDR;
-#endif
-
-/* all strings should be either NULL or dynamically allocated */
-/* address and nick are mandatory, rest are optional */
-typedef struct {
- /* if we're connecting via proxy, or just NULLs */
- char *proxy;
- int proxy_port;
- char *proxy_string;
-
- char *address;
- int port;
- char *ircnet;
-
- IPADDR *own_ip;
-
- char *password;
- char *nick;
- char *username;
- char *realname;
-
- /* when reconnecting, the old server status */
- int reconnection:1; /* we're trying to reconnect */
- char *channels;
- char *away_reason;
- char *usermode;
-} SERVER_CONNECT_REC;
-
-typedef struct {
- int type; /* server type */
-
- SERVER_CONNECT_REC *connrec;
- time_t connect_time; /* connection time */
- time_t real_connect_time; /* time when server replied that we really are connected */
-
- char *tag; /* tag name for addressing server */
- char *nick; /* current nick */
-
- int connected:1; /* connected to server */
- int connection_lost:1; /* Connection lost unintentionally */
-
- void *handle; /* NET_SENDBUF_REC socket */
- int readtag; /* input tag */
-
- /* for net_connect_nonblock() */
- int connect_pipe[2];
- int connect_tag;
- int connect_pid;
-
- /* For deciding if event should be handled internally */
- GHashTable *eventtable; /* "event xxx" : GSList* of REDIRECT_RECs */
- GHashTable *eventgrouptable; /* event group : GSList* of REDIRECT_RECs */
- GHashTable *cmdtable; /* "command xxx" : REDIRECT_CMD_REC* */
-
- void *rawlog;
- void *buffer; /* receive buffer */
- GHashTable *module_data;
-
- char *version; /* server version */
- char *away_reason;
- int usermode_away:1;
- int banned:1; /* not allowed to connect to this server */
-} SERVER_REC;
-
-extern GSList *servers, *lookup_servers;
-
-/* Connect to server */
-int server_connect(SERVER_REC *server);
-/* Disconnect from server */
-void server_disconnect(SERVER_REC *server);
-
-SERVER_REC *server_find_tag(const char *tag);
-SERVER_REC *server_find_ircnet(const char *ircnet);
-
-void servers_init(void);
-void servers_deinit(void);
-
-#endif
diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c
new file mode 100644
index 00000000..d4d8b942
--- /dev/null
+++ b/src/core/servers-reconnect.c
@@ -0,0 +1,356 @@
+/*
+ servers-reconnect.c : irssi
+
+ Copyright (C) 1999-2000 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "commands.h"
+#include "network.h"
+#include "signals.h"
+
+#include "servers-setup.h"
+#include "servers-reconnect.h"
+
+#include "settings.h"
+
+GSList *reconnects;
+static int last_reconnect_tag;
+static int reconnect_timeout_tag;
+static int reconnect_time;
+
+static void server_reconnect_add(SERVER_CONNECT_REC *conn,
+ time_t next_connect)
+{
+ RECONNECT_REC *rec;
+
+ g_return_if_fail(IS_SERVER_CONNECT(conn));
+
+ rec = g_new(RECONNECT_REC, 1);
+ rec->tag = ++last_reconnect_tag;
+ rec->conn = conn;
+ rec->next_connect = next_connect;
+
+ reconnects = g_slist_append(reconnects, rec);
+}
+
+static void server_reconnect_destroy(RECONNECT_REC *rec, int free_conn)
+{
+ g_return_if_fail(rec != NULL);
+
+ reconnects = g_slist_remove(reconnects, rec);
+
+ signal_emit("server reconnect remove", 1, rec);
+ if (free_conn) server_connect_free(rec->conn);
+ g_free(rec);
+
+ if (reconnects == NULL)
+ last_reconnect_tag = 0;
+}
+
+static int server_reconnect_timeout(void)
+{
+ SERVER_CONNECT_REC *conn;
+ GSList *list, *tmp;
+ time_t now;
+
+ /* If server_connect() removes the next reconnection in queue,
+ we're screwed. I don't think this should happen anymore, but just
+ to be sure we don't crash, do this safely. */
+ list = g_slist_copy(reconnects);
+ now = time(NULL);
+ for (tmp = list; tmp != NULL; tmp = tmp->next) {
+ RECONNECT_REC *rec = tmp->data;
+
+ if (g_slist_find(reconnects, rec) == NULL)
+ continue;
+
+ if (rec->next_connect <= now) {
+ conn = rec->conn;
+ server_reconnect_destroy(rec, FALSE);
+ server_connect(conn);
+ }
+ }
+
+ g_slist_free(list);
+ return 1;
+}
+
+static void sserver_connect(SERVER_SETUP_REC *rec, SERVER_CONNECT_REC *conn)
+{
+ conn->address = g_strdup(rec->address);
+ conn->port = rec->port;
+
+ server_setup_fill_reconn(conn, rec);
+ if (rec->last_connect > time(NULL)-reconnect_time) {
+ /* can't reconnect this fast, wait.. */
+ server_reconnect_add(conn, rec->last_connect+reconnect_time);
+ } else {
+ /* connect to server.. */
+ server_connect(conn);
+ }
+}
+
+static SERVER_CONNECT_REC *
+server_connect_copy_skeleton(SERVER_CONNECT_REC *src)
+{
+ SERVER_CONNECT_REC *dest;
+
+ dest = NULL;
+ signal_emit("server connect copy", 2, &dest, src);
+ g_return_val_if_fail(dest != NULL, NULL);
+
+ dest->proxy = g_strdup(src->proxy);
+ dest->proxy_port = src->proxy_port;
+ dest->proxy_string = g_strdup(src->proxy_string);
+
+ dest->chatnet = g_strdup(src->chatnet);
+ dest->nick = g_strdup(src->nick);
+ dest->username = g_strdup(src->username);
+ dest->realname = g_strdup(src->realname);
+
+ if (src->own_ip != NULL) {
+ dest->own_ip = g_new(IPADDR, 1);
+ memcpy(dest->own_ip, src->own_ip, sizeof(IPADDR));
+ }
+
+ dest->channels = g_strdup(src->channels);
+ dest->away_reason = g_strdup(src->away_reason);
+
+ return dest;
+}
+
+#define server_should_reconnect(server) \
+ ((server)->connection_lost && \
+ ((server)->connrec->chatnet != NULL || !(server)->banned))
+
+static void sig_reconnect(SERVER_REC *server)
+{
+ SERVER_CONNECT_REC *conn;
+ SERVER_SETUP_REC *sserver;
+ GSList *tmp;
+ int found, through;
+ time_t now;
+
+ g_return_if_fail(IS_SERVER(server));
+
+ if (reconnect_time == -1 || !server_should_reconnect(server))
+ return;
+
+ conn = server_connect_copy_skeleton(server->connrec);
+
+ /* save the server status */
+ if (server->connected) {
+ conn->reconnection = TRUE;
+
+ g_free_not_null(conn->away_reason);
+ conn->away_reason = !server->usermode_away ? NULL :
+ g_strdup(server->away_reason);
+
+ signal_emit("server reconnect save status", 2, conn, server);
+ }
+
+ sserver = server_setup_find(server->connrec->address,
+ server->connrec->port);
+
+ if (sserver != NULL) {
+ /* save the last connection time/status */
+ sserver->last_connect = server->connect_time == 0 ?
+ time(NULL) : server->connect_time;
+ sserver->last_failed = !server->connected;
+ if (server->banned) sserver->banned = TRUE;
+ }
+
+ if (sserver == NULL || conn->chatnet == NULL) {
+ /* not in any chatnet, just reconnect back to same server */
+ conn->address = g_strdup(server->connrec->address);
+ conn->port = server->connrec->port;
+ conn->password = server->connrec->password == NULL ? NULL :
+ g_strdup(server->connrec->password);
+
+ if (server->connect_time != 0 &&
+ time(NULL)-server->connect_time > reconnect_time) {
+ /* there's been enough time since last connection,
+ reconnect back immediately */
+ server_connect(conn);
+ } else {
+ /* reconnect later.. */
+ server_reconnect_add(conn, (server->connect_time == 0 ? time(NULL) :
+ server->connect_time) + reconnect_time);
+ }
+ return;
+ }
+
+ /* always try to first connect to the first on the list where we
+ haven't got unsuccessful connection attempts for the last half
+ an hour. */
+
+ now = time(NULL);
+ for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
+ SERVER_SETUP_REC *rec = tmp->data;
+
+ if (rec->chatnet != NULL && g_strcasecmp(conn->chatnet, rec->chatnet) == 0 &&
+ !rec->banned && (!rec->last_connect || !rec->last_failed ||
+ rec->last_connect < now-FAILED_RECONNECT_WAIT)) {
+ sserver_connect(rec, conn);
+ return;
+ }
+ }
+
+ /* just try the next server in list */
+ found = through = FALSE;
+ for (tmp = setupservers; tmp != NULL; ) {
+ SERVER_SETUP_REC *rec = tmp->data;
+
+ if (!found && g_strcasecmp(rec->address, server->connrec->address) == 0 &&
+ server->connrec->port == rec->port)
+ found = TRUE;
+ else if (found && !rec->banned && rec->chatnet != NULL &&
+ g_strcasecmp(conn->chatnet, rec->chatnet) == 0) {
+ sserver_connect(rec, conn);
+ break;
+ }
+
+ if (tmp->next != NULL) {
+ tmp = tmp->next;
+ continue;
+ }
+
+ if (through) {
+ /* shouldn't happen unless there's no servers in
+ this chatnet in setup.. */
+ server_connect_free(conn);
+ break;
+ }
+
+ tmp = setupservers;
+ found = through = TRUE;
+ }
+}
+
+/* Remove all servers from reconnect list */
+/* SYNTAX: RMRECONNS */
+static void cmd_rmreconns(void)
+{
+ while (reconnects != NULL)
+ server_reconnect_destroy(reconnects->data, TRUE);
+}
+
+static RECONNECT_REC *reconnect_find_tag(int tag)
+{
+ GSList *tmp;
+
+ for (tmp = reconnects; tmp != NULL; tmp = tmp->next) {
+ RECONNECT_REC *rec = tmp->data;
+
+ if (rec->tag == tag)
+ return rec;
+ }
+
+ return NULL;
+}
+
+/* Try to reconnect immediately */
+/* SYNTAX: RECONNECT <tag> */
+static void cmd_reconnect(const char *data, SERVER_REC *server)
+{
+ SERVER_CONNECT_REC *conn;
+ RECONNECT_REC *rec;
+ char *str;
+ int tag;
+
+ if (*data == '\0') {
+ /* reconnect back to same server */
+ if (server == NULL) cmd_return_error(CMDERR_NOT_CONNECTED);
+ str = g_strdup_printf("%s %d %s %s", server->connrec->address,
+ server->connrec->port, server->connrec->password,
+ server->connrec->nick);
+ signal_emit("command server", 2, str, server);
+ g_free(str);
+ return;
+ }
+
+ if (g_strncasecmp(data, "RECON-", 6) == 0)
+ data += 6;
+
+ tag = atoi(data);
+ rec = tag <= 0 ? NULL : reconnect_find_tag(tag);
+
+ if (rec == NULL)
+ signal_emit("server reconnect not found", 1, data);
+ else {
+ conn = rec->conn;
+ server_reconnect_destroy(rec, FALSE);
+ server_connect(conn);
+ }
+}
+
+static void cmd_disconnect(const char *data, SERVER_REC *server)
+{
+ RECONNECT_REC *rec;
+ int tag;
+
+ if (g_strncasecmp(data, "RECON-", 6) != 0)
+ return; /* handle only reconnection removing */
+
+ rec = sscanf(data+6, "%d", &tag) == 1 && tag > 0 ?
+ reconnect_find_tag(tag) : NULL;
+
+ if (rec == NULL)
+ signal_emit("server reconnect not found", 1, data);
+ else
+ server_reconnect_destroy(rec, TRUE);
+ signal_stop();
+}
+
+static void read_settings(void)
+{
+ reconnect_time = settings_get_int("server_reconnect_time");
+}
+
+void servers_reconnect_init(void)
+{
+ settings_add_int("server", "server_reconnect_time", 300);
+
+ reconnects = NULL;
+ last_reconnect_tag = 0;
+
+ reconnect_timeout_tag = g_timeout_add(1000, (GSourceFunc) server_reconnect_timeout, NULL);
+ read_settings();
+
+ signal_add("server connect failed", (SIGNAL_FUNC) sig_reconnect);
+ signal_add("server disconnected", (SIGNAL_FUNC) sig_reconnect);
+ command_bind("rmreconns", NULL, (SIGNAL_FUNC) cmd_rmreconns);
+ command_bind("reconnect", NULL, (SIGNAL_FUNC) cmd_reconnect);
+ command_bind_first("disconnect", NULL, (SIGNAL_FUNC) cmd_disconnect);
+ signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void servers_reconnect_deinit(void)
+{
+ g_source_remove(reconnect_timeout_tag);
+
+ while (reconnects != NULL)
+ server_reconnect_destroy(reconnects->data, TRUE);
+
+ signal_remove("server connect failed", (SIGNAL_FUNC) sig_reconnect);
+ signal_remove("server disconnected", (SIGNAL_FUNC) sig_reconnect);
+ command_unbind("rmreconns", (SIGNAL_FUNC) cmd_rmreconns);
+ command_unbind("reconnect", (SIGNAL_FUNC) cmd_reconnect);
+ command_unbind("disconnect", (SIGNAL_FUNC) cmd_disconnect);
+ signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/src/core/servers-reconnect.h b/src/core/servers-reconnect.h
new file mode 100644
index 00000000..6b1da025
--- /dev/null
+++ b/src/core/servers-reconnect.h
@@ -0,0 +1,20 @@
+#ifndef __SERVER_RECONNECT_H
+#define __SERVER_RECONNECT_H
+
+/* wait for half an hour before trying to reconnect to host where last
+ connection failed */
+#define FAILED_RECONNECT_WAIT (60*30)
+
+typedef struct {
+ int tag;
+ time_t next_connect;
+
+ SERVER_CONNECT_REC *conn;
+} RECONNECT_REC;
+
+extern GSList *reconnects;
+
+void servers_reconnect_init(void);
+void servers_reconnect_deinit(void);
+
+#endif
diff --git a/src/core/server-redirect.c b/src/core/servers-redirect.c
index cd5e0d04..ca340fc6 100644
--- a/src/core/server-redirect.c
+++ b/src/core/servers-redirect.c
@@ -22,8 +22,8 @@
#include "signals.h"
#include "misc.h"
-#include "server.h"
-#include "server-redirect.h"
+#include "servers.h"
+#include "servers-redirect.h"
static int redirect_group;
@@ -60,7 +60,7 @@ static void server_cmdtable_destroy(char *key, REDIRECT_CMD_REC *value)
static void sig_disconnected(SERVER_REC *server)
{
- g_return_if_fail(server != NULL);
+ g_return_if_fail(IS_SERVER(server));
if (server->eventtable != NULL) {
g_hash_table_foreach(server->eventtable,
@@ -84,7 +84,7 @@ void server_redirect_initv(SERVER_REC *server, const char *command,
{
REDIRECT_CMD_REC *rec;
- g_return_if_fail(server != NULL);
+ g_return_if_fail(IS_SERVER(server));
g_return_if_fail(command != NULL);
g_return_if_fail(last > 0);
@@ -125,7 +125,7 @@ int server_redirect_single_event(SERVER_REC *server, const char *arg,
GSList *list, *grouplist;
char *origkey;
- g_return_val_if_fail(server != NULL, 0);
+ g_return_val_if_fail(IS_SERVER(server), 0);
g_return_val_if_fail(event != NULL, 0);
g_return_val_if_fail(signal != NULL, 0);
g_return_val_if_fail(arg != NULL || argpos == -1, 0);
@@ -171,7 +171,7 @@ void server_redirect_event(SERVER_REC *server, const char *arg, int last, ...)
char *event, *signal;
int argpos, group;
- g_return_if_fail(server != NULL);
+ g_return_if_fail(IS_SERVER(server));
va_start(args, last);
@@ -197,7 +197,7 @@ void server_redirect_default(SERVER_REC *server, const char *command)
char *event, *origkey;
int last;
- g_return_if_fail(server != NULL);
+ g_return_if_fail(IS_SERVER(server));
g_return_if_fail(command != NULL);
if (server->cmdtable == NULL)
@@ -245,7 +245,7 @@ void server_redirect_remove_next(SERVER_REC *server, const char *event,
char *origkey;
int group;
- g_return_if_fail(server != NULL);
+ g_return_if_fail(IS_SERVER(server));
g_return_if_fail(event != NULL);
if (!g_hash_table_lookup_extended(server->eventtable, event,
@@ -321,6 +321,9 @@ GSList *server_redirect_getqueue(SERVER_REC *server, const char *event,
char **arglist;
int found;
+ g_return_val_if_fail(IS_SERVER(server), NULL);
+ g_return_val_if_fail(event != NULL, NULL);
+
list = g_hash_table_lookup(server->eventtable, event);
for (; list != NULL; list = list->next) {
@@ -328,7 +331,7 @@ GSList *server_redirect_getqueue(SERVER_REC *server, const char *event,
if (rec->argpos == -1)
break;
- if (rec->arg == NULL)
+ if (rec->arg == NULL || args == NULL)
continue;
/* we need to check that the argument is right.. */
diff --git a/src/core/server-redirect.h b/src/core/servers-redirect.h
index 977dded1..b47f7b3d 100644
--- a/src/core/server-redirect.h
+++ b/src/core/servers-redirect.h
@@ -1,7 +1,7 @@
-#ifndef __SERVER_REDIRECT_H
-#define __SERVER_REDIRECT_H
+#ifndef __SERVERS_REDIRECT_H
+#define __SERVERS_REDIRECT_H
-#include "server.h"
+#include "servers.h"
typedef struct {
int last; /* number of "last" events at the start of the events list */
diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c
new file mode 100644
index 00000000..b314ca95
--- /dev/null
+++ b/src/core/servers-setup.c
@@ -0,0 +1,456 @@
+/*
+ servers-setup.c : irssi
+
+ Copyright (C) 1999-2000 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "network.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "servers.h"
+#include "servers-setup.h"
+#include "chatnets.h"
+
+GSList *setupservers;
+
+int source_host_ok; /* Use source_host_ip .. */
+IPADDR *source_host_ip; /* Resolved address */
+
+static void get_source_host_ip(void)
+{
+ IPADDR ip;
+
+ if (source_host_ok)
+ return;
+
+ /* FIXME: This will block! */
+ source_host_ok = *settings_get_str("hostname") != '\0' &&
+ net_gethostbyname(settings_get_str("hostname"), &ip) == 0;
+ if (source_host_ok) {
+ if (source_host_ip == NULL)
+ source_host_ip = g_new(IPADDR, 1);
+ memcpy(source_host_ip, &ip, sizeof(IPADDR));
+ }
+}
+
+static void conn_set_ip(SERVER_CONNECT_REC *conn,
+ IPADDR **own_ip, const char *own_host)
+{
+ IPADDR ip;
+
+ if (*own_ip != NULL) {
+ /* use already resolved IP */
+ if (conn->own_ip == NULL)
+ conn->own_ip = g_new(IPADDR, 1);
+ memcpy(conn->own_ip, *own_ip, sizeof(IPADDR));
+ return;
+ }
+
+
+ /* resolve the IP and use it */
+ if (net_gethostbyname(own_host, &ip) == 0) {
+ if (conn->own_ip == NULL)
+ conn->own_ip = g_new(IPADDR, 1);
+ memcpy(conn->own_ip, &ip, sizeof(IPADDR));
+
+ *own_ip = g_new(IPADDR, 1);
+ memcpy(*own_ip, &ip, sizeof(IPADDR));
+ }
+}
+
+/* Fill information to connection from server setup record */
+void server_setup_fill_reconn(SERVER_CONNECT_REC *conn,
+ SERVER_SETUP_REC *sserver)
+{
+ g_return_if_fail(IS_SERVER_CONNECT(conn));
+ g_return_if_fail(IS_SERVER_SETUP(sserver));
+
+ if (sserver->own_host != NULL)
+ conn_set_ip(conn, &sserver->own_ip, sserver->own_host);
+
+ if (sserver->chatnet != NULL && conn->chatnet == NULL)
+ conn->chatnet = g_strdup(sserver->chatnet);
+
+ if (sserver->password != NULL && conn->password == NULL)
+ conn->password = g_strdup(sserver->password);
+
+ signal_emit("server setup fill reconn", 2, conn, sserver);
+}
+
+static void server_setup_fill(SERVER_CONNECT_REC *conn,
+ const char *address, int port)
+{
+ g_return_if_fail(conn != NULL);
+ g_return_if_fail(address != NULL);
+
+ conn->type = module_get_uniq_id("SERVER CONNECT", 0);
+
+ conn->address = g_strdup(address);
+ conn->port = port > 0 ? port : 6667;
+
+ if (!conn->nick) conn->nick = g_strdup(settings_get_str("default_nick"));
+ conn->username = g_strdup(settings_get_str("user_name"));
+ conn->realname = g_strdup(settings_get_str("real_name"));
+
+ /* proxy settings */
+ if (settings_get_bool("use_proxy")) {
+ conn->proxy = g_strdup(settings_get_str("proxy_address"));
+ conn->proxy_port = settings_get_int("proxy_port");
+ conn->proxy_string = g_strdup(settings_get_str("proxy_string"));
+ }
+
+ /* source IP */
+ get_source_host_ip();
+ if (source_host_ok) {
+ conn->own_ip = g_new(IPADDR, 1);
+ memcpy(conn->own_ip, source_host_ip, sizeof(IPADDR));
+ }
+}
+
+static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
+ SERVER_SETUP_REC *sserver)
+{
+ g_return_if_fail(IS_SERVER_CONNECT(conn));
+ g_return_if_fail(IS_SERVER_SETUP(sserver));
+
+ sserver->last_connect = time(NULL);
+
+ if (sserver->port > 0) conn->port = sserver->port;
+ server_setup_fill_reconn(conn, sserver);
+
+ signal_emit("server setup fill server", 2, conn, sserver);
+}
+
+static void server_setup_fill_chatnet(SERVER_CONNECT_REC *conn,
+ CHATNET_REC *chatnet)
+{
+ g_return_if_fail(IS_SERVER_CONNECT(conn));
+ g_return_if_fail(IS_CHATNET(chatnet));
+
+ if (chatnet->nick) {
+ g_free(conn->nick);
+ conn->nick = g_strdup(chatnet->nick);;
+ }
+ if (chatnet->username) {
+ g_free(conn->username);
+ conn->username = g_strdup(chatnet->username);;
+ }
+ if (chatnet->realname) {
+ g_free(conn->realname);
+ conn->realname = g_strdup(chatnet->realname);;
+ }
+ if (chatnet->own_host != NULL)
+ conn_set_ip(conn, &chatnet->own_ip, chatnet->own_host);
+
+ signal_emit("server setup fill chatnet", 2, conn, chatnet);
+}
+
+static SERVER_CONNECT_REC *
+create_addr_conn(const char *address, int port,
+ const char *password, const char *nick)
+{
+ SERVER_CONNECT_REC *conn;
+ SERVER_SETUP_REC *sserver;
+ CHATNET_REC *chatnet;
+
+ g_return_val_if_fail(address != NULL, NULL);
+
+ sserver = server_setup_find(address, port);
+ chatnet = sserver->chatnet == NULL ? NULL :
+ chatnet_find(sserver->chatnet);
+ conn = NULL;
+ signal_emit("server setup connect", 2, &conn, chatnet);
+ if (conn == NULL) {
+ /* no chat protocol wanted this server? */
+ return NULL;
+ }
+
+ /* fill in the defaults */
+ server_setup_fill(conn, address, port);
+
+ /* fill the rest from chat network settings */
+ if (chatnet != NULL)
+ server_setup_fill_chatnet(conn, chatnet);
+
+ /* fill the information from setup */
+ if (sserver != NULL)
+ server_setup_fill_server(conn, sserver);
+
+ /* nick / password given in command line overrides all settings */
+ if (password && *password) {
+ g_free_not_null(conn->password);
+ conn->password = g_strdup(password);
+ }
+ if (nick && *nick) {
+ g_free_not_null(conn->nick);
+ conn->nick = g_strdup(nick);
+ }
+
+ return conn;
+}
+
+/* Connect to server where last connect succeeded (or we haven't tried to
+ connect yet). If there's no such server, connect to server where we
+ haven't connected for the longest time */
+static SERVER_CONNECT_REC *
+create_chatnet_conn(const char *dest, int port,
+ const char *password, const char *nick)
+{
+ SERVER_SETUP_REC *bestrec;
+ GSList *tmp;
+ time_t now, besttime;
+
+ now = time(NULL);
+ bestrec = NULL; besttime = now;
+ for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
+ SERVER_SETUP_REC *rec = tmp->data;
+
+ if (rec->chatnet == NULL ||
+ g_strcasecmp(rec->chatnet, dest) != 0)
+ continue;
+
+ if (!rec->last_failed) {
+ bestrec = rec;
+ break;
+ }
+
+ if (bestrec == NULL || besttime > rec->last_connect) {
+ bestrec = rec;
+ besttime = rec->last_connect;
+ }
+ }
+
+ return bestrec == NULL ? NULL :
+ create_addr_conn(bestrec->address, 0, NULL, nick);
+}
+
+/* Create server connection record. `dest' is required, rest can be NULL.
+ `dest' is either a server address or chat network */
+SERVER_CONNECT_REC *
+server_create_conn(const char *dest, int port,
+ const char *password, const char *nick)
+{
+ SERVER_CONNECT_REC *rec;
+
+ g_return_val_if_fail(dest != NULL, NULL);
+
+ if (chatnet_find(dest)) {
+ rec = create_chatnet_conn(dest, port, password, nick);
+ if (rec != NULL)
+ return rec;
+ }
+
+ return create_addr_conn(dest, port, password, nick);
+}
+
+/* Find matching server from setup. Try to find record with a same port,
+ but fallback to any server with the same address. */
+SERVER_SETUP_REC *server_setup_find(const char *address, int port)
+{
+ SERVER_SETUP_REC *server;
+ GSList *tmp;
+
+ g_return_val_if_fail(address != NULL, NULL);
+
+ server = NULL;
+ for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
+ SERVER_SETUP_REC *rec = tmp->data;
+
+ if (g_strcasecmp(rec->address, address) == 0) {
+ server = rec;
+ if (rec->port == port)
+ break;
+ }
+ }
+
+ return server;
+}
+
+/* Find matching server from setup. Ports must match or NULL is returned. */
+SERVER_SETUP_REC *server_setup_find_port(const char *address, int port)
+{
+ SERVER_SETUP_REC *rec;
+
+ rec = server_setup_find(address, port);
+ return rec == NULL || rec->port != port ? NULL : rec;
+}
+
+static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
+{
+ SERVER_SETUP_REC *rec;
+ char *server, *chatnet;
+ int port;
+
+ g_return_val_if_fail(node != NULL, NULL);
+
+ server = config_node_get_str(node, "address", NULL);
+ if (server == NULL)
+ return NULL;
+
+ port = config_node_get_int(node, "port", 6667);
+ if (server_setup_find_port(server, port) != NULL) {
+ /* already exists - don't let it get there twice or
+ server reconnects will screw up! */
+ return NULL;
+ }
+
+ rec = NULL;
+ chatnet = config_node_get_str(node, "chatnet", NULL);
+ signal_emit("server setup read", 3, &rec, node,
+ chatnet == NULL ? NULL : chatnet_find(chatnet));
+ if (rec == NULL) {
+ /* no chat protocol wanted this server? */
+ return NULL;
+ }
+
+ rec->type = module_get_uniq_id("SERVER SETUP", 0);
+ rec->chatnet = g_strdup(chatnet);
+ rec->address = g_strdup(server);
+ rec->password = g_strdup(config_node_get_str(node, "password", NULL));
+ rec->port = port;
+ rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
+ rec->own_host = g_strdup(config_node_get_str(node, "own_host", 0));
+
+ setupservers = g_slist_append(setupservers, rec);
+ return rec;
+}
+
+static void server_setup_save(SERVER_SETUP_REC *rec)
+{
+ CONFIG_NODE *parentnode, *node;
+ int index;
+
+ index = g_slist_index(setupservers, rec);
+
+ parentnode = iconfig_node_traverse("(servers", TRUE);
+ node = config_node_index(parentnode, index);
+ if (node == NULL)
+ node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
+
+ iconfig_node_clear(node);
+ iconfig_node_set_str(node, "address", rec->address);
+ iconfig_node_set_str(node, "chatnet", rec->chatnet);
+
+ config_node_set_int(node, "port", rec->port);
+ iconfig_node_set_str(node, "password", rec->password);
+ iconfig_node_set_str(node, "own_host", rec->own_host);
+
+ if (rec->autoconnect)
+ config_node_set_bool(node, "autoconnect", TRUE);
+
+ signal_emit("server setup saved", 2, rec, node);
+}
+
+static void server_setup_remove_config(SERVER_SETUP_REC *rec)
+{
+ CONFIG_NODE *node;
+ int index;
+
+ node = iconfig_node_traverse("servers", FALSE);
+ if (node != NULL) {
+ index = g_slist_index(setupservers, rec);
+ iconfig_node_list_remove(node, index);
+ }
+}
+
+static void server_setup_destroy(SERVER_SETUP_REC *rec)
+{
+ setupservers = g_slist_remove(setupservers, rec);
+ signal_emit("server setup destroyed", 1, rec);
+
+ g_free_not_null(rec->own_host);
+ g_free_not_null(rec->own_ip);
+ g_free_not_null(rec->chatnet);
+ g_free(rec->address);
+ g_free_not_null(rec->password);
+ g_free(rec);
+}
+
+void server_setup_add(SERVER_SETUP_REC *rec)
+{
+ if (g_slist_find(setupservers, rec) == NULL)
+ setupservers = g_slist_append(setupservers, rec);
+ server_setup_save(rec);
+}
+
+void server_setup_remove(SERVER_SETUP_REC *rec)
+{
+ server_setup_remove_config(rec);
+ server_setup_destroy(rec);
+}
+
+static void read_servers(void)
+{
+ CONFIG_NODE *node;
+ GSList *tmp;
+
+ while (setupservers != NULL)
+ server_setup_destroy(setupservers->data);
+
+ /* Read servers */
+ node = iconfig_node_traverse("servers", FALSE);
+ if (node != NULL) {
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next)
+ server_setup_read(tmp->data);
+ }
+}
+
+static void read_settings(void)
+{
+ g_free_and_null(source_host_ip);
+
+ source_host_ok = FALSE;
+ get_source_host_ip();
+}
+
+void servers_setup_init(void)
+{
+ settings_add_str("server", "hostname", "");
+
+ settings_add_str("server", "default_nick", NULL);
+ settings_add_str("server", "user_name", NULL);
+ settings_add_str("server", "real_name", NULL);
+
+ settings_add_bool("proxy", "use_proxy", FALSE);
+ settings_add_str("proxy", "proxy_address", "");
+ settings_add_int("proxy", "proxy_port", 6667);
+ settings_add_str("proxy", "proxy_string", "CONNECT %s %d");
+
+ source_host_ip = NULL;
+ read_settings();
+
+ signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+ signal_add("setup reread", (SIGNAL_FUNC) read_servers);
+ signal_add("irssi init read settings", (SIGNAL_FUNC) read_servers);
+}
+
+void servers_setup_deinit(void)
+{
+ g_free_not_null(source_host_ip);
+
+ while (setupservers != NULL)
+ server_setup_destroy(setupservers->data);
+
+ signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+ signal_remove("setup reread", (SIGNAL_FUNC) read_servers);
+ signal_remove("irssi init read settings", (SIGNAL_FUNC) read_servers);
+
+ module_uniq_destroy("SERVER SETUP");
+}
diff --git a/src/core/servers-setup.h b/src/core/servers-setup.h
new file mode 100644
index 00000000..247189ff
--- /dev/null
+++ b/src/core/servers-setup.h
@@ -0,0 +1,46 @@
+#ifndef __SERVERS_SETUP_H
+#define __SERVERS_SETUP_H
+
+#include "servers.h"
+
+#define IS_SERVER_SETUP(server) \
+ ((server) != NULL && \
+ module_find_id("SERVER SETUP", (server)->type) != -1)
+
+#define SERVER_SETUP(server) \
+ (IS_SERVER_SETUP(server) ? (SERVER_SETUP_REC *) (server) : NULL)
+
+/* servers */
+typedef struct {
+#include "server-setup-rec.h"
+} SERVER_SETUP_REC;
+
+extern GSList *setupservers;
+
+extern IPADDR *source_host_ip; /* Resolved address */
+extern int source_host_ok; /* Use source_host_ip .. */
+
+/* Fill reconnection specific information to connection
+ from server setup record */
+void server_setup_fill_reconn(SERVER_CONNECT_REC *conn,
+ SERVER_SETUP_REC *sserver);
+
+/* Create server connection record. `dest' is required, rest can be NULL.
+ `dest' is either a server address or chat network */
+SERVER_CONNECT_REC *
+server_create_conn(const char *dest, int port,
+ const char *password, const char *nick);
+
+/* Find matching server from setup. Try to find record with a same port,
+ but fallback to any server with the same address. */
+SERVER_SETUP_REC *server_setup_find(const char *address, int port);
+/* Find matching server from setup. Ports must match or NULL is returned. */
+SERVER_SETUP_REC *server_setup_find_port(const char *address, int port);
+
+void server_setup_add(SERVER_SETUP_REC *rec);
+void server_setup_remove(SERVER_SETUP_REC *rec);
+
+void servers_setup_init(void);
+void servers_setup_deinit(void);
+
+#endif
diff --git a/src/core/server.c b/src/core/servers.c
index 029433c5..f6f353d6 100644
--- a/src/core/server.c
+++ b/src/core/servers.c
@@ -19,24 +19,26 @@
*/
#include "module.h"
-
-#include "modules.h"
#include "signals.h"
#include "line-split.h"
#include "net-nonblock.h"
#include "net-sendbuffer.h"
-#include "rawlog.h"
#include "misc.h"
-#include "server.h"
-#include "server-redirect.h"
+#include "rawlog.h"
#include "settings.h"
+#include "servers.h"
+#include "servers-redirect.h"
+#include "servers-setup.h"
+#include "channels.h"
+#include "queries.h"
+
GSList *servers, *lookup_servers;
/* connection to server failed */
static void server_cant_connect(SERVER_REC *server, const char *msg)
{
- g_return_if_fail(server != NULL);
+ g_return_if_fail(IS_SERVER(server));
lookup_servers = g_slist_remove(lookup_servers, server);
@@ -52,6 +54,7 @@ static void server_cant_connect(SERVER_REC *server, const char *msg)
}
MODULE_DATA_DEINIT(server);
+ server_connect_free(server->connrec);
g_free_not_null(server->nick);
g_free(server->tag);
g_free(server);
@@ -62,6 +65,8 @@ static char *server_create_address_tag(const char *address)
{
const char *start, *end;
+ g_return_val_if_fail(address != NULL, NULL);
+
/* try to generate a reasonable server tag */
if (strchr(address, '.') == NULL) {
start = end = NULL;
@@ -91,7 +96,9 @@ static char *server_create_tag(SERVER_CONNECT_REC *conn)
char *tag;
int num;
- tag = conn->ircnet != NULL ? g_strdup(conn->ircnet) :
+ g_return_val_if_fail(IS_SERVER_CONNECT(conn), NULL);
+
+ tag = conn->chatnet != NULL ? g_strdup(conn->chatnet) :
server_create_address_tag(conn->address);
/* then just append numbers after tag until unused is found.. */
@@ -109,6 +116,8 @@ static void server_connect_callback_init(SERVER_REC *server, int handle)
{
int error;
+ g_return_if_fail(IS_SERVER(server));
+
error = net_geterror(handle);
if (error != 0) {
server->connection_lost = TRUE;
@@ -122,8 +131,12 @@ static void server_connect_callback_init(SERVER_REC *server, int handle)
server->connect_tag = -1;
server->connect_time = time(NULL);
server->rawlog = rawlog_create();
- servers = g_slist_append(servers, server);
+ server->eventtable = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
+ server->eventgrouptable = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
+ server->cmdtable = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
+
+ servers = g_slist_append(servers, server);
signal_emit("server connected", 1, server);
}
@@ -134,6 +147,8 @@ static void server_connect_callback_readpipe(SERVER_REC *server)
const char *errormsg;
int handle;
+ g_return_if_fail(IS_SERVER(server));
+
g_source_remove(server->connect_tag);
server->connect_tag = -1;
@@ -182,13 +197,14 @@ static void server_connect_callback_readpipe(SERVER_REC *server)
signal_emit("server connecting", 2, server, &iprec.ip);
}
-int server_connect(SERVER_REC *server)
+int server_start_connect(SERVER_REC *server)
{
const char *connect_address;
g_return_val_if_fail(server != NULL, FALSE);
MODULE_DATA_INIT(server);
+ server->type = module_get_uniq_id("SERVER", 0);
if (pipe(server->connect_pipe) != 0) {
g_warning("server_connect(): pipe() failed.");
@@ -213,9 +229,48 @@ int server_connect(SERVER_REC *server)
return TRUE;
}
+/* Connect to server */
+SERVER_REC *server_connect(SERVER_CONNECT_REC *conn)
+{
+ SERVER_REC *server;
+
+ g_return_val_if_fail(IS_SERVER_CONNECT(conn), NULL);
+
+ server = NULL;
+ signal_emit("server connect", 2, &server, conn);
+ return server;
+}
+
+static int server_remove_channels(SERVER_REC *server)
+{
+ GSList *tmp;
+ int found;
+
+ g_return_val_if_fail(server != NULL, FALSE);
+
+ found = FALSE;
+ for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+ CHANNEL_REC *channel = tmp->data;
+
+ channel->server = NULL;
+ channel_destroy(channel);
+ found = TRUE;
+ }
+
+ for (tmp = server->queries; tmp != NULL; tmp = tmp->next)
+ query_change_server(tmp->data, NULL);
+
+ g_slist_free(server->channels);
+ g_slist_free(server->queries);
+
+ return found;
+}
+
void server_disconnect(SERVER_REC *server)
{
- g_return_if_fail(server != NULL);
+ int chans;
+
+ g_return_if_fail(IS_SERVER(server));
if (server->connect_tag != -1) {
/* still connecting to server.. */
@@ -229,12 +284,27 @@ void server_disconnect(SERVER_REC *server)
signal_emit("server disconnected", 1, server);
- if (server->handle != NULL)
- net_sendbuffer_destroy(server->handle, TRUE);
+ /* close all channels */
+ chans = server_remove_channels(server);
+
+ if (server->handle != NULL) {
+ if (!chans || server->connection_lost)
+ net_sendbuffer_destroy(server->handle, TRUE);
+ else {
+ /* we were on some channels, try to let the server
+ disconnect so that our quit message is guaranteed
+ to get displayed */
+ net_disconnect_later(net_sendbuffer_handle(server->handle));
+ net_sendbuffer_destroy(server->handle, FALSE);
+ }
+ server->handle = NULL;
+ }
+
if (server->readtag > 0)
g_source_remove(server->readtag);
MODULE_DATA_DEINIT(server);
+ server_connect_free(server->connrec);
rawlog_destroy(server->rawlog);
line_split_free(server->buffer);
g_free_not_null(server->version);
@@ -268,29 +338,54 @@ SERVER_REC *server_find_tag(const char *tag)
return NULL;
}
-SERVER_REC *server_find_ircnet(const char *ircnet)
+SERVER_REC *server_find_chatnet(const char *chatnet)
{
GSList *tmp;
- g_return_val_if_fail(ircnet != NULL, NULL);
- if (*ircnet == '\0') return NULL;
+ g_return_val_if_fail(chatnet != NULL, NULL);
+ if (*chatnet == '\0') return NULL;
for (tmp = servers; tmp != NULL; tmp = tmp->next) {
SERVER_REC *server = tmp->data;
- if (server->connrec->ircnet != NULL &&
- g_strcasecmp(server->connrec->ircnet, ircnet) == 0)
+ if (server->connrec->chatnet != NULL &&
+ g_strcasecmp(server->connrec->chatnet, chatnet) == 0)
return server;
}
return NULL;
}
+void server_connect_free(SERVER_CONNECT_REC *conn)
+{
+ g_return_if_fail(IS_SERVER_CONNECT(conn));
+
+ signal_emit("server connect free", 1, conn);
+ g_free_not_null(conn->proxy);
+ g_free_not_null(conn->proxy_string);
+
+ g_free_not_null(conn->address);
+ g_free_not_null(conn->chatnet);
+
+ g_free_not_null(conn->own_ip);
+
+ g_free_not_null(conn->password);
+ g_free_not_null(conn->nick);
+ g_free_not_null(conn->username);
+ g_free_not_null(conn->realname);
+
+ g_free_not_null(conn->channels);
+ g_free_not_null(conn->away_reason);
+ g_free_not_null(conn->usermode);
+ g_free(conn);
+}
+
void servers_init(void)
{
lookup_servers = servers = NULL;
servers_redirect_init();
+ servers_setup_init();
}
void servers_deinit(void)
@@ -300,5 +395,9 @@ void servers_deinit(void)
while (lookup_servers != NULL)
server_cant_connect(lookup_servers->data, NULL);
+ servers_setup_deinit();
servers_redirect_deinit();
+
+ module_uniq_destroy("SERVER");
+ module_uniq_destroy("SERVER CONNECT");
}
diff --git a/src/core/servers.h b/src/core/servers.h
new file mode 100644
index 00000000..169c8e88
--- /dev/null
+++ b/src/core/servers.h
@@ -0,0 +1,52 @@
+#ifndef __SERVERS_H
+#define __SERVERS_H
+
+#include "modules.h"
+
+#ifndef __NETWORK_H
+typedef struct _ipaddr IPADDR;
+#endif
+
+#define IS_SERVER(server) \
+ ((server) != NULL && module_find_id("SERVER", (server)->type) != -1)
+
+#define IS_SERVER_CONNECT(conn) \
+ ((conn) != NULL && \
+ module_find_id("SERVER CONNECT", (conn)->type) != -1)
+
+/* Returns SERVER_REC if it's server, NULL if it isn't. */
+#define SERVER(server) \
+ (IS_SERVER(server) ? (SERVER_REC *) (server) : NULL)
+
+/* Returns SERVER_CONNECT_REC if it's server connection, NULL if it isn't. */
+#define SERVER_CONNECT(conn) \
+ (IS_SERVER_CONNECT(conn) ? (SERVER_CONNECT_REC *) (conn) : NULL)
+
+/* all strings should be either NULL or dynamically allocated */
+/* address and nick are mandatory, rest are optional */
+typedef struct {
+#include "server-connect-rec.h"
+} SERVER_CONNECT_REC;
+
+#define STRUCT_SERVER_CONNECT_REC SERVER_CONNECT_REC
+typedef struct {
+#include "server-rec.h"
+} SERVER_REC;
+
+extern GSList *servers, *lookup_servers;
+
+void servers_init(void);
+void servers_deinit(void);
+
+/* Connect to server */
+SERVER_REC *server_connect(SERVER_CONNECT_REC *conn);
+/* Disconnect from server */
+void server_disconnect(SERVER_REC *server);
+
+SERVER_REC *server_find_tag(const char *tag);
+SERVER_REC *server_find_chatnet(const char *chatnet);
+
+int server_start_connect(SERVER_REC *server);
+void server_connect_free(SERVER_CONNECT_REC *conn);
+
+#endif
diff --git a/src/core/settings.h b/src/core/settings.h
index 0a53a6ee..a887797a 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -32,6 +32,7 @@ typedef struct {
#define iconfig_node_set_str(a, b, c) config_node_set_str(mainconfig, a, b, c)
#define iconfig_node_list_remove(a, b) config_node_list_remove(mainconfig, a, b)
#define iconfig_node_remove(a, b) config_node_remove(mainconfig, a, b)
+#define iconfig_node_clear(a) config_node_clear(mainconfig, a)
extern CONFIG_REC *mainconfig;
diff --git a/src/core/signals.c b/src/core/signals.c
index 1b62f55c..7b309785 100644
--- a/src/core/signals.c
+++ b/src/core/signals.c
@@ -310,7 +310,8 @@ void signal_stop_by_name(const char *signal)
static void signal_remove_module(void *signal, SIGNAL_REC *rec, const char *module)
{
- int signal_id, list, index;
+ unsigned int index;
+ int signal_id, list;
signal_id = GPOINTER_TO_INT(signal);
diff --git a/src/core/special-vars.c b/src/core/special-vars.c
index 20f82e10..1aa081e9 100644
--- a/src/core/special-vars.c
+++ b/src/core/special-vars.c
@@ -25,6 +25,10 @@
#include "misc.h"
#include "irssi-version.h"
+#include "channels.h"
+#include "queries.h"
+#include "window-item-def.h"
+
#include <sys/utsname.h>
#define ALIGN_RIGHT 0x01
@@ -99,7 +103,7 @@ static char *get_internal_setting(const char *key, int type, int *free_ret)
return NULL;
}
-static char *get_long_variable_value(const char *key, void *server,
+static char *get_long_variable_value(const char *key, SERVER_REC *server,
void *item, int *free_ret)
{
EXPANDO_FUNC func;
@@ -126,7 +130,7 @@ static char *get_long_variable_value(const char *key, void *server,
return NULL;
}
-static char *get_long_variable(char **cmd, void *server,
+static char *get_long_variable(char **cmd, SERVER_REC *server,
void *item, int *free_ret)
{
char *start, *var, *ret;
@@ -142,7 +146,7 @@ static char *get_long_variable(char **cmd, void *server,
}
/* return the value of the variable found from `cmd' */
-static char *get_variable(char **cmd, void *server, void *item,
+static char *get_variable(char **cmd, SERVER_REC *server, void *item,
char **arglist, int *free_ret, int *arg_used)
{
if (isdigit(**cmd) || **cmd == '*' || **cmd == '-' || **cmd == '~') {
@@ -183,7 +187,7 @@ static char *get_history(char **cmd, void *item, int *free_ret)
return ret;
}
-static char *get_special_value(char **cmd, void *server, void *item,
+static char *get_special_value(char **cmd, SERVER_REC *server, void *item,
char **arglist, int *free_ret, int *arg_used)
{
char command, *value, *p;
@@ -313,7 +317,7 @@ static char *get_alignment(const char *text, int align, int flags, char pad)
/* Parse and expand text after '$' character. return value has to be
g_free()'d if `free_ret' is TRUE. */
-char *parse_special(char **cmd, void *server, void *item,
+char *parse_special(char **cmd, SERVER_REC *server, void *item,
char **arglist, int *free_ret, int *arg_used)
{
static char **nested_orig_cmd = NULL; /* FIXME: KLUDGE! */
@@ -405,7 +409,7 @@ char *parse_special(char **cmd, void *server, void *item,
}
/* parse the whole string. $ and \ chars are replaced */
-char *parse_special_string(const char *cmd, void *server, void *item,
+char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
const char *data, int *arg_used)
{
char code, **arglist, *ret;
@@ -466,7 +470,7 @@ char *parse_special_string(const char *cmd, void *server, void *item,
/* execute the commands in string - commands can be split with ';' */
void eval_special_string(const char *cmd, const char *data,
- void *server, void *item)
+ SERVER_REC *server, void *item)
{
const char *cmdchars;
char *orig, *str, *start, *ret;
@@ -556,40 +560,101 @@ void special_history_func_set(SPECIAL_HISTORY_FUNC func)
history_func = func;
}
+/* text of your AWAY message, if any */
+static char *expando_awaymsg(SERVER_REC *server, void *item, int *free_ret)
+{
+ return server == NULL ? "" : server->away_reason;
+}
+
+/* current channel */
+static char *expando_channel(SERVER_REC *server, void *item, int *free_ret)
+{
+ return !IS_CHANNEL(item) ? NULL : CHANNEL(item)->name;
+}
+
/* time client was started, $time() format */
-static char *expando_clientstarted(void *server, void *item, int *free_ret)
+static char *expando_clientstarted(SERVER_REC *server, void *item, int *free_ret)
{
*free_ret = TRUE;
return g_strdup_printf("%ld", (long) client_start_time);
}
/* client version text string */
-static char *expando_version(void *server, void *item, int *free_ret)
+static char *expando_version(SERVER_REC *server, void *item, int *free_ret)
{
return IRSSI_VERSION;
}
/* current value of CMDCHARS */
-static char *expando_cmdchars(void *server, void *item, int *free_ret)
+static char *expando_cmdchars(SERVER_REC *server, void *item, int *free_ret)
{
return (char *) settings_get_str("cmdchars");
}
+/* modes of current channel, if any */
+static char *expando_chanmode(SERVER_REC *server, void *item, int *free_ret)
+{
+ return !IS_CHANNEL(item) ? NULL : CHANNEL(item)->mode;
+}
+
+/* current nickname */
+static char *expando_nick(SERVER_REC *server, void *item, int *free_ret)
+{
+ return server == NULL ? "" : server->nick;
+}
+
+/* value of STATUS_OPER if you are an irc operator */
+static char *expando_statusoper(SERVER_REC *server, void *item, int *free_ret)
+{
+ return server == NULL || !server->server_operator ? "" :
+ (char *) settings_get_str("STATUS_OPER");
+}
+
+/* if you are a channel operator in $C, expands to a '@' */
+static char *expando_chanop(SERVER_REC *server, void *item, int *free_ret)
+{
+ return IS_CHANNEL(item) && CHANNEL(item)->chanop ? "@" : "";
+}
+
+/* nickname of whomever you are QUERYing */
+static char *expando_query(SERVER_REC *server, void *item, int *free_ret)
+{
+ return !IS_QUERY(item) ? "" : QUERY(item)->name;
+}
+
+/* version of current server */
+static char *expando_serverversion(SERVER_REC *server, void *item, int *free_ret)
+{
+ return server == NULL ? "" : server->version;
+}
+
+/* target of current input (channel or QUERY nickname) */
+static char *expando_target(SERVER_REC *server, void *item, int *free_ret)
+{
+ return ((WI_ITEM_REC *) item)->name;
+}
+
/* client release date (numeric version string) */
-static char *expando_releasedate(void *server, void *item, int *free_ret)
+static char *expando_releasedate(SERVER_REC *server, void *item, int *free_ret)
{
return IRSSI_VERSION_DATE;
}
/* current working directory */
-static char *expando_workdir(void *server, void *item, int *free_ret)
+static char *expando_workdir(SERVER_REC *server, void *item, int *free_ret)
{
*free_ret = TRUE;
return g_get_current_dir();
}
+/* value of REALNAME */
+static char *expando_realname(SERVER_REC *server, void *item, int *free_ret)
+{
+ return server == NULL ? "" : server->connrec->realname;
+}
+
/* time of day (hh:mm) */
-static char *expando_time(void *server, void *item, int *free_ret)
+static char *expando_time(SERVER_REC *server, void *item, int *free_ret)
{
time_t now = time(NULL);
struct tm *tm;
@@ -600,13 +665,13 @@ static char *expando_time(void *server, void *item, int *free_ret)
}
/* a literal '$' */
-static char *expando_dollar(void *server, void *item, int *free_ret)
+static char *expando_dollar(SERVER_REC *server, void *item, int *free_ret)
{
return "$";
}
/* system name */
-static char *expando_sysname(void *server, void *item, int *free_ret)
+static char *expando_sysname(SERVER_REC *server, void *item, int *free_ret)
{
struct utsname un;
@@ -619,7 +684,7 @@ static char *expando_sysname(void *server, void *item, int *free_ret)
}
/* system release */
-static char *expando_sysrelease(void *server, void *item, int *free_ret)
+static char *expando_sysrelease(SERVER_REC *server, void *item, int *free_ret)
{
struct utsname un;
@@ -631,8 +696,22 @@ static char *expando_sysrelease(void *server, void *item, int *free_ret)
}
+/* Server tag */
+static char *expando_servertag(SERVER_REC *server, void *item, int *free_ret)
+{
+ return server == NULL ? "" : server->tag;
+}
+
+/* Server chatnet */
+static char *expando_chatnet(SERVER_REC *server, void *item, int *free_ret)
+{
+ return server == NULL ? "" : server->connrec->chatnet;
+}
+
void special_vars_init(void)
{
+ settings_add_str("misc", "STATUS_OPER", "*");
+
client_start_time = time(NULL);
memset(char_expandos, 0, sizeof(char_expandos));
@@ -640,22 +719,36 @@ void special_vars_init(void)
(GCompareFunc) g_str_equal);
history_func = NULL;
+ char_expandos['A'] = expando_awaymsg;
+ char_expandos['C'] = expando_channel;
char_expandos['F'] = expando_clientstarted;
char_expandos['J'] = expando_version;
char_expandos['K'] = expando_cmdchars;
+ char_expandos['M'] = expando_chanmode;
+ char_expandos['N'] = expando_nick;
+ char_expandos['O'] = expando_statusoper;
+ char_expandos['P'] = expando_chanop;
+ char_expandos['Q'] = expando_query;
+ char_expandos['R'] = expando_serverversion;
+ char_expandos['T'] = expando_target;
char_expandos['V'] = expando_releasedate;
char_expandos['W'] = expando_workdir;
+ char_expandos['Y'] = expando_realname;
char_expandos['Z'] = expando_time;
char_expandos['$'] = expando_dollar;
expando_create("sysname", expando_sysname);
expando_create("sysrelease", expando_sysrelease);
+ expando_create("tag", expando_servertag);
+ expando_create("chatnet", expando_chatnet);
}
void special_vars_deinit(void)
{
expando_destroy("sysname", expando_sysname);
expando_destroy("sysrelease", expando_sysrelease);
+ expando_destroy("tag", expando_servertag);
+ expando_destroy("chatnet", expando_chatnet);
g_hash_table_destroy(expandos);
}
diff --git a/src/core/special-vars.h b/src/core/special-vars.h
index c40a2fcb..729ac991 100644
--- a/src/core/special-vars.h
+++ b/src/core/special-vars.h
@@ -1,18 +1,25 @@
#ifndef __SPECIAL_VARS_H
#define __SPECIAL_VARS_H
-typedef char* (*EXPANDO_FUNC) (void *server, void *item, int *free_ret);
-typedef char* (*SPECIAL_HISTORY_FUNC) (const char *text, void *item, int *free_ret);
+#include "servers.h"
+
+typedef char* (*EXPANDO_FUNC)
+ (SERVER_REC *server, void *item, int *free_ret);
+typedef char* (*SPECIAL_HISTORY_FUNC)
+ (const char *text, void *item, int *free_ret);
/* Parse and expand text after '$' character. return value has to be
g_free()'d if `free_ret' is TRUE. */
-char *parse_special(char **cmd, void *server, void *item, char **arglist, int *free_ret, int *arg_used);
+char *parse_special(char **cmd, SERVER_REC *server, void *item,
+ char **arglist, int *free_ret, int *arg_used);
/* parse the whole string. $ and \ chars are replaced */
-char *parse_special_string(const char *cmd, void *server, void *item, const char *data, int *arg_used);
+char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
+ const char *data, int *arg_used);
/* execute the commands in string - commands can be split with ';' */
-void eval_special_string(const char *cmd, const char *data, void *server, void *item);
+void eval_special_string(const char *cmd, const char *data,
+ SERVER_REC *server, void *item);
/* Create expando - overrides any existing ones. */
void expando_create(const char *key, EXPANDO_FUNC func);
diff --git a/src/core/window-item-def.h b/src/core/window-item-def.h
new file mode 100644
index 00000000..5b54414a
--- /dev/null
+++ b/src/core/window-item-def.h
@@ -0,0 +1,9 @@
+#ifndef __WINDOW_ITEM_DEF_H
+#define __WINDOW_ITEM_DEF_H
+
+#define STRUCT_SERVER_REC SERVER_REC
+typedef struct {
+#include "window-item-rec.h"
+} WI_ITEM_REC;
+
+#endif
diff --git a/src/core/window-item-rec.h b/src/core/window-item-rec.h
new file mode 100644
index 00000000..9cbfbabe
--- /dev/null
+++ b/src/core/window-item-rec.h
@@ -0,0 +1,14 @@
+/* WI_ITEM_REC definition, used for inheritance */
+
+int type; /* window item type - channel/query/.. */
+int chat_type; /* chat server type - irc/silc/.. */
+GHashTable *module_data;
+
+STRUCT_SERVER_REC *server;
+char *name;
+
+time_t createtime;
+int new_data;
+int last_color; /* if NEWDATA_HILIGHT is set, color number could be specified here */
+
+#undef STRUCT_SERVER_REC