diff options
author | Timo Sirainen <cras@irssi.org> | 2000-08-26 15:39:44 +0000 |
---|---|---|
committer | cras <cras@dbcabf3a-b0e7-0310-adc4-f8d773084564> | 2000-08-26 15:39:44 +0000 |
commit | e395e87dedd9aa85f05e5c74330a76f1ef700371 (patch) | |
tree | 1184487b13038499f1771e4c553222f85b8524d2 /src/core | |
parent | 3d124da13b8da5c0b535abfe6265fc471d9d2ebd (diff) | |
download | irssi-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')
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 |